#!/usr/bin/perl
#
#  IOCA - Isilon On-Cluster Analysis
#    Current version: 0.1664
#
# Copyright (c) 2016-25 Dell Inc. or its subsidiaries. All Rights Reserved.
#
package IOCA;
use Cwd;
use Term::ANSIColor;
use Time::Local;
import Logger qw( :ALL );
import Helper qw( :ALL );
import Parsers qw( :ALL );
import Converters qw( :ALL );

##############################################################
# Global variables
# Container for script and upgrade specific information
our %RPS = ();
@{$RPS{'Upgrade Plan'}} = ();
$RPS{'Release Timestamp'} = 1787320141; # date +%s
$RPS{'Version'} = '0.1664';
$RPS{'Issues'} = 0;
$RPS{'DSP'} = '1.61';
$RPS{'IsiFw'} = '14.1';
$RPS{'IsiFw10'} = '10.4.0';
$RPS{'IsiFw11'} = '11.5';
$RPS{'IsiFw13'} = '14.1';
$RPS{'IsiFw9'} = '9.3.5';
$RPS{'CheckList'} = 'healthcheck';

# Container for a description of each check
our %CHECKS = ();

# Container for per node information
our %NODE_INFO = ();

# Container for cluster wide information
our %CLUSTER_INFO = ();

# Sorted list of lnns
our @LNNS = ();

# Conversion table used to lookup lnn from a devid
our %LNN_FROM_DEVID = ();

# SUDO variable is used for compliance mode clusters
our $SUDO = '';

our %COMPLETE_RESULT = ();

# Set list print behavior
$" = $, = ', ';

sub main {
    # Initialize the script
    initIOCA();

    # Print JSON
    if ($RPS{'JSON'}) {
	for (@{$CheckLists{$RPS{'CheckList'}}}) {
	    if ($CHECKS{$_}{'Exclude'}) { next }
	    &{$_}();
	}
	if ($RPS{'Issues'} == 0) {
	    $COMPLETE_RESULT{'checks'}{'result'} = "UPGRADE MAY PROCEED";
	} else {
	    $COMPLETE_RESULT{'checks'}{'result'} = "UPGRADE ISSUE DETECTED";
	}
    upgradePlan() if $RPS{'UpgradePlan'};
    push (@COMPLETED_RESULTS, \%COMPLETE_RESULT);
	print toJSON(\@COMPLETED_RESULTS)."\n" if $RPS{'JSON'};
	return 0;
    }

    # Run all checks in list
    &{$_}() for @{$CheckLists{$RPS{'CheckList'}}};
    #upgradePlan() if $RPS{'UpgradePlan'};

    # If Issues is still 0, then no issues detected
    if ($RPS{'CheckList'} eq 'pre-upgrade') {
	if ($RPS{'Issues'} == 0) {
	    print "\nUPGRADE MAY PROCEED\n\n";
	} else {
	    print "\nUPGRADE ISSUE DETECTED\n\n";
	}
    }

    # Display a summary of checks with warnings/failures.
#    printSummary();

    # Develop the upgrade plan
    upgradePlan() if $RPS{'UpgradePlan'};

    return 0;
}
BEGIN {
# Activity names
our %Activities = (
    'FCO F042415EE' => {
	5   => 'FCO F042415EE Drive Firmware Upgrade 1-5 nodes',
	10  => 'FCO F042415EE Drive Firmware Upgrade 6-10 nodes',
	15  => 'FCO F042415EE Drive Firmware Upgrade 11-15 nodes',
	20  => 'FCO F042415EE Drive Firmware Upgrade 16-20 nodes',
	25  => 'FCO F042415EE Drive Firmware Upgrade 21-25 nodes',
    },
    'Isilon Hourly' => {
	1   => 'Isilon 1 Hour',
	1.5 => 'Isilon 1.5 Hours',
	2   => 'Isilon 2 Hours',
	2.5 => 'Isilon 2.5 Hours',
	3   => 'Isilon 3 Hours',
	3.5 => 'Isilon 3.5 Hours',
	4   => 'Isilon 4 Hours',
	4.5 => 'Isilon 4.5 Hours',
	5   => 'Isilon 5 Hours',
	5.5 => 'Isilon 5.5 Hours',
	6   => 'Isilon 6 Hours',
	6.5 => 'Isilon 6.5 Hours',
	7   => 'Isilon 7 Hours',
	7.5 => 'Isilon 7.5 Hours',
	8   => 'Isilon 8 Hours',
	8.5 => 'Isilon 8.5 Hours',
	9   => 'Isilon 9 Hours',
	9.5 => 'Isilon 9.5 Hours',
	10  => 'Isilon 10 Hours',
	10.5=> 'Isilon 10.5 Hours',
	11  => 'Isilon 11 Hours',
	11.5=> 'Isilon 11.5 Hours',
	12  => 'Isilon 12 Hours',
    },
   );
###
# Bundles: With exception of the OneFS installation file, a list of the files
#  included in each installation bundle
our %Bundles = (
   'Isilon OneFS 9.14.0.1 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.5',
    	'Isilon Drive Support Package 1.60.1',
   ],
   'Isilon OneFS 9.13.1.1 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.5',
    	'Isilon Drive Support Package 1.60.1',
   ],
   'Isilon OneFS 9.13.1.0 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.2',
    	'Isilon Drive Support Package 1.59',
   ],
   'Isilon OneFS 9.13.0.2 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.2',
    	'Isilon Drive Support Package 1.59',
   ],
   'Isilon OneFS 9.13.0.1 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.1',
    	'Isilon Drive Support Package 1.59',
   ],
   'Isilon OneFS 9.12.0.1 installation bundle' => [
    	'Isilon Node Firmware Package 13.1',
    	'Isilon Drive Support Package 1.57',
   ],
   'Isilon OneFS 9.11.0.6 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.1',
    	'Isilon Drive Support Package 1.59',
   ],
   'Isilon OneFS 9.11.0.5 installation bundle' => [
    	'Isilon Node Firmware Package 13.1',
    	'Isilon Drive Support Package 1.56',
   ],
   'Isilon OneFS 9.11.0.3 installation bundle' => [
    	'Isilon Node Firmware Package 13.1',
    	'Isilon Drive Support Package 1.56',
   ],
   'Isilon OneFS 9.10.1.8 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.5',
    	'Isilon Drive Support Package 1.60.1',
   ],
   'Isilon OneFS 9.10.1.7 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.2',
    	'Isilon Drive Support Package 1.59',
   ],
   'Isilon OneFS 9.10.1.6 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.1',
    	'Isilon Drive Support Package 1.59',
   ],
   'Isilon OneFS 9.10.1.5 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.1',
    	'Isilon Drive Support Package 1.59',
   ],
   'Isilon OneFS 9.10.1.4 installation bundle' => [
    	'Isilon Node Firmware Package 13.1.3',
    	'Isilon Drive Support Package 1.57',
   ],
   'Isilon OneFS 9.10.1.3 installation bundle' => [
    	'Isilon Node Firmware Package 13.1',
    	'Isilon Drive Support Package 1.53',
   ],
   'Isilon OneFS 9.10.1.2 installation bundle' => [
    	'Isilon Node Firmware Package 12.4.2',
    	'Isilon Drive Support Package 1.53',
   ],
   'Isilon OneFS 9.10.1.1 installation bundle' => [
    	'Isilon Node Firmware Package 12.4.2',
    	'Isilon Drive Support Package 1.53',
   ],
   'Isilon OneFS 9.10.1.0 installation bundle' => [
    	'Isilon Node Firmware Package 12.4.2',
    	'Isilon Drive Support Package 1.52',
   ],
   'Isilon OneFS 9.10.0.1 installation bundle' => [
    	'Isilon Node Firmware Package 12.3',
    	'Isilon Drive Support Package 1.51',
   ],
   'Isilon OneFS 9.9.0.2 installation bundle' => [
    	'Isilon Node Firmware Package 12.4.2',
    	'Isilon Drive Support Package 1.53',
       ],
   'Isilon OneFS 9.9.0.1 installation bundle' => [
    	'Isilon Node Firmware Package 12.2.1',
    	'Isilon Drive Support Package 1.51',
       ],
   'Isilon OneFS 9.8.0.3 installation bundle' => [
    	'Isilon Node Firmware Package 12.4.2',
    	'Isilon Drive Support Package 1.53',
       ],
   'Isilon OneFS 9.8.0.2 installation bundle' => [
    	'Isilon Node Firmware Package 12.2.1',
    	'Isilon Drive Support Package 1.50.1',
       ],
   'Isilon OneFS 9.8.0.1 installation bundle' => [
    	'Isilon Node Firmware Package 12.1',
    	'Isilon Drive Support Package 1.48',
       ],
   'Isilon OneFS 9.7.1.16 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.5',
    	'Isilon Drive Support Package 1.60.1',
       ],
   'Isilon OneFS 9.7.1.15 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.5',
    	'Isilon Drive Support Package 1.60.1',
       ],
   'Isilon OneFS 9.7.1.14 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.1',
    	'Isilon Drive Support Package 1.59',
       ],
   'Isilon OneFS 9.7.1.13 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.1',
    	'Isilon Drive Support Package 1.59',
       ],
   'Isilon OneFS 9.7.1.12 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.1',
    	'Isilon Drive Support Package 1.59',
       ],
   'Isilon OneFS 9.7.1.11 installation bundle' => [
    	'Isilon Node Firmware Package 13.1.3',
    	'Isilon Drive Support Package 1.58',
       ],
   'Isilon OneFS 9.7.1.10 installation bundle' => [
    	'Isilon Node Firmware Package 13.1',
    	'Isilon Drive Support Package 1.53',
       ],
   'Isilon OneFS 9.7.1.9 installation bundle' => [
    	'Isilon Node Firmware Package 13.1',
    	'Isilon Drive Support Package 1.53',
       ],
   'Isilon OneFS 9.7.1.8 installation bundle' => [
    	'Isilon Node Firmware Package 13.1',
    	'Isilon Drive Support Package 1.53',
       ],
   'Isilon OneFS 9.7.1.7 installation bundle' => [
    	'Isilon Node Firmware Package 12.4.2',
    	'Isilon Drive Support Package 1.53',
       ],
   'Isilon OneFS 9.7.1.6 installation bundle' => [
    	'Isilon Node Firmware Package 12.4.2',
    	'Isilon Drive Support Package 1.52',
       ], 
   'Isilon OneFS 9.7.1.5 installation bundle' => [
    	'Isilon Node Firmware Package 12.3',
    	'Isilon Drive Support Package 1.52',
       ],    
   'Isilon OneFS 9.7.1.4 installation bundle' => [
    	'Isilon Node Firmware Package 12.3',
    	'Isilon Drive Support Package 1.51',
       ], 
   'Isilon OneFS 9.7.1.3 installation bundle' => [
    	'Isilon Node Firmware Package 12.3',
    	'Isilon Drive Support Package 1.51',
       ], 
   'Isilon OneFS 9.7.1.2 installation bundle' => [
    	'Isilon Node Firmware Package 12.2.1',
    	'Isilon Drive Support Package 1.50.1',
       ],    
   'Isilon OneFS 9.7.1.0 installation bundle' => [
    	'Isilon Node Firmware Package 12.1',
    	'Isilon Drive Support Package 1.48',
       ],
   'Isilon OneFS 9.7.0.3 installation bundle' => [
    	'Isilon Node Firmware Package 12.1',
    	'Isilon Drive Support Package 1.47',
       ],
   'Isilon OneFS 9.7.0.2 installation bundle' => [
    	'Isilon Node Firmware Package 12.0',
    	'Isilon Drive Support Package 1.47',
       ],
   'Isilon OneFS 9.7.0.1 installation bundle' => [
    	'Isilon Node Firmware Package 12.0',
    	'Isilon Drive Support Package 1.47',
       ],
   'Isilon OneFS 9.5.1.7 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.2',
    	'Isilon Drive Support Package 1.59',
       ], 
   'Isilon OneFS 9.5.1.6 installation bundle' => [
    	'Isilon Node Firmware Package 13.2.1',
    	'Isilon Drive Support Package 1.59',
       ], 
   'Isilon OneFS 9.5.1.5 installation bundle' => [
    	'Isilon Node Firmware Package 13.1',
    	'Isilon Drive Support Package 1.56',
       ],  
   'Isilon OneFS 9.5.1.4 installation bundle' => [
    	'Isilon Node Firmware Package 13.1',
    	'Isilon Drive Support Package 1.53',
       ],  
   'Isilon OneFS 9.5.1.3 installation bundle' => [
    	'Isilon Node Firmware Package 12.4.2',
    	'Isilon Drive Support Package 1.53',
       ],    
   'Isilon OneFS 9.5.1.2 installation bundle' => [
    	'Isilon Node Firmware Package 12.3',
    	'Isilon Drive Support Package 1.51',
       ],    
   'Isilon OneFS 9.5.1.1 installation bundle' => [
    	'Isilon Node Firmware Package 12.2.1',
    	'Isilon Drive Support Package 1.50.1',
       ],    
   'Isilon OneFS 9.5.1.0 installation bundle' => [
    	'Isilon Node Firmware Package 12.1',
    	'Isilon Drive Support Package 1.48',
       ],    
   'Isilon OneFS 9.5.0.8 installation bundle' => [
    	'Isilon Node Firmware Package 12.1',
    	'Isilon Drive Support Package 1.47',
       ],
   'Isilon OneFS 9.5.0.7 installation bundle' => [
    	'Isilon Node Firmware Package 12.0',
    	'Isilon Drive Support Package 1.47',
       ],
   'Isilon OneFS 9.5.0.6 installation bundle' => [
    	'Isilon Node Firmware Package 11.7.3',
    	'Isilon Drive Support Package 1.45.2',
       ],
   'Isilon OneFS 9.5.0.5 installation bundle' => [
    	'Isilon Node Firmware Package 11.7.1',
    	'Isilon Drive Support Package 1.45',
       ],
   'Isilon OneFS 9.5.0.3 installation bundle' => [
    	'Isilon Node Firmware Package 11.6.1',
    	'Isilon Drive Support Package 1.45',
       ],
   'Isilon OneFS 9.5.0.2 installation bundle' => [
    	'Isilon Node Firmware Package 11.6.1',
    	'Isilon Drive Support Package 1.44',
       ],
   'Isilon OneFS 9.5.0.1 installation bundle' => [
    	'Isilon Node Firmware Package 11.6.1',
    	'Isilon Drive Support Package 1.43.2',
       ],
   'Isilon OneFS 9.4.0.21 installation bundle' => [
    	'Isilon Node Firmware Package 12.3',
    	'Isilon Drive Support Package 1.52',
       ],
   'Isilon OneFS 9.4.0.19 installation bundle' => [
    	'Isilon Node Firmware Package 12.2.1',
    	'Isilon Drive Support Package 1.50.1',
       ],
   'Isilon OneFS 9.4.0.18 installation bundle' => [
    	'Isilon Node Firmware Package 12.1',
    	'Isilon Drive Support Package 1.48',
       ],
   'Isilon OneFS 9.4.0.17 installation bundle' => [
    	'Isilon Node Firmware Package 12.0',
    	'Isilon Drive Support Package 1.47',
       ],
   'Isilon OneFS 9.4.0.16 installation bundle' => [
    	'Isilon Node Firmware Package 11.7.3',
    	'Isilon Drive Support Package 1.45.2',
       ],
   'Isilon OneFS 9.4.0.15 installation bundle' => [
    	'Isilon Node Firmware Package 11.7.3',
    	'Isilon Drive Support Package 1.45.2',
       ],
   'Isilon OneFS 9.4.0.14 installation bundle' => [
    	'Isilon Node Firmware Package 11.6.1',
    	'Isilon Drive Support Package 1.45',
       ],
   'Isilon OneFS 9.4.0.13 installation bundle' => [
    	'Isilon Node Firmware Package 11.6.1',
    	'Isilon Drive Support Package 1.43.2',
       ],
   'Isilon OneFS 9.4.0.12 installation bundle' => [
    	'Isilon Node Firmware Package 11.5.1',
    	'Isilon Drive Support Package 1.43.2',
       ],
    'Isilon OneFS 9.4.0.11 installation bundle' => [
    	'Isilon Node Firmware Package 11.5',
    	'Isilon Drive Support Package 1.42.5',
       ],
    'Isilon OneFS 9.4.0.10 installation bundle' => [
    	'Isilon Node Firmware Package 11.5',
    	'Isilon Drive Support Package 1.42.5',
       ],
    'Isilon OneFS 9.4.0.9 installation bundle' => [
    	'Isilon Node Firmware Package 11.5',
    	'Isilon Drive Support Package 1.41.1',
       ],
    'Isilon OneFS 9.4.0.7 installation bundle' => [
    	'Isilon Node Firmware Package 11.5',
    	'Isilon Drive Support Package 1.41.1',
       ],
    'Isilon OneFS 9.4.0.6 installation bundle' => [
    	'Isilon Node Firmware Package 11.5',
    	'Isilon Drive Support Package 1.41.1',
       ],
    'Isilon OneFS 9.4.0.5 installation bundle' => [
    	'Isilon Node Firmware Package 11.4',
    	'Isilon Drive Support Package 1.41',
       ],
    'Isilon OneFS 9.4.0.4 installation bundle' => [
    	'Isilon Node Firmware Package 11.4',
    	'Isilon Drive Support Package 1.41',
       ],
    'Isilon OneFS 9.4.0.3 installation bundle' => [
    	'Isilon Node Firmware Package 11.4',
    	'Isilon Drive Support Package 1.40.1',
       ],
    'Isilon OneFS 9.3.0.9 installation bundle' => [
    	'Isilon Node Firmware Package 11.5',
    	'Isilon Drive Support Package 1.41.1',
       ],
    'Isilon OneFS 9.3.0.7 installation bundle' => [
    	'Isilon Node Firmware Package 11.4',
    	'Isilon Drive Support Package 1.41',
       ],
    'Isilon OneFS 9.3.0.6 installation bundle' => [
    	'Isilon Node Firmware Package 11.4',
    	'Isilon Drive Support Package 1.40.1',
       ],
    'Isilon OneFS 9.3.0.5 installation bundle' => [
    	'Isilon Node Firmware Package 11.2',
    	'Isilon Drive Support Package 1.39.1',
       ],
    'Isilon OneFS 9.3.0.4 installation bundle' => [
    	'Isilon Node Firmware Package 11.2',
    	'Isilon Drive Support Package 1.39.1',
       ],
    'Isilon OneFS 9.3.0.3 installation bundle' => [
    	'Isilon Node Firmware Package 11.2',
    	'Isilon Drive Support Package 1.39.1',
       ],
    'Isilon OneFS 9.3.0.2 installation bundle' => [
    	'Isilon Node Firmware Package 11.3',
    	'Isilon Drive Support Package 1.38.1',
       ],
    'Isilon OneFS 9.3.0.1 installation bundle' => [
    	'Isilon Node Firmware Package 11.3',
    	'Isilon Drive Support Package 1.38.1',
       ],
   'Isilon OneFS 9.2.1.25 installation bundle' => [
    	'Isilon Node Firmware Package 11.7.3',
    	'Isilon Drive Support Package 1.46.1',
       ],
   'Isilon OneFS 9.2.1.24 installation bundle' => [
    	'Isilon Node Firmware Package 11.7.3',
    	'Isilon Drive Support Package 1.45.2',
       ],
   'Isilon OneFS 9.2.1.23 installation bundle' => [
    	'Isilon Node Firmware Package 11.7',
    	'Isilon Drive Support Package 1.45.2',
       ],
   'Isilon OneFS 9.2.1.22 installation bundle' => [
    	'Isilon Node Firmware Package 11.6.1',
    	'Isilon Drive Support Package 1.43.2',
       ],
    'Isilon OneFS 9.2.1.21 installation bundle' => [
    	'Isilon Node Firmware Package 11.5.1',
    	'Isilon Drive Support Package 1.43.2',
       ],
    'Isilon OneFS 9.2.1.20 installation bundle' => [
    	'Isilon Node Firmware Package 11.5',
    	'Isilon Drive Support Package 1.42.5',
       ],
    'Isilon OneFS 9.2.1.19 installation bundle' => [
    	'Isilon Node Firmware Package 11.5',
    	'Isilon Drive Support Package 1.42.5',
       ],
    'Isilon OneFS 9.2.1.18 installation bundle' => [
    	'Isilon Node Firmware Package 11.5',
    	'Isilon Drive Support Package 1.41.1',
       ],
    'Isilon OneFS 9.2.1.17 installation bundle' => [
    	'Isilon Node Firmware Package 11.5',
    	'Isilon Drive Support Package 1.41.1',
       ],
    'Isilon OneFS 9.2.1.16 installation bundle' => [
    	'Isilon Node Firmware Package 11.5',
    	'Isilon Drive Support Package 1.41.1',
       ],
    'Isilon OneFS 9.2.1.15 installation bundle' => [
    	'Isilon Node Firmware Package 11.4',
    	'Isilon Drive Support Package 1.41',
       ],
    'Isilon OneFS 9.2.1.14 installation bundle' => [
    	'Isilon Node Firmware Package 11.4',
    	'Isilon Drive Support Package 1.41',
       ],
    'Isilon OneFS 9.2.1.13 installation bundle' => [
    	'Isilon Node Firmware Package 11.4',
    	'Isilon Drive Support Package 1.40.1',
       ],
    'Isilon OneFS 9.2.1.12 installation bundle' => [
    	'Isilon Node Firmware Package 11.4',
    	'Isilon Drive Support Package 1.40.1',
       ],
    'Isilon OneFS 9.2.1.11 installation bundle' => [
    	'Isilon Node Firmware Package 11.4',
    	'Isilon Drive Support Package 1.40.1',
       ],
    'Isilon OneFS 9.2.1.10 installation bundle' => [
    	'Isilon Node Firmware Package 11.2',
    	'Isilon Drive Support Package 1.39.1',
       ],
    'Isilon OneFS 9.2.1.9 installation bundle' => [
    	'Isilon Node Firmware Package 11.2',
    	'Isilon Drive Support Package 1.39.1',
       ],
    'Isilon OneFS 9.2.1.8 installation bundle' => [
    	'Isilon Node Firmware Package 11.2',
    	'Isilon Drive Support Package 1.39.1',
       ],
    'Isilon OneFS 9.2.1.7 installation bundle' => [
    	'Isilon Node Firmware Package 11.3',
    	'Isilon Drive Support Package 1.39.1',
       ],
    'Isilon OneFS 9.2.1.6 installation bundle' => [
    	'Isilon Node Firmware Package 11.3',
    	'Isilon Drive Support Package 1.38.1',
       ],
    'Isilon OneFS 9.2.1.5 installation bundle' => [
    	'Isilon Node Firmware Package 11.1.4',
    	'Isilon Drive Support Package 1.38.1',
       ],
    'Isilon OneFS 9.2.1.4 installation bundle' => [
    	'Isilon Node Firmware Package 11.1.4',
    	'Isilon Drive Support Package 1.37.2',
       ],
    'Isilon OneFS 9.2.1.3 installation bundle' => [
    	'Isilon Node Firmware Package 11.1.4',
    	'Isilon Drive Support Package 1.37.2',
       ],
    'Isilon OneFS 9.2.1.2 installation bundle' => [
    	'Isilon Node Firmware Package 11.1',
    	'Isilon Drive Support Package 1.37.2',
       ],
    'Isilon OneFS 9.2.1.1 installation bundle' => [
    	'Isilon Node Firmware Package 11.1',
    	'Isilon Drive Support Package 1.37.2',
       ],
    'Isilon OneFS 9.2.0.1 installation bundle' => [
    	'Isilon Node Firmware Package 11.1',
    	'Isilon Drive Support Package 1.37.2',
       ],
   'Isilon OneFS 9.1.0.28 installation bundle' => [
	'Isilon Node Firmware Package 11.5.1',
	'Isilon Drive Support Package 1.43.2',
       ],    
   'Isilon OneFS 9.1.0.27 installation bundle' => [
	'Isilon Node Firmware Package 11.5',
	'Isilon Drive Support Package 1.42.5',
       ],
   'Isilon OneFS 9.1.0.26 installation bundle' => [
	'Isilon Node Firmware Package 11.5',
	'Isilon Drive Support Package 1.42.5',
       ],
   'Isilon OneFS 9.1.0.25 installation bundle' => [
	'Isilon Node Firmware Package 11.5',
	'Isilon Drive Support Package 1.41.1',
       ],
    'Isilon OneFS 9.1.0.24 installation bundle' => [
	'Isilon Node Firmware Package 11.5',
	'Isilon Drive Support Package 1.41.1',
       ],
    'Isilon OneFS 9.1.0.23 installation bundle' => [
	'Isilon Node Firmware Package 11.5',
	'Isilon Drive Support Package 1.41.1',
       ],
    'Isilon OneFS 9.1.0.22 installation bundle' => [
	'Isilon Node Firmware Package 11.4',
	'Isilon Drive Support Package 1.41',
       ],
    'Isilon OneFS 9.1.0.21 installation bundle' => [
	'Isilon Node Firmware Package 11.4',
	'Isilon Drive Support Package 1.41',
       ],
    'Isilon OneFS 9.1.0.19 installation bundle' => [
	'Isilon Node Firmware Package 11.4',
	'Isilon Drive Support Package 1.40.1',
       ],
    'Isilon OneFS 9.1.0.18 installation bundle' => [
	'Isilon Node Firmware Package 11.4',
	'Isilon Drive Support Package 1.40.1',
       ],
    'Isilon OneFS 9.1.0.17 installation bundle' => [
	'Isilon Node Firmware Package 11.2',
	'Isilon Drive Support Package 1.39.1',
       ],
    'Isilon OneFS 9.1.0.16 installation bundle' => [
	'Isilon Node Firmware Package 11.2',
	'Isilon Drive Support Package 1.39.1',
       ],
    'Isilon OneFS 9.1.0.15 installation bundle' => [
	'Isilon Node Firmware Package 11.2',
	'Isilon Drive Support Package 1.39.1',
       ],
    'Isilon OneFS 9.1.0.14 installation bundle' => [
	'Isilon Node Firmware Package 11.3',
	'Isilon Drive Support Package 1.39.1',
       ],
    'Isilon OneFS 9.1.0.13 installation bundle' => [
	'Isilon Node Firmware Package 11.3',
	'Isilon Drive Support Package 1.38.1',
       ],
    'Isilon OneFS 9.1.0.12 installation bundle' => [
	'Isilon Node Firmware Package 11.1.4',
	'Isilon Drive Support Package 1.38.1',
       ],
    'Isilon OneFS 9.1.0.11 installation bundle' => [
	'Isilon Node Firmware Package 11.1.4',
	'Isilon Drive Support Package 1.37.2',
       ],
    'Isilon OneFS 9.1.0.10 installation bundle' => [
	'Isilon Node Firmware Package 11.1.4',
	'Isilon Drive Support Package 1.37.2',
       ],
    'Isilon OneFS 9.1.0.9 installation bundle' => [
	'Isilon Node Firmware Package 11.1',
	'Isilon Drive Support Package 1.37.2',
       ],
    'Isilon OneFS 9.1.0.8 installation bundle' => [
	'Isilon Node Firmware Package 11.1',
	'Isilon Drive Support Package 1.37.2',
       ],
    'Isilon OneFS 9.1.0.7 installation bundle' => [
	'Isilon Node Firmware Package 11.1',
	'Isilon Drive Support Package 1.37.2',
       ],
    'Isilon OneFS 9.1.0.6 installation bundle' => [
	'Isilon Node Firmware Package 11.1',
	'Isilon Drive Support Package 1.35',
       ],
    'Isilon OneFS 9.1.0.5 installation bundle' => [
	'Isilon Node Firmware Package 11.1',
	'Isilon Drive Support Package 1.35',
       ],
    'Isilon OneFS 9.1.0.4 installation bundle' => [
	'Isilon Node Firmware Package 11.1',
	'Isilon Drive Support Package 1.35',
       ],
    'Isilon OneFS 9.1.0.3 installation bundle' => [
	'Isilon Node Firmware Package 11.1',
	'Isilon Drive Support Package 1.34',
       ],
    'Isilon OneFS 9.1.0.2 installation bundle' => [
	'Isilon Node Firmware Package 11.1',
	'Isilon Drive Support Package 1.34',
       ],
    'Isilon OneFS 9.1.0.1 installation bundle' => [
	'Isilon Node Firmware Package 11.0',
	'Isilon Drive Support Package 1.34',
       ],
    'Isilon OneFS 9.0.0.0 installation bundle' => [
	'Isilon Node Firmware Package 11.0',
	'Isilon Drive Support Package 1.34',
       ],
    'Isilon OneFS 8.2.2.0 installation bundle' => [
    	'Isilon Node Firmware Package 10.3.7',
    	'Isilon Drive Support Package 1.40.1',
       ],
    'Isilon OneFS 8.2.1.0 installation bundle' => [
	'Isilon Node Firmware Package 10.3.5',
	'Isilon Drive Support Package 1.35',
       ],
    'Isilon OneFS 8.2.0.0 installation bundle' => [
	'Isilon Node Firmware Package 10.3.3',
	'Isilon Drive Support Package 1.33',
       ],
    'Isilon OneFS 8.1.2.0 installation bundle' => [
    	'Isilon Node Firmware Package 10.3.5',
    	'Isilon Drive Support Package 1.37.2',
       ],
   );
###
# %CheckLists: A list of checks that will be run for each checklist
our %CheckLists = (
	'current_versions' => [
		'checkOneFSVersions',
		'checkSupportability',
		'checkPatches',
		'checkDriveFirmware',
		'checkNodeFirmware',
	],
    'pre-upgrade' => [
	'checkOneFSVersions',
	'checkInsightIQVersion',
	'checkKB544854',
	'checkNodeCompatibility',
	'checkSupportability',
	'checkPatches',
	'checkSymLink',
	'checkUpgradePath',
	'checkKB000213188',
	'checkCapacity',
	#'checkIndexSnapshotCurrent',
	#'checkInternalPing',
	'checkHealth',
	'checkEvents',
	'checkJobStatus',
	'checkTardis',
	'checkKB000066019',
	'checkPartitions',
	'checkServices',
	'checkLocalUsers',
	'checkServicesMonitoring',
	'checkProcesses',
	'checkIBInterfaces',
	'checkKB000197850',
	'checkUptime',
	'checkCrontab',
	'checkCMOSTimeCentury',
	'checkUpgrade',
	'checkSecureBoot',
	'checkHardening',
	'checkRealACL',
	'checkUpgradeAgentPort',
	'checkNetworkParallelUpgrade',
	'checkKB000212387',
	'checkProtectionLevel',
	#'checkSystemFlag',
	'checkNetworkPoolIFaces',
	'checkHardwareStatus',
	'checkPSIReceipt',
	'checkKB519119',
	'checkBBUDegCap',
	'checkBMCandCMC',
	'checkBootDisks',
	'checkKB000290094',
	'checkVaultCard',
	'checkMirrors',
	'checkA100Root',
	'checkMemory',
	'checkKB000421406',
	'checkNamespace',
	'checkDrivesHealth',
	'checkKB000314255',
	'checkSED',
	'checkDriveFirmware',
	#'checkOverlappingPackage',
	'checkNodeFirmware',
	'checkKB000215473',
	'checkKB000464795',
	'checkFirmwarePackages',
	'checkSmartConnect',
	'checkIBPCIeSlot',
	'checkVNODE',
	'checkFlush',
	'checkKB000354078',
	'checkMaintenanceMode',
	'checkGatewayPriority',
	'checkStaticRouteConflict',
	'checkGroups',
	'checkNetBIOS',
	'checkKB000253297',
	'checkSNMPDConfig',
	'checkSyncIQ',
	'checkNDMP',
	'checkNDMPUpgradeTimeout',
	'checkNDMP16GB',
	'checkIsiVolCopy',
	'checkHDFS',
	'checkAggregationMode',
	'checkKB000218103',
	'checkLACPSFP',
	'checkKB000391477',
	'checkDiskpools',
	'checkZoneProviders',
	'checkAuthStatus',
	'checkKB000299293',
	'checkZoneLocalAuth',
	'checkNFS',
	#'checkKB000460984',
	'checkNFSUpgComplete',
	'checkSMB',
	'checkNTLM',
	'checkSWIFTAccounts',
	'checkLicense',
	'checkSnapshot',
	'checkHardwareUpgrade',
	'checkISCSI',
	'checkAccessZones',
	'checkAspera',
	'checkContact',
	'checkEncoding',
	'checkCloudPools',
	'checkKB000260843',
	'checkFilepoolPolicies',
	'checkLegacyLDAP',
	'checkTimeZone',
	'checkLinMasterPadding',
	'checkSwitchCompatibility',
	'checkLeakFreeBlocks',
	'checkUIDGID',
	'checkConfCmtSyntax',
	'checkKB000192800',
	'checkKB000196175',
	'checkKB000196762',
	'checkPSCALE136276',
	'checkKB201933',
	'checkKB544401',
	'checkKB201666',
	'checkKB540872',
	'checkKB527312',
	'checkKB507031',
	'checkKB521890',
	'checkKB524082',
	'checkKB516613',
	'checkKB530050',
	'checkKB496993',
	'checkKB519423',
	'checkKB537785',
	'checkKB540000',
	'checkKB540071',
	'checkKB540513',
	'checkKB462202',
	'checkKB540901',
	'checkKB535582',
	'checkKB490849',
	'checkKB201488',
	'checkKB203381',
	'checkKB000081658',
	'checkDTA000194434',
	'checkDTA000221299',
	'checkSSHDConfig',
	'checkKerberos8000',
	'checkLastZoneID8000',
	'checkSBR8000',
	'checkNFS8004',
	'checkSRS',
	'checkSupportAssist',
	#'checkRemoteSupport',
	'checkBESwitch',
	'checkSFTP',
	'checkVSFTP',
	'checkKB220014',
	'checkKB496582',
	'checkKB519388',
	'checkKB519890',
	'checkKB173892',
	'checkETAs',
	'checkDTA219904',
	'checkScreenDTA219904',
	'checkBXENodes',
	'checkNodesInstalled',
       ],
    'firmware' => [
	'checkDriveFirmware',
	'checkNodeFirmware',
	'checkFirmwarePackages',
       ],
    'healthcheck' => [
	'checkContact',
	'checkEmailSettings',
	'checkDriveFirmware',
	#'checkOverlappingPackage',
	'checkFirmwarePackages',
	'checkSupportability',
	'checkETAs',
	'checkKB000464795',
	'checkDTA219904',
	'checkScreenDTA219904',
	'checkAggregationMode',
	'checkBMCandCMC',
	'checkBootDisks',
	'checkKB000290094',
	'checkVaultCard',
	'checkCMOSTimeCentury',
	'checkBXENodes',
	'checkDrivesHealth',
	'checkDriveLoad',
	'checkDriveStallTimeout',
	'checkKB000314255',
	'checkGatewayPriority',
	'checkStaticRouteConflict',
	'checkProtectionLevel',
	'checkNetworkPoolIFaces',
	'checkHardwareStatus',
	'checkNamespace',
	'checkPSIReceipt',
	'checkKB519119',
	'checkBBUDegCap',
	'checkIBInterfaces',
	'checkKB000197850',
	'checkLACPSFP',
	'checkKB000391477',
	'checkMemory',
	'checkHardwareUpgrade',
	'checkCloudPools',
	'checkKB000260843',
	'checkMirrors',
	'checkA100Root',
	'checkNodeCompatibility',
	'checkNodeFirmware',
	'checkAccessZones',
	'checkDiskpools',
	'checkSystemFlag',
	'checkFilepoolPolicies',
	'checkKerberos8000',
	'checkLastZoneID8000',
	'checkAPIAuth',
	'checkLegacyLDAP',
	'checkNFS8004',
	'checkLogs',
	'checkSFTP',
	'checkLogLevel',
	'checkCoreDumps',
	'checkOneFSVersions',
	'checkInsightIQVersion',
	'checkSBR8000',
	'checkKB544401',
	'checkKB201666',
	'checkKB533516',
	'checkKB540872',
	'checkKB507031',
	'checkKB521890',
	'checkKB530050',
 	'checkKB516613',
	'checkKB496993',
	'checkKB540901',
	'checkKB519423',
	'checkKB537785',
	'checkKB535582',
	'checkKB490849',
	'checkKB000196762',
	'checkMaintenanceMode',
	'checkKB540000',
	'checkKB540071',
	'checkKB462202',
	'checkKB501267',
	'checkKB544854',
	'checkKB201488',
	'checkKB000081658',
	'checkKB000066019',
	'checkSSHDConfig',
	'checkUpgradePath',
	'checkAuthStatus',
	'checkCapacity',
	'checkVNODE',
	'checkIndexSnapshotCurrent',
	'checkEncoding',
	'checkSRS',
	#'checkRemoteSupport',
	'checkSupportAssist',
	'checkBESwitch',
	'checkKB220014',
	'checkEvents',
	'checkFileSharing',
	'checkSwitchCompatibility',
	'checkLeakFreeBlocks',
	'checkHDFS',
	'checkSPNs',
	'checkHealth',
	'checkIDI',
	'checkISCSI',
	'checkJobStatus',
	'checkJobHistory',
	'checkJobs',
	'checkKB000354078',
#	'checkLinMasterPadding',
	'checkKB496582',
	'checkKB519890',
	'checkDTA000221299',
	'checkPSCALE136276',
	'checkLicense',
	'checkGroups',
	'checkLWIODLog',
#	'checkMessagesLog',
#	'checkNetstat',
	'checkListenQueue',
	'checkKB000218103',
	'checkNFS',
	'checkNFSUpgComplete',
	'checkSMB',
#	'checkNTLM',
	'checkOpenFiles',
	'checkSNMPDConfig',
	'checkPartitions',
	'checkPatches',
	'checkSymLink',
	'checkStoragePools',
	'checkProcesses',
#	'checkRoutingTables',
	'checkServices',
	'checkServicesMonitoring',
	'checkSmartConnect',
	'checkIBPCIeSlot',
	'checkConfCmtSyntax',
	'checkSnapshot',
	'checkSyncIQ',
	'checkKB173892',
	'checkTimeDrift',
	'checkTimeSync',
	'checkTimeZone',
	'checkUpgrade',
	'checkRealACL',
	'checkUpgradeAgentPort',
	'checkUptime',
	'checkNodesInstalled',
       ],
#	'file_verification' => [
#		'checkVerifyFiles',
#		],
   );
###
# CodeLinks: File links and sha256 checksum values
our %CodeLinks = (
    # OneFS installation bundles
    'Isilon OneFS 9.14.0.1 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/N7R8G',
    'sha256' => '9609c2768912466dd45b9d8c6f1174a997836b1e54e52293ca7f8fd6c2ef668a',
    },
    'Isilon OneFS 9.13.1.1 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/2FRTX',
    'sha256' => '218555b6a38c9c7a434945d718d1c0466f90207d3638af1d9ba18fd3a474b93d',
    },
    'Isilon OneFS 9.13.1.0 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/01XDT',
    'sha256' => 'ba8aef28f377815ea4f663dba11367c03ab4d54bdcc33befa2d33257be06a1b2',
    },
    'Isilon OneFS 9.13.0.2 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/17CC9',
    'sha256' => 'd1ed8c4275fbed03f0c57b79fbce88c9da96b7298ccd8c2a77c34c3b0a24b85f',
    },
    'Isilon OneFS 9.13.0.1 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '3dd829fc25a04c3a92069e6fac913874dd3958d692ad5072f0aa712d38cb03a3',
    },
    'Isilon OneFS 9.12.0.1 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/0P20P',
    'sha256' => '05c5862a6803446ae84feed1c59ec816c1a2d97bc78e2d088404ad22f7c89f6c',
    },
    'Isilon OneFS 9.11.0.6 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '9f4cc4d4500e57e3027448710f49c90ca3b6ab50a4f91c69b2eab96c14daffea',
    },
    'Isilon OneFS 9.11.0.5 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'a8ca0853b3a1e0074334b05c36398462bdb0b4ee19113b6058234e9dfee3af4f',
    },
    'Isilon OneFS 9.11.0.3 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'ad6e1a53a61f163212645c1eb499a07b277159a03fa2e551f51151fae8f3b210',
    },
    'Isilon OneFS 9.10.1.8 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/T23CP',
    'sha256' => 'ba7528107c5a89e4f82b403711fa4a441363e52326556b3ed490ae7ea0ef3313',
    },
    'Isilon OneFS 9.10.1.7 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '18c156273ffca63f1176a35865655693066964692ac6bba92f61bcbc6325c296',
    },
    'Isilon OneFS 9.10.1.6 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '0076485214c2466960fd45a8dd2c5b4bba434c0e273074aa243fbe74ad03cf1a',
    },
    'Isilon OneFS 9.10.1.5 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '989960ae1d6dec18616c90215e642813a026eb583f99734ed93456c34fb57660',
    },
    'Isilon OneFS 9.10.1.4 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '16603c182f37575a40e28175f0914f71137e3465953348aaf58f3f0425b3363b',
    },
    'Isilon OneFS 9.10.1.3 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '0a50662af95adfb374f45da0f2ff85bf0ef3340c9307321796015c75a89bd0fc',
    },
    'Isilon OneFS 9.10.1.2 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '2bc09df65fa6d931dda55491b8bcc79d9c3b24aaa26945f0814a9c5428b777a6',
    },
    'Isilon OneFS 9.10.1.1 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '85bd2a4471507efcb17ba86fa186f39de1264778aadcc89fe678888b685ca360',
    },
    'Isilon OneFS 9.10.1.0 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'ecc85c0957cd83354e94237c984d6039b32d5b22ce46c305f8deab1fcb5ced50',
    },
    'Isilon OneFS 9.10.0.1 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '29e26f56b42d9036068b09e6143bb0155221aae8a5d9ee31b4c9502bc6bbf301',
    },
    'Isilon OneFS 9.9.0.2 installation bundle' => {
	'Code' => 'https://dl.dell.com/downloads/74GPJ',
	'sha256' => '9e7614257bca89382e93d3d4de980e5233f044e30da5b8f9525a5bfbc2a1018a',
    },
    'Isilon OneFS 9.9.0.1 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '7b4398a051e49bcaa7d5140af19a17087786f429257b926327fe1f7bf6ef05ef',
    },
    'Isilon OneFS 9.8.0.3 installation bundle' => {
	'Code' => 'https://dl.dell.com/downloads/KR7V3',
	'sha256' => '9328c3ff030b9d148e4a18d69aa25516e4a7e2952ba12b2e624f74f34dda2f52',
    },
    'Isilon OneFS 9.8.0.2 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '1dd4a4462a6bee593db020664972888d95ab0e11b9232940eb01b3efcb5bde0a',
    },
    'Isilon OneFS 9.8.0.1 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => 'd1764b4da9c6447d218444475313e8d8d2bc13c31f7d08570d5c0faee1c67d94',
    },
    'Isilon OneFS 9.7.1.16 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/H05PW',
    'sha256' => '1ea710645ae71dffe35a75c9713c417e450997ca7fd72702cde4ead768167e9c',
    },
    'Isilon OneFS 9.7.1.15 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/C62MM',
    'sha256' => '5dbb83926dee1252606ef77c7cde44ae55d9495fb68626e06c1b140ef2db102c',
    },
    'Isilon OneFS 9.7.1.14 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/JMD5H',
    'sha256' => '9f25ca239c0ab25424da1b305a796ea3876b109d6b373eaa712b60dd8d56060b',
    },
    'Isilon OneFS 9.7.1.13 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '4069f4f5cc6a355819bc4b865955dd926e0ac3b205105cbbd60f72412c9ba53a',
    },
    'Isilon OneFS 9.7.1.12 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'af62774a6eef7ce9e5647cdb770f3d4f91ef669e8feb09ae841e4cc3e15898de',
    },
    'Isilon OneFS 9.7.1.11 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '5c436b5b450f40cc1f1e5e5d06444d68f8ea66c3a827f3e3d47160833ddd1630',
    },
    'Isilon OneFS 9.7.1.10 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'c8730b7d85d80b5dfda9a372266aaa5648609946cf3c3bb430c4acbb2a49dbeb',
    },
    'Isilon OneFS 9.7.1.9 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'ec06dbd7d4eacea4a81c2d1091805606c3ed9a3d63bc618cf2373819f117077c',
    },
    'Isilon OneFS 9.7.1.8 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '9d25ad97f5cc00226084a6d07e657c3b2b41a814efe6f1a364d544c05d871c51',
    },
    'Isilon OneFS 9.7.1.7 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'e20a1eafeb19a331455638aa865b7a1069549ec999d52cef99a10f9d290e3d39',
    },
    'Isilon OneFS 9.7.1.6 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '9bec3d42a318e23b42fcff1f4cd3e3644eb969082f46990f510f377e9cd34671',
    },
    'Isilon OneFS 9.7.1.5 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '68872644085605675dfe572dbe3040ccea67209f491841d2f99c27b6c5766094',
    },
    'Isilon OneFS 9.7.1.4 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '479c3de933583149e7d52cd7d1e0806f9ddc8f181a041e0b7bf9c49319d1e49d',
    },
    'Isilon OneFS 9.7.1.3 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'a7d22de19a94b4bb027e541d9e13cda5ed274c10f9281ef1ade926a6c74500e5',
    },
    'Isilon OneFS 9.7.1.2 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'b8ba9fd382d8fc62564d0dc75e3bfbb7783d5da0c3457a52db4bb53b606eeac3',
    },
    'Isilon OneFS 9.7.1.0 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'a7192f7ae71ac182ee222e275bbd8b98cdcb01aba63ca0f3f476a668d3916005',
    },
    'Isilon OneFS 9.7.0.3 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '4d8fa86edc61bbc342a486aab2fa74c2e927f1d790da84a645fb0487e12a71b8',
    },
    'Isilon OneFS 9.7.0.2 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '8062dceda1c1790849bafc157df889524edab1fb02e26831f1e2d0f828669e36',
    },
    'Isilon OneFS 9.7.0.1 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '2f4986f7f18bbd7452a0796b4928e117b6737cdafb42b8ce2cbb1c1ac0c08e17',
    },
    'Isilon OneFS 9.5.1.7 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/KRNT5',
    'sha256' => '4f21beb88bc9fb43f30059a4eb14b992e881732ba82a481050c3bd74c1234013',
    },
    'Isilon OneFS 9.5.1.6 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'a2efd554e3a3568c041e1f7456794399b41d8009d1e6dc40492ee6777193794e',
    },
    'Isilon OneFS 9.5.1.5 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'a23909190398585eeca3f9c95a6ea9d01c17c1069977b1279e03434762f9622e',
    },
    'Isilon OneFS 9.5.1.4 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'b552062cebf7fede102108d9c6875db5120b75e8c951de422ccf05e28a68ea42',
    },
    'Isilon OneFS 9.5.1.3 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '003c2e327710ee296aa5a49156f577187f06d4edda95aafa76ec121474736247',
    },
    'Isilon OneFS 9.5.1.2 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'ab28841de108330eb991ac25b046f4e5b2becd639dbddd41cb3def8d21ae192d',
    },
    'Isilon OneFS 9.5.1.1 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '1e2944f591480865306d1a8ca19944fd4ed93853fd82e93908097aa742955091',
    },
    'Isilon OneFS 9.5.1.0 installation bundle' => {
    'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => '9ff4ecd0b09a6407f5a2776cd912053b2de2ec6224661971b99e74da4b63178d',
    },
    'Isilon OneFS 9.5.0.8 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/44HNN',
    'sha256' => '4f7c2a3d485534df8867b0315d26d9396f926e373b7722ee68c22218ad7ac666',
    },
    'Isilon OneFS 9.5.0.7 installation bundle' => {
    'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => 'fd242c307566381be93f255c1245f222eda0d8991460992cc0fb83ca73c748cb',
    },
    'Isilon OneFS 9.5.0.6 installation bundle' => {
    'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => '72797c7c6602fece479e8406d5b3be439326c11da0cbd0733a18b4f218f84841',
    },
    'Isilon OneFS 9.5.0.5 installation bundle' => {
    'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => '6c96e755550bddfa0b6420944d93938e11ba4376df9517d415d4123e249a55b1',
    },
    'Isilon OneFS 9.5.0.3 installation bundle' => {
    'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => '0efe997b2fec917c78e83c6c19bec96a5f66b907f3cc052236b2b6ddb20ba98c',
    },
    'Isilon OneFS 9.5.0.2 installation bundle' => {
    'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => 'e03a1b558ca461ac82856d8b0da032aff75303af6d9c6bca5e00cd7ee07df918',
    },
    'Isilon OneFS 9.5.0.1 installation bundle' => {
    'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => '084741dd5a12bbbffaf025ea33cf52909c68d15b18136e8fcdd939f41e87ecca',
    },
    'Isilon OneFS 9.4.0.21 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '70e8270db009f4c84b2975b0ea377742ed5de2852f44f6a7b18fe3fff8d8dedf',
    },
    'Isilon OneFS 9.4.0.19 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '63abaa33bf3bae74b76f6cbca91497ea517b678514997466d7757687a6e5d4f4',
    },
    'Isilon OneFS 9.4.0.18 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '39232976b8280ca9fb3a2099cd53dc0afe08548c2047401abe3d8fe02652c483',
    },
    'Isilon OneFS 9.4.0.17 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '0b9a149319d8b80b877e86621ddd7175a602cfa4c4ea37f60e3c76e755b7a8a6',
    },
    'Isilon OneFS 9.4.0.16 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '5fe75defb9e5372347e91e1d19825907610fba85e2f9baa18c26a5c830be801f',
    },
    'Isilon OneFS 9.4.0.15 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '6d3bf299a24c95fa21d32872ba1044c2fa4fbdeb4ba533251c27e7d524491c0b',
    },
    'Isilon OneFS 9.4.0.14 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '4236d51ae237a84b6a40f3009b020c35cce4b4e09a10ce5859c352882d6aa398',
    },
    'Isilon OneFS 9.4.0.13 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '1b0de21a6b25385ddcf7739ac595c2ffba275a03fef33375d6dd56c1918ebade',
    },
    'Isilon OneFS 9.4.0.12 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '0dcc2afed6d335c36cb273fddc9d513c898d0f59df7fc2999e0bb7ae1204b924',
    },
    'Isilon OneFS 9.4.0.11 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '7703bfd3f27a6a8893cf466e64ea2a959f7416c68fcd4e1d959b1ba908c758c5',
    },
    'Isilon OneFS 9.4.0.10 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '92fcba0024df2edfff28e8bc2ac8d54dae9d2b22258c056c540aa2067eb4e5ac',
    },
    'Isilon OneFS 9.4.0.9 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'e1901a1a76dc1c1c403bedd3ffc332c3fdaa4c77f066b83174328cac6962f2a3',
    },
    'Isilon OneFS 9.4.0.7 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '639f5b68b6411a711c1709a43f49613b1338c0957b12c239040bd2564fb9f234',
    },
    'Isilon OneFS 9.4.0.6 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'cdbe7ed182a31a6815cda6ea258bfcbafa1a3130fa8c721bbb693889c2d8a734',
    },
    'Isilon OneFS 9.4.0.5 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '48db3a4c12cfa8139ec312dd7183c4ecd8e07bffc804c87302fd7f92b9329aae',
    },
    'Isilon OneFS 9.4.0.4 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '2fa6183aac8ba318f7d6fa2cd11ebed67cd4365c25b9dcb8c33bc019fbf985a8',
    },
    'Isilon OneFS 9.4.0.3 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'fa1a719e28fd90de14af9eed89f4620a1709f5ca62778f7ce7422e664b5f4292',
    },
    'Isilon OneFS 9.3.0.9 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '5f4126d560bd947f3c870d9f2822da0f24cae42f9479a1965a5a1ab6a5d35f40',
    },
    'Isilon OneFS 9.3.0.7 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'ee95129618e302c6697d949a51c60dc366750aa83b6b25967a7a07c3a807d83e',
    },
    'Isilon OneFS 9.3.0.6 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'e12aa9b26288d4ee25cb618cff097009b948bb60ac4911eb62e89639efd6ac7c',
    },
    'Isilon OneFS 9.3.0.5 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'ff6b57e94052f258276db94e0f6e668945aa46d5901c67547adcac2de1d2c5f6',
    },
    'Isilon OneFS 9.3.0.4 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '900733b0272bcf6990691f3f067396e83db088045914e4e6f9f93f790076d388',
    },
    'Isilon OneFS 9.3.0.3 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '1a9434347f315af60a71428cf161dcc95771d44beef26ef15ec19de751d7ef03',
    },
    'Isilon OneFS 9.3.0.2 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '32d67df5dfc5133f7899417058733d20f333d3bf008d4568d971e62a37410153',
    },
    'Isilon OneFS 9.3.0.1 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '419deffd5d7e1945de45ea7af168db2d3b65d1c482ae6b7d84facfbe101633c7',
    },
    'Isilon OneFS 9.2.1.25 installation bundle' => {
    'Code' => 'https://dl.dell.com/downloads/MF8MH',
    'sha256' => '7c1a105146cde397f21339482f5c060a1fb763ba1815dd31ad1607d97e5bbc40',
    },
    'Isilon OneFS 9.2.1.24 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '86e85c1b805f0cec36e544f12a6afb8db28e37df27ca979f45f5deb7bb785197',
    },
    'Isilon OneFS 9.2.1.23 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '7857a09515a892b6b5c51004ee781e074949f61b03fe764aa8403a99ad375764',
    },
    'Isilon OneFS 9.2.1.22 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '59c2e5b08ccd8a02e005eea040f03db3b8bb45e4813283e97792a7a4b78cb165',
    },
    'Isilon OneFS 9.2.1.21 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '096472a92123498a578e518df7541182e5ec64f0f63a8e99d3dd25473b2afed6',
    },
    'Isilon OneFS 9.2.1.20 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'd49acabaded077a5a336bec4169fbff853cc1b5f0063480462ab2cd9b13ce2f6',
    },
    'Isilon OneFS 9.2.1.19 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '90acd67510dbe1de330b9a7401d8bb53e1c91a1fa30e68a01429b8ba87e1c25d',
    },    
    'Isilon OneFS 9.2.1.18 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '9b7648cb774243d9b0fe062ecd0c0c5e0be413f5cc7b664560d709e257f32dcf',
    },
    'Isilon OneFS 9.2.1.17 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '35ed6e81e35ff9b740745b533ac7cdadf945392136638b2ee80431ef18fffe65',
    },
    'Isilon OneFS 9.2.1.16 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'c283a0ead9c4063b1d361a4d69e7b61ea37e0b2e2bfa3321863f73ed472d0697',
    },
    'Isilon OneFS 9.2.1.15 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'c0236584e5e6492f1a77e4dfc0ac1a177999651024873c066aecf8047c8b73af',
    },
    'Isilon OneFS 9.2.1.14 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '73f88818c8af29b85b8007e333b993518e99db79f761fa28956b7d110df75c68',
    },
    'Isilon OneFS 9.2.1.13 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '99679ffbfe6aa46dcf25dd557909780fe5d6fa076a9e700f32159914b2b68580',
    },
    'Isilon OneFS 9.2.1.12 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '0b7f863a0d3910fa9a2a430de10d3b3da3dd9ef80d5b479d14e7a90a81ff711a',
    },
    'Isilon OneFS 9.2.1.11 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '93c20864bf210232dea2ed0d78957545a04d09ece79042844251004e98fed631',
    },
    'Isilon OneFS 9.2.1.10 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'f84a7682b479fe480bc685c99922f23b1a7f788c64eef21df851169fb83fca8c',
    },
    'Isilon OneFS 9.2.1.9 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'dca09f54fc2f3581000629c08d55e508088551841f242c65e05e3669223893a8',
    },
    'Isilon OneFS 9.2.1.8 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'dde8b888607a20c23dc13cb0976da04b7841441ae130debbacc8fa8804500365',
    },
    'Isilon OneFS 9.2.1.7 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'b9625509273264bba8dbaa36a0ff221e6336ca65df16d88e99b0c642ab5b5c24',
    },
    'Isilon OneFS 9.2.1.6 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '1b7fae95e9b61000d6fb9f891c29fb0ca7bf47d3f03efa68194e22d2fd9bb7ac',
    },
    'Isilon OneFS 9.2.1.5 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '818f9278d96ff4004b09d877835289f9a230bbd4c2bb5dcfa919261d919b9b83',
    },
    'Isilon OneFS 9.1.0.28 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '60a22a3d84018169363c7cc4dabf6bc209113a5fcbd48b508c099573604b5057',
    },
    'Isilon OneFS 9.1.0.27 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'f0a0db629d4934aa8ba323da71382312d383c41367f082bb9e6d1bffc0a5fdea',
    },
    'Isilon OneFS 9.1.0.26 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'b3d6a5c5cb822c208a7f512c202eb20f9b35c7cf8d6bb2eacd4985a1e97c4698',
    },
    'Isilon OneFS 9.1.0.25 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '0395ba0d1c4b5517b0afd0cfccd27542086960122a6a5220fd2e26ad81ded2bb',
    },
    'Isilon OneFS 9.1.0.24 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'c2318c47e67bc3a92fd2a64a5e3db7c7359dc12f5f13dc1a529ae7669dd9a0dc',
    },
    'Isilon OneFS 9.1.0.23 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '81eac8d9cf352416b68b9f473d173d9363552b9637a1c9a4124a8db4f37f831c',
    },
    'Isilon OneFS 9.1.0.22 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'f73a1ef104d371d7d423d05707e9de38ecc2b4ed24b11e3d7a5a538694ac1d24',
    },
    'Isilon OneFS 9.1.0.21 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '29f19a5faa2339301ff9a8de58e23085e17674a2c69ce4e0e4f2eae061916bcf',
    },
    'Isilon OneFS 9.1.0.19 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '81a18ba4205a7a4c61657ac2291bb58af8ecf05ee043fa6c8e677bd1496ce049',
    },
    'Isilon OneFS 9.1.0.18 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '7b3f14c05892bdcf77d7d638bf43b57d37ab424e9bc76943fa86d76ce57e4d41',
    },
    'Isilon OneFS 9.1.0.17 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '9c2d22e152aa666bf4155dbb4fc78069cb220626531949a23a8f19add709e064',
    },
    'Isilon OneFS 9.1.0.16 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '71abc01156349ec08d47d0b8a1d717e4dc7d420192ddc4cd4e6f883b9ece8662',
    },
    'Isilon OneFS 9.1.0.15 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '97406a234d9207a93f1fe9b7d959b874ae28b19a7f0ec0e6a202e47a518843ed',
    },
    'Isilon OneFS 9.1.0.14 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '54cc0b805eceac8dfc8bd1cbd1f72a27cce311115ddcc6ac403085c12cf7ec93',
    },
    'Isilon OneFS 9.1.0.13 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'f48bad14d19addb0d0ededd7ce650fefb7cb584b35b35d4f34ea1d1486bcc59c',
    },
    'Isilon OneFS 9.1.0.12 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'dcd925ca95c40dc7d9e8e0f2c94a89e45a68f9259bf1c21015145d86a2eec485',
    },
    'Isilon OneFS 8.2.2.0 installation bundle' => {
    'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '5542951375fa5c11b16505e2587929a7941e5063410a7e906431e1158437b9d6',
    },
    'Isilon OneFS 8.1.2.0 installation bundle' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '2d0e3160a67186c5c15a71a104345bf53c7bc979618a32eadc0eeac71f353d9e',
    },

    # OneFS installation files
    'Isilon OneFS 9.15.0.0 installation file' => {
	'Code' => 'https://dl.dell.com/downloads/20WHN',
	'sha256' => '487a0d9483b1a2c0a9c26ae58e65baba65a3b5bdac1265a267a266c7c7df8c5d',
    },
    'Isilon OneFS 9.14.0.0 installation file' => {
	'Code' => 'https://dl.dell.com/downloads/K1K3R',
	'sha256' => 'd578913a7ab5cb4b7d4a0fbf38704e91cdf4b988aa47a46206052b9ab4e0b4d0',
    },
    'Isilon OneFS 9.13.0.0 installation file' => {
	'Code' => 'https://dl.dell.com/downloads/DWWRF',
	'sha256' => 'a6f12de38d4d2ec8227f065fa868e90696965e6c01924f9f5159a7e42865de68',
    },
    'Isilon OneFS 9.12.0.0 installation file' => {
	'Code' => 'https://dl.dell.com/downloads/XFMR1',
	'sha256' => '41738aff52b045a4084bf7f8966d66430bb3baa555daa386851aa13372e4cdb0',
    },
    'Isilon OneFS 9.10.0.0 installation file' => {
	'Code' => 'https://support.emc.com/kb/544296',
	'sha256' => '056065c72474d8c139826f2416daaab9d99ad13d46632bbc7f510e0af1383b39',
    },
    'Isilon OneFS 9.9.0.1 installation file' => {
	'Code' => 'https://www.dell.com/support/kbdoc/000157593',
	'sha256' => '76c006934e45d9a99267f94aed2f0ddc53d596bc928c6f3e90b4f1e547a7c378',
    },
    'Isilon OneFS 9.9.0.0 installation file' => {
	'Code' => 'https://www.dell.com/support/kbdoc/000157593',
	'sha256' => '76c006934e45d9a99267f94aed2f0ddc53d596bc928c6f3e90b4f1e547a7c378',
    },
    'Isilon OneFS 9.8.0.0 installation file' => {
	'Code' => 'https://www.dell.com/support/kbdoc/000157593',
	'sha256' => 'dba7a4fff12d43cb9bcae7bd38e2d296a7b6d3999bb151711bbc437abc45dcad',
    },
    'Isilon OneFS 9.7.0.0 installation file' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '97c5b74f52d1c94c82e2f48d896f86abd77dcb377b01d0f1943560d182df7cf1',
    },
    'Isilon OneFS 9.5.0.0 installation file' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'e5c90e026b1097d048e5b3ea4815172b4214f8eaaa6ffacf8cb8fa4ceb1b9ece',
    },
    'Isilon OneFS 7.2.0.5 installation file' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '2c195e27425e87543c188754f086a01a28188bbba40bf8860b9bc68db558a54d',
    },
    'Isilon OneFS 7.1.1.11 installation file' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '37d7dd688a9f6c038418a5b2959ff5f8fc490d5429eab605a3cc17d3e541502c',
    },

    # Firmware Packages
    'Isilon Node Firmware Package 14.1' => {
	'Code' => 'https://dl.dell.com/downloads/V0J88',
	'sha256' => 'c7a060d679a6831b5bab10cc9df9bb08d393c5b188e732172a49501a3b93924a',
    },
    'Isilon Node Firmware Package 10.4.0' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '18d6d0cea9c8723865328905f2b95e68068cac92b0f656894d509eaa5801ede1',
    },
    'Isilon Drive Support Package 1.61' => {
    'Code' => 'https://dl.dell.com/downloads/6M8PD',
    'sha256' => 'f15f73fae602a54a7cd1d72cf0d74a5917aab3cc31db6861708fbd11424c4a37',
    },


# Health Check Packages
'HealthCheck_9.14_43.2.0' => {
'Code' => 'https://dl.dell.com/downloads/180F8',
'sha256' => 'b8d57b115698cee04c20fd6ee9232240dd155fd62e0025836258308c01e71a09',
},
'HealthCheck_9.13_43.2.0' => {
'Code' => 'https://dl.dell.com/downloads/X5V46',
'sha256' => '8950999082d0236d523adc60820206f9c19dd1847f37b2422247e509fe14ee79',
},
'HealthCheck_9.12_43.2.0' => {
'Code' => 'https://dl.dell.com/downloads/G6J93',
'sha256' => '49841ba03c385d3d4f9e568c77760bd5094085af3902203125d23edd8db25bc1',
},
'HealthCheck_9.11_43.2.0' => {
'Code' => 'https://dl.dell.com/downloads/MYD8P',
'sha256' => 'fc56503b63b3939983fe618752516fe7bc181597c8db522211b6f667a969f256',
},
'HealthCheck_9.10_43.2.0' => {
'Code' => 'https://dl.dell.com/downloads/WXV0F',
'sha256' => 'ee5811fa066f7fdf8dece31961fcc0d9e470ef3f1f98c717f905e033db74a7ae',
},
'HealthCheck_9.9_43.2.0' => {
'Code' => 'https://dl.dell.com/downloads/15JGC',
'sha256' => 'f12511f12b17f717686da455539dccb313cab19229b416efd0b2633b8fac9a05',
},
'HealthCheck_9.8_43.2.0' => {
'Code' => 'https://dl.dell.com/downloads/R99F7',
'sha256' => 'a927613eec50b1fbc9a5cf34a0159020c79c9382dfc2319a230af2e56202c75c',
},
'HealthCheck_9.7_43.2.0' => {
'Code' => 'https://dl.dell.com/downloads/M35VK',
'sha256' => '2e3ebc8d59bd60aef7c0bbcc47d625ad5329c123b89d86fce31a840e8b8be45a',
},
'HealthCheck_9.5_43.2.0' => {
'Code' => 'https://dl.dell.com/downloads/3KKG7',
'sha256' => '905da3f017afbc50f9223584e489b1f714fdff4922959a7a56238f57e392b4a7',
},
'HealthCheck_9.4.0_40.0.0' => {
'Code' => 'https://dl.dell.com/downloads/VCTR8',
'sha256' => 'd296d86076fbb0207716bbcc5db53aece9d4292211241e925ae6dc86829efafd',
},
'HealthCheck_9.3.0_38.1.2' => {
'Code' => 'https://dl.dell.com/downloads/VGMG2',
'sha256' => '27dec7786039a6b8e11bfcc3133fbebeec8c72ce370e75f0ce2a77d9eb4572ff',
 },
'HealthCheck_9.2.1_37.1.4' => {
'Code' => 'https://dl.dell.com/downloads/43WV8',
'sha256' => '1af278af494dd8f5a3daf10dde89465abc6553e8efad17808594cf8117fe3da5',
 },
'HealthCheck_9.2.0_37.1.4' => {
'Code' => 'https://dl.dell.com/downloads/PFW09',
'sha256' => '6c4a5d4a9a8dcc68739a44c4167809887b8307737309049dbcac8ead830881d9',
 },

    # Support Tools Packages
    '8.X_GA-TOOLS_2021-05_PSP-1034' => {
	'Code' => 'https://dl.dell.com/downloads/RW3YV',
	'sha256' => '017536fe54a360c8fff6ba58bea8f3183b3ccaf8982c131f90f00492f4725101',
    },
    '9.X_GA-TOOLS_2021-10_PSP-1695' => {
	'Code' => 'https://dl.dell.com/downloads/D12N0',
	'sha256' => 'fb017892af561780f01e77ea30b65c775d2f24f1b1ac94896d52fc1af58cb585',
    },
    '9.4.0_GA-TOOLS_2023-03_PSP-3323' => {
	'Code' => 'https://dl.dell.com/downloads/CPMW3',
	'sha256' => '6f4c3ff1de061bd40b1f0b6976f0994a5465f1f6ae8546fe5860e9f29a2af52b',
    },
    '9.5.0_UGA-TOOL_2023-04_PSP-2835' => {
	'Code' => 'https://dl.dell.com/downloads/D89DY',
	'sha256' => '42711c76979fc8989285cf8655ff4349b86fc8f50e67e115e88c39235a6709f9',
    },

    # Patch Infrastructure Packages
    '9.5.0_UGA-PATCH-INFRA_2023-03_PSP-3206' => {
    'Code' => 'https://dl.dell.com/downloads/KGMR3',
    'sha256' => 'db84f2118c4904975aa13ffbafd031fd890be0dc08c274aa5a7d0aa30ccbd119',
    },
    '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186' => {
	'Code' => 'https://dl.dell.com/downloads/2HTH4',
	'sha256' => '21658f07f524721757f79bb47fa3dbf5ef87f19b2760f0415e8c7e03b56689f6',
    },
    '9.4.0_UGA-PATCH-INFRA_2023-01_PSP-3071' => {
	'Code' => 'https://dl.dell.com/downloads/F0V6K',
	'sha256' => 'ad1fd015d38debc6e5980081835e1537cdb35a6951d68e420bd4861b0224ef96',
    },
    '9.4.0_UGA-PATCH-INFRA_2022-12_PSP-3031' => {
	'Code' => 'https://dl.dell.com/downloads/F0XTR',
	'sha256' => 'dc9cd5501fbb92cace7ed97095c1eb509386cb2d641129a07457c68f61c89683',
    },
    '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925' => {
	'Code' => 'https://dl.dell.com/downloads/0D98D',
	'sha256' => '5e997f188012aa98ccba13fd71bf5767da51c8dbe53ba8e00a6c1615b4428b24',
    },
    '9.1.0_UGA-PATCH-INFRA_2022-07_PSP-2682' => {
	'Code' => 'https://dl.dell.com/downloads/RH62F',
	'sha256' => '6e24638591c6317224a4d5a5ff7f5215ee6b059fe61f4031463b4ea362e15269',
    },

    # Cert Update Package (9.5 and higher)
    '9.X_GA-RUP_2025-10_PSP-4737' => {
	'Code' => 'https://dl.dell.com/downloads/875YC',
	'sha256' => '46d5b0b7c1f3c68497eb87bd5ff38200fedf1e6e5ee2e5782ca3ba4ef3a7fcf5',
    },

    # Roll-Up Patches
    '9.5.0.8_LTS2023_GA-RUP_PSP-4211' => {
    'Code' => 'https://dl.dell.com/downloads/YN5MN',
    'sha256' => '36ed2703606ba5cc7c91f8ed9d69d48773ad19793e9d45e6b5513ffeca1d24a0',
    },
    '9.5.0.7_LTS2023_GA-RUP_PSP-3967' => {
    'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => 'e7a1ae934a0db3be3fef341ed36041b25fdba7cd437d051fe12f2d7a4d1cb696',
    },
    '9.5.0.6_LTS2023_GA-RUP_PSP-3839' => {
        'Code' => 'https://www.dell.com/support/kbdoc/000224314',
        'sha256' => 'f2176a27ba08ea1fecc6ec4b9c87b68d9ec20014c61c3f66d9565814799e3760',
    },
    '9.5.0.5_LTS2023_GA-RUP_PSP-3793' => {
    'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => 'e25c1c5ab16de01fff94deef1c23957eadc71dba1497eb55f0666710fa047d4e',
    },
    '9.5.0.3_LTS2023_GA-RUP_PSP-3332' => {
	'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => '0d90a50a737a8d7c46fc1fa8e2b836a994ef9c404152d5c242c566396c43a707',
    },
    '9.5.0.2_GA-RUP_2023-04_PSP-3208' => {
	'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => '20db93565200585ccb5bb8e23198b5088d7c0835a740a008cddb30a32872e7db',
    },
    '9.5.0.1_GA-RUP_2023-03_PSP-3199' => {
	'Code' => 'https://www.dell.com/support/kbdoc/000224314',
    'sha256' => '98675ecf845f9370bc6b4570ccf707dc7fec62b1b93b98541df0c2c612052e2b',
    },
    '9.4.0.21_GA-RUP_2025-01_PSP-4579' => {
	'Code' => 'https://dl.dell.com/downloads/6TMK6',
    'sha256' => 'fc00d572910dcf4a8b66f1f45f597fcf4c3cb86e7db0289def2d5ddc69332208',
    },
    '9.4.0.20_GA-RUP_2024-10_PSP-4496' => {
	'Code' => 'https://dl.dell.com/downloads/N08WN',
    'sha256' => '068c5990cc15d1e21793dea74c95bf1b120cbd63a752b951626231eb8248bf60',
    },
    '9.4.0.19_GA-RUP_2024-04_PSP-4392' => {
	'Code' => 'https://dl.dell.com/downloads/H2MHG',
    'sha256' => 'f6f38fe71c838301ac37445d9393c0e2128ef0e9c34c909bf72740d1d0bc15d0',
    },
    '9.4.0.18_GA-RUP_2024-04_PSP-4292' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '88a2935fe54248505f37e3e1b1a34f3c246610a11268ef00e513685f3f3417a7',
    },
    '9.4.0.17_GA-RUP_2024-02_PSP-4007' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'd476c0ddf74e65a3b9511f7d6faab181ece98d9e3e1150f344247d6c783b2ecd',
    },
    '9.4.0.16_GA-RUP_2023-11_PSP-3881' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => 'a5d63df0fbf14b8069fb0d49175d7771cdf175a180546a46e39ced04ab399819',
    },
    '9.4.0.15_GA-RUP_2023-08_PSP-3593' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => 'c05a8164f9ecc3e12713e961a9d0872abbcf6aa0debcc0de5772ed0d68d00834',
    },
    '9.4.0.14_GA-RUP_2023-05_PSP-3388' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '293de14d7c71855a44c1f5b6263923d533155cac509ab5a61d814ca444e440bb',
    },
    '9.4.0.13_GA-RUP_2023-03_PSP-3140' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '711abc7b220c26c1888c0fc9ef91c5ccc81d8dcc69515b247b7c4d5b8a77e929',
    },
    '9.4.0.12_GA-RUP_2023-02_PSP-3079' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '07e7f99b23ea7c56e4ed544feef347ec09136afb58d9139400f56fc442f14b64',
    },
    '9.4.0.11_GA-RUP_2023-01_PSP-3035' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '91eef3782b93769bf12d54124158e39d503925c05ff2e69f4b257c14d3a87459',
    },
    '9.4.0.10_GA-RUP_2022-12_PSP-2937' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '7f59405622cd78abd7259c6ab15de319517f42c07585aee1389737a539898c08',
    },
    '9.4.0.9_GA-RUP_2022-11_PSP-2975' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '215aae067555dec902aa13826c31804e2363addf48e36dafd165576e1ad68e55',
    },
    '9.3.0.9_GA-RUP_2022-10_PSP-2935' => {
	'Code' => 'https://dl.dell.com/downloads/WD59N',
	'sha256' => '67f0e0ae7681a553d4f6709ceccfce397b94165be6bc90f606f585d8175128a2',
    },
    '9.2.1.25_GA-RUP_2023-12_PSP-3889' => {
    'Code' => 'https://dl.dell.com/downloads/C7VWW',
    'sha256' => 'b83da534dd4972028f6d48852303dced6917696cba3eefd808d1a869aaf101fb',
    },
    '9.2.1.24_GA-RUP_2023-09_PSP-3730' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'a34f37e9d750600223d650697bf84fc41a290c295c4d7e4c2a943e68f97a5f7d',
    },
    '9.2.1.23_GA-RUP_2023-06_PSP-3514' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => '5432528caf1eb09bd4d9ffdbdbe27ccd7787b8e75b383837ced4e7c49356ccdf',
    },
    '9.2.1.22_GA-RUP_2023-03_PSP-3171' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
    'sha256' => 'd720e222aac2a8146e4584944b24a477b5074472827e19f5aeef40a58c6aa84e',
    },
    '9.2.1.21_GA-RUP_2023-02_PSP-3109' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '26fe6967e801635555d23b44a97f25ae186f30afdfb918e661cca5686f57b4d3',
    },
    '9.2.1.20_GA-RUP_2023-01_PSP-3037' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => 'b180ec573726aaee4694b30ec2a5f7f689bc414c6b2bf3b0f81e42ae3b043b67',
    },
    '9.2.1.19_GA-RUP_2022-12_PSP-2940' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => 'f809bba723e4c43d3aba7f1fb4ca014a3b04b28c99ecb0f52f08e681f11319a8',
    },
    '9.2.1.18_GA-RUP_2022-11_PSP-2896' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => 'bd4f322dfd5de28c71ace6139bde1fa665f6b2d3832f14bb4406091a4d348af3',
    },
    '9.2.0.1_GA-RUP_2021-06_PSP-1322' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '169ad5f046c2a231a12b24c7540b82369d7438ef2bbe65c3493151a8d7b0a1f6',
    },
    '9.1.0.25_GA-RUP_2022-11_PSP-2900' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '749cc0fcf9c30504167127a680f668059e2593c80f296fe1e81ae49c4bd02bc8',
    },
    '9.1.0.26_GA-RUP_2022-12_PSP-2978' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '63ce9e96d4a5ada3b0c832e5664f408a455b1bcad215cd8603b9f01d5cfb6233',
    },
    '9.1.0.27_GA-RUP_2023-01_PSP-3078' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => '6d962410c4478bb9b0c6820c843527e3c8c8991fdb7309e233b2d91a2f7e1f57',
    },
    '9.1.0.28_GA-RUP_2023-02_PSP-3119' => {
	'Code' => 'https://dl.dell.com/downloads/796VV',
	'sha256' => '41f137dfb1e11515a3871cd0deca83458fcd23cd50c0a9cf627e0a1b0b14630f',
    },
    # Scripts
    'A100 Root Resize Script' => {
	'Code' => 'Please reach out to your scheduling rep / account rep to obtain the required code package',
	'sha256' => 'bab94dd33582bd41126110aa3e9c7669ba0834ecb3f948d9cf76b00760adc2ec',
    },
   );
our %LTS_Codes = (
    '9.10.1' => [ '9.10.1.8','9.10.1.7','9.10.1.6', '9.10.1.5', '9.10.1.4','9.10.1.3','9.10.1.2', '9.10.1.1', ],
    '9.7.1' => [ '9.7.1.16','9.7.1.15','9.7.1.14','9.7.1.13','9.7.1.12','9.7.1.11','9.7.1.10','9.7.1.9','9.7.1.8','9.7.1.7','9.7.1.6','9.7.1.5','9.7.1.2','9.7.1.1','9.7.1.0', ],
    '9.5.1' => [ '9.5.1.7','9.5.1.6', '9.5.1.5','9.5.1.4','9.5.1.3','9.5.1.2','9.5.1.1','9.5.1.0' ],
);

# Supported out-of-family upgrade paths
# Source => Target versions
# Highest Sources at the top
# Target versions are the lowest supported version that can be upgraded to at each family excluding within family
# Target versions MUST be 4 decimal separated digits in length
# Target versions are generated based on the keys when no target is found in the list
our %Code_Matrix = (
    '9.15.0' => [ ],
    '9.14.0' => [ '9.15.0.0','9.14.0.1', ],
    '9.13.1' => [ '9.15.0.0','9.14.0.1','9.14.0.0', ],
    '9.13.0' => [ '9.15.0.0','9.14.0.1','9.14.0.0','9.13.1.1','9.13.1.0','9.13.0.2','9.13.0.1',],
    '9.12.0' => [ '9.15.0.0','9.14.0.1','9.14.0.0','9.13.1.1','9.13.1.0','9.13.0.2','9.13.0.1','9.13.0.0','9.12.0.1', ],
    '9.11.0' => [ '9.15.0.0','9.14.0.1','9.14.0.0','9.13.1.1','9.13.1.0','9.13.0.2','9.13.0.1','9.13.0.0','9.12.0.1','9.12.0.0', '9.11.0.6', '9.11.0.5', '9.11.0.3', ],
    '9.10.1' => [ '9.15.0.0','9.14.0.1','9.14.0.0','9.13.1.1','9.13.1.0','9.13.0.2','9.13.0.1','9.13.0.0','9.12.0.1','9.12.0.0', '9.11.0.6','9.11.0.5','9.11.0.3','9.10.1.8','9.10.1.7','9.10.1.6','9.10.1.5','9.10.1.4','9.10.1.3','9.10.1.2','9.10.1.1', ],
    '9.10.0' => [ '9.15.0.0','9.14.0.1','9.14.0.0','9.13.1.1','9.13.1.0','9.13.0.2','9.13.0.1','9.13.0.0','9.12.0.1','9.12.0.0', '9.11.0.6','9.11.0.5','9.11.0.3','9.10.1.8','9.10.1.7','9.10.1.6','9.10.1.5','9.10.1.4','9.10.1.3','9.10.1.2','9.10.1.1','9.10.1.0','9.10.0.1', ],
    '9.9.0' => [ '9.15.0.0','9.14.0.1','9.14.0.0','9.13.1.1','9.13.1.0','9.13.0.2','9.13.0.1','9.13.0.0','9.12.0.1','9.12.0.0', '9.11.0.6','9.11.0.5','9.11.0.3','9.10.1.8','9.10.1.7','9.10.1.6','9.10.1.5','9.10.1.4','9.10.1.3','9.10.1.2','9.10.1.1','9.10.1.0','9.10.0.1','9.10.0.0','9.9.0.2','9.9.0.1', ],
    '9.8.0' => [ '9.15.0.0','9.14.0.1','9.14.0.0','9.13.1.1','9.13.1.0','9.13.0.2','9.13.0.1','9.13.0.0','9.12.0.1','9.12.0.0', '9.11.0.6','9.11.0.5','9.11.0.3','9.10.1.8','9.10.1.7','9.10.1.6','9.10.1.5','9.10.1.4','9.10.1.3','9.10.1.2','9.10.1.1','9.10.1.0','9.10.0.1','9.10.0.0','9.9.0.2','9.9.0.1','9.9.0.0','9.8.0.3','9.8.0.2','9.8.0.1', ],
    '9.7.1' => [ '9.15.0.0','9.14.0.1','9.14.0.0','9.13.1.1','9.13.1.0','9.13.0.2','9.13.0.1','9.13.0.0','9.12.0.1','9.12.0.0', '9.11.0.6','9.11.0.5','9.11.0.3','9.10.1.8','9.10.1.7','9.10.1.6','9.10.1.5','9.10.1.4','9.10.1.3','9.10.1.2','9.10.1.1','9.10.1.0','9.10.0.1','9.10.0.0','9.9.0.2','9.9.0.1','9.9.0.0','9.8.0.3','9.8.0.2','9.8.0.1','9.8.0.0','9.7.1.16','9.7.1.15','9.7.1.14','9.7.1.13','9.7.1.12','9.7.1.11','9.7.1.10','9.7.1.9','9.7.1.8','9.7.1.7','9.7.1.6','9.7.1.5','9.7.1.4','9.7.1.3','9.7.1.2', ],
    '9.7.0' => [ '9.15.0.0','9.14.0.1','9.14.0.0','9.13.1.1','9.13.1.0','9.13.0.2','9.13.0.1','9.13.0.0','9.12.0.1','9.12.0.0', '9.11.0.6','9.11.0.5','9.11.0.3','9.10.1.8','9.10.1.7','9.10.1.6','9.10.1.5','9.10.1.4','9.10.1.3','9.10.1.2','9.10.1.1','9.10.1.0','9.10.0.1','9.10.0.0','9.9.0.2','9.9.0.1','9.9.0.0','9.8.0.3','9.8.0.2','9.8.0.1','9.8.0.0','9.7.1.16','9.7.1.15','9.7.1.14','9.7.1.13','9.7.1.12','9.7.1.11','9.7.1.10','9.7.1.9','9.7.1.8','9.7.1.7','9.7.1.6','9.7.1.5','9.7.1.4','9.7.1.3','9.7.1.2','9.7.1.0','9.7.0.3','9.7.0.2','9.7.0.1', ],
    '9.5.1' => [ '9.15.0.0','9.13.1.1','9.13.1.0','9.13.0.2','9.13.0.1','9.13.0.0','9.12.0.1','9.12.0.0', '9.11.0.6','9.11.0.5','9.11.0.3','9.10.1.8','9.10.1.7','9.10.1.6','9.10.1.5','9.10.1.4','9.10.1.3','9.10.1.2','9.10.1.1','9.10.1.0','9.10.0.1','9.10.0.0','9.9.0.2','9.9.0.1','9.9.0.0','9.8.0.3','9.8.0.2','9.8.0.1','9.8.0.0','9.7.1.16','9.7.1.15','9.7.1.14','9.7.1.13','9.7.1.12','9.7.1.11','9.7.1.10','9.7.1.9','9.7.1.8','9.7.1.7','9.7.1.6','9.7.1.5','9.7.1.4','9.7.1.3','9.7.1.2','9.7.1.0','9.7.0.3','9.7.0.2','9.7.0.1','9.7.0.0','9.5.1.6','9.5.1.5','9.5.1.4','9.5.1.3','9.5.1.2','9.5.1.1',  ],
    '9.5.0' => [ '9.15.0.0','9.13.1.1','9.13.1.0','9.13.0.2','9.13.0.1','9.13.0.0','9.12.0.1','9.12.0.0', '9.11.0.6','9.11.0.5','9.11.0.3','9.10.1.8','9.10.1.7','9.10.1.6','9.10.1.5','9.10.1.4','9.10.1.3','9.10.1.2','9.10.1.1','9.10.1.0','9.10.0.1','9.10.0.0','9.9.0.2','9.9.0.1','9.9.0.0','9.8.0.3','9.8.0.2','9.8.0.1','9.8.0.0','9.7.1.16','9.7.1.15','9.7.1.14','9.7.1.13','9.7.1.12','9.7.1.11','9.7.1.10','9.7.1.9','9.7.1.8','9.7.1.7','9.7.1.6','9.7.1.5','9.7.1.4','9.7.1.3','9.7.1.2','9.7.1.0','9.7.0.3','9.7.0.2','9.7.0.1','9.7.0.0','9.5.1.6','9.5.1.5','9.5.1.4','9.5.1.3','9.5.1.2','9.5.1.1','9.5.1.0',  ],
    '9.4.0' => [ '9.11.0.6','9.11.0.5','9.11.0.3','9.10.1.7','9.10.1.6','9.10.1.5','9.10.1.4','9.10.1.3','9.10.1.2','9.10.1.1','9.10.1.0','9.10.0.1','9.10.0.0','9.9.0.2','9.9.0.1','9.9.0.0','9.8.0.3','9.8.0.2','9.8.0.1','9.8.0.0','9.7.1.16','9.7.1.15','9.7.1.14','9.7.1.13','9.7.1.12','9.7.1.11','9.7.1.10','9.7.1.9','9.7.1.8','9.7.1.7','9.7.1.6','9.7.1.5','9.7.1.4','9.7.1.3','9.7.1.2','9.7.1.0','9.7.0.3','9.7.0.2','9.7.0.1','9.7.0.0','9.5.1.6','9.5.1.5','9.5.1.4','9.5.1.3','9.5.1.2', '9.5.1.1','9.5.0.0','9.5.1.0', ],
    '9.3.0' => [ '9.4.0.3', ],
    '9.2.1' => [ '9.4.0.3', '9.3.0.0', ],
    '9.2.0' => [ '9.4.0.3', '9.3.0.0', '9.2.1.0', ],
    '9.1.1' => [ '9.4.0.3', '9.3.0.0' ],
    '9.1.0' => [ '9.4.0.3', '9.3.0.0', '9.2.1.0', '9.2.0.0' ],
    '9.0.0.0' => [ '9.4.0.3', '9.3.0.0', '9.2.1.0', '9.2.0.0', '9.1.0.0', ],
    '8.2.2.0' => [ '9.5.1.6','9.5.1.5','9.5.1.4','9.5.1.3','9.5.1.2','9.5.1.1','9.5.1.0','9.5.0.0', '9.4.0.3', '9.3.0.0', '9.2.1.0', '9.2.0.0', '9.1.0.0', '9.0.0.0', ],
    '8.2.1.0' => [ '9.4.0.3', '9.3.0.0', '9.2.1.0', '9.2.0.0', '9.1.0.0', '9.0.0.0', '8.2.2.0', ],
    '8.2.0.0' => [ '9.4.0.3', '9.3.0.0', '9.2.1.0', '9.2.0.0', '9.1.0.0', '9.0.0.0', '8.2.2.0', '8.2.1.0', ],
    '8.1.3.0' => [ '9.1.0.0', '9.0.0.0', '8.2.2.0', '8.2.1.0', ],
    '8.1.2.0' => [ '9.2.1.0', '9.2.0.0', '9.1.0.0', '9.0.0.0', '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0' ],
    '8.1.1.1' => [ '9.2.1.0', '9.2.0.0', '9.1.0.0', '9.0.0.0', '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0' ],
    '8.1.1.0' => [ '9.2.1.0', '9.2.0.0', '9.1.0.0', '9.0.0.0', '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0' ],
    '8.1.0.4' => [ '9.2.1.0', '9.2.0.0', '9.1.0.0', '9.0.0.0', '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0' ],
    '8.1.0.3' => [ '9.2.1.0', '9.2.0.0', '9.1.0.0', '9.0.0.0', '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0' ],
    '8.1.0.2' => [ '9.2.1.0', '9.2.0.0', '9.1.0.0', '9.0.0.0', '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0' ],
    '8.1.0.1' => [ '9.2.1.0', '9.2.0.0', '9.1.0.0', '9.0.0.0', '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0' ],
    '8.1.0.0' => [ '9.2.1.0', '9.2.0.0', '9.1.0.0', '9.0.0.0', '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0' ],
    '8.0.1.2' => [ '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.2' ],
    '8.0.1.1' => [ '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.1' ],
    '8.0.1.0' => [ '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0' ],
    '8.0.0.7' => [ '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0' ],
    '8.0.0.6' => [ '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.2' ],
    '8.0.0.5' => [ '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.1', '8.0.1.2' ],
    '8.0.0.4' => [ '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.1' ],
    '8.0.0.3' => [ '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.1' ],
    '8.0.0.2' => [ '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.1' ],
    '8.0.0.1' => [ '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.0' ],
    '8.0.0.0' => [ '8.2.2.0', '8.2.1.0', '8.2.0.0', '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.0' ],
    '7.2.1.6' => [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.1', '8.0.1.2', '8.0.0.5' ],
    '7.2.1.5' => [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.1', '8.0.0.4' ],
    '7.2.1.4' => [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.1', '8.0.0.3' ],
    '7.2.1.3' => [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.0', '8.0.0.2' ],
    '7.2.1.2' => [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.0', '8.0.0.1' ],
    '7.2.1.1' => [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.0', '8.0.0.0' ],
    '7.2.1.0' => [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.0', '8.0.0.0' ],
    '7.2.0.5' => [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.0', '8.0.0.1', '7.2.1.2' ],
    '7.2.0.4' => [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.0', '8.0.0.0', '7.2.1.1' ],
    '7.2.0.3' => [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.0', '8.0.0.0', '7.2.1.1' ],
    '7.2.0.2' => [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.1.0.0', '8.0.1.0', '8.0.0.0', '7.2.1.0' ],
    '7.2.0.1' => [ ],
    '7.2.0.0' => [ ],
    '7.1.1.11'=> [ '8.1.3.0', '8.1.2.0', '8.1.1.0', '8.0.1.1', '8.0.0.4', '7.2.1.5' ],
    '7.1.1.9' => [ '8.0.1.0', '8.0.0.1', '7.2.1.3' ],
    '7.1.1.8' => [ '8.0.1.0', '8.0.0.1', '7.2.1.2' ],
    '7.1.1.7' => [ '8.0.1.0', '8.0.0.0', '7.2.1.1', '7.2.0.4' ],
    '7.1.1.6' => [ '8.0.1.0', '8.0.0.0', '7.2.1.1', '7.2.0.3' ],
    '7.1.1.5' => [ '8.0.1.0', '8.0.0.0', '7.2.1.1', '7.2.0.3' ],
    '7.1.1.4' => [ '8.0.1.0', '8.0.0.0', '7.2.1.0', '7.2.0.2' ],
    '7.1.1.3' => [ '7.2.0.1' ],
    '7.1.1.2' => [ '7.2.0.1' ],
    '7.1.1.1' => [ '7.2.0.0' ],
    '7.1.1.0' => [ '7.2.0.0' ],
    '7.1.0'   => [ '7.2.0.1', '7.1.1.2' ],
    '7.0'     => [ '7.2.0.2', '7.1.1.3' ],
    '6.5.5.29'=> [ '7.1.1.1', '7.1.0.0' ],
    '6.5'     => [ '7.1.1.1', '7.1.0.0' ],
    '6.0'     => [ '6.5.5.29' ],
   );
our %DriveSupport = (
    'None' => undef,
    '1.61' => {
        'MTFDLAL122T8QHF-1BQ1JABDA' => {'1.0.2' => ['allow'],},
        'MTFDLAL30T7QHF-1BQ1JABDA' => {'1.0.2' => ['allow'],},
        'MTFDLAL61T4QHF-1BQ1JABDA' => {'1.0.2' => ['allow'],},
    },
    '1.60.1' => {
        'SEAGATE ST12000NM007J' => {'P705' => ['P704', '9P73'],},
        'SEAGATE ST16000NM007J' => {'P705' => ['P704', '9P73'],},
        'SEAGATE ST4000NM017B' => {'L708' => ['L706', '9L77'],},
        'SEAGATE ST8000NM011B' => {'L708' => ['L706', '9L77'],},
    },
    '1.60' => {
        'SEAGATE ST12000NM009H' => {'SW75' => ['SW74'],},
        'SEAGATE ST16000NM006H' => {'SY75' => ['SY74'],},
        'SEAGATE ST20000NM006H' => {'SY75' => ['SY74'],},
        'SEAGATE ST24000NM006H' => {'SU75' => ['SU74'],},
        'ST32000NM001K-3SH132' => {'MLR2' => ['MLR1'],},
    },
    '1.59' => {
        'Dell Ent NVMe FIPS P5316 RI 15.36TB' => {'1.7.0' => ['allow'],},
        'Dell Ent NVMe FIPS P5316 RI 30.72TB' => {'1.7.0' => ['allow'],},
        'Dell Ent NVMe P5316 RI 15.36TB' => {'1.7.0' => ['allow'],},
        'Dell Ent NVMe P5316 RI 30.72TB' => {'1.7.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI FIPS 122.88TB' => {'1.2.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI FIPS 15.36TB' => {'1.4.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI FIPS 30.72TB' => {'1.4.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI FIPS 61.44TB' => {'1.4.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI ISE 122.88TB' => {'1.2.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI ISE 15.36TB' => {'1.4.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI ISE 30.72TB' => {'1.4.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI ISE 61.44TB' => {'1.4.0' => ['allow'],},
        'Dell Ent NVMe SED P5316 RI 15.36TB' => {'1.7.0' => ['allow'],},
        'Dell Ent NVMe SED P5316 RI 30.72TB' => {'1.7.0' => ['allow'],},
        'Dell NVMe BM1743 QLC U.2 122.88TB' => {'1.1.1' => ['allow'],},
        'Dell NVMe BM1743 QLC U.2 15.36TB' => {'1.1.1' => ['allow'],},
        'Dell NVMe BM1743 QLC U.2 30.72TB' => {'1.1.1' => ['allow'],},
        'Dell NVMe BM1743 QLC U.2 61.44TB' => {'1.1.1' => ['allow'],},
    },
    '1.58' => {
        'ST12000NM002J-2TY133' => {'P20F' => ['P20B', 'P20D'],},
        'ST16000NM002J-2TW133' => {'P20F' => ['P20B', 'P20D'],},
        'ST20000NM008D-3DJ133' => {'G208' => ['G202', 'G203', 'G206'],},
        'ST4000NM030B-2TF133' => {'L208' => ['L205', 'L207'],},
        'ST8000NM023B-2TJ133' => {'L208' => ['L205', 'L207'],},
    },
    '1.57' => {
        'Dell Ent NVMe P5336 RI FIPS 122.88TB' => {'1.1.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI ISE 122.88TB' => {'1.1.0' => ['allow'],},
        'Dell NVMe PM1743 RI U.2 1.92TB' => {'1.0.0' => ['allow'],},
        'Dell NVMe PM1743 RI U.2 15.36TB' => {'1.0.0' => ['allow'],},
        'Dell NVMe PM1743 RI U.2 3.84TB' => {'1.0.0' => ['allow'],},
        'Dell NVMe PM1743 RI U.2 7.68TB' => {'1.0.0' => ['allow'],},
    },
    '1.56' => {
        'SEAGATE ST12000NM007J' => {'P705' => ['P704'],},
        'SEAGATE ST16000NM007J' => {'P705' => ['P704'],},
        'SEAGATE ST20000NM005D' => {'G706' => ['G703', 'G705'],},
        'SEAGATE ST4000NM017B' => {'L708' => ['L706'],},
        'SEAGATE ST8000NM011B' => {'L708' => ['L706'],},
    },
    '1.55' => {
        'Dell Ent NVMe P5336 RI FIPS 122.88TB' => {'1.0.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI FIPS 15.36TB' => {'1.3.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI FIPS 30.72TB' => {'1.3.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI FIPS 61.44TB' => {'1.3.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI ISE 122.88TB' => {'1.0.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI ISE 15.36TB' => {'1.3.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI ISE 30.72TB' => {'1.3.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI ISE 61.44TB' => {'1.3.0' => ['allow'],},
        'Dell NVMe BM1743 QLC U.2 15.36TB' => {'1.0.1' => ['allow'],},
        'Dell NVMe BM1743 QLC U.2 30.72TB' => {'1.0.1' => ['allow'],},
        'Dell NVMe BM1743 QLC U.2 61.44TB' => {'1.0.1' => ['allow'],},
        'KIOXIA P7M800GD EMC800' => {'PE78' => ['PD78'],},
        'KIOXIA P7R15T3D EMC15T3' => {'PD78' => ['PD76'],'PE78' => ['PD78'],},
        'KIOXIA P7R3T84D EMC3840' => {'PD78' => ['PD76'],'PE78' => ['PD78'],},
        'KIOXIA P7R7T68D EMC7680' => {'PD78' => ['PD76'],'PE78' => ['PD78'],},
        'KIOXIA P7V1T60D EMC1600' => {'PD78' => ['PD76'],'PE78' => ['PD78'],},
        'KIOXIA P7V3T20D EMC3200' => {'PD78' => ['PD76'],'PE78' => ['PD78'],},
        'KIOXIA P7V800GD EMC800' => {'PD78' => ['PD76'],'PE78' => ['PD78'],},
    },
    '1.53' => {
        'Dell Ent NVMe FIPS P5316 RI 15.36TB' => {'1.6.0' => ['allow'],},
        'Dell Ent NVMe FIPS P5316 RI 30.72TB' => {'1.6.0' => ['allow'],},
        'Dell Ent NVMe P5316 RI 15.36TB' => {'1.6.0' => ['allow'],},
        'Dell Ent NVMe P5316 RI 30.72TB' => {'1.6.0' => ['allow'],},
        'Dell Ent NVMe P5336 RI FIPS 61.44TB' => {'1.2.0' => ['allow'],},
        'Dell Ent NVMe SED P5316 RI 15.36TB' => {'1.6.0' => ['allow'],},
        'Dell Ent NVMe SED P5316 RI 30.72TB' => {'1.6.0' => ['allow'],},
    },
    '1.52' => {
        'Dell Ent NVMe FIPS PM1733a RI 1.92TB' => {'3.1.0' => ['allow'],},
        'Dell Ent NVMe FIPS PM1733a RI 15.36TB' => {'3.1.0' => ['allow'],},
        'Dell Ent NVMe FIPS PM1733a RI 3.84TB' => {'3.1.0' => ['allow'],},
        'Dell Ent NVMe FIPS PM1733a RI 7.68TB' => {'3.1.0' => ['allow'],},
        'SEAGATE STSBSNF3CLAR1200' => {'SE0G' => ['SE07'],},
        'SEAGATE STSBSNF5 CLAR600' => {'SE0G' => ['SE07'],},
        'SEAGATE STSBSNI3CLAR1200' => {'SI0G' => ['SI07', 'SI0F'],},
        'SEAGATE STSBSNI5 CLAR600' => {'SI0G' => ['SI07', 'SI0F'],},
    },
    '1.51' => {
        'Dell Ent NVMe P5336 RI ISE 15.36TB' => {'1.0.1' => ['allow'],},
        'Dell Ent NVMe P5336 RI ISE 30.72TB' => {'1.0.1' => ['allow'],},
        'Dell Ent NVMe P5336 RI ISE 61.44TB' => {'1.0.1' => ['allow'],},
    },
    '1.50.1' => {
        'Dell Ent NVMe 6500 RI 30.72TB' => {'1.0.0' => ['allow'],},
        'Dell Ent NVMe CM6 RI 1.92TB' => {'2.2.1' => ['allow'],},
        'Dell Ent NVMe CM6 RI 15.36TB' => {'2.2.1' => ['allow'],},
        'Dell Ent NVMe CM6 RI 3.84TB' => {'2.2.1' => ['allow'],},
        'Dell Ent NVMe CM6 RI 7.68TB' => {'2.2.1' => ['allow'],},
        'Dell Ent NVMe CM7 FIPS U.2 RI 1.92TB' => {'3.0.1' => ['allow'],},
        'Dell Ent NVMe CM7 FIPS U.2 RI 15.36TB' => {'3.0.1' => ['allow'],},
        'Dell Ent NVMe CM7 FIPS U.2 RI 3.84TB' => {'3.0.1' => ['allow'],},
        'Dell Ent NVMe CM7 FIPS U.2 RI 7.68TB' => {'3.0.1' => ['allow'],},
        'KIOXIA KPM7WRUG1T92' => {'C40E' => ['allow'],},
        'KIOXIA KPM7WRUG3T84' => {'C40E' => ['allow'],},
        'KIOXIA KPM7WRUG7T68' => {'C40E' => ['allow'],},
        'KIOXIA KPM7WRUG960G' => {'C40E' => ['allow'],},
        'KIOXIA P7R15T3A EMC15T3' => {'PA78' => ['PA76'],},
        'KIOXIA P7R15T3D EMC15T3' => {'PD78' => ['PD76'],},
        'KIOXIA P7R3T84A EMC3840' => {'PA78' => ['PA76'],},
        'KIOXIA P7R3T84D EMC3840' => {'PD78' => ['PD76'],},
        'KIOXIA P7R7T68A EMC7680' => {'PA78' => ['PA76'],},
        'KIOXIA P7R7T68D EMC7680' => {'PD78' => ['PD76'],},
        'KIOXIA P7V1T60A EMC1600' => {'PA78' => ['PA76'],},
        'KIOXIA P7V1T60D EMC1600' => {'PD78' => ['PD76'],},
        'KIOXIA P7V3T20A EMC3200' => {'PA78' => ['PA76'],},
        'KIOXIA P7V3T20D EMC3200' => {'PD78' => ['PD76'],},
        'KIOXIA P7V800GA EMC800' => {'PA78' => ['PA76'],},
        'KIOXIA P7V800GD EMC800' => {'PD78' => ['PD76'],},
    },
    '1.48' => {
        'Dell Ent NVMe CM7 U.2 RI 1.92TB' => {'2.0.0' => ['allow'],},
        'Dell Ent NVMe CM7 U.2 RI 15.36TB' => {'2.0.0' => ['allow'],},
        'Dell Ent NVMe CM7 U.2 RI 3.84TB' => {'2.0.0' => ['allow'],},
        'Dell Ent NVMe CM7 U.2 RI 7.68TB' => {'2.0.0' => ['allow'],},
        'KIOXIA KPM7XRUG1T92' => {'C10C' => ['allow'],},
        'KIOXIA KPM7XRUG3T84' => {'C10C' => ['allow'],},
        'KIOXIA KPM7XRUG7T68' => {'C10C' => ['allow'],},
        'KIOXIA KPM7XRUG960G' => {'C10C' => ['allow'],},
    },
    '1.47' => undef,
    '1.46.1' => {
        'Dell Ent NVMe SED P5316 RI 15.36TB' => {'1.2.0' => ['allow'],},
        'Dell Ent NVMe SED P5316 RI 30.72TB' => {'1.2.0' => ['allow'],},
        'ST12000NM002J-2TY133' => {'P20D' => ['P20B'],},
        'ST16000NM002J-2TW133' => {'P20D' => ['P20B'],},
    },
    '1.45.2' => {
        'Dell Ent NVMe FIPS P5316 RI 15.36TB' => {'1.2.0' => ['allow'],},
        'Dell Ent NVMe FIPS P5316 RI 30.72TB' => {'1.2.0' => ['allow'],},
        'KIOXIA KPM7WRUG1T92' => {'C40A' => ['allow'],},
        'KIOXIA KPM7WRUG3T84' => {'C40A' => ['allow'],},
        'KIOXIA KPM7WRUG7T68' => {'C40A' => ['allow'],},
        'KIOXIA KPM7WRUG960G' => {'C40A' => ['allow'],},
        'SEAGATE STSBSNI3CLAR1200' => {'SI0F' => ['SI07'],},
        'SEAGATE STSBSNI5 CLAR600' => {'SI0F' => ['SI07'],},
        'ST4000NM030B-2TF133' => {'L207' => ['L205'],},
        'ST8000NM023B-2TJ133' => {'L207' => ['L205'],},
    },
    '1.45' => {
        'Dell Ent NVMe P5316 RI 15.36TB' => {'1.2.0' => ['allow'],},
        'Dell Ent NVMe P5316 RI 30.72TB' => {'1.2.0' => ['allow'],},
        'KIOXIA KPM7XRUG1T92' => {'C10A' => ['allow'],},
        'KIOXIA KPM7XRUG3T84' => {'C10A' => ['allow'],},
        'KIOXIA KPM7XRUG7T68' => {'C10A' => ['allow'],},
        'KIOXIA KPM7XRUG960G' => {'C10A' => ['allow'],},
        'ST20000NM008D-3DJ133' => {'G206' => ['G202', 'G203'],},
    },
    '1.44' => {
        'Dell Ent NVMe P5520 RI 3.84TB' => {'1.0.0' => ['allow'],},
        'SAMSUNG MZILG1T9HCJRAD3' => {'DSG7' => ['allow'],},
        'SAMSUNG MZILG3T8HCLSAD3' => {'DSG7' => ['allow'],},
        'SAMSUNG MZILG7T6HBLAAD3' => {'DSG7' => ['allow'],},
        'SAMSUNG MZILG960HCHQAD3' => {'DSG7' => ['allow'],},
    },
    '1.43.2' => {
        'Dell Ent NVMe FIPS CM6 RI 1.92TB' => {'3.1.3' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI 3.84TB' => {'3.1.3' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI 7.68TB' => {'3.1.3' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI15.36TB' => {'3.1.3' => ['allow'],},
        'Dell Ent NVMe FIPS PM1733a RI 1.92TB' => {'3.0.0' => ['allow'],},
        'Dell Ent NVMe FIPS PM1733a RI 15.36TB' => {'3.0.0' => ['allow'],},
        'Dell Ent NVMe FIPS PM1733a RI 3.84TB' => {'3.0.0' => ['allow'],},
        'Dell Ent NVMe FIPS PM1733a RI 7.68TB' => {'3.0.0' => ['allow'],},
        'Dell Ent NVMe PM1733a RI 1.92TB' => {'1.0.0' => ['allow'],},
        'Dell Ent NVMe PM1733a RI 3.84TB' => {'1.0.0' => ['allow'],},
        'Dell Ent NVMe SED P5316 RI 15.36TB' => {'1.1.0' => ['allow'],},
        'Dell Ent NVMe SED P5316 RI 30.72TB' => {'1.1.0' => ['allow'],},
        'KIOXIA KPM7XRUG1T92' => {'C108' => ['allow'],},
        'KIOXIA KPM7XRUG3T84' => {'C108' => ['allow'],},
        'KIOXIA KPM7XRUG7T68' => {'C108' => ['allow'],},
        'KIOXIA KPM7XRUG960G' => {'C108' => ['allow'],},
        'SEAGATE ST20000NM005D' => {'G705' => ['G703'],},
    },
    '1.42.5' => {
        'Dell Ent NVMe AGN RI U.2 1.92TB' => {'2.1.2' => ['allow'],},
        'Dell Ent NVMe AGN RI U.2 15.36TB' => {'2.1.2' => ['allow'],},
        'Dell Ent NVMe AGN RI U.2 3.84TB' => {'2.1.2' => ['allow'],},
        'Dell Ent NVMe AGN RI U.2 7.68TB' => {'2.1.2' => ['allow'],},
        'Dell Ent NVMe CM6 RI 1.92TB' => {'2.1.8' => ['allow'],},
        'Dell Ent NVMe CM6 RI 15.36TB' => {'2.1.8' => ['allow'],},
        'Dell Ent NVMe CM6 RI 3.84TB' => {'2.1.8' => ['allow'],},
        'Dell Ent NVMe CM6 RI 7.68TB' => {'2.1.8' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI 1.92TB' => {'3.0.9' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI 3.84TB' => {'3.0.9' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI 7.68TB' => {'3.0.9' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI15.36TB' => {'3.0.9' => ['allow'],},
        'Dell Ent NVMe P5316 RI 15.36TB' => {'1.0.4' => ['allow'],},
        'Dell Ent NVMe P5316 RI 30.72TB' => {'1.0.4' => ['allow'],},
        'Dell Ent NVMe PM1733a RI 15.36TB' => {'1.0.0' => ['allow'],},
        'Dell Ent NVMe PM1733a RI 7.68TB' => {'1.0.0' => ['allow'],},
        'Dell Ent NVMe v2 AGN RI U.2 1.92TB' => {'2.0.0' => ['allow'],},
        'Dell Ent NVMe v2 AGN RI U.2 15.36TB' => {'2.0.0' => ['allow'],},
        'Dell Ent NVMe v2 AGN RI U.2 3.84TB' => {'2.0.0' => ['allow'],},
        'Dell Ent NVMe v2 AGN RI U.2 7.68TB' => {'2.0.0' => ['allow'],},
        'Dell Express Flash CD5 7.68T SFF' => {'1.2.0' => ['allow'],},
        'Dell Express Flash NVMe P4510 2TB SFF' => {'VDV1DP25' => ['allow'],},
        'Dell Express Flash NVMe P4510 4TB SFF' => {'VDV1DP25' => ['allow'],},
        'HDS725050KLA360' => {'K2AOAD1A' => ['K2AOA10C', 'K2AOA51A', 'K2AOAB0A'],},
        'HGST H5472606CLAR6000' => {'NDY0' => ['N9L0'],},
        'HGST H5H72808CLAR8000' => {'MDY0' => ['M9L0'],},
        'HGST H5S726T4CLAR4000' => {'S9C0' => ['S430'],},
        'HGST HUC101812CSS200' => {'AE00' => ['A9C1', 'AD40'],},
        'HGST HUC101830CSS200' => {'AE00' => ['AAA2', 'AD40'],},
        'HGST HUC101860CSS200' => {'AE00' => ['AAA2', 'AD40'],},
        'HGST HUC101890CSS200' => {'AE00' => ['AAA2', 'AD40'],},
        'HGST HUC101890CSS205' => {'DE00' => ['DAA2', 'DDY0'],},
        'HGST HUS724020ALA640' => {'MF6OAC50' => ['MF6OA8B0', 'MF6OAAC0', 'MF6OABW0'],},
        'HGST HUS724030ALA640' => {'MF8OAC50' => ['MF8OA8B0', 'MF8OAAC0', 'MF8OABW0'],},
        'HGST HUS724040ALA640' => {'MFAOAC50' => ['MFAOA8B0', 'MFAOAAC0', 'MFAOABW0'],},
        'HGST HUS726060ALA640' => {'AHIST1EZ' => ['AHIST1ER'],},
        'HGST HUSMM1620ASS200' => {'A204' => ['A100'],},
        'HGST HUSMM1640ASS200' => {'A204' => ['A100'],},
        'HGST HUSMM1680ASS200' => {'A204' => ['A100'],},
        'HGST HUSMM1680ASS205' => {'D32A' => ['D252', 'd322', 'D326', 'D328'],},
        'HGST HUSMM8020ASS200' => {'A210' => ['A100', 'A160'],},
        'HGST HUSMM8040ASS200' => {'A210' => ['A100', 'A160'],},
        'HGST HUSMM8080ASS200' => {'A210' => ['A100', 'A160'],},
        'HITACHI H4SMM321CLAR1600' => {'S177' => ['S116'],},
        'HITACHI H4SMM328 CLAR800' => {'S177' => ['S116'],},
        'HITACHI H4SMR321CLAR1600' => {'S177' => ['S116'],},
        'HITACHI H4SMR323CLAR3200' => {'S177' => ['S116'],},
        'HITACHI H4SMR324 CLAR400' => {'S177' => ['S116'],},
        'HITACHI H4SMR328 CLAR800' => {'S177' => ['S116'],},
        'HITACHI H4STR763CLAR3840' => {'S52K' => ['S520'],},
        'HITACHI H4STR767CLAR7680' => {'S52K' => ['S520'],},
        'HITACHI H5472602CLAR2000' => {'DDW0' => ['D930', 'DD9H'],},
        'HITACHI H5472604CLAR4000' => {'DDW0' => ['D930', 'DD9H'],},
        'HITACHI H54726T4CLAR4000' => {'D9C0' => ['D430'],},
        'HITACHI H54726T6CLAR6000' => {'D9C0' => ['D430'],},
        'HITACHI H54728T8CLAR8000' => {'D9C0' => ['D430'],},
        'HITACHI H5C10181CLAR1200' => {'DE00' => ['DAA2', 'DDY0'],},
        'HITACHI H5C10186 CLAR600' => {'DE00' => ['DAA2', 'DDY0'],},
        'HITACHI H5H72101CLAR10T0' => {'D614' => ['D32A'],},
        'HITACHI H5H72108CLAR8000' => {'D614' => ['D32A'],},
        'HITACHI H5SMM321CLAR1600' => {'D176' => ['S116'],},
        'HITACHI H5SMM328 CLAR800' => {'D176' => ['S116'],},
        'HITACHI H5SMR321CLAR1600' => {'D176' => ['S116'],},
        'HITACHI H5SMR323CLAR3200' => {'D176' => ['S116'],},
        'HITACHI H5SMR324 CLAR400' => {'D176' => ['S116'],},
        'HITACHI H5SMR328 CLAR800' => {'D176' => ['S116'],},
        'HITACHI HUC10181CLAR1200' => {'AE00' => ['L9H0'],},
        'HITACHI HUC10186 CLAR600' => {'AE00' => ['L9H0'],},
        'HITACHI HUC106030CSS600' => {'A430' => ['A180', 'A202'],},
        'HITACHI HUC106060CSS600' => {'A430' => ['A180', 'A202'],},
        'HITACHI HUC109030CSS600' => {'A5B0' => ['A222', 'A2D0', 'A350', 'A440'],},
        'HITACHI HUC109060CSS600' => {'A5B0' => ['A222', 'A2D0', 'A350', 'A440'],},
        'HITACHI HUC109090CSS600' => {'A5B0' => ['A222', 'A2D0', 'A350', 'A440'],},
        'HITACHI HUS156045VLS600' => {'A760' => ['A5D0'],},
        'HITACHI HUSMM151CLAR1600' => {'D348' => ['D344', 'D346'],},
        'HITACHI HUSSL4010ASS600' => {'A182' => ['A170'],'A18M' => ['A131'],},
        'HITACHI HUSSL4010BSS600' => {'A1D0' => ['A090', 'A110', 'A190'],},
        'HITACHI HUSSL4020ASS600' => {'A182' => ['A170'],'A18M' => ['A131'],},
        'HITACHI HUSSL4020BSS600' => {'A1D0' => ['A090', 'A110', 'A190'],},
        'HITACHI HUSSL4040ASS600' => {'A182' => ['A170'],'A18M' => ['A131'],},
        'HITACHI HUSSL4040BSS600' => {'A1D0' => ['A090', 'A110', 'A190'],},
        'HUH728080ALN600' => {'A4EITD40' => ['A4EIT921'],},
        'HUS726020ALA610' => {'A5EITDY0' => ['A5EIT922', 'A5EITD40'],},
        'HUS726020ALN610' => {'APGNTDY0' => ['APGNT840', 'APGNTDCH'],},
        'HUS726040ALA610' => {'A5EITDY0' => ['A5EIT922', 'A5EITD40'],},
        'HUS726040ALN610' => {'APGNTDY0' => ['APGNT840', 'APGNTDCH'],},
        'HUS726060ALN610' => {'APEITDY0' => ['APEIT922', 'APEITD40'],},
        'HUS726T4TALA6L0' => {'VLEIT9C0' => ['VLEIT430'],},
        'HUS726T4TALN6L0' => {'VKEMT9C0' => ['VKEMT430'],},
        'HUS726T6TALN6L0' => {'VKEIT9C0' => ['VKEIT430'],},
        'HUS728T8TALN6L0' => {'V8EMT9C0' => ['V8EMT430'],},
        'Hitachi HUA722010CLA330' => {'JP4OA3PA' => ['JP4OA3GB', 'JP4OA3MA'],},
        'Hitachi HUA722020ALA330' => {'JKAOA3PA' => ['JKAOA3GA', 'JKAOA3MA', 'JKAOA20N', 'JKAOA28A'],},
        'Hitachi HUA722050CLA330' => {'JP2OA3PA' => ['JP2OA3GB', 'JP2OA3MA'],},
        'Hitachi HUA723020ALA640' => {'MK7OAA50' => ['MK7OA6L0', 'MK7OA8P0', 'MK7OA580', 'MK7OA5V0'],},
        'Hitachi HUA723030ALA640' => {'MKAOAA50' => ['MKAOA6L0', 'MKAOA8P0', 'MKAOA3B0', 'MKAOA580', 'MKAOA5V0'],},
        'KIOXIA KPM6WRUG1T92' => {'BD0D' => ['allow'],},
        'KIOXIA KPM6WRUG3T84' => {'BD0D' => ['allow'],},
        'KIOXIA KPM6WRUG7T68' => {'BD0D' => ['allow'],},
        'KIOXIA KPM6WVUG960G' => {'BD0D' => ['allow'],},
        'KIOXIA KPM6XRUG1T92' => {'BA0D' => ['allow'],},
        'KIOXIA KPM6XRUG3T84' => {'BA0D' => ['allow'],},
        'KIOXIA KPM6XRUG7T68' => {'BA0D' => ['allow'],},
        'KIOXIA KPM6XRUG960G' => {'BA0D' => ['allow'],},
        'KIOXIA P6M400GA EMC400' => {'PA68' => ['PA65'],},
        'KIOXIA P6M400GD EMC400' => {'PD68' => ['PD65'],},
        'KIOXIA P6R15T3A EMC15T3' => {'PA68' => ['PA65'],},
        'KIOXIA P6R15T3D EMC15T3' => {'PD68' => ['PD65'],},
        'KIOXIA P6R3T84A EMC3840' => {'PA68' => ['PA65'],},
        'KIOXIA P6R3T84D EMC3840' => {'PD68' => ['PD65'],},
        'KIOXIA P6R7T68A EMC7680' => {'PA68' => ['PA65'],},
        'KIOXIA P6R7T68D EMC7680' => {'PD68' => ['PD65'],},
        'KIOXIA P6V1T60A EMC1600' => {'PA68' => ['PA65'],},
        'KIOXIA P6V1T60D EMC1600' => {'PD68' => ['PD65'],},
        'KIOXIA P6V3T20A EMC3200' => {'PA68' => ['PA65'],},
        'KIOXIA P6V3T20D EMC3200' => {'PD68' => ['PD65'],},
        'KIOXIA P6V800GA EMC800' => {'PA68' => ['PA65'],},
        'KIOXIA P6V800GD EMC800' => {'PD68' => ['PD65'],},
        'SAMSUNG MZILT1T9HBJR0D3' => {'DSA3' => ['allow'],},
        'SAMSUNG MZILT3T8HBLS0D3' => {'DSA3' => ['allow'],},
        'SAMSUNG MZILT7T6HALA0D3' => {'DSA3' => ['allow'],},
        'SAMSUNG MZILT960HBHQ0D3' => {'DSA3' => ['allow'],},
        'SAMSUNG P043S15T EMC15T3' => {'ESV5' => ['ESV4'],},
        'SEAGATE ST3000NM0063' => {'G007' => ['0003', '0004', '0006', 'C007', 'F007'],},
        'SEAGATE ST33000652SS' => {'A006' => ['0005', '0006'],},
        'SEAGATE ST4000NM0063' => {'G007' => ['0003', '0004', '0006', 'C007', 'F007'],},
        'SEAGATE ST6000NM0114' => {'KFH5' => ['KF05'],},
        'SEAGATE STCRSKF5CLAR2000' => {'CV06' => ['CV05'],},
        'SEAGATE STMFSKF2CLAR4000' => {'BV01' => ['BV00'],},
        'SEAGATE STMFSKF4CLAR2000' => {'BV01' => ['BV00'],},
        'SEAGATE XS1920SE70114' => {'3D01' => ['allow'],},
        'SEAGATE XS3840SE70114' => {'3D01' => ['allow'],},
        'SEAGATE XS7680SE70114' => {'3D01' => ['allow'],},
        'SEAGATE XS960SE70114' => {'3D01' => ['allow'],},
        'ST1000NM0033-9ZM173' => {'EN09' => ['SN03', 'SNG4', 'EN06', 'EN07'],},
        'ST20000NM008D-3DJ133' => {'G203' => ['G202'],},
        'ST2000NM0033-9ZM175' => {'EN09' => ['SN03', 'SNG4', 'EN06', 'EN07'],},
        'ST3000NM0033-9ZM178' => {'EN09' => ['SN03', 'SNG4', 'EN06', 'EN07'],},
        'ST4000NM0033-9ZM170' => {'EN09' => ['SN02', 'SN03', 'SNG4', 'EN06', 'EN07'],},
        'ST8000NM0045-1RL112' => {'UG07' => ['UG05'],},
        'STEC    M8ISB2B-200UC' => {'2832' => ['2686'],},
        'STEC    MACH8 IOPS' => {'2828' => ['2269', '2366', '2608', '2755'],},
        'TOSHIBA KPM5WVUG1T92' => {'B320' => ['allow'],},
        'TOSHIBA KPM5WVUG3T84' => {'B320' => ['allow'],},
        'TOSHIBA KPM5WVUG960G' => {'B320' => ['allow'],},
        'TOSHIBA KPM5XRUG1T92' => {'B01C' => ['allow'],},
        'TOSHIBA KPM5XRUG3T84' => {'B01C' => ['allow'],},
        'TOSHIBA KPM5XRUG7T68' => {'B01C' => ['allow'],},
        'TOSHIBA KPM5XRUG960G' => {'B01C' => ['allow'],},
        'TOSHIBA KRM5XRUG1T92' => {'B709' => ['allow'],},
        'TOSHIBA KRM5XRUG3T84' => {'B709' => ['allow'],},
        'TOSHIBA KRM5XRUG960G' => {'B709' => ['allow'],},
        'WDC WD1002FBYS-02A6B0' => {'03.02C06' => ['03.01C06'],},
    },
    '1.42.3' => {
        'Dell Ent NVMe P5316 RI 15.36TB' => {'1.0.4' => ['allow'],},
        'Dell Ent NVMe P5316 RI 30.72TB' => {'1.0.4' => ['allow'],},
        'Dell Ent NVMe PM1733a RI 15.36TB' => {'1.0.0' => ['allow'],},
        'Dell Ent NVMe PM1733a RI 7.68TB' => {'1.0.0' => ['allow'],},
        'Dell Express Flash NVMe P4510 2TB SFF' => {'VDV1DP25' => ['allow'],},
        'Dell Express Flash NVMe P4510 4TB SFF' => {'VDV1DP25' => ['allow'],},
        'ST20000NM008D-3DJ133' => {'G203' => ['G202'],},
    },
    '1.41.1' => undef,
    '1.41' => {
	'Dell Ent NVMe CM6 RI 1.92TB' => {'2.1.8' => ['allow'],},
        'Dell Ent NVMe CM6 RI 15.36TB' => {'2.1.8' => ['allow'],},
        'Dell Ent NVMe CM6 RI 3.84TB' => {'2.1.8' => ['allow'],},
        'Dell Ent NVMe CM6 RI 7.68TB' => {'2.1.8' => ['allow'],},
    },
    '1.40.1' => {
        'Dell Ent NVMe FIPS CM6 RI 1.92TB' => {'3.0.9' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI 3.84TB' => {'3.0.9' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI 7.68TB' => {'3.0.9' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI15.36TB' => {'3.0.9' => ['allow'],},
        'HGST H5S726T4CLAR4000' => {'S9C0' => ['S430'],},
        'HUS726T4TALN6L0' => {'VKEMT9C0' => ['VKEMT430'],},
        'HUS728T8TALN6L0' => {'V8EMT9C0' => ['V8EMT430'],},
        'SEAGATE XS1920SE70114' => {'3D01' => ['allow'],},
        'SEAGATE XS3840SE70114' => {'3D01' => ['allow'],},
        'SEAGATE XS7680SE70114' => {'3D01' => ['allow'],},
        'SEAGATE XS960SE70114' => {'3D01' => ['allow'],},
    },
    '1.39.1' => {
        'Dell Ent NVMe AGN RI U.2 1.92TB' => {'2.1.2' => ['allow'],},
        'Dell Ent NVMe AGN RI U.2 15.36TB' => {'2.1.2' => ['allow'],},
        'Dell Ent NVMe AGN RI U.2 3.84TB' => {'2.1.2' => ['allow'],},
        'Dell Ent NVMe AGN RI U.2 7.68TB' => {'2.1.2' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI 1.92TB' => {'3.0.6' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI 3.84TB' => {'3.0.6' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI 7.68TB' => {'3.0.6' => ['allow'],},
        'Dell Ent NVMe FIPS CM6 RI15.36TB' => {'3.0.6' => ['allow'],},
        'Dell Ent NVMe v2 AGN RI U.2 1.92TB' => {'2.0.0' => ['allow'],},
        'Dell Ent NVMe v2 AGN RI U.2 15.36TB' => {'2.0.0' => ['allow'],},
        'Dell Ent NVMe v2 AGN RI U.2 3.84TB' => {'2.0.0' => ['allow'],},
        'Dell Ent NVMe v2 AGN RI U.2 7.68TB' => {'2.0.0' => ['allow'],},
        'HITACHI H54726T6CLAR6000' => {'D9C0' => ['D430'],},
        'HITACHI H54728T8CLAR8000' => {'D9C0' => ['D430'],},
        'HUS726T4TALA6L0' => {'VLEIT9C0' => ['VLEIT430'],},
        'HUS726T6TALN6L0' => {'VKEIT9C0' => ['VKEIT430'],},
    },
    '1.38.1' => {
        'Dell Ent NVMe CM6 RI 1.92TB' => {'2.1.5' => ['allow'],},
        'Dell Ent NVMe CM6 RI 15.36TB' => {'2.1.5' => ['allow'],},
        'Dell Ent NVMe CM6 RI 3.84TB' => {'2.1.5' => ['allow'],},
        'Dell Ent NVMe CM6 RI 7.68TB' => {'2.1.5' => ['allow'],},
        'Dell Express Flash CD5 7.68T SFF' => {'1.2.0' => ['allow'],},
        'HITACHI H54726T4CLAR4000' => {'D9C0' => ['D430'],},
        'KIOXIA KPM6WRUG1T92' => {'BD08' => ['allow'],},
        'KIOXIA KPM6WRUG3T84' => {'BD08' => ['allow'],},
        'KIOXIA KPM6WRUG7T68' => {'BD08' => ['allow'],},
        'KIOXIA KPM6WVUG960G' => {'BD08' => ['allow'],},
        'KIOXIA KPM6XRUG1T92' => {'BA08' => ['allow'],},
        'KIOXIA KPM6XRUG3T84' => {'BA08' => ['allow'],},
        'KIOXIA KPM6XRUG7T68' => {'BA08' => ['allow'],},
        'KIOXIA KPM6XRUG960G' => {'BA08' => ['allow'],},
    },
    '1.37.2' => {
        'Dell Ent NVMe AGN RI U.2 15.36TB' => {'2.0.2' => ['allow'],},
        'Dell Express Flash CD5 7.68T SFF' => {'1.1.5' => ['allow'],},
        'SAMSUNG MZILT7T6HALA0D3' => {'DSA3' => ['allow'],},
        'SEAGATE STCRSKF5CLAR2000' => {'CV06' => ['CV05'],},
        'TOSHIBA KPM5WVUG1T92' => {'B320' => ['allow'],},
        'TOSHIBA KPM5WVUG3T84' => {'B320' => ['allow'],},
        'TOSHIBA KPM5WVUG960G' => {'B320' => ['allow'],},
        'TOSHIBA KPM5XRUG7T68' => {'B01C' => ['allow'],},
    },
    '1.35' => {
        'Dell Ent NVMe AGN RI U.2 1.92TB' => {'2.0.2' => ['allow'],},
        'Dell Ent NVMe AGN RI U.2 3.84TB' => {'2.0.2' => ['allow'],},
        'Dell Ent NVMe AGN RI U.2 7.68TB' => {'2.0.2' => ['allow'],},
        'Dell Express Flash CD5 7.68T SFF' => {'1.1.1' => ['allow'],},
        'Dell Express Flash NVMe P4510 2TB SFF' => {'VDV1DP23' => ['allow'],},
        'Dell Express Flash NVMe P4510 4TB SFF' => {'VDV1DP23' => ['allow'],},
        'SAMSUNG MZILT1T9HBJR0D3' => {'DSA3' => ['allow'],},
        'SAMSUNG MZILT3T8HBLS0D3' => {'DSA3' => ['allow'],},
        'SAMSUNG MZILT960HBHQ0D3' => {'DSA3' => ['allow'],},
        'TOSHIBA KPM5XRUG1T92' => {'B01C' => ['allow'],},
        'TOSHIBA KPM5XRUG3T84' => {'B01C' => ['allow'],},
        'TOSHIBA KPM5XRUG960G' => {'B01C' => ['allow'],},
        'TOSHIBA KRM5XRUG1T92' => {'B709' => ['allow'],},
        'TOSHIBA KRM5XRUG3T84' => {'B709' => ['allow'],},
        'TOSHIBA KRM5XRUG960G' => {'B709' => ['allow'],},
    },
    '1.34' => undef,
    '1.33' => {
        'HGST HUC101812CSS200' => {'AE00' => ['A9C1', 'AD40'],},
        'HGST HUC101830CSS200' => {'AE00' => ['AAA2', 'AD40'],},
        'HGST HUC101860CSS200' => {'AE00' => ['AAA2', 'AD40'],},
        'HGST HUC101890CSS200' => {'AE00' => ['AAA2', 'AD40'],},
        'HGST HUC101890CSS205' => {'DE00' => ['DAA2', 'DDY0'],},
        'HITACHI H4SMM321CLAR1600' => {'S177' => ['S116'],},
        'HITACHI H4SMM328 CLAR800' => {'S177' => ['S116'],},
        'HITACHI H4SMR321CLAR1600' => {'S177' => ['S116'],},
        'HITACHI H4SMR323CLAR3200' => {'S177' => ['S116'],},
        'HITACHI H4SMR324 CLAR400' => {'S177' => ['S116'],},
        'HITACHI H4SMR328 CLAR800' => {'S177' => ['S116'],},
        'HITACHI H4STR763CLAR3840' => {'S52K' => ['S520'],},
        'HITACHI H4STR767CLAR7680' => {'S52K' => ['S520'],},
        'HITACHI H5C10181CLAR1200' => {'DE00' => ['DAA2', 'DDY0'],},
        'HITACHI H5C10186 CLAR600' => {'DE00' => ['DAA2', 'DDY0'],},
        'HITACHI H5H72101CLAR10T0' => {'D614' => ['D32A'],},
        'HITACHI H5H72108CLAR8000' => {'D614' => ['D32A'],},
        'HITACHI H5SMM321CLAR1600' => {'D176' => ['S116'],},
        'HITACHI H5SMM328 CLAR800' => {'D176' => ['S116'],},
        'HITACHI H5SMR321CLAR1600' => {'D176' => ['S116'],},
        'HITACHI H5SMR323CLAR3200' => {'D176' => ['S116'],},
        'HITACHI H5SMR324 CLAR400' => {'D176' => ['S116'],},
        'HITACHI H5SMR328 CLAR800' => {'D176' => ['S116'],},
        'HITACHI HUC10181CLAR1200' => {'AE00' => ['L9H0'],},
        'HITACHI HUC10186 CLAR600' => {'AE00' => ['L9H0'],},
        'HUS726020ALN610' => {'APGNTDY0' => ['APGNT840', 'APGNTDCH'],},
        'HUS726040ALN610' => {'APGNTDY0' => ['APGNT840', 'APGNTDCH'],},
        'SEAGATE STMFSKF2CLAR4000' => {'BV01' => ['BV00'],},
        'SEAGATE STMFSKF4CLAR2000' => {'BV01' => ['BV00'],},
    },
    '1.32' => {
        'HUS726020ALA610' => {'A5EITDY0' => ['A5EIT922', 'A5EITD40'],},
        'HUS726040ALA610' => {'A5EITDY0' => ['A5EIT922', 'A5EITD40'],},
        'HUS726060ALN610' => {'APEITDY0' => ['APEIT922', 'APEITD40'],},
    },
    '1.31' => undef,
    '1.30' => {
        'HDS725050KLA360' => {'K2AOAD1A' => ['K2AOA10C', 'K2AOA51A', 'K2AOAB0A'],},
        'HGST H5472606CLAR6000' => {'NDY0' => ['N9L0'],},
        'HGST H5H72808CLAR8000' => {'MDY0' => ['M9L0'],},
        'HGST HUC101812CSS200' => {'AD40' => ['A9C1'],},
        'HGST HUC101830CSS200' => {'AD40' => ['AAA2'],},
        'HGST HUC101860CSS200' => {'AD40' => ['AAA2'],},
        'HGST HUC101890CSS200' => {'AD40' => ['AAA2'],},
        'HGST HUC101890CSS205' => {'DDY0' => ['DAA2'],},
        'HGST HUS724020ALA640' => {'MF6OAC50' => ['MF6OA8B0', 'MF6OAAC0', 'MF6OABW0'],},
        'HGST HUS724030ALA640' => {'MF8OAC50' => ['MF8OA8B0', 'MF8OAAC0', 'MF8OABW0'],},
        'HGST HUS724040ALA640' => {'MFAOAC50' => ['MFAOA8B0', 'MFAOAAC0', 'MFAOABW0'],},
        'HGST HUS726060ALA640' => {'AHIST1EZ' => ['AHIST1ER'],},
        'HGST HUSMM1620ASS200' => {'A204' => ['A100'],},
        'HGST HUSMM1640ASS200' => {'A204' => ['A100'],},
        'HGST HUSMM1680ASS200' => {'A204' => ['A100'],},
        'HGST HUSMM1680ASS205' => {'D32A' => ['D252', 'd322', 'D326', 'D328'],},
        'HGST HUSMM8020ASS200' => {'A210' => ['A100', 'A160'],},
        'HGST HUSMM8040ASS200' => {'A210' => ['A100', 'A160'],},
        'HGST HUSMM8080ASS200' => {'A210' => ['A100', 'A160'],},
        'HITACHI H5472602CLAR2000' => {'DDW0' => ['D930', 'DD9H'],},
        'HITACHI H5472604CLAR4000' => {'DDW0' => ['D930', 'DD9H'],},
        'HITACHI H5C10181CLAR1200' => {'DDY0' => ['DAA2'],},
        'HITACHI H5C10186 CLAR600' => {'DDY0' => ['DAA2'],},
        'HITACHI HUC106030CSS600' => {'A430' => ['A180', 'A202'],},
        'HITACHI HUC106060CSS600' => {'A430' => ['A180', 'A202'],},
        'HITACHI HUC109030CSS600' => {'A5B0' => ['A222', 'A2D0', 'A350', 'A440'],},
        'HITACHI HUC109060CSS600' => {'A5B0' => ['A222', 'A2D0', 'A350', 'A440'],},
        'HITACHI HUC109090CSS600' => {'A5B0' => ['A222', 'A2D0', 'A350', 'A440'],},
        'HITACHI HUS156045VLS600' => {'A760' => ['A5D0'],},
        'HITACHI HUSMM151CLAR1600' => {'D348' => ['D344', 'D346'],},
        'HITACHI HUSSL4010ASS600' => {'A182' => ['A170'],'A18M' => ['A131'],},
        'HITACHI HUSSL4010BSS600' => {'A1D0' => ['A090', 'A110', 'A190'],},
        'HITACHI HUSSL4020ASS600' => {'A182' => ['A170'],'A18M' => ['A131'],},
        'HITACHI HUSSL4020BSS600' => {'A1D0' => ['A090', 'A110', 'A190'],},
        'HITACHI HUSSL4040ASS600' => {'A182' => ['A170'],'A18M' => ['A131'],},
        'HITACHI HUSSL4040BSS600' => {'A1D0' => ['A090', 'A110', 'A190'],},
        'HUH728080ALN600' => {'A4EITD40' => ['A4EIT921'],},
        'HUS726020ALA610' => {'A5EITD40' => ['A5EIT922'],},
        'HUS726020ALN610' => {'APGNTDCH' => ['APGNT840'],},
        'HUS726040ALA610' => {'A5EITD40' => ['A5EIT922'],},
        'HUS726040ALN610' => {'APGNTDCH' => ['APGNT840'],},
        'HUS726060ALN610' => {'APEITD40' => ['APEIT922'],},
        'Hitachi HUA722010CLA330' => {'JP4OA3PA' => ['JP4OA3GB', 'JP4OA3MA'],},
        'Hitachi HUA722020ALA330' => {'JKAOA3PA' => ['JKAOA3GA', 'JKAOA3MA', 'JKAOA20N', 'JKAOA28A'],},
        'Hitachi HUA722050CLA330' => {'JP2OA3PA' => ['JP2OA3GB', 'JP2OA3MA'],},
        'Hitachi HUA723020ALA640' => {'MK7OAA50' => ['MK7OA6L0', 'MK7OA8P0', 'MK7OA580', 'MK7OA5V0'],},
        'Hitachi HUA723030ALA640' => {'MKAOAA50' => ['MKAOA6L0', 'MKAOA8P0', 'MKAOA3B0', 'MKAOA580', 'MKAOA5V0'],},
        'SAMSUNG P043S15T EMC15T3' => {'ESV5' => ['ESV4'],},
        'SEAGATE ST3000NM0063' => {'G007' => ['0003', '0004', '0006', 'C007', 'F007'],},
        'SEAGATE ST33000652SS' => {'A006' => ['0005', '0006'],},
        'SEAGATE ST4000NM0063' => {'G007' => ['0003', '0004', '0006', 'C007', 'F007'],},
        'SEAGATE ST6000NM0114' => {'KFH5' => ['KF05'],},
        'ST1000NM0033-9ZM173' => {'EN09' => ['SN03', 'SNG4', 'EN06', 'EN07'],},
        'ST2000NM0033-9ZM175' => {'EN09' => ['SN03', 'SNG4', 'EN06', 'EN07'],},
        'ST3000NM0033-9ZM178' => {'EN09' => ['SN03', 'SNG4', 'EN06', 'EN07'],},
        'ST4000NM0033-9ZM170' => {'EN09' => ['SN02', 'SN03', 'SNG4', 'EN06', 'EN07'],},
        'ST8000NM0045-1RL112' => {'UG07' => ['UG05'],},
        'STEC    M8ISB2B-200UC' => {'2832' => ['2686'],},
        'STEC    MACH8 IOPS' => {'2828' => ['2269', '2366', '2608', '2755'],},
        'WDC WD1002FBYS-02A6B0' => {'03.02C06' => ['03.01C06'],},
    },
   );
# Activity durations
# Base -> Time required to validate checksum and complete and pre/post
#  steps such as installing/uninstalling a firmware package
# Drives/Nodes -> Time required per drive or node for updates to be performed
# Regex Expression -> Used to add additional time based on regex criteria
#  These must be accomodated for in the getActivityDuration routine
our %Durations = (
    'Drive Support Package' => {
	'Base' => 15,
	'Nodes' => 10,
	'Drives' => 1.5,
    },
    'Node Firmware Update' => {
	'Base' => 15,
	'Nodes' => 10,
	'^(CMC|CMD|CAR)' => 7,
	'^(DE|EP)' => 3,
	'Integrated Remote Access Controller' => 10,
	'BIOS' => 10,
	'^Mellanox' => 2,
    },
    'OneFS Upgrade' => {
	'Base' => 25,
	'Nodes' => 15,
    },
    'Reboot' => {
	'Nodes' => 8,
	'^(S210|X410|X210|NL410)' => 3,
	'^(HD400)' => 10,
	'^(?:A2000?|A3000?|A250|A2050)' => 3,
	'^(?:H400|H500|H600|H7000?|H5600|H550|H5650)' => 3,
	'^(?:F2[01]0|F600|F800|F810|F850|F710)' => 3,
	'^(?:F9[01]0)' => 30,
    },
    'Patch' => {
	'Base' => 10,
	'Nodes' => 14,
	'Service Restart' => 4.5,
    },
    );
# Message container
our %MSG = (
    'KB_1' => 'For more information refer to KB article %1$s found at https://www.dell.com/support/kbdoc/%1$s.',
    'KB_INT_1' => 'For assistance contact Dell EMC Support and reference KB %s.',
    'UNSUP_VIRT' => 'Not supported on virtual platforms',
    'UNSUP_COMP' => 'Not supported on SmartLock Compliance mode enabled clusters',
    'UNSUP_LOGS' => 'Not supported for log gather analysis',
    'UNSUP_GEN5' => 'Supported on clusters with Gen5 nodes',
    'UNSUP_GEN6' => 'Supported on clusters with Gen6 nodes',
    'UNSUP_GEN45' => 'Supported on clusters with Gen4 and Gen5 nodes',
    'UNSUP_GEN56' => 'Supported on clusters with Gen5 and Gen6 nodes',
    'UNSUP_HW_1' => 'Supported on clusters with the following node types: %s',
    'UNSUP_TGT' => 'Supported when the parameter target_version is set',
    'UNSUP_TGT_1' => 'Supported when the parameter target_version is set to %s',
    'UNSUP_TGT_NO' => 'Not supported when the parameter target_version is set for a OneFS Upgrade',
    'UNSUP_CUR' => 'Not supported on the current OneFS release',
    'UNSUP_QP'  =>  'Not Supported for HA - only 1 IB interface in use',
    'UNSUP_NO_RISK' => 'Not supported for OneFS releases not at risk',
    'UNSUP_LIC_1' => 'Supported on clusters with the following enabled licenses: %s',
    'UNSUP_SVC_1' => 'Supported on clusters with the following enabled services: %s',
    'UNSUP_SIQ_TAR' => 'Supported on SyncIQ target clusters',
    'UNSUP_NO_FW_1' => 'No %s was selected',
    'UNSUP_DSP_OLD' => 'The Drive Support Package is not supported on older node types',
    'UNSUP_BE_DN' => 'No back-end Dell Networking switches detected',
    'UNSUP_BE_IB' => 'No back-end InfiniBand network detected',
    'UNSUP_VAR_1GB' => 'Supported on clusters with nodes that have 1GB /var partitions',
   );
###
# @NFP is a list of node firmware packages in order by release, used when
# checking for updates by traversing the newest packages first
our @NFP = (
    '14.1','13.2.5','13.2.3','13.2.2','13.2.1','13.1.3','13.1','13.0', '12.4.2', '12.4.1', '12.4', '12.3', '12.2.1','12.2','12.1','12.0','11.7.3','11.7.1', '11.7', '10.4.0', '11.6.1','11.5.1', '11.5', '11.4', '10.3.8', '11.3', '10.3.7', '11.2', '11.1.4',
    '10.3.6', '11.1', '10.3.5', '11.0', '10.3.3', '10.3.2', '10.3.1', '10.3.0',
    '9.3.5');
###
# %NodeFirmware is a hash of each release and the included firmware updates
our %NodeFirmware = (
    'None' => undef,
    '14.1' => {
        'AltoSaxadpt_symphony' => ['03.70'],
        'AltoSaxaltosax' => ['01.10'],
        'AltoSaxast26xx_cec_region0' => ['03.00'],
        'AltoSaxast26xx_cec_region1' => ['32.03'],
        'AltoSaxast26xx_cec_region3' => ['00.03'],
        'AltoSaxbem_as_mongoose_lw_m1b' => ['03.20'],
        'AltoSaxccdes_catamaran' => ['1.10.8+21.00'],
        'AltoSaxccdes_catamarancos' => ['1.10.8+21.00'],
        'AltoSaxcmd_100dc12pin_v1' => ['02.12.75.03'],
        'AltoSaxcmd_64pin' => ['02.05.102.02'],
        'AltoSaxcmd_64pincos' => ['02.09.117.03'],
        'AltoSaxcmd_rev_table' => ['20.84'],
        'AltoSaxeth_bcm5720_altosax' => ['01.00'],
        'AltoSaxfp_fpinffw' => ['02.0c'],
        'AltoSaxfp_fpioc' => ['00.0b'],
        'AltoSaxfw_map_table' => ['00.04'],
        'AltoSaxgem_symphony' => ['11.01'],
        'AltoSaxps0_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps0_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps0_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxps1_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps1_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps1_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxsas_catamaran_3008' => ['0015.0004.0000.0000'],
        'AltoSaxsas_catamaran_3808' => ['0028.0015.0000.0000'],
        'AltoSaxssp_altosax' => ['02.00'],
        'AltoSaxstoragebios_altosax' => ['01.10'],
        'AltoSaxvrd0_altosax_inf' => ['00.05'],
        'AltoSaxvrd0_altosax_rea' => ['00.20'],
        'AltoSaxvrd1_altosax_inf_v1' => ['02.03'],
        'AltoSaxvrd1_altosax_rea_v1' => ['02.20'],
        'B100' => {
                'BIOS' => ['2.27.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.184'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'BIOS' => ['1.7.6'],
        'BOSS ISE 7450 RI M.2 960GB DISK0' => ['1.4.0'],
        'BOSS ISE 7450 RI M.2 960GB DISK1' => ['1.4.0'],
        'BOSS ISE PE9010 RI M.2 960GB DISK0' => ['1.3.1'],
        'BOSS ISE PE9010 RI M.2 960GB DISK1' => ['1.3.1'],
        'BOSS-N1 DC-MHS' => ['2.2.13.2038'],
        'Backplane 1' => ['1.92'],
        'Backplane 2' => ['1.92'],
        'Backplane 3' => ['1.92'],
        'Broadcom 25G NIC-BE' => ['34.11.46.00'],
        'Broadcom 25G NIC-FE' => ['34.11.46.00'],
        'Broadcom OCP MGMT' => ['236.1.173.0'],
        'DEadpt_moons' => ['01.80'],
        'DEbem_mongoose' => ['03.20'],
        'DEbem_mongoose_lw' => ['03.20'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.8+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.18'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.18'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbem_mongoose' => ['03.20'],
        'EPbem_mongoose_lw' => ['03.20'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.8+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.18'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.18'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.27.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.184'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.10.1'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.30.30.51'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'F600' => {
                'BIOS' => ['2.27.0'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.184'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.10.1'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.30.30.51'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'F900' => {
                'BIOS' => ['2.27.0'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.184'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.10.1'],
                'Backplane 1' => ['3.33'],
                'Backplane 2' => ['3.33'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.30.30.51'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'Integrated Remote Access Controller' => ['1.30.30.50'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'M.2-NVMe-7400-FIPS' => ['2.5.0'],
        'M.2-NVMe-7450-FIPS' => ['3.4.0'],
        'M.2-NVMe-7450-ISE' => ['1.4.0'],
        'M.2-NVMe-PE9010-ISE' => ['1.3.1'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['03.20'],
        'MLKbem_mongoose_lw_m1b' => ['03.20'],
        'MLKbios_gryphon' => ['10.81'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.8+21.00'],
        'MLKccdes_catamarancos' => ['1.10.8+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.13.75.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.85'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.81'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.18'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.18'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['10.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.47.1088+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.47.1088+DEL0000000031'],
        'Mellanox-CX8-400GbE-BE' => ['40.47.1088+MT_0000001222'],
        'Mellanox-CX8-400GbE-FE0' => ['40.47.1088+MT_0000001222'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-EN1' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.27.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.184'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.10.1'],
                'BOSS in SL 12 Dell EC NVMe ISE 7400 RI M.2 960GB' => ['1.6.0'],
                'BOSS in SL 12 Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'BOSS in SL 12 Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LPe35002-M2-D 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.30.30.51'],
                'System CPLD' => ['1.2.7'],
        },
        'System FPGA' => ['109.126.100'],
        'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
        'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
    },
        '13.2.5' => {
        'AltoSaxadpt_symphony' => ['03.70'],
        'AltoSaxaltosax' => ['01.10'],
        'AltoSaxast26xx_cec_region0' => ['03.00'],
        'AltoSaxast26xx_cec_region1' => ['32.03'],
        'AltoSaxast26xx_cec_region3' => ['00.03'],
        'AltoSaxbem_as_mongoose_lw_m1b' => ['03.20'],
        'AltoSaxccdes_catamaran' => ['1.10.8+21.00'],
        'AltoSaxccdes_catamarancos' => ['1.10.8+21.00'],
        'AltoSaxcmd_100dc12pin_v1' => ['02.12.75.03'],
        'AltoSaxcmd_64pin' => ['02.05.102.02'],
        'AltoSaxcmd_64pincos' => ['02.09.117.03'],
        'AltoSaxcmd_rev_table' => ['20.84'],
        'AltoSaxeth_bcm5720_altosax' => ['01.00'],
        'AltoSaxfp_fpinffw' => ['02.0c'],
        'AltoSaxfp_fpioc' => ['00.0b'],
        'AltoSaxfw_map_table' => ['00.04'],
        'AltoSaxgem_symphony' => ['11.01'],
        'AltoSaxps0_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps0_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps0_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxps1_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps1_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps1_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxsas_catamaran_3008' => ['0015.0004.0000.0000'],
        'AltoSaxsas_catamaran_3808' => ['0028.0015.0000.0000'],
        'AltoSaxssp_altosax' => ['02.00'],
        'AltoSaxstoragebios_altosax' => ['01.10'],
        'AltoSaxvrd0_altosax_inf' => ['00.05'],
        'AltoSaxvrd0_altosax_rea' => ['00.20'],
        'AltoSaxvrd1_altosax_inf_v1' => ['02.03'],
        'AltoSaxvrd1_altosax_rea_v1' => ['02.20'],
        'B100' => {
                'BIOS' => ['2.26.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.184'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'Broadcom 25G NIC-BE' => ['34.11.46.00'],
        'Broadcom 25G NIC-FE' => ['34.11.46.00'],
        'DEadpt_moons' => ['01.80'],
        'DEbem_mongoose' => ['03.20'],
        'DEbem_mongoose_lw' => ['03.20'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.8+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.18'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.18'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbem_mongoose' => ['03.20'],
        'EPbem_mongoose_lw' => ['03.20'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.8+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.18'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.18'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.26.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.184'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.9.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.30.10.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'F600' => {
                'BIOS' => ['2.26.1'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.184'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.9.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.30.10.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'F900' => {
                'BIOS' => ['2.26.1'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.184'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.9.4'],
                'Backplane 1' => ['3.33'],
                'Backplane 2' => ['3.33'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.30.10.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'M.2-NVMe-7400-FIPS' => ['2.5.0'],
        'M.2-NVMe-7450-FIPS' => ['3.1.0'],
        'M.2-NVMe-7450-ISE' => ['1.1.0'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['03.20'],
        'MLKbem_mongoose_lw_m1b' => ['03.20'],
        'MLKbios_gryphon' => ['10.81'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.8+21.00'],
        'MLKccdes_catamarancos' => ['1.10.8+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.13.75.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.85'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.81'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.18'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.18'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['10.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+ISL1090110018', '2.42.5000+MT_1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX8-400GbE-BE' => ['40.45.1202+MT_0000001222'],
        'Mellanox-CX8-400GbE-FE0' => ['40.45.1202+MT_0000001222'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC0000000002', '2.42.5000+EMC1090111023'],
        'Mellanox-EN1' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC0000000002', '2.42.5000+EMC1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.26.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.184'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.9.4'],
                'BOSS in SL 12 Dell EC NVMe ISE 7400 RI M.2 960GB' => ['1.6.0'],
                'BOSS in SL 12 Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.1.0'],
                'BOSS in SL 12 Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.0.0'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LPe35002-M2-D 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.30.10.50'],
                'System CPLD' => ['1.2.7'],
        },
    },
        '13.2.3' => {
        'AltoSaxadpt_symphony' => ['03.70'],
        'AltoSaxaltosax' => ['01.10'],
        'AltoSaxast26xx_cec_region0' => ['03.00'],
        'AltoSaxast26xx_cec_region1' => ['32.03'],
        'AltoSaxast26xx_cec_region3' => ['00.03'],
        'AltoSaxbem_as_mongoose_lw_m1b' => ['03.20'],
        'AltoSaxccdes_catamaran' => ['1.10.8+21.00'],
        'AltoSaxccdes_catamarancos' => ['1.10.8+21.00'],
        'AltoSaxcmd_100dc12pin_v1' => ['02.12.75.03'],
        'AltoSaxcmd_64pin' => ['02.05.102.02'],
        'AltoSaxcmd_64pincos' => ['02.09.117.03'],
        'AltoSaxcmd_rev_table' => ['20.84'],
        'AltoSaxeth_bcm5720_altosax' => ['01.00'],
        'AltoSaxfp_fpinffw' => ['02.0c'],
        'AltoSaxfp_fpioc' => ['00.0b'],
        'AltoSaxfw_map_table' => ['00.04'],
        'AltoSaxgem_symphony' => ['11.01'],
        'AltoSaxps0_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps0_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps0_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxps1_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps1_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps1_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxsas_catamaran_3008' => ['0015.0004.0000.0000'],
        'AltoSaxsas_catamaran_3808' => ['0028.0015.0000.0000'],
        'AltoSaxssp_altosax' => ['02.00'],
        'AltoSaxstoragebios_altosax' => ['01.10'],
        'AltoSaxvrd0_altosax_inf' => ['00.05'],
        'AltoSaxvrd0_altosax_rea' => ['00.20'],
        'AltoSaxvrd1_altosax_inf_v1' => ['02.03'],
        'AltoSaxvrd1_altosax_rea_v1' => ['02.20'],
        'B100' => {
                'BIOS' => ['2.25.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.183'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'Broadcom 25G NIC-BE' => ['34.11.46.00'],
        'Broadcom 25G NIC-FE' => ['34.11.46.00'],
        'DEadpt_moons' => ['01.80'],
        'DEbem_mongoose' => ['03.20'],
        'DEbem_mongoose_lw' => ['03.20'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.8+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.18'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.18'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbem_mongoose' => ['03.20'],
        'EPbem_mongoose_lw' => ['03.20'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.8+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.18'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.18'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.25.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.183'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.8.2'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.80.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'F600' => {
                'BIOS' => ['2.25.0'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.183'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.8.2'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.80.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'F900' => {
                'BIOS' => ['2.25.0'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.183'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.8.2'],
                'Backplane 1' => ['3.33'],
                'Backplane 2' => ['3.33'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.80.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'M.2-NVMe-7400-FIPS' => ['2.5.0'],
        'M.2-NVMe-7450-FIPS' => ['3.1.0'],
        'M.2-NVMe-7450-ISE' => ['1.1.0'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['03.20'],
        'MLKbem_mongoose_lw_m1b' => ['03.20'],
        'MLKbios_gryphon' => ['10.81'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.8+21.00'],
        'MLKccdes_catamarancos' => ['1.10.8+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.13.75.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.85'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.81'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.18'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.18'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['10.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX8-400GbE-BE' => ['40.45.1202+MT_0000001222'],
        'Mellanox-CX8-400GbE-FE0' => ['40.45.1202+MT_0000001222'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.25.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.183'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.8.2'],
                'BOSS in SL 12 Dell EC NVMe ISE 7400 RI M.2 960GB' => ['1.6.0'],
                'BOSS in SL 12 Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.1.0'],
                'BOSS in SL 12 Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.0.0'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LPe35002-M2-D 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.20.80.50'],
                'System CPLD' => ['1.2.7'],
        },
    },
        '13.2.2' => {
        'AltoSaxadpt_symphony' => ['03.70'],
        'AltoSaxaltosax' => ['01.03'],
        'AltoSaxast26xx_cec_region0' => ['03.00'],
        'AltoSaxast26xx_cec_region1' => ['32.03'],
        'AltoSaxast26xx_cec_region3' => ['00.03'],
        'AltoSaxbem_as_mongoose_lw_m1b' => ['03.20'],
        'AltoSaxccdes_catamaran' => ['1.10.8+21.00'],
        'AltoSaxccdes_catamarancos' => ['1.10.8+21.00'],
        'AltoSaxcmd_100dc12pin_v1' => ['02.12.75.03'],
        'AltoSaxcmd_64pin' => ['02.05.102.02'],
        'AltoSaxcmd_64pincos' => ['02.09.117.03'],
        'AltoSaxcmd_rev_table' => ['20.84'],
        'AltoSaxeth_bcm5720_altosax' => ['01.00'],
        'AltoSaxfp_fpinffw' => ['02.0c'],
        'AltoSaxfp_fpioc' => ['00.0b'],
        'AltoSaxfw_map_table' => ['00.04'],
        'AltoSaxgem_symphony' => ['10.02'],
        'AltoSaxps0_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps0_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps0_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxps1_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps1_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps1_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxsas_catamaran_3008' => ['0015.0004.0000.0000'],
        'AltoSaxsas_catamaran_3808' => ['0028.0015.0000.0000'],
        'AltoSaxssp_altosax' => ['02.00'],
        'AltoSaxstoragebios_altosax' => ['01.03'],
        'AltoSaxvrd0_altosax_inf' => ['00.05'],
        'AltoSaxvrd0_altosax_rea' => ['00.20'],
        'AltoSaxvrd1_altosax_inf_v1' => ['02.03'],
        'AltoSaxvrd1_altosax_rea_v1' => ['02.20'],
        'B100' => { 
                'BIOS' => ['2.25.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.183'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'Broadcom 25G NIC-BE' => ['34.11.46.00'],
        'Broadcom 25G NIC-FE' => ['34.11.46.00'],
        'DEadpt_moons' => ['01.80'],
        'DEbem_mongoose' => ['03.20'],
        'DEbem_mongoose_lw' => ['03.20'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.8+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.18'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.18'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbem_mongoose' => ['03.20'],
        'EPbem_mongoose_lw' => ['03.20'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.8+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.18'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.18'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => { 
                'BIOS' => ['2.25.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.183'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => { 
                'BIOS' => ['2.8.2'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.80.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'F600' => { 
                'BIOS' => ['2.25.0'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.183'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => { 
                'BIOS' => ['2.8.2'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.80.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'F900' => { 
                'BIOS' => ['2.25.0'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.183'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => { 
                'BIOS' => ['2.8.2'],
                'Backplane 1' => ['3.33'],
                'Backplane 2' => ['3.33'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.80.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell DC NVMe PM9D3a RI 80M.2 960GB ISE' => ['1.0.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe FIPS 7450 RI M.2 80 960GB' => ['3.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.4.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
                'VOSS Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.3.1'],
        },
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'M.2-NVMe-7400-FIPS' => ['2.5.0'],
        'M.2-NVMe-7450-FIPS' => ['3.1.0'],
        'M.2-NVMe-7450-ISE' => ['1.1.0'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['03.20'],
        'MLKbem_mongoose_lw_m1b' => ['03.20'],
        'MLKbios_gryphon' => ['10.79'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.8+21.00'],
        'MLKccdes_catamarancos' => ['1.10.8+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.13.75.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.85'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.79'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.18'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.18'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+ISL1090110018', '2.42.5000+MT_1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX8-400GbE-BE' => ['40.45.1202+MT_0000001222'],
        'Mellanox-CX8-400GbE-FE0' => ['40.45.1202+MT_0000001222'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-EN1' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => { 
                'BIOS' => ['2.25.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.183'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => { 
                'BIOS' => ['2.8.2'],
                'BOSS in SL 12 Dell EC NVMe ISE 7400 RI M.2 960GB' => ['1.6.0'],
                'BOSS in SL 12 Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.1.0'],
                'BOSS in SL 12 Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.0.0'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LPe35002-M2-D 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.20.80.50'],
                'System CPLD' => ['1.2.7'],
        },
    },
    '13.2.1' => {
        'AltoSaxadpt_symphony' => ['03.70'],
        'AltoSaxaltosax' => ['01.03'],
        'AltoSaxast26xx_cec_region0' => ['03.00'],
        'AltoSaxast26xx_cec_region1' => ['32.03'],
        'AltoSaxast26xx_cec_region3' => ['00.03'],
        'AltoSaxbem_as_mongoose_lw_m1b' => ['03.20'],
        'AltoSaxccdes_catamaran' => ['1.10.8+21.00'],
        'AltoSaxccdes_catamarancos' => ['1.10.8+21.00'],
        'AltoSaxcmd_100dc12pin_v1' => ['02.12.75.03'],
        'AltoSaxcmd_64pin' => ['02.05.102.02'],
        'AltoSaxcmd_64pincos' => ['02.09.117.03'],
        'AltoSaxcmd_rev_table' => ['20.84'],
        'AltoSaxeth_bcm5720_altosax' => ['01.00'],
        'AltoSaxfp_fpinffw' => ['02.0c'],
        'AltoSaxfp_fpioc' => ['00.0b'],
        'AltoSaxfw_map_table' => ['00.04'],
        'AltoSaxgem_symphony' => ['10.02'],
        'AltoSaxps0_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps0_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps0_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxps1_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps1_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps1_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxsas_catamaran_3008' => ['0015.0004.0000.0000'],
        'AltoSaxsas_catamaran_3808' => ['0028.0015.0000.0000'],
        'AltoSaxssp_altosax' => ['02.00'],
        'AltoSaxstoragebios_altosax' => ['01.03'],
        'AltoSaxvrd0_altosax_inf' => ['00.05'],
        'AltoSaxvrd0_altosax_rea' => ['00.20'],
        'AltoSaxvrd1_altosax_inf_v1' => ['02.03'],
        'AltoSaxvrd1_altosax_rea_v1' => ['02.20'],
        'B100' => {
                'BIOS' => ['2.24.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.181'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'Broadcom 25G NIC-BE' => ['34.11.46.00'],
        'Broadcom 25G NIC-FE' => ['34.11.46.00'],
        'CFFPS1' => ['06.06', '03.09', '01.03', '04.14', '02.02', '0a.01', '09.0b'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['06.06', '03.09', '01.03', '04.14', '02.02', '0a.01', '09.0b'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMC_HFHB' => ['02.07'],
        'DEadpt_moons' => ['01.80'],
        'DEbem_mongoose' => ['03.20'],
        'DEbem_mongoose_lw' => ['03.20'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.8+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.18'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.18'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbem_mongoose' => ['03.20'],
        'EPbem_mongoose_lw' => ['03.20'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.8+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.18'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.18'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.24.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.181'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.7.5'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.60.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F600' => {
                'BIOS' => ['2.24.0'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.181'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.7.5'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.60.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F900' => {
                'BIOS' => ['2.24.0'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.181'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.7.5'],
                'Backplane 1' => ['3.33'],
                'Backplane 2' => ['3.33'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.60.50'],
                'System CPLD' => ['1.2.7'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'M.2-NVMe-7400-FIPS' => ['2.5.0'],
        'M.2-NVMe-7450-FIPS' => ['3.1.0'],
        'M.2-NVMe-7450-ISE' => ['1.1.0'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['03.20'],
        'MLKbem_mongoose_lw_m1b' => ['03.20'],
        'MLKbios_gryphon' => ['10.79'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.8+21.00'],
        'MLKccdes_catamarancos' => ['1.10.8+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.13.75.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.85'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.79'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.18'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.18'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+ISL1090110018', '2.42.5000+MT_1090110018', '4.8.930+MT_02F0140002'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX8-400GbE-BE' => ['40.45.1202+MT_0000001222'],
        'Mellanox-CX8-400GbE-FE0' => ['40.45.1202+MT_0000001222'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-EN1' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.24.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.181'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.7.5'],
                'BOSS in SL 12 Dell EC NVMe ISE 7400 RI M.2 960GB' => ['1.6.0'],
                'BOSS in SL 12 Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.1.0'],
                'BOSS in SL 12 Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.0.0'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LPe35002-M2-D 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.20.60.50'],
                'System CPLD' => ['1.2.7'],
        },
        'QLogic-NX2' => ['7.13.0'],
    },
    '13.1.3' => {
        'AltoSaxadpt_symphony' => ['03.70'],
        'AltoSaxaltosax' => ['01.03'],
        'AltoSaxast26xx_cec_region0' => ['03.00'],
        'AltoSaxast26xx_cec_region1' => ['32.03'],
        'AltoSaxast26xx_cec_region3' => ['00.03'],
        'AltoSaxbem_as_mongoose_lw_m1b' => ['03.20'],
        'AltoSaxccdes_catamaran' => ['1.10.8+21.00'],
        'AltoSaxccdes_catamarancos' => ['1.10.8+21.00'],
        'AltoSaxcmd_100dc12pin_v1' => ['02.12.75.03'],
        'AltoSaxcmd_64pin' => ['02.05.102.02'],
        'AltoSaxcmd_64pincos' => ['02.09.117.03'],
        'AltoSaxcmd_rev_table' => ['20.84'],
        'AltoSaxeth_bcm5720_altosax' => ['01.00'],
        'AltoSaxfp_fpinffw' => ['02.0c'],
        'AltoSaxfp_fpioc' => ['00.0b'],
        'AltoSaxfw_map_table' => ['00.04'],
        'AltoSaxgem_symphony' => ['10.02'],
        'AltoSaxps0_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps0_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps0_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxps1_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps1_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps1_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxsas_catamaran_3008' => ['0015.0004.0000.0000'],
        'AltoSaxsas_catamaran_3808' => ['0028.0015.0000.0000'],
        'AltoSaxssp_altosax' => ['02.00'],
        'AltoSaxstoragebios_altosax' => ['01.03'],
        'AltoSaxvrd0_altosax_inf' => ['00.05'],
        'AltoSaxvrd0_altosax_rea' => ['00.20'],
        'AltoSaxvrd1_altosax_inf_v1' => ['02.03'],
        'AltoSaxvrd1_altosax_rea_v1' => ['02.20'],
        'B100' => {
                'BIOS' => ['2.24.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.181'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'CFFPS1' => ['03.09', '04.14', '0a.01', '09.0b', '01.03', '02.02', '06.06'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['03.09', '04.14', '0a.01', '09.0b', '01.03', '02.02', '06.06'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMC_HFHB' => ['02.07'],
        'DEadpt_moons' => ['01.80'],
        'DEbem_mongoose' => ['03.20'],
        'DEbem_mongoose_lw' => ['03.20'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.8+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.18'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.18'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbem_mongoose' => ['03.20'],
        'EPbem_mongoose_lw' => ['03.20'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.8+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.18'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.18'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.24.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.181'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.7.5'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.60.50'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F600' => {
                'BIOS' => ['2.24.0'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.181'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.7.5'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.60.50'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F900' => {
                'BIOS' => ['2.24.0'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.181'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.7.5'],
                'Backplane 1' => ['3.33'],
                'Backplane 2' => ['3.33'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.20.60.50'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.1.1'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['03.20'],
        'MLKbem_mongoose_lw_m1b' => ['03.20'],
        'MLKbios_gryphon' => ['10.79'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.8+21.00'],
        'MLKccdes_catamarancos' => ['1.10.8+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.13.75.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.85'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.79'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.18'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.18'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.18'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '4.8.930+MT_02F0140002', '2.42.5000+ISL1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.24.0'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.181'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.7.5'],
                'BOSS in SL 12 Dell EC NVMe ISE 7400 RI M.2 960GB' => ['1.6.0'],
                'BOSS in SL 12 Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.1.0'],
                'BOSS in SL 12 Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.0.0'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LPe35002-M2-D 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.20.60.50'],
                'System CPLD' => ['1.2.6'],
        },
        'QLogic-NX2' => ['7.13.0'],
    },
'13.1' => {
        'AltoSaxadpt_symphony' => ['03.70'],
        'AltoSaxaltosax' => ['01.01'],
        'AltoSaxast26xx_cec_region0' => ['03.00'],
        'AltoSaxast26xx_cec_region1' => ['32.03'],
        'AltoSaxast26xx_cec_region3' => ['00.03'],
        'AltoSaxbem_as_mongoose_lw_m1b' => ['03.20'],
        'AltoSaxccdes_catamaran' => ['1.10.8+21.00'],
        'AltoSaxccdes_catamarancos' => ['1.10.8+21.00'],
        'AltoSaxcmd_100dc12pin_v1' => ['02.12.75.03'],
        'AltoSaxcmd_64pin' => ['02.05.102.02'],
        'AltoSaxcmd_64pincos' => ['02.09.117.03'],
        'AltoSaxcmd_rev_table' => ['20.84'],
        'AltoSaxeth_bcm5720_altosax' => ['01.00'],
        'AltoSaxfp_fpinffw' => ['02.0c'],
        'AltoSaxfp_fpioc' => ['00.0b'],
        'AltoSaxfw_map_table' => ['00.04'],
        'AltoSaxgem_symphony' => ['10.02'],
        'AltoSaxps0_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps0_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps0_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxps1_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps1_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps1_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxsas_catamaran_3008' => ['0015.0004.0000.0000'],
        'AltoSaxsas_catamaran_3808' => ['0028.0015.0000.0000'],
        'AltoSaxssp_altosax' => ['02.00'],
        'AltoSaxstoragebios_altosax' => ['01.01'],
        'AltoSaxvrd0_altosax_inf' => ['00.05'],
        'AltoSaxvrd0_altosax_rea' => ['00.20'],
        'AltoSaxvrd1_altosax_inf_v1' => ['02.03'],
        'AltoSaxvrd1_altosax_rea_v1' => ['02.20'],
        'B100' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'CFFPS1' => ['0a.01', '06.06', '02.02', '03.09', '09.0b', '04.14', '01.03'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '06.06', '02.02', '03.09', '09.0b', '04.14', '01.03'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMC_HFHB' => ['02.07'],
        'DEadpt_moons' => ['01.80'],
        'DEbem_mongoose' => ['03.20'],
        'DEbem_mongoose_lw' => ['03.20'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.8+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbem_mongoose' => ['03.20'],
        'EPbem_mongoose_lw' => ['03.20'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.8+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.4.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F600' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.4.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F900' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.4.4'],
                'Backplane 1' => ['3.32'],
                'Backplane 2' => ['3.32'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['20.00.04.00', '13.00.66.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['03.20'],
        'MLKbem_mongoose_lw_m1b' => ['03.20'],
        'MLKbios_gryphon' => ['10.79'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.8+21.00'],
        'MLKccdes_catamarancos' => ['1.10.8+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.13.75.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.85'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.79'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-EN1' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.4.4'],
                'BOSS in SL 12 Dell EC NVMe ISE 7400 RI M.2 960GB' => ['1.6.0'],
                'BOSS in SL 12 Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.1.0'],
                'BOSS in SL 12 Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.0.0'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LPe35002-M2-D 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
        },
        'QLogic-NX2' => ['7.13.0'],
    },
        '13.0' => {
        'AltoSaxadpt_symphony' => ['03.70'],
        'AltoSaxaltosax' => ['00.90'],
        'AltoSaxast26xx_cec_region0' => ['03.00'],
        'AltoSaxast26xx_cec_region1' => ['32.03'],
        'AltoSaxast26xx_cec_region3' => ['00.03'],
        'AltoSaxbem_as_mongoose_lw_m1b' => ['03.20'],
        'AltoSaxccdes_catamaran' => ['1.10.8+21.00'],
        'AltoSaxccdes_catamarancos' => ['1.10.8+21.00'],
        'AltoSaxcmd_100dc12pin_v1' => ['02.12.75.03'],
        'AltoSaxcmd_64pin' => ['02.05.102.02'],
        'AltoSaxcmd_64pincos' => ['02.09.117.03'],
        'AltoSaxcmd_rev_table' => ['20.84'],
        'AltoSaxeth_bcm5720_altosax' => ['01.00'],
        'AltoSaxfp_fpinffw' => ['02.0c'],
        'AltoSaxfp_fpioc' => ['00.0b'],
        'AltoSaxfw_map_table' => ['00.04'],
        'AltoSaxgem_symphony' => ['09.01'],
        'AltoSaxps0_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps0_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps0_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxps1_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps1_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps1_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxsas_catamaran_3008' => ['0015.0004.0000.0000'],
        'AltoSaxsas_catamaran_3808' => ['0028.0015.0000.0000'],
        'AltoSaxssp_altosax' => ['01.90'],
        'AltoSaxstoragebios_altosax' => ['00.90'],
        'AltoSaxvrd0_altosax_inf' => ['00.05'],
        'AltoSaxvrd0_altosax_rea' => ['00.20'],
        'AltoSaxvrd1_altosax_inf_v1' => ['02.03'],
        'AltoSaxvrd1_altosax_rea_v1' => ['02.20'],
        'B100' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'CFFPS1' => ['02.02', '03.09', '06.06', '04.14', '09.0b', '01.03', '0a.01'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['02.02', '03.09', '06.06', '04.14', '09.0b', '01.03', '0a.01'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMC_HFHB' => ['02.07'],
        'DEadpt_moons' => ['01.80'],
        'DEbem_mongoose' => ['03.20'],
        'DEbem_mongoose_lw' => ['03.20'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.8+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbem_mongoose' => ['03.20'],
        'EPbem_mongoose_lw' => ['03.20'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.8+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.4.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F600' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.4.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F900' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.4.4'],
                'Backplane 1' => ['3.31'],
                'Backplane 2' => ['3.31'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['03.20'],
        'MLKbem_mongoose_lw_m1b' => ['03.20'],
        'MLKbios_gryphon' => ['10.79'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.8+21.00'],
        'MLKccdes_catamarancos' => ['1.10.8+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.13.75.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.85'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.79'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '4.8.930+MT_02F0140002', '2.42.5000+ISL1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.4.4'],
                'BOSS in SL 12 Dell EC NVMe ISE 7400 RI M.2 960GB' => ['1.6.0'],
                'BOSS in SL 12 Dell EC NVMe ISE 7450 RI M.2 80 960GB' => ['1.1.0'],
                'BOSS in SL 12 Dell NVMe ISE PE9010 GEN4 RI M.2 960GB' => ['1.0.0'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LPe35002-M2-D 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
        },
        'QLogic-NX2' => ['7.13.0'],
    },
        '12.4.2' => {
        'AltoSaxadpt_symphony' => ['03.70'],
        'AltoSaxaltosax' => ['00.61'],
        'AltoSaxast26xx_cec_region0' => ['03.00'],
        'AltoSaxast26xx_cec_region1' => ['32.03'],
        'AltoSaxast26xx_cec_region3' => ['00.03'],
        'AltoSaxbem_as_mongoose_lw_m1b' => ['03.12'],
        'AltoSaxccdes_catamaran' => ['1.10.8+21.00'],
        'AltoSaxccdes_catamarancos' => ['1.10.8+21.00'],
        'AltoSaxcmd_100dc12pin_v1' => ['02.12.75.03'],
        'AltoSaxcmd_64pin' => ['02.05.102.02'],
        'AltoSaxcmd_64pincos' => ['02.09.117.03'],
        'AltoSaxcmd_rev_table' => ['20.84'],
        'AltoSaxeth_bcm5720_altosax' => ['01.00'],
        'AltoSaxfp_fpinffw' => ['02.0c'],
        'AltoSaxfp_fpioc' => ['00.0b'],
        'AltoSaxfw_map_table' => ['00.04'],
        'AltoSaxgem_symphony' => ['06.04'],
        'AltoSaxps0_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps0_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps0_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxps1_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps1_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps1_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxsas_catamaran_3008' => ['0015.0004.0000.0000'],
        'AltoSaxsas_catamaran_3808' => ['0028.0015.0000.0000'],
        'AltoSaxssp_altosax' => ['01.60'],
        'AltoSaxstoragebios_altosax' => ['00.61'],
        'AltoSaxvrd0_altosax_inf' => ['00.04'],
        'AltoSaxvrd0_altosax_rea' => ['00.20'],
        'AltoSaxvrd1_altosax_inf_v1' => ['02.03'],
        'AltoSaxvrd1_altosax_rea_v1' => ['02.20'],
        'B100' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'CFFPS1' => ['09.0b', '04.14', '02.02', '01.03', '06.06', '0a.01', '03.09'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['09.0b', '04.14', '02.02', '01.03', '06.06', '0a.01', '03.09'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMC_HFHB' => ['02.07'],
        'DEadpt_moons' => ['01.80'],
        'DEbem_mongoose' => ['03.20'],
        'DEbem_mongoose_lw' => ['03.20'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.8+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbem_mongoose' => ['03.20'],
        'EPbem_mongoose_lw' => ['03.20'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.8+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.4.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F600' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.4.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F900' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.4.4'],
                'Backplane 1' => ['3.31'],
                'Backplane 2' => ['3.31'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'LOx' => ['rp180d01+rp180d01', 'rp180d01'],
        'LOx2' => ['rp180d01+rp180d01', 'rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01+rp180d01', 'rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['03.20'],
        'MLKbem_mongoose_lw_m1b' => ['03.20'],
        'MLKbios_gryphon' => ['10.79'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.8+21.00'],
        'MLKccdes_catamarancos' => ['1.10.8+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.13.75.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.85'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.79'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+ISL1090110018', '2.42.5000+MT_1090110018', '4.8.930+MT_02F0140002'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC0000000002', '2.42.5000+EMC1090111023'],
        'Mellanox-EN1' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC0000000002', '2.42.5000+EMC1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.4.4'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LightPulse LPe35002-M2 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.10.90.00'],
                'System CPLD' => ['1.2.6'],
        },
        'QLogic-NX2' => ['7.13.0'],
    },
        '12.4.1' => {
        'AltoSaxadpt_symphony' => ['03.70'],
        'AltoSaxaltosax' => ['00.61'],
        'AltoSaxast26xx_cec_region0' => ['03.00'],
        'AltoSaxast26xx_cec_region1' => ['32.03'],
        'AltoSaxast26xx_cec_region3' => ['00.03'],
        'AltoSaxbem_as_mongoose_lw_m1b' => ['03.12'],
        'AltoSaxccdes_catamaran' => ['1.10.8+21.00'],
        'AltoSaxccdes_catamarancos' => ['1.10.8+21.00'],
        'AltoSaxcmd_100dc12pin_v1' => ['02.12.75.03'],
        'AltoSaxcmd_64pin' => ['02.05.102.02'],
        'AltoSaxcmd_64pincos' => ['02.09.117.03'],
        'AltoSaxcmd_rev_table' => ['20.84'],
        'AltoSaxeth_bcm5720_altosax' => ['01.00'],
        'AltoSaxfp_fpinffw' => ['02.0c'],
        'AltoSaxfp_fpioc' => ['00.0b'],
        'AltoSaxfw_map_table' => ['00.04'],
        'AltoSaxgem_symphony' => ['06.04'],
        'AltoSaxps0_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps0_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps0_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxps1_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps1_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps1_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxsas_catamaran_3008' => ['0015.0004.0000.0000'],
        'AltoSaxsas_catamaran_3808' => ['0028.0015.0000.0000'],
        'AltoSaxssp_altosax' => ['01.60'],
        'AltoSaxstoragebios_altosax' => ['00.61'],
        'AltoSaxvrd0_altosax_inf' => ['00.04'],
        'AltoSaxvrd0_altosax_rea' => ['00.20'],
        'AltoSaxvrd1_altosax_inf_v1' => ['02.03'],
        'AltoSaxvrd1_altosax_rea_v1' => ['02.20'],
        'B100' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'CFFPS1' => ['06.06', '01.03', '0a.01', '04.14', '03.09', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['06.06', '01.03', '0a.01', '04.14', '03.09', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMC_HFHB' => ['02.07'],
        'DEadpt_moons' => ['01.80'],
        'DEbem_mongoose' => ['03.20'],
        'DEbem_mongoose_lw' => ['03.20'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.8+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbem_mongoose' => ['03.20'],
        'EPbem_mongoose_lw' => ['03.20'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.8+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.3.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.70.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F600' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.3.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.70.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F900' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.3.4'],
                'Backplane 1' => ['3.31'],
                'Backplane 2' => ['3.31'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.70.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['20.00.04.00', '13.00.66.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['03.20'],
        'MLKbem_mongoose_lw_m1b' => ['03.20'],
        'MLKbios_gryphon' => ['10.79'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.8+21.00'],
        'MLKccdes_catamarancos' => ['1.10.8+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.13.75.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.85'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.79'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-EN1' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.22.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.174'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.3.4'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LightPulse LPe35002-M2 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.10.70.00'],
                'System CPLD' => ['1.2.6'],
        },
        'QLogic-NX2' => ['7.13.0'],
    },
    '12.4' => {
        'AltoSaxadpt_symphony' => ['03.70'],
        'AltoSaxaltosax' => ['00.61'],
        'AltoSaxast26xx_cec_region0' => ['03.00'],
        'AltoSaxast26xx_cec_region1' => ['32.03'],
        'AltoSaxast26xx_cec_region3' => ['00.03'],
        'AltoSaxbem_as_mongoose_lw_m1b' => ['03.12'],
        'AltoSaxccdes_catamaran' => ['1.10.8+21.00'],
        'AltoSaxccdes_catamarancos' => ['1.10.8+21.00'],
        'AltoSaxcmd_100dc12pin_v1' => ['02.12.75.03'],
        'AltoSaxcmd_64pin' => ['02.05.102.02'],
        'AltoSaxcmd_64pincos' => ['02.09.117.03'],
        'AltoSaxcmd_rev_table' => ['20.84'],
        'AltoSaxeth_bcm5720_altosax' => ['01.00'],
        'AltoSaxfp_fpinffw' => ['02.0c'],
        'AltoSaxfp_fpioc' => ['00.0b'],
        'AltoSaxfw_map_table' => ['00.04'],
        'AltoSaxgem_symphony' => ['06.04'],
        'AltoSaxps0_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps0_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps0_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxps1_gen3_acbel_optimuswr' => ['03.01'],
        'AltoSaxps1_gen3_artesyn_ramjet' => ['02.30'],
        'AltoSaxps1_gen3_liteon_atlas' => ['03.08'],
        'AltoSaxsas_catamaran_3008' => ['0015.0004.0000.0000'],
        'AltoSaxsas_catamaran_3808' => ['0028.0015.0000.0000'],
        'AltoSaxssp_altosax' => ['01.60'],
        'AltoSaxstoragebios_altosax' => ['00.61'],
        'AltoSaxvrd0_altosax_inf' => ['00.04'],
        'AltoSaxvrd0_altosax_rea' => ['00.20'],
        'AltoSaxvrd1_altosax_inf_v1' => ['02.03'],
        'AltoSaxvrd1_altosax_rea_v1' => ['02.20'],
        'B100' => {
                'BIOS' => ['2.22.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.173'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'CFFPS1' => ['04.14', '09.0b', '06.06', '0a.01', '01.03', '02.02', '03.09'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['04.14', '09.0b', '06.06', '0a.01', '01.03', '02.02', '03.09'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMC_HFHB' => ['02.07'],
        'DEadpt_moons' => ['01.80'],
        'DEbem_mongoose' => ['03.20'],
        'DEbem_mongoose_lw' => ['03.20'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.8+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbem_mongoose' => ['03.20'],
        'EPbem_mongoose_lw' => ['03.20'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.8+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.22.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.173'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.3.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.70.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F600' => {
                'BIOS' => ['2.22.1'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.173'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.3.4'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.70.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F900' => {
                'BIOS' => ['2.22.1'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.173'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.3.4'],
                'Backplane 1' => ['3.31'],
                'Backplane 2' => ['3.31'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.70.00'],
                'System CPLD' => ['1.2.6'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.5.0'],
                'VOSS Dell EC NVMe ISE 7450 RI M.2 110 960GB' => ['1.1.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'LOx' => ['rp180d01+rp180d01', 'rp180d01'],
        'LOx2' => ['rp180d01+rp180d01', 'rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01+rp180d01', 'rp180d01'],
        'Lsi' => ['20.00.04.00', '13.00.66.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['03.20'],
        'MLKbem_mongoose_lw_m1b' => ['03.20'],
        'MLKbios_gryphon' => ['10.79'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.8+21.00'],
        'MLKccdes_catamarancos' => ['1.10.8+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.13.75.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.85'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.79'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+ISL1090110018', '4.8.930+MT_02F0140002', '2.42.5000+MT_1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-EN1' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.22.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.173'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.3.4'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LightPulse LPe35002-M2 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.10.70.00'],
                'System CPLD' => ['1.2.6'],
        },
        'QLogic-NX2' => ['7.13.0'],
    },
    '12.3' => {
        'B100' => {
                'BIOS' => ['2.22.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.173'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'CFFPS1' => ['0a.01', '03.09', '06.06', '01.03', '09.0b', '02.02', '04.14'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '03.09', '06.06', '01.03', '09.0b', '02.02', '04.14'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMC_HFHB' => ['02.07'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.40'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.40'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.22.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.173'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.2.7'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.50.201'],
                'System CPLD' => ['1.2.1'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.3.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F600' => {
                'BIOS' => ['2.22.1'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.173'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.2.7'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.50.201'],
                'System CPLD' => ['1.2.1'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.3.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F900' => {
                'BIOS' => ['2.22.1'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.173'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.2.7'],
                'Backplane 1' => ['3.31'],
                'Backplane 2' => ['3.31'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.50.201'],
                'System CPLD' => ['1.2.1'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.3.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.40'],
        'MLKbem_mongoose_lw_m1b' => ['02.85'],
        'MLKbios_gryphon' => ['10.77'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.7+21.00'],
        'MLKccdes_catamarancos' => ['1.10.7+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.73'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.77'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.42.5000+ISL1090110018', '2.42.5000+MT_1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.22.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.173'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.2.7'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LightPulse LPe35002-M2 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.10.50.201'],
                'System CPLD' => ['1.2.1'],
        },
        'QLogic-NX2' => ['7.13.0'],
    },
        '12.2.1' => {
        'B100' => {
                'BIOS' => ['2.21.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.171'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'CFFPS1' => ['01.03', '03.09', '04.14', '0a.01', '09.0b', '06.06', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['01.03', '03.09', '04.14', '0a.01', '09.0b', '06.06', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMC_HFHB' => ['02.07'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.40'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.40'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.21.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.171'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.1.5'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.30.00'],
                'System CPLD' => ['1.1.0'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.3.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F600' => {
                'BIOS' => ['2.21.2'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.171'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.1.5'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.30.00'],
                'System CPLD' => ['1.1.0'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.3.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F900' => {
                'BIOS' => ['2.21.2'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.171'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.1.5'],
                'Backplane 1' => ['3.31'],
                'Backplane 2' => ['3.31'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.30.00'],
                'System CPLD' => ['1.1.0'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.3.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'LOx' => ['rp180d01+rp180d01', 'rp180d01'],
        'LOx2' => ['rp180d01+rp180d01', 'rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01+rp180d01', 'rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.40'],
        'MLKbem_mongoose_lw_m1b' => ['02.85'],
        'MLKbios_gryphon' => ['10.77'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.7+21.00'],
        'MLKccdes_catamarancos' => ['1.10.7+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.73'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.77'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+ISL1090110018', '4.8.930+MT_02F0140002', '2.42.5000+MT_1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC0000000002', '2.42.5000+EMC1090111023'],
        'Mellanox-EN1' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC0000000002', '2.42.5000+EMC1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.21.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.171'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.1.5'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LightPulse LPe35002-M2 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.10.30.00'],
                'System CPLD' => ['1.1.0'],
        },
        'QLogic-NX2' => ['7.13.0'],
    },
        '12.2' => {
        'B100' => {
                'BIOS' => ['2.21.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.171'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'CFFPS1' => ['06.06', '04.14', '0a.01', '01.03', '09.0b', '02.02', '03.09'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['06.06', '04.14', '0a.01', '01.03', '09.0b', '02.02', '03.09'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMC_HFHB' => ['02.07'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.40'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.40'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.21.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.171'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['2.1.5'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.30.00'],
                'System CPLD' => ['1.1.0'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.3.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F600' => {
                'BIOS' => ['2.21.2'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.171'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['2.1.5'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.30.00'],
                'System CPLD' => ['1.1.0'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.3.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'F900' => {
                'BIOS' => ['2.21.2'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.171'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['2.1.5'],
                'Backplane 1' => ['3.31'],
                'Backplane 2' => ['3.31'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.10.30.00'],
                'System CPLD' => ['1.1.0'],
                'VOSS Dell DC NVMe PM9A3 RI M.2 110 960GB' => ['1.0.0'],
                'VOSS Dell EC NVMe FIPS 7400 RI M.2 110 960GB' => ['2.3.0'],
                'VOSS Dell EC NVMe OPAL 7400 RI M.2 110 960GB' => ['1.4.0'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['20.00.04.00', '13.00.66.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.40'],
        'MLKbem_mongoose_lw_m1b' => ['02.85'],
        'MLKbios_gryphon' => ['10.77'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.7+21.00'],
        'MLKccdes_catamarancos' => ['1.10.7+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.73'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.77'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '4.8.930+MT_02F0140002', '2.42.5000+ISL1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.36.1010+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.36.1010+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-200GbE-BE' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-200GbE-FE0' => ['20.39.1002+DEL0000000052'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-EN1' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.21.2'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.171'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PA110' => {
                'BIOS' => ['2.1.5'],
                'BOSS-N1 Monolithic' => ['2.1.13.2025'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Emulex LightPulse LPe35002-M2 2-Port 32Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.10.30.00'],
                'System CPLD' => ['1.1.0'],
        },
        'QLogic-NX2' => ['7.13.0'],
    },
    '12.1' => {
        'B100' => {
                'BIOS' => ['2.20.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['7.00.00.00'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '02.02', '03.09', '09.0b', '04.14', '06.06', '01.03'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '02.02', '03.09', '09.0b', '04.14', '06.06', '01.03'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.40'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.44.0+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.40'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.44.0+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.20.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.00'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['1.6.6'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.60.00'],
                'System CPLD' => ['1.0.7'],
        },
        'F600' => {
                'BIOS' => ['2.20.1'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.00'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['1.6.6'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.60.00'],
                'System CPLD' => ['1.0.7'],
        },
        'F900' => {
                'BIOS' => ['2.20.1'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.00.00'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['1.6.6'],
                'Backplane 1' => ['3.31'],
                'Backplane 2' => ['3.31'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.60.00'],
                'System CPLD' => ['1.0.7'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01+rp180d01', 'rp180d01'],
        'LOx2' => ['rp180d01+rp180d01', 'rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01+rp180d01', 'rp180d01'],
        'Lsi' => ['20.00.04.00', '13.00.66.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.40'],
        'MLKbios_gryphon' => ['10.76'],
        'MLKcatamaran' => ['0028.0015.0000.0000'],
        'MLKccdes_catamaran' => ['1.10.7+21.00'],
        'MLKccdes_catamarancos' => ['1.10.7+21.00'],
        'MLKcdes' => ['2.44.0+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_64pincos' => ['02.09.117.03'],
        'MLKcmd_rev_table' => ['20.73'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.76'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018', '4.8.930+MT_02F0140002'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.36.1010+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.36.1010+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.20.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['7.00.00.00'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '12.0' => {
        'B100' => {
                'BIOS' => ['2.18.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
                'Integrated Remote Access Controller' => ['6.10.80.201'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
                'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
                'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        },
        'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '04.14', '01.03', '02.02', '09.0b', '06.06', '03.09'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '04.14', '01.03', '02.02', '09.0b', '06.06', '03.09'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.40'],
        'DEbios_banshee' => ['37.44'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.41.1+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.44'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.18'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps0_gen3_acbel_optimuswr' => ['03.01'],
        'DEps0_gen3_artesyn_ramjet' => ['02.30'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEps1_gen3_acbel_optimuswr' => ['03.01'],
        'DEps1_gen3_artesyn_ramjet' => ['02.30'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.40'],
        'EPbios_warthog' => ['32.54'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.41.1+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.54'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.17'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps0_gen3_acbel_optimuswr' => ['03.01'],
        'EPps0_gen3_artesyn_ramjet' => ['02.30'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPps1_gen3_acbel_optimuswr' => ['03.01'],
        'EPps1_gen3_artesyn_ramjet' => ['02.30'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'F200' => {
                'BIOS' => ['2.18.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['6.10.80.201'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F210' => {
                'BIOS' => ['1.5.6'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.30.00'],
                'System CPLD' => ['1.0.7'],
        },
        'F600' => {
                'BIOS' => ['2.18.1'],
                'Backplane 1' => ['2.46'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['6.10.80.201'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F710' => {
                'BIOS' => ['1.5.6'],
                'Backplane 1' => ['7.10'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.30.00'],
                'System CPLD' => ['1.0.7'],
        },
        'F900' => {
                'BIOS' => ['2.18.1'],
                'Backplane 1' => ['4.35'],
                'Backplane 2' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['6.10.80.201'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'F910' => {
                'BIOS' => ['1.5.6'],
                'Backplane 1' => ['3.29'],
                'Backplane 2' => ['3.29'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Integrated Remote Access Controller' => ['7.00.30.00'],
                'System CPLD' => ['1.0.7'],
        },
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['20.00.04.00', '13.00.66.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.40'],
        'MLKbios_gryphon' => ['10.75'],
        'MLKccdes' => ['1.10.4+21.00'],
        'MLKcdes' => ['2.41.1+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_rev_table' => ['20.73'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.75'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.43'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.50'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.44'],
        'MLKuefi_fw_volume' => ['40.87'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+ISL1090110018', '4.8.930+MT_02F0140002', '2.42.5000+MT_1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.35.1012+DEL0000000004'],
        'Mellanox-CX6-100GbE-BE' => ['22.36.1010+DEL0000000027'],
        'Mellanox-CX6-100GbE-FE0' => ['22.36.1010+DEL0000000027'],
        'Mellanox-CX6-100GbE-IB-BE' => ['20.37.1014+EMC0000000029'],
        'Mellanox-CX6-25GbE-BE' => ['26.36.1010+DEL0000000031'],
        'Mellanox-CX6-25GbE-FE0' => ['26.36.1010+DEL0000000031'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-EN1' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'P100' => {
                'BIOS' => ['2.18.1'],
                'Backplane 1' => ['4.35'],
                'Broadcom Gigabit Ethernet BCM5720' => ['22.31.6'],
                'Broadcom NetXtreme Gigabit Ethernet (BCM5720)' => ['22.31.6'],
                'Dell HBA330 Mini' => ['16.17.01.00'],
                'Integrated Remote Access Controller' => ['6.10.80.201'],
                'Intel(R) Gigabit 4P I350-t rNDC' => ['22.0.9'],
        },
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '11.7.3' => {
        'BIOS' => ['2.16.15'],
        'BMCSDR_A100' => ['01.07'],
        'Backplane 1' => ['4.35', '2.46'],
        'Backplane 2' => ['4.35'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['03.09', '0a.01', '09.0b', '01.03', '04.14', '02.02', '06.06'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['03.09', '0a.01', '09.0b', '01.03', '04.14', '02.02', '06.06'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.40'],
        'DEbios_banshee' => ['37.43'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.43'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.16'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.40'],
        'EPbios_warthog' => ['32.53'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.53'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.15'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
        'Integrated Remote Access Controller' => ['5.10.50.203'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.40'],
        'MLKbios_gryphon' => ['10.72'],
        'MLKccdes' => ['1.10.4+21.00'],
        'MLKcdes' => ['2.41.1+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_rev_table' => ['20.66'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.72'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.39'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.40'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.43'],
        'MLKuefi_fw_volume' => ['40.86'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.32.2004+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.32.2004+DEL0000000004'],
        'Mellanox-CX6-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '11.7.1' => {
        'BIOS' => ['2.16.15'],
        'BMCSDR_A100' => ['01.07'],
        'Backplane 1' => ['2.46', '4.35'],
        'Backplane 2' => ['4.35'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['09.0b', '01.03', '02.02', '03.09', '04.14', '06.06', '0a.01'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['09.0b', '01.03', '02.02', '03.09', '04.14', '06.06', '0a.01'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.40'],
        'DEbios_banshee' => ['37.43'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.43'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.16'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.40'],
        'EPbios_warthog' => ['32.53'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.53'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.15'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
        'Integrated Remote Access Controller' => ['5.10.50.203'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01+rp180d01', 'rp180d01'],
        'LOx2' => ['rp180d01+rp180d01', 'rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01+rp180d01', 'rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.40'],
        'MLKbios_gryphon' => ['10.72'],
        'MLKccdes' => ['1.10.4+21.00'],
        'MLKcdes' => ['2.41.1+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_rev_table' => ['20.66'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.72'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.39'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.40'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.43'],
        'MLKuefi_fw_volume' => ['40.86'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018', '4.8.930+MT_02F0140002'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.32.2004+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.32.2004+DEL0000000004'],
        'Mellanox-CX6-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '11.7' => {
        'BIOS' => ['2.16.15'],
        'BMCSDR_A100' => ['01.07'],
        'Backplane 1' => ['2.46', '4.35'],
        'Backplane 2' => ['4.35'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['04.14', '01.03', '09.0b', '02.02', '0a.01', '03.09', '06.06'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['04.14', '01.03', '09.0b', '02.02', '0a.01', '03.09', '06.06'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.40'],
        'DEbios_banshee' => ['37.43'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.43'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.16'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.40'],
        'EPbios_warthog' => ['32.53'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.53'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.15'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre Channel Adapter' => ['03.05.23'],
        'Integrated Remote Access Controller' => ['5.10.50.203'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['20.00.04.00', '13.00.66.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.40'],
        'MLKbios_gryphon' => ['10.72'],
        'MLKccdes' => ['1.10.4+21.00'],
        'MLKcdes' => ['2.41.1+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_rev_table' => ['20.66'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.72'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.66'],
        'MLKpost_gryphon' => ['10.39'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps0_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps0_gen3_artesyn_ramjet' => ['02.30'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen3_acbel_optimuswr' => ['03.01'],
        'MLKps1_gen3_artesyn_ramjet' => ['02.30'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.40'],
        'MLKspime_gryphon' => ['08.00'],
        'MLKssp_infinitymlk' => ['01.43'],
        'MLKuefi_fw_volume' => ['40.86'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.42.5000+ISL1090110018', '2.42.5000+MT_1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.32.2004+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.32.2004+DEL0000000004'],
        'Mellanox-CX6-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC0000000002', '2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC0000000002', '2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '11.6.1' => {
        'BIOS' => ['2.13.37'],
        'BMCSDR_A100' => ['01.07'],
        'Backplane 1' => ['2.46', '4.35'],
        'Backplane 2' => ['4.35'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['02.02', '06.06', '04.14', '0a.01', '09.0b', '03.09', '01.03'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['02.02', '06.06', '04.14', '0a.01', '09.0b', '03.09', '01.03'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.40'],
        'DEbios_banshee' => ['37.43'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.43'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.16'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.40'],
        'EPbios_warthog' => ['32.53'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.53'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.15'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Integrated Remote Access Controller' => ['4.40.10.00'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.40'],
        'MLKbios_gryphon' => ['10.68'],
        'MLKccdes' => ['1.10.4+21.00'],
        'MLKcdes' => ['2.41.1+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_rev_table' => ['20.66'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.68'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.65'],
        'MLKpost_gryphon' => ['10.36'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.40'],
        'MLKspime_gryphon' => ['07.00'],
        'MLKssp_infinitymlk' => ['01.43'],
        'MLKuefi_fw_volume' => ['40.86'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '4.8.930+MT_02F0140002', '2.42.5000+ISL1090110018'],
        'Mellanox-CX4' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.32.2004+DEL2420110034'],
        'Mellanox-CX4R' => ['14.32.2004+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.32.2004+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.32.2004+DEL0000000004'],
        'Mellanox-CX6-BE' => ['22.32.2004+DEL0000000027'],
        'Mellanox-CX6-FE0' => ['22.32.2004+DEL0000000027'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.05.23'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '11.5.1' => {
        'BIOS' => ['2.13.37'],
        'BMCSDR_A100' => ['01.07'],
        'Backplane 1' => ['2.46', '4.35'],
        'Backplane 2' => ['4.35'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.42'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.51'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.42'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.16'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['03.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.52'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.51'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.52'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.15'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['09.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Integrated Remote Access Controller' => ['4.40.10.00'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.20'],
        'MLKbios_gryphon' => ['10.63'],
        'MLKccdes' => ['1.10.4+21.00'],
        'MLKcdes' => ['2.41.1+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_rev_table' => ['20.66'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.63'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.63'],
        'MLKpost_gryphon' => ['10.34'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.40'],
        'MLKspime_gryphon' => ['06.00'],
        'MLKssp_infinitymlk' => ['01.41'],
        'MLKuefi_fw_volume' => ['40.86'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018', '4.8.930+MT_02F0140002'],
        'Mellanox-CX4' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4R' => ['14.27.6008+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX6-BE' => ['22.28.4512+DEL0000000027'],
        'Mellanox-CX6-FE0' => ['22.28.4512+DEL0000000027'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.04.25'],
        'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.04.25'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '11.5' => {
        'BIOS' => ['2.8.2'],
        'BMCSDR_A100' => ['01.07'],
        'Backplane 1' => ['2.46', '4.35'],
        'Backplane 2' => ['4.35'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.42'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.51'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.42'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.16'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['03.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.52'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.51'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.52'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.15'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['09.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Integrated Remote Access Controller' => ['4.40.10.00'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.20'],
        'MLKbios_gryphon' => ['10.63'],
        'MLKccdes' => ['1.10.4+21.00'],
        'MLKcdes' => ['2.41.1+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_rev_table' => ['20.66'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.63'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.63'],
        'MLKpost_gryphon' => ['10.34'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.40'],
        'MLKspime_gryphon' => ['06.00'],
        'MLKssp_infinitymlk' => ['01.41'],
        'MLKuefi_fw_volume' => ['40.86'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd0_infinitymlk_ti' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_ti' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_ti' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'MLKvrd3_infinitymlk_ti' => ['01.01'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018', '4.8.930+MT_02F0140002'],
        'Mellanox-CX4' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4R' => ['14.27.6008+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX6-BE' => ['22.28.4512+DEL0000000027'],
        'Mellanox-CX6-FE0' => ['22.28.4512+DEL0000000027'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.04.25'],
        'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.04.25'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '11.4' => {
        'BIOS' => ['2.8.2'],
        'BMCSDR_A100' => ['01.07'],
        'Backplane 1' => ['2.46', '4.35'],
        'Backplane 2' => ['4.35'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.41'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.51'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.41'],
        'DEfw_map_table' => ['12.17'],
        'DEpost_banshee' => ['28.15'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11192'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.02'],
        'DEssp_infinity' => ['02.83'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.51'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.51'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.51'],
        'EPfw_map_table' => ['12.17'],
        'EPpost_warthog' => ['22.14'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11192'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['08.00'],
        'EPssp_infinity' => ['02.83'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Integrated Remote Access Controller' => ['4.40.10.00'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.20'],
        'MLKbios_gryphon' => ['10.61'],
        'MLKccdes' => ['1.10.4+21.00'],
        'MLKcdes' => ['2.41.1+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_rev_table' => ['20.66'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.61'],
        'MLKfw_map_table' => ['40.52'],
        'MLKgem_skylanders' => ['45.62'],
        'MLKpost_gryphon' => ['10.33'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11193'],
        'MLKserdes_infinitymlk' => ['00.40'],
        'MLKspime_gryphon' => ['04.00'],
        'MLKssp_infinitymlk' => ['01.41'],
        'MLKuefi_fw_volume' => ['40.86'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018', '4.8.930+MT_02F0140002'],
        'Mellanox-CX4' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4R' => ['14.27.6008+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX6-BE' => ['22.28.4512+DEL0000000027'],
        'Mellanox-CX6-FE0' => ['22.28.4512+DEL0000000027'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.04.25'],
        'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.04.25'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '11.2' => {
        'BIOS' => ['2.8.2'],
        'BMCSDR_A100' => ['01.07'],
        'BP14G+ 0:1' => ['4.35'],
        'BP14G+EXP 0:1' => ['2.46'],
        'Backplane 1' => ['2.46', '4.35'],
        'Backplane 2' => ['2.46', '4.35'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.29'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.20'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.29'],
        'DEfw_map_table' => ['12.17'],
        'DEpost_banshee' => ['28.14'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0003.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11181'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.01'],
        'DEssp_infinity' => ['02.83'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.37'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.20'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.37'],
        'EPfw_map_table' => ['12.17'],
        'EPpost_warthog' => ['22.13'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0003.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11181'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['07.00'],
        'EPssp_infinity' => ['02.83'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Integrated Remote Access Controller' => ['4.40.10.00'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.20'],
        'MLKbios_gryphon' => ['09.24'],
        'MLKccdes' => ['1.10.4+21.00'],
        'MLKcdes' => ['2.41.1+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_rev_table' => ['20.66'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['09.24'],
        'MLKfw_map_table' => ['40.49'],
        'MLKgem_skylanders' => ['37.31'],
        'MLKpost_gryphon' => ['09.36'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11181'],
        'MLKserdes_infinitymlk' => ['00.30'],
        'MLKspime_gryphon' => ['04.00'],
        'MLKssp_infinitymlk' => ['01.10'],
        'MLKuefi_fw_volume' => ['40.79'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-CX4' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4R' => ['14.27.6008+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX6-BE' => ['22.28.4512+DEL0000000027'],
        'Mellanox-CX6-FE0' => ['22.28.4512+DEL0000000027'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '11.1.4' => {
        'BIOS' => ['2.8.2'],
        'BMCSDR_A100' => ['01.07'],
        'BP14G+ 0:1' => ['4.35'],
        'BP14G+EXP 0:1' => ['2.46'],
        'Backplane 1' => ['2.46', '4.35'],
        'Backplane 2' => ['2.46', '4.35'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.29'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.20'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.29'],
        'DEfw_map_table' => ['12.17'],
        'DEpost_banshee' => ['28.14'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0003.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11181'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.01'],
        'DEssp_infinity' => ['02.83'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.37'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.20'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.37'],
        'EPfw_map_table' => ['12.17'],
        'EPpost_warthog' => ['22.13'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0003.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11181'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['07.00'],
        'EPssp_infinity' => ['02.83'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Integrated Remote Access Controller' => ['4.40.10.00'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-CX4' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4R' => ['14.27.6008+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.27.6106+DEL0000000004'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '11.1' => {
        'BIOS' => ['2.8.2'],
        'BMCSDR_A100' => ['01.07'],
        'BP14G+ 0:1' => ['4.35'],
        'BP14G+EXP 0:1' => ['2.46'],
        'Backplane 1' => ['2.46', '4.35'],
        'Backplane 2' => ['2.46', '4.35'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.28'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.20'],
        'DEccdes' => ['1.10.2+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.28'],
        'DEfw_map_table' => ['12.17'],
        'DEpost_banshee' => ['28.09'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0002.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11181'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.01'],
        'DEssp_infinity' => ['02.83'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.35'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.20'],
        'EPccdes' => ['1.10.2+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.35'],
        'EPfw_map_table' => ['12.17'],
        'EPpost_warthog' => ['22.09'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0002.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11181'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['07.00'],
        'EPssp_infinity' => ['02.83'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Integrated Remote Access Controller' => ['4.20.20.20'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-CX4' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4R' => ['14.27.6008+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.27.6106+DEL0000000004'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x1002+0x1002', '0x100a+0x100a'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['16.24.4020+EMC0000000017', '16.23.4010+MT_0000000158'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '11.0' => {
        'BIOS' => ['2.4.8'],
        'BMCSDR_A100' => ['01.07'],
        'BP14G+ 0:1' => ['4.35'],
        'BP14G+EXP 0:1' => ['2.46'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.24'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.20'],
        'DEccdes' => ['1.10.2+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.24'],
        'DEfw_map_table' => ['12.14'],
        'DEpost_banshee' => ['28.07'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0002.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11181'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.01'],
        'DEssp_infinity' => ['02.83'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.00.05'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.32'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.20'],
        'EPccdes' => ['1.10.2+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.32'],
        'EPfw_map_table' => ['12.14'],
        'EPpost_warthog' => ['22.07'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0002.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11181'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['07.00'],
        'EPssp_infinity' => ['02.83'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Integrated Remote Access Controller' => ['4.00.00.00'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-CX4' => ['14.25.8000+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.25.8000+DEL2420110034'],
        'Mellanox-CX4R' => ['14.25.8000+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.25.8000+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.25.8000+DEL0000000004'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x1002+0x1002', '0x100a+0x100a'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['16.24.4020+EMC0000000017', '16.23.4010+MT_0000000158'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
    },
    '10.4.0' => {
        'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['04.14', '03.09', '01.03', '0a.01', '06.06', '02.02', '09.0b'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['04.14', '03.09', '01.03', '0a.01', '06.06', '02.02', '09.0b'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.40'],
        'DEbios_banshee' => ['37.43'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.52'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.43'],
        'DEfw_map_table' => ['12.18'],
        'DEpost_banshee' => ['28.16'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11193'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['04.00'],
        'DEssp_infinity' => ['02.84'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.40'],
        'EPbios_warthog' => ['32.53'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.52'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.53'],
        'EPfw_map_table' => ['12.18'],
        'EPpost_warthog' => ['22.15'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11193'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['10.00'],
        'EPssp_infinity' => ['02.84'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['20.00.04.00', '13.00.66.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018', '4.8.930+MT_02F0140002'],
        'Mellanox-EN' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC1090111023'],
        'Mellanox-EN0' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC0000000002', '2.42.5000+EMC1090111023'],
        'Mellanox-EN1' => ['2.42.5000+MT_1090111023', '2.42.5000+EMC0000000002', '2.42.5000+EMC1090111023'],
        'Mellanox-FPGA' => ['0x100a+0x100a', '0x1002+0x1002'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['14.27.6008+DEL2420110034', '16.28.1002+EMC0000000017', '16.23.4010+MT_0000000158'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
        'XL710' => ['5.04+80002505'],
    },
    '10.3.8' => {
        'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.41'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.51'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.41'],
        'DEfw_map_table' => ['12.17'],
        'DEpost_banshee' => ['28.15'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11192'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.02'],
        'DEssp_infinity' => ['02.83'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.51'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.51'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.51'],
        'EPfw_map_table' => ['12.17'],
        'EPpost_warthog' => ['22.14'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11192'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['08.00'],
        'EPssp_infinity' => ['02.83'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['2.42.5000+MT_1090110018', '2.42.5000+ISL1090110018', '4.8.930+MT_02F0140002'],
        'Mellanox-EN' => ['2.42.5000+EMC1090111023', '2.42.5000+MT_1090111023'],
        'Mellanox-EN0' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-EN1' => ['2.42.5000+EMC1090111023', '2.42.5000+EMC0000000002', '2.42.5000+MT_1090111023'],
        'Mellanox-FPGA' => ['0x1002+0x1002', '0x100a+0x100a'],
        'Mellanox0' => ['2.42.5000+EMC0000000004'],
        'Mellanox1' => ['2.42.5000+EMC0000000004'],
        'Mellanox5-EN0' => ['14.27.6008+DEL2420110034', '16.23.4010+MT_0000000158', '16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
        'XL710' => ['5.04+80002505'],
    },
    '10.3.6' => {
        'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.29'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.20'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.29'],
        'DEfw_map_table' => ['12.17'],
        'DEpost_banshee' => ['28.14'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0003.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11181'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.01'],
        'DEssp_infinity' => ['02.83'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.37'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.20'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.37'],
        'EPfw_map_table' => ['12.17'],
        'EPpost_warthog' => ['22.13'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0003.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11181'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['07.00'],
        'EPssp_infinity' => ['02.83'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x1002+0x1002', '0x100a+0x100a'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['14.25.1020+DEL2420110034', '16.23.4010+MT_0000000158', '16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
        'XL710' => ['5.04+80002505'],
    },
    '10.3.5' => {
        'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.28'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.20'],
        'DEccdes' => ['1.10.2+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.28'],
        'DEfw_map_table' => ['12.17'],
        'DEpost_banshee' => ['28.09'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0002.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11181'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.01'],
        'DEssp_infinity' => ['02.83'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.35'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.20'],
        'EPccdes' => ['1.10.2+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.35'],
        'EPfw_map_table' => ['12.17'],
        'EPpost_warthog' => ['22.09'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0002.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11181'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['07.00'],
        'EPssp_infinity' => ['02.83'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x1002+0x1002', '0x100a+0x100a'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['16.24.4020+EMC0000000017', '14.25.1020+DEL2420110034', '16.23.4010+MT_0000000158'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
        'XL710' => ['5.04+80002505'],
    },
    '10.3.3' => {
        'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.24'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.20'],
        'DEccdes' => ['1.10.2+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.24'],
        'DEfw_map_table' => ['12.14'],
        'DEpost_banshee' => ['28.07'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0002.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11181'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.01'],
        'DEssp_infinity' => ['02.81'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.32'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.20'],
        'EPccdes' => ['1.10.2+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.32'],
        'EPfw_map_table' => ['12.14'],
        'EPpost_warthog' => ['22.07'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0002.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11181'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['07.00'],
        'EPssp_infinity' => ['02.81'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x1002+0x1002', '0x100a+0x100a'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['16.24.4020+EMC0000000017', '14.25.1020+DEL2420110034', '16.23.4010+MT_0000000158'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
        'XL710' => ['5.04+80002505'],
    },
    '10.3.2' => {
	'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.22'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.00'],
        'DEccdes' => ['1.10.1+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.22'],
        'DEfw_map_table' => ['12.14'],
        'DEpost_banshee' => ['28.07'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0002.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11181'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.01'],
        'DEssp_infinity' => ['02.81'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.31'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.00'],
        'EPccdes' => ['1.10.1+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.31'],
        'EPfw_map_table' => ['12.14'],
        'EPpost_warthog' => ['22.07'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0002.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11181'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['07.00'],
        'EPssp_infinity' => ['02.81'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x1002+0x1002', '0x100a+0x100a'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['16.24.4020+EMC0000000017', '16.23.4010+MT_0000000158'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
        'XL710' => ['5.04+80002505'],
    },
    '10.3.1' => {
        'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.22'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.00'],
        'DEccdes' => ['1.10.1+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.22'],
        'DEfw_map_table' => ['12.14'],
        'DEpost_banshee' => ['28.07'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0002.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11181'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.01'],
        'DEssp_infinity' => ['02.80'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.31'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.00'],
        'EPccdes' => ['1.10.1+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.31'],
        'EPfw_map_table' => ['12.14'],
        'EPpost_warthog' => ['22.07'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0002.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11181'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['07.00'],
        'EPssp_infinity' => ['02.80'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x1002+0x1002', '0x100a+0x100a'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['16.24.4020+EMC0000000017', '16.23.4010+MT_0000000158'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
        'XL710' => ['5.04+80002505'],
    },
    '10.3.0' => {
        'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['01.20'],
        'DEbios_banshee' => ['37.19'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.00'],
        'DEccdes' => ['1.10.1+21.00'],
        'DEcdes' => ['2.37.1+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.19'],
        'DEfw_map_table' => ['12.14'],
        'DEpost_banshee' => ['28.05'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0002.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11181'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.01'],
        'DEssp_infinity' => ['02.80'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['01.20'],
        'EPbios_warthog' => ['32.27'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.00'],
        'EPccdes' => ['1.10.1+21.00'],
        'EPcdes' => ['2.37.1+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPefi_sas_catamaran' => ['0017.0000.0003.0000'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.27'],
        'EPfw_map_table' => ['12.14'],
        'EPpost_warthog' => ['22.05'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0002.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11181'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['07.00'],
        'EPssp_infinity' => ['02.80'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x1004+0x1004', '0x1002+0x1002'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['16.24.4020+EMC0000000017', '16.23.4010+MT_0000000158'],
        'PMC8056' => ['2.13.0+0.11.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
        'XL710' => ['5.04+80002505'],
    },
    '9.3.5' => {
        'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.05'],
        'CAR_Ermine' => ['02.05'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.03', '02.02', '09.05', '06.06'],
        'CFFPS1_Blastoff' => ['03.03'],
        'CFFPS1_Octane' => ['09.05'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.03', '02.02', '09.05', '06.06'],
        'CFFPS2_Blastoff' => ['03.03'],
        'CFFPS2_Octane' => ['09.05'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.05'],
        'CMC_Ermine' => ['02.05'],
        'CMC_HFHB' => ['02.05'],
        'CMC_Yeti' => ['02.05'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180c01', 'rp180c01+rp180c01'],
        'LOx2' => ['rp180c01', 'rp180c01+rp180c01'],
        'LOx2-MLC-YD' => ['rp180c01', 'rp180c01+rp180c01'],
        'Lsi' => ['1.023', '6.28.00.00+01.28.02.00+1.17', '0.99c', '6.28.00.00+01.28.02.00+1.00', '13.00.66.00', '20.00.04.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['2.30.8020+ISL1090110018', '2.30.8020+MT_1090110018', '4.8.930+MT_02F0140002'],
        'Mellanox-EN' => ['2.34.5000+MT_1090111023'],
        'PMC8056' => ['2.13.0+0.11.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
        'Vitesse0' => ['1.024', '0.99c'],
        'Vitesse1' => ['1.024'],
    },
   );
###
# Patch_Releases: For OneFS 9.1 and later releases, a table of which patches
#  correspond to each OneFS version
our %Patch_Releases = (
    '9.1.0.0' => undef,
    '9.1.0.1' => { 'GA-RUP' => '9.1.0.1_GA-RUP_2020-11_PSP-464', },
    '9.1.0.2' => { 'GA-RUP' => '9.1.0.2_GA-RUP_2020-12_PSP-524',
		   'DA-RUP' => '9.1.0.2_DA-RUP_2020-12_PSP-645', },
    '9.1.0.3' => { 'GA-RUP' => '9.1.0.3_GA-RUP_2021-01_PSP-664',
		   'DA-RUP' => '9.1.0.3_DA-RUP_2021-01_PSP-665', },
    '9.1.0.4' => { 'GA-RUP' => '9.1.0.4_GA-RUP_2021-02_PSP-769',
		   'DA-RUP' => '9.1.0.4_DA-RUP_2021-02_PSP-770', },
    '9.1.0.5' => { 'GA-RUP' => '9.1.0.5_GA-RUP_2021-03_PSP-842',
		   'DA-RUP' => '9.1.0.5_DA-RUP_2021-03_PSP-843', },
    '9.1.0.6' => { 'GA-RUP' => '9.1.0.6_GA-RUP_2021-04_PSP-928',
		   'DA-RUP' => '9.1.0.6_DA-RUP_2021-04_PSP-929', },
    '9.1.0.7' => { 'GA-RUP' => '9.1.0.7_GA-RUP_2021-05_PSP-1037',
		   'DA-RUP' => '9.1.0.7_DA-RUP_2021-05_PSP-1038', },
    '9.1.0.8' => { 'GA-RUP' => '9.1.0.8_GA-RUP_2021-06_PSP-1118',
		   'DA-RUP' => '9.1.0.8_DA-RUP_2021-06_PSP-1119', },
    '9.1.0.9' => { 'GA-RUP' => '9.1.0.9_GA-RUP_2021-07_PSP-1248',
		   'DA-RUP' => '9.1.0.9_DA-RUP_2021-07_PSP-1249', },
    '9.1.0.10' =>{ 'GA-RUP' => '9.1.0.10_GA-RUP_2021-08_PSP-1398',
		   'DA-RUP' => '9.1.0.10_DA-RUP_2021-08_PSP-1399', },
    '9.1.0.11' =>{ 'GA-RUP' => '9.1.0.11_GA-RUP_2021-09_PSP-1496',
		   'DA-RUP' => '9.1.0.11_DA-RUP_2021-09_PSP-1497', },
    '9.1.0.12' =>{ 'GA-RUP' => '9.1.0.12_GA-RUP_2021-10_PSP-1548',
		   'DA-RUP' => '9.1.0.12_DA-RUP_2021-10_PSP-1549', },
    '9.1.0.13' =>{ 'GA-RUP' => '9.1.0.13_GA-RUP_2021-11_PSP-1650',
		   'DA-RUP' => '9.1.0.13_DA-RUP_2021-11_PSP-1651', },
    '9.1.0.14' =>{ 'GA-RUP' => '9.1.0.14_GA-RUP_2021-12_PSP-1722',
		   'DA-RUP' => '9.1.0.14_DA-RUP_2021-12_PSP-1723', },
    '9.1.0.15' =>{ 'GA-RUP' => '9.1.0.15_GA-RUP_2022-01_PSP-1860',
		   'DA-RUP' => '9.1.0.15_DA-RUP_2022-01_PSP-1861', },
    '9.1.0.16' =>{ 'GA-RUP' => '9.1.0.16_GA-RUP_2022-02_PSP-1994',
		   'DA-RUP' => '9.1.0.16_DA-RUP_2022-02_PSP-1995', },
    '9.1.0.17' =>{ 'GA-RUP' => '9.1.0.17_GA-RUP_2022-03_PSP-2077',
		   'DA-RUP' => '9.1.0.17_DA-RUP_2022-03_PSP-2078', },
    '9.1.0.18' =>{ 'GA-RUP' => '9.1.0.18_GA-RUP_2022-04_PSP-2155',
		   'DA-RUP' => '9.1.0.18_DA-RUP_2022-04_PSP-2156', },
    '9.1.0.19' =>{ 'GA-RUP' => '9.1.0.19_GA-RUP_2022-05_PSP-2242',
		   'DA-RUP' => '9.1.0.19_DA-RUP_2022-05_PSP-2243', },
    '9.1.0.20' =>{ 'GA-RUP' => '9.1.0.20_GA-RUP_2022-06_PSP-2333',
		   'DA-RUP' => '9.1.0.20_DA-RUP_2022-06_PSP-2334', },
    '9.1.0.21' =>{ 'GA-RUP' => '9.1.0.21_GA-RUP_2022-07_PSP-2469',
		   'DA-RUP' => '9.1.0.21_DA-RUP_2022-07_PSP-2470', },
    '9.1.0.22' =>{ 'GA-RUP' => '9.1.0.22_GA-RUP_2022-08_PSP-2625',
		   'DA-RUP' => '9.1.0.22_DA-RUP_2022-08_PSP-2626', },
    '9.1.0.23' =>{ 'GA-RUP' => '9.1.0.23_GA-RUP_2022-09_PSP-2725',
		   'DA-RUP' => '9.1.0.23_DA-RUP_2022-09_PSP-2726', },
    '9.1.0.24' =>{ 'GA-RUP' => '9.1.0.24_GA-RUP_2022-10_PSP-2801', },
    '9.1.0.25' =>{ 'GA-RUP' => '9.1.0.25_GA-RUP_2022-11_PSP-2900', },
    '9.1.0.26' =>{ 'GA-RUP' => '9.1.0.26_GA-RUP_2022-12_PSP-2978', },
    '9.1.0.27' =>{ 'GA-RUP' => '9.1.0.27_GA-RUP_2023-01_PSP-3078', },
    '9.1.0.28' =>{ 'GA-RUP' => '9.1.0.28_GA-RUP_2023-02_PSP-3119', },
    '9.2.0.0' => undef,
    '9.2.0.1' => { 'GA-RUP' => '9.2.0.1_GA-RUP_2021-06_PSP-1322', },
    '9.2.1.0' => undef,
    '9.2.1.1' => { 'GA-RUP' => '9.2.1.1_GA-RUP_2021-06_PSP-1313', },
    '9.2.1.2' => { 'GA-RUP' => '9.2.1.2_GA-RUP_2021-07_PSP-1246',
		   'DA-RUP' => '9.2.1.2_DA-RUP_2021-07_PSP-1247', },
    '9.2.1.3' => { 'GA-RUP' => '9.2.1.3_GA-RUP_2021-08_PSP-1394',
		   'DA-RUP' => '9.2.1.3_DA-RUP_2021-08_PSP-1395', },
    '9.2.1.4' => { 'GA-RUP' => '9.2.1.4_GA-RUP_2021-09_PSP-1432',
		   'DA-RUP' => '9.2.1.4_DA-RUP_2021-09_PSP-1433', },
    '9.2.1.5' => { 'GA-RUP' => '9.2.1.5_GA-RUP_2021-10_PSP-1525',
		   'DA-RUP' => '9.2.1.5_DA-RUP_2021-10_PSP-1526', },
    '9.2.1.6' => { 'GA-RUP' => '9.2.1.6_GA-RUP_2021-11_PSP-1640',
		   'DA-RUP' => '9.2.1.6_DA-RUP_2021-11_PSP-1641', },
    '9.2.1.7' => { 'GA-RUP' => '9.2.1.7_GA-RUP_2021-12_PSP-1698',
		   'DA-RUP' => '9.2.1.7_DA-RUP_2021-12_PSP-1699', },
    '9.2.1.8' => { 'GA-RUP' => '9.2.1.8_GA-RUP_2022-01_PSP-1816',
		   'DA-RUP' => '9.2.1.8_DA-RUP_2022-01_PSP-1817', },
    '9.2.1.9' => { 'GA-RUP' => '9.2.1.9_GA-RUP_2022-02_PSP-1964',
		   'DA-RUP' => '9.2.1.9_DA-RUP_2022-02_PSP-1965', },
    '9.2.1.10' => { 'GA-RUP' => '9.2.1.10_GA-RUP_2022-03_PSP-2075',
		    'DA-RUP' => '9.2.1.10_DA-RUP_2022-03_PSP-2076', },
    '9.2.1.11' => { 'GA-RUP' => '9.2.1.11_GA-RUP_2022-04_PSP-2126',
		    'DA-RUP' => '9.2.1.11_DA-RUP_2022-04_PSP-2127', },
    '9.2.1.12' => { 'GA-RUP' => '9.2.1.12_GA-RUP_2022-05_PSP-2217',
		    'DA-RUP' => '9.2.1.12_DA-RUP_2022-05_PSP-2218', },
    '9.2.1.13' => { 'GA-RUP' => '9.2.1.13_GA-RUP_2022-06_PSP-2315',
		    'DA-RUP' => '9.2.1.13_DA-RUP_2022-06_PSP-2316', },
    '9.2.1.14' => { 'GA-RUP' => '9.2.1.14_GA-RUP_2022-07_PSP-2429',
		    'DA-RUP' => '9.2.1.14_DA-RUP_2022-07_PSP-2430', },
    '9.2.1.15' => { 'GA-RUP' => '9.2.1.15_GA-RUP_2022-08_PSP-2610',
		    'DA-RUP' => '9.2.1.15_DA-RUP_2022-08_PSP-2611', },
    '9.2.1.16' => { 'GA-RUP' => '9.2.1.16_GA-RUP_2022-09_PSP-2690',
		    'DA-RUP' => '9.2.1.16_DA-RUP_2022-09_PSP-2691', },
    '9.2.1.17' => { 'GA-RUP' => '9.2.1.17_GA-RUP_2022-10_PSP-2803', },
    '9.2.1.18' => { 'GA-RUP' => '9.2.1.18_GA-RUP_2022-11_PSP-2896', },
    '9.2.1.19' => { 'GA-RUP' => '9.2.1.19_GA-RUP_2022-12_PSP-2940', },
    '9.2.1.20' => { 'GA-RUP' => '9.2.1.20_GA-RUP_2023-01_PSP-3037', },
    '9.2.1.21' => { 'GA-RUP' => '9.2.1.21_GA-RUP_2023-02_PSP-3109', },
    '9.2.1.22' => { 'GA-RUP' => '9.2.1.22_GA-RUP_2023-03_PSP-3171', },
    '9.2.1.23' => { 'GA-RUP' => '9.2.1.23_GA-RUP_2023-06_PSP-3514', },
    '9.2.1.24' => { 'GA-RUP' => '9.2.1.24_GA-RUP_2023-09_PSP-3730', },
    '9.2.1.25' => { 'GA-RUP' => '9.2.1.25_GA-RUP_2023-12_PSP-3889', },
    '9.3.0.0' => undef,
    '9.3.0.1' => { 'GA-RUP' => '9.3.0.1_GA-RUP_2021-11_PSP-1764', },
    '9.3.0.2' => { 'GA-RUP' => '9.3.0.2_GA-RUP_2021-12_PSP-1643',
		   'DA-RUP' => '9.3.0.2_DA-RUP_2021-12_PSP-1644', },
    '9.3.0.3' => { 'GA-RUP' => '9.3.0.3_GA-RUP_2022-01_PSP-1724',
		   'DA-RUP' => '9.3.0.3_DA-RUP_2022-01_PSP-1725', },
    '9.3.0.4' => { 'GA-RUP' => '9.3.0.4_GA-RUP_2022-02_PSP-1929',
		   'DA-RUP' => '9.3.0.4_DA-RUP_2022-02_PSP-1930', },
    '9.3.0.5' => { 'GA-RUP' => '9.3.0.5_GA-RUP_2022-03_PSP-1956',
		   'DA-RUP' => '9.3.0.5_DA-RUP_2022-03_PSP-1957', },
    '9.3.0.6' => { 'GA-RUP' => '9.3.0.6_GA-RUP_2022-04_PSP-2151',
		   'DA-RUP' => '9.3.0.6_DA-RUP_2022-04_PSP-2152', },
    '9.3.0.7' => { 'GA-RUP' => '9.3.0.7_GA-RUP_2022-07_PSP-2193' },
    '9.3.0.9' => { 'GA-RUP' => '9.3.0.9_GA-RUP_2022-10_PSP-2935' },
    '9.4.0.3' => { 'GA-RUP' => '9.4.0.3_GA-RUP_2022-06_PSP-2331',
		   'DA-RUP' => '9.4.0.3_DA-RUP_2022-06_PSP-2332', },
    '9.4.0.4' => { 'GA-RUP' => '9.4.0.4_GA-RUP_2022-07_PSP-2398',
		   'DA-RUP' => '9.4.0.4_DA-RUP_2022-07_PSP-2399', },
    '9.4.0.5' => { 'GA-RUP' => '9.4.0.5_GA-RUP_2022-08_PSP-2576',
		   'DA-RUP' => '9.4.0.5_DA-RUP_2022-08_PSP-2577', },
    '9.4.0.6' => { 'GA-RUP' => '9.4.0.6_GA-RUP_2022-09_PSP-2687',
		   'DA-RUP' => '9.4.0.6_DA-RUP_2022-09_PSP-2688', },
    '9.4.0.7' => { 'GA-RUP' => '9.4.0.7_GA-RUP_2022-10_PSP-2740', },
    '9.4.0.9' => { 'GA-RUP' => '9.4.0.9_GA-RUP_2022-11_PSP-2975', },
    '9.4.0.10' => { 'GA-RUP' => '9.4.0.10_GA-RUP_2022-12_PSP-2937', },
    '9.4.0.11' => { 'GA-RUP' => '9.4.0.11_GA-RUP_2023-01_PSP-3035', },
    '9.4.0.12' => { 'GA-RUP' => '9.4.0.12_GA-RUP_2023-02_PSP-3079', },
    '9.4.0.13' => { 'GA-RUP' => '9.4.0.13_GA-RUP_2023-03_PSP-3140', },
    '9.4.0.14' => { 'GA-RUP' => '9.4.0.14_GA-RUP_2023-05_PSP-3388', },
    '9.4.0.15' => { 'GA-RUP' => '9.4.0.15_GA-RUP_2023-08_PSP-3593', },
    '9.4.0.16' => { 'GA-RUP' => '9.4.0.16_GA-RUP_2023-11_PSP-3881', },
    '9.4.0.17' => { 'GA-RUP' => '9.4.0.17_GA-RUP_2024-02_PSP-4007', },
    '9.4.0.18' => { 'GA-RUP' => '9.4.0.18_GA-RUP_2024-04_PSP-4292', },
    '9.4.0.19' => { 'GA-RUP' => '9.4.0.19_GA-RUP_2024-07_PSP-4392', },
    '9.4.0.20' => { 'GA-RUP' => '9.4.0.20_GA-RUP_2024-10_PSP-4496', }, 
    '9.4.0.21' => { 'GA-RUP' => '9.4.0.21_GA-RUP_2025-01_PSP-4579', },
    '9.5.0.0' => undef,
    '9.5.0.1' => { 'GA-RUP' => '9.5.0.1_GA-RUP_2023-03_PSP-3199', },
    '9.5.0.2' => { 'GA-RUP' => '9.5.0.2_GA-RUP_2023-04_PSP-3208', },
    '9.5.0.3' => { 'GA-RUP' => '9.5.0.3_LTS2023_GA-RUP_PSP-3332', },
    '9.5.0.5' => { 'GA-RUP' => '9.5.0.5_LTS2023_GA-RUP_PSP-3793', },
    '9.5.0.6' => { 'GA-RUP' => '9.5.0.6_LTS2023_GA-RUP_PSP-3839', },
    '9.5.0.7' => { 'GA-RUP' => '9.5.0.7_LTS2023_GA-RUP_PSP-3967', },
    '9.5.0.8' => { 'GA-RUP' => '9.5.0.8_LTS2023_GA-RUP_PSP-4211', },
    '9.5.1.0' => undef,
    '9.5.1.1' => undef,
    '9.5.1.2' => undef,
    '9.5.1.3' => undef,
    '9.5.1.4' => undef,
    '9.5.1.5' => undef,
    '9.5.1.6' => undef,
    '9.5.1.7' => undef,
    '9.7.0.0' => undef,
    '9.7.0.1' => undef,
    '9.7.0.2' => undef,
    '9.7.0.3' => undef,
    '9.7.1.0' => undef,
    '9.7.1.2' => undef,
    '9.7.1.3' => undef,
    '9.7.1.4' => undef,
    '9.7.1.5' => undef,
    '9.7.1.6' => undef,
    '9.7.1.7' => undef,
    '9.7.1.8' => undef,
    '9.7.1.9' => undef,
    '9.7.1.10' => undef,
    '9.7.1.11' => undef,
    '9.7.1.12' => undef,
    '9.7.1.13' => undef,
    '9.7.1.14' => undef,
    '9.7.1.15' => undef,
    '9.7.1.16' => undef,
    '9.8.0.0' => undef,
    '9.8.0.1' => undef,
    '9.8.0.2' => undef,
    '9.8.0.3' => undef,
    '9.9.0.0' => undef,
    '9.9.0.1' => undef,
    '9.9.0.2' => undef,
    '9.10.0.0' => undef,
    '9.10.0.1' => undef,
    '9.10.1.0' => undef,
    '9.10.1.1' => undef,
    '9.10.1.2' => undef,
    '9.10.1.3' => undef,
    '9.10.1.4' => undef,
    '9.10.1.5' => undef,
    '9.10.1.6' => undef,
    '9.10.1.7' => undef,
    '9.10.1.8' => undef,
    '9.11.0.3' => undef,
    '9.11.0.5' => undef,
    '9.11.0.6' => undef,
    '9.12.0.0' => undef,
    '9.12.0.1' => undef,
    '9.13.0.0' => undef,
    '9.13.0.1' => undef,
    '9.13.0.2' => undef,
    '9.13.1.0' => undef,
    '9.13.1.1' => undef,
    '9.14.0.0' => undef,        
    '9.14.0.1' => undef,
    '9.15.0.0' => undef,
   );
###
# Patched_OneFS: For pre-patched OneFS installation files, includes a list of
#  the patches that have been pre-loaded in them
our %Patched_OneFS = (
	'9.15.0.0' => undef,
    '9.14.0.1' => undef,
    '9.14.0.0' => undef,
	'9.13.1.1' => undef,
	'9.13.1.0' => undef,
	'9.13.0.2' => undef,
	'9.13.0.1' => undef,
	'9.13.0.0' => undef,
	'9.12.0.1' => undef,
	'9.12.0.0' => undef,
	'9.11.0.6' => undef,
	'9.11.0.5' => undef,
	'9.11.0.3' => undef,
	'9.10.1.8' => undef,
	'9.10.1.7' => undef,
	'9.10.1.6' => undef,
	'9.10.1.5' => undef,
	'9.10.1.4' => undef,
	'9.10.1.3' => undef,
	'9.10.1.2' => undef,
	'9.10.1.1' => undef,
	'9.10.1.0' => undef,
	'9.10.0.1' => undef,
	'9.10.0.0' => undef,
	'9.9.0.2' => undef,
	'9.9.0.1' => undef,
	'9.9.0.0' => undef,
	'9.8.0.3' => undef,
	'9.8.0.2' => undef,
	'9.8.0.1' => undef,
	'9.8.0.0' => undef,
	'9.7.1.16' => undef,
	'9.7.1.15' => undef,
	'9.7.1.14' => undef,
    '9.7.1.13' => undef,
	'9.7.1.12' => undef,
	'9.7.1.11' => undef,
	'9.7.1.10' => undef,
	'9.7.1.9' => undef,
	'9.7.1.8' => undef,
	'9.7.1.7' => undef,
	'9.7.1.6' => undef,
	'9.7.1.5' => undef,
	'9.7.1.4' => undef,
	'9.7.1.3' => undef,
	'9.7.1.2' => undef,
	'9.7.1.0' => undef,
	'9.7.0.3' => undef,
	'9.7.0.2' => undef,
	'9.7.0.1' => undef,
	'9.7.0.0' => undef,
	'9.5.1.7' => undef,
	'9.5.1.6' => undef,
	'9.5.1.5' => undef,
	'9.5.1.4' => undef,
	'9.5.1.3' => undef,
	'9.5.1.2' => undef,
	'9.5.1.1' => undef,
	'9.5.1.0' => undef,
	'9.5.0.8' => [ '9.5.0_UGA-PATCH-INFRA_2023-03_PSP-3206',
			'9.5.0.8_LTS2023_GA-RUP_PSP-4211',
			'HealthCheck_9.5.0_36.0.7' ],
	'9.5.0.7' => [ '9.5.0_UGA-PATCH-INFRA_2023-03_PSP-3206',
			'9.5.0.7_LTS2023_GA-RUP_PSP-3967',
			'HealthCheck_9.5.0_35.0.0' ],
	'9.5.0.6' => [ '9.5.0_UGA-PATCH-INFRA_2023-03_PSP-3206',
			'9.5.0.6_LTS2023_GA-RUP_PSP-3839',
			'HealthCheck_9.5.0_35.0.0' ],
	'9.5.0.5' => [ '9.5.0_UGA-PATCH-INFRA_2023-03_PSP-3206',
			'9.5.0.5_LTS2023_GA-RUP_PSP-3793',
			'HealthCheck_9.5.0_34.1.0' ],
	'9.5.0.3' => [ '9.5.0_UGA-PATCH-INFRA_2023-03_PSP-3206',
			'9.5.0.3_LTS2023_GA-RUP_PSP-3332',
			'HealthCheck_9.5.0_34.1.0' ],
	'9.5.0.2' => [ '9.5.0_UGA-PATCH-INFRA_2023-03_PSP-3206',
			'9.5.0.2_GA-RUP_2023-04_PSP-3208',
			'HealthCheck_9.5.0_34.1.0' ],
	'9.5.0.1' => [ '9.5.0.1_GA-RUP_2023-03_PSP-3199' ],
	'9.5.0.0' => undef,
	'9.4.0.21' => [ '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186',
			'9.4.0.21_GA-RUP_2025-01_PSP-4579',
			'HealthCheck_9.4.0_39.1.2'],
	'9.4.0.19' => [ '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186',
			'9.4.0.19_GA-RUP_2024-07_PSP-4392',
			'HealthCheck_9.4.0_36.0.7'],
	'9.4.0.18' => [ '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186',
			'9.4.0.18_GA-RUP_2024-04_PSP-4292',
			'HealthCheck_9.4.0_37.1.4'],
	'9.4.0.17' => [ '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186',
			'9.4.0.17_GA-RUP_2024-02_PSP-4007',
			'HealthCheck_9.4.0_35.0.0'],
	'9.4.0.16' => [ '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186',
			'9.4.0.16_GA-RUP_2023-11_PSP-3881',
			'HealthCheck_9.4.0_35.0.0'],
	'9.4.0.15' => [ '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186',
			'9.4.0.15_GA-RUP_2023-08_PSP-3593',
			'HealthCheck_9.4.0_35.0.0'],
	'9.4.0.14' => [ '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186',
		   '9.4.0.14_GA-RUP_2023-05_PSP-3388',
		   'HealthCheck_9.4.0_34.0.0'],
	'9.4.0.13' => [ '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186',
		   '9.4.0.13_GA-RUP_2023-03_PSP-3140',
		   'HealthCheck_9.4.0_34.0.0'],
	'9.4.0.12' => [ '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186',
		   '9.4.0.12_GA-RUP_2023-02_PSP-3079',
		   'HealthCheck_9.4.0_34.0.0'],
    '9.4.0.11' => [ '9.4.0_UGA-PATCH-INFRA_2023-01_PSP-3071',
		   '9.4.0.11_GA-RUP_2023-01_PSP-3035',
		   'HealthCheck_9.4.0_33.0.5'],
    '9.4.0.10' => [ '9.4.0_UGA-PATCH-INFRA_2022-12_PSP-3031',
		   '9.4.0.10_GA-RUP_2022-12_PSP-2937',
		   'HealthCheck_9.4.0_33.0.5'],
    '9.4.0.9' => [ '9.4.0_UGA-PATCH-INFRA_2022-10_PSP-2901',
		   '9.4.0.9_GA-RUP_2022-11_PSP-2975',
		   'HealthCheck_9.4.0_33.0.5'],
    '9.4.0.7' => [ '9.4.0_UGA-PATCH-INFRA_2022-09_PSP-2787',
		   '9.4.0.7_GA-RUP_2022-10_PSP-2740',
		   'HealthCheck_9.4.0_33.0.5'],
    '9.4.0.6' => [ '9.4.0_UGA-PATCH-INFRA_2022-09_PSP-2787',
		   '9.4.0.6_GA-RUP_2022-09_PSP-2687',
		   'HealthCheck_9.4.0_33.0.5'],
    '9.4.0.5' => [ '9.4.0_UGA-PATCH-INFRA_2022-05_PSP-2427',
		   '9.4.0.5_GA-RUP_2022-08_PSP-2576',
		   'HealthCheck_9.4.0_33.0.5'],
    '9.4.0.4' => [ '9.4.0_UGA-PATCH-INFRA_2022-05_PSP-2427',
		   '9.4.0.4_GA-RUP_2022-07_PSP-2398',
		   'HealthCheck_9.4.0_33.0.5'],
    '9.4.0.3' => [ '9.4.0_UGA-PATCH-INFRA_2022-05_PSP-2427',
		   '9.4.0.3_GA-RUP_2022-06_PSP-2331',],
    '9.3.0.9' => [ '9.3.0.9_GA-RUP_2022-10_PSP-2935',
		   'HealthCheck_9.3.0_33.0.5',],
    '9.3.0.7' => [ '9.3.0.7_GA-RUP_2022-07_PSP-2193',
		   'HealthCheck_9.3.0_33.0.5',],
    '9.3.0.6' => [ '9.3.0.6_GA-RUP_2022-04_PSP-2151',
		   'HealthCheck_9.3.0_32.0.6',],
    '9.3.0.5' => [ '9.3.0.5_GA-RUP_2022-03_PSP-1956', ],
    '9.3.0.4' => [ '9.3.0.4_GA-RUP_2022-02_PSP-1929', ],
    '9.3.0.3' => [ '9.3.0.3_GA-RUP_2022-01_PSP-1724', ],
    '9.3.0.2' => [ '9.3.0.2_GA-RUP_2021-12_PSP-1643', ],
    '9.3.0.1' => [ '9.3.0.1_GA-RUP_2021-11_PSP-1764', ],
    '9.3.0.0' => undef,
	'9.2.1.25' => [ '9.2.1.25_GA-RUP_2023-12_PSP-3889',
	    '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925',
	    'HealthCheck_9.2.1_35.0.0', ],
	'9.2.1.24' => [ '9.2.1.24_GA-RUP_2023-09_PSP-3730',
	    '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925',
	    'HealthCheck_9.2.1_35.0.0', ],
	'9.2.1.23' => [ '9.2.1.23_GA-RUP_2023-06_PSP-3514',
	    '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925',
	    'HealthCheck_9.2.1_34.0.0', ],
	'9.2.1.22' => [ '9.2.1.22_GA-RUP_2023-03_PSP-3171',
	    '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925',
	    'HealthCheck_9.2.1_34.0.0', ],
	'9.2.1.21' => [ '9.2.1.21_GA-RUP_2023-02_PSP-3109',
		    '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925',
		    'HealthCheck_9.2.1_34.0.0', ],
    '9.2.1.20' => [ '9.2.1.20_GA-RUP_2023-01_PSP-3037',
		    '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925',
		    'HealthCheck_9.2.1_34.0.0', ],
    '9.2.1.19' => [ '9.2.1.19_GA-RUP_2022-12_PSP-2940',
		    '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925',
		    'HealthCheck_9.2.1_33.0.5', ],
    '9.2.1.18' => [ '9.2.1.18_GA-RUP_2022-11_PSP-2896',
		    '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925',
		    'HealthCheck_9.2.1_33.0.5', ],
    '9.2.1.17' => [ '9.2.1.17_GA-RUP_2022-10_PSP-2803',
		    '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925',
		    'HealthCheck_9.2.1_33.0.5', ],
    '9.2.1.16' => [ '9.2.1.16_GA-RUP_2022-09_PSP-2690',
		    '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925',
		    'HealthCheck_9.2.1_33.0.5', ],
    '9.2.1.15' => [ '9.2.1.15_GA-RUP_2022-08_PSP-2610',
		    'HealthCheck_9.2.1_33.0.5', ],
    '9.2.1.14' => [ '9.2.1.14_GA-RUP_2022-07_PSP-2429',
		    'HealthCheck_9.2.1_33.0.5', ],
    '9.2.1.13' => [ '9.2.1.13_GA-RUP_2022-06_PSP-2315',
		    'HealthCheck_9.2.1_32.0.6', ],
    '9.2.1.12' => [ '9.2.1.12_GA-RUP_2022-05_PSP-2217',
		    'HealthCheck_9.2.1_32.0.6', ],
    '9.2.1.11' => [ '9.2.1.11_GA-RUP_2022-04_PSP-2126',
		    'HealthCheck_9.2.1_32.0.6', ],
    '9.2.1.10' => [ '9.2.1.10_GA-RUP_2022-03_PSP-2075',
		    'HealthCheck_9.2.1_2021-09', ],
    '9.2.1.9' => [ '9.2.1.9_GA-RUP_2022-02_PSP-1964',
		   'HealthCheck_9.2.1_2021-09', ],
    '9.2.1.8' => [ '9.2.1.8_GA-RUP_2022-01_PSP-1816',
		   'HealthCheck_9.2.1_2021-09', ],
    '9.2.1.7' => [ '9.2.1.7_GA-RUP_2021-12_PSP-1698',
		   'HealthCheck_9.2.1_2021-09', ],
    '9.2.1.6' => [ '9.2.1.6_GA-RUP_2021-11_PSP-1640',
		   'HealthCheck_9.2.1_2021-09', ],
    '9.2.1.5' => [ '9.2.1.5_GA-RUP_2021-10_PSP-1525',
		   'HealthCheck_9.2.1_2021-09',
		   '9.X_GA-TOOLS_2021-06_PSP-1306', ],
    '9.2.1.4' => [ '9.2.1.4_GA-RUP_2021-09_PSP-1432',
		   'HealthCheck_9.2.1_2021-09',
		   '9.X_GA-TOOLS_2021-06_PSP-1306', ],
    '9.2.1.3' => [ '9.2.1.3_GA-RUP_2021-08_PSP-1394',
		   'HealthCheck_9.2.1_2021-06',
		   '9.X_GA-TOOLS_2021-06_PSP-1306', ],
    '9.2.1.2' => [ '9.2.1.2_GA-RUP_2021-07_PSP-1246',
		   'HealthCheck_9.2.1_2021-06',
		   '9.X_GA-TOOLS_2021-06_PSP-1306', ],
    '9.2.1.1' => [ '9.2.1.1_GA-RUP_2021-06_PSP-1313',
		   'HealthCheck_9.2.1_2021-06' ],
    '9.2.1.0' => undef,
    '9.2.0.1' => [ '9.2.0.1_GA-RUP_2021-06_PSP-1322',
		   'HealthCheck_9.2.0_2021-06', ],
    '9.2.0.0' => undef,
	'9.1.0.28' => ['9.1.0_UGA-PATCH-INFRA_2022-07_PSP-2682',
			'9.1.0.28_GA-RUP_2023-02_PSP-3119',
			'HealthCheck_9.1.0_34.0.0',],
    '9.1.0.27' => ['9.1.0.27_GA-RUP_2023-01_PSP-3078',
		   'HealthCheck_9.1.0_34.0.0',
		   '9.1.0_UGA-PATCH-INFRA_2022-07_PSP-2682',],
    '9.1.0.26' => ['9.1.0.26_GA-RUP_2022-12_PSP-2978',
		   'HealthCheck_9.1.0_33.0.5',
		   '9.1.0_UGA-PATCH-INFRA_2022-07_PSP-2682',],
    '9.1.0.25' => ['9.1.0.25_GA-RUP_2022-11_PSP-2900',
		   'HealthCheck_9.1.0_33.0.5',
		   '9.1.0_UGA-PATCH-INFRA_2022-07_PSP-2682',],
    '9.1.0.24' => ['9.1.0.24_GA-RUP_2022-10_PSP-2801',
		   'HealthCheck_9.1.0_33.0.5',
		   '9.1.0_UGA-PATCH-INFRA_2022-07_PSP-2682',],
    '9.1.0.23' => ['9.1.0.23_GA-RUP_2022-09_PSP-2725',
		   'HealthCheck_9.1.0_33.0.5',
		   '9.1.0_UGA-PATCH-INFRA_2021-12_PSP-902',],
    '9.1.0.22' => ['9.1.0.22_GA-RUP_2022-08_PSP-2625',
		   'HealthCheck_9.1.0_33.0.5',
		   '9.1.0_UGA-PATCH-INFRA_2021-12_PSP-902',],
    '9.1.0.21' => ['9.1.0.21_GA-RUP_2022-07_PSP-2469',
		   'HealthCheck_9.1.0_33.0.5',
		   '9.1.0_UGA-PATCH-INFRA_2021-12_PSP-902',],
    '9.1.0.19' => ['9.1.0.19_GA-RUP_2022-05_PSP-2242',
		   'HealthCheck_9.1.0_32.0.6',
		   '9.1.0_UGA-PATCH-INFRA_2021-12_PSP-902',],
    '9.1.0.18' => ['9.1.0.18_GA-RUP_2022-04_PSP-2155',
		   'HealthCheck_9.1.0_32.0.6',
		   '9.1.0_UGA-PATCH-INFRA_2021-12_PSP-902',],
    '9.1.0.17' => ['9.1.0.17_GA-RUP_2022-03_PSP-2077',
		   'HealthCheck_9.1.0_2021-09', ],
    '9.1.0.16' => ['9.1.0.16_GA-RUP_2022-02_PSP-1994',
		   'HealthCheck_9.1.0_2021-09', ],
    '9.1.0.15' => ['9.1.0.15_GA-RUP_2022-01_PSP-1860',
		   'HealthCheck_9.1.0_2021-09', ],
    '9.1.0.14' => ['9.1.0.14_GA-RUP_2021-12_PSP-1722',
		   'HealthCheck_9.1.0_2021-09', ],
    '9.1.0.13' => ['9.1.0.13_GA-RUP_2021-11_PSP-1650',
		   'HealthCheck_9.1.0_2021-09', ],
    '9.1.0.12' => ['9.1.0.12_GA-RUP_2021-10_PSP-1548',
		   '9.X_GA-TOOLS_2021-06_PSP-1306',
		   'HealthCheck_9.1.0_2021-09', ],
    '9.1.0.11' => ['9.1.0.11_GA-RUP_2021-09_PSP-1496',
		   '9.X_GA-TOOLS_2021-06_PSP-1306',
		   'HealthCheck_9.1.0_2021-09', ],
    '9.1.0.10' => ['9.1.0.10_GA-RUP_2021-08_PSP-1398',
		   '9.X_GA-TOOLS_2021-06_PSP-1306',
		   'HealthCheck_9.1.0_2021-06', ],
    '9.1.0.9' => [ '9.1.0.9_GA-RUP_2021-07_PSP-1248',
		   '9.X_GA-TOOLS_2021-06_PSP-1306',
		   'HealthCheck_9.1.0_2021-06', ],
    '9.1.0.8' => [ '9.1.0.8_GA-RUP_2021-06_PSP-1118',
		   '9.X_GA-TOOLS_2021-06_PSP-1306',
		   'HealthCheck_9.1.0_2021-06', ],
    '9.1.0.7' => [ '9.1.0.7_GA-RUP_2021-05_PSP-1037',
		   'MULTI_GA-TOOL_2021-05_PSP-1034',
		   'HealthCheck_9.1.0_2021-03', ],
    '9.1.0.6' => [ '9.1.0.6_GA-RUP_2021-04_PSP-928',
		   'HealthCheck_9.1.0_2021-03', ],
    '9.1.0.5' => [ '9.1.0.5_GA-RUP_2021-03_PSP-842',
		   'HealthCheck_9.1.0_2021-03', ],
    '9.1.0.4' => [ '9.1.0.4_GA-RUP_2021-02_PSP-769',
		   'HealthCheck_9.1.0_2021-02', ],
    '9.1.0.3' => [ '9.1.0.3_GA-RUP_2021-01_PSP-664',
		   'HealthCheck_9.1.0_2021-01', ],
    '9.1.0.2' => [ '9.1.0.2_GA-RUP_2020-12_PSP-524',
		   'HealthCheck_9.1.0_2020-12', ],
    '9.1.0.1' => [ '9.1.0.1_GA-RUP_2020-11_PSP-464',
		   'HealthCheck_9.1.0_2020-10', ],
    '9.1.0.0' => undef,
    '9.0.0.0' => [ '9.0.0_UGA-PATCH-INFRA_2020-07_PSP-53',
		   '9.0.0_GA-RUP_2020-11_PSP-406',
		   'HealthCheck_9.0.0_2020-11', ],
    '8.2.2.0' => [ '8.2.2_UGA-PATCH-INFRA_2020-06_274207',
		   '8.2.2_GA-RUP_2022-06_PSP-2240',
		   'HealthCheck_8.2.2_2021-09',
		   '8.X_GA-TOOLS_2021-05_PSP-1034', ],
    '8.2.1.0' => [ '8.2.1_GA-RUP_2021-01_PSP-397',
		   '8.2.1_UGA-PATCH-INFRA_2020-03_267223',
		   'HealthCheck_8.2.1_2021-01', ],
    '8.2.0.0' => [ '8.2.0_UGA-PATCH-INFRA_2019-11_263084',
		   '8.2.0_GA-RUP_2020-06_273332',
		   'HealthCheck_8.2.0_2020-06', ],
    '8.1.2.0' => [ '8.1.2.0_UGA-PATCH-INFRA_2019-12_263531',
		   '8.1.2.0_GA-RUP_2021-06_PSP-1194',
		   '8.X_GA-TOOLS_2021-05_PSP-1034',
		   'HealthCheck_8.1.2_2021-05', ],
   );
our %ReleaseNotes = (
    'OneFS' => {
	'9.15.0' => 'https://dl.dell.com/content/manual56308328',
	'9.14.0' => 'https://dl.dell.com/content/manual53894477',
	'9.13.1' => 'https://dl.dell.com/content/manual54056589',
	'9.13.0' => 'https://dl.dell.com/content/manual52543583',
	'9.12.0' => 'https://dl.dell.com/content/manual54272401',
	'9.11.0' => 'https://dl.dell.com/content/manual55085014',
	'9.10.1' => 'https://dl.dell.com/content/manual81322492',
	'9.10.0' => 'https://dl.dell.com/content/manual55775512',
	'9.9.0' => 'https://dl.dell.com/content/manual47380315',
	'9.8.0' => 'https://dl.dell.com/content/manual78158158',
	'9.7.1' => 'https://dl.dell.com/content/manual76564693',	
	'9.7.0' => 'https://dl.dell.com/content/manual53083939',
	'9.5.1' => 'https://dl.dell.com/content/manual52361291',
	'9.5.0' => 'https://dl.dell.com/content/manual58071479',
	'9.4.0' => 'https://dl.dell.com/content/manual62544979',
	'9.3.0' => 'https://dl.dell.com/content/manual1305332',
	'9.2.1' => 'https://dl.dell.com/content/manual62107414',
	'9.2.0' => 'https://dl.dell.com/content/docu103229',
	'9.1.0' => 'https://dl.dell.com/content/docu100463',
	'9.0.0' => 'https://dl.dell.com/content/docu98858',
	'8.2.2' => 'https://dl.dell.com/content/docu96915',
	'8.2.1' => 'https://dl.dell.com/content/docu95361',
	'8.2.0' => 'https://dl.dell.com/content/docu93613',
	'8.1.2' => 'https://dl.dell.com/content/docu90168',
	'8.1.1' => 'https://dl.dell.com/content/docu87518',
	'8.1.0' => 'https://dl.dell.com/content/docu84278',
	'8.0.1' => 'https://dl.dell.com/content/docu78727',
	'8.0.0' => 'https://dl.dell.com/content/docu65098',
	'7.2.1' => 'https://dl.dell.com/content/docu60143',
	'7.2.0' => 'https://dl.dell.com/content/docu56010',
	'7.1.1' => 'https://dl.dell.com/content/docu54294',
    },
    'DF' => 'https://dl.dell.com/content/manual37609860',
    'NF' => {
	'14.1' => 'https://dl.dell.com/content/manual41024395',
	'10.4.0' => 'https://dl.dell.com/content/manual23103106',
	'9.3.5' => 'https://dl.dell.com/content/docu84897',
    },
    'RUP' => {
	'9.7.0' => 'https://dl.dell.com/content/manual86303444',
	'9.5.0' => 'https://dl.dell.com/content/manual58071479',
	'9.4.0' => 'https://www.dell.com/support/kbdoc/000201074',
	'9.3.0' => 'https://www.dell.com/support/kbdoc/000194281',
	'9.2.1' => 'https://www.dell.com/support/kbdoc/000189200',
	'9.2.0' => 'https://www.dell.com/support/kbdoc/000189197',
	'9.1.0' => 'https://www.dell.com/support/kbdoc/000181149',
	'9.0.0' => 'https://www.dell.com/support/kbdoc/000157639',
	'8.2.2' => 'https://www.dell.com/support/kbdoc/000157589',
	'8.2.1' => 'https://www.dell.com/support/kbdoc/000157866',
	'8.1.2' => 'https://www.dell.com/support/kbdoc/000157717',
    }
   );
}
$CHECKS{'checkCM6FWBug'} = {
    'Description' => 'Checks drive firmware versions for the criteria of FCO F022318EE',
    'Reference' => 'https://www.dell.com/support/kbdoc/000030345',
};
sub checkCM6FWBug {
    if (wasCalled()) { return }
    if (isVirtual()) { return unsupported('UNSUP_VIRT') }
    printCheck('NVMe FIPS FW');
    gatherDriveFirmware();
    if ($RPS{'Drive Firmware'}{'NVMe Bug'}) {
    $RPS{'Drive Support Package'}{'Position'} = '1';
	warning('Checks have identified NVMe FIPS drives on F900 nodes with current FW version less than 3.1.0.');
	#info('An additional 12 minutes per node has been added to the total time of each reboot activity, alternatively, please consider updating your drive firmware prior to any reboot activity to avoid hitting this extended reboot time.');
    info('Drive updates should be performed prior to any reboot activity to avoid being impacted by KB 000215684.');
    info('Please reference KB 000215684 (https://www.dell.com/support/kbdoc/000215684) for additional information.');
    addCustomerInformation('Drive updates should be performed prior to any reboot activity to avoid being impacted by KB 000215684 (https://www.dell.com/support/kbdoc/000215684).');
    #debug("Duration for F900 was: $Durations{'Reboot'}{'^(?:F900)'}");
    #$Durations{'Reboot'}{'^(?:F900)'} += 12;
    #debug("Duration for F900 is now set to: $Durations{'Reboot'}{'^(?:F900)'}");
    }
    printResults();
} # End checkCM6FWBug
$CHECKS{'checkDriveFirmware'} = {
    'Description' => 'Checks for out of date Drive Firmware and calls other related drive firmware checks',
    'Exclude' => 1,
};
sub checkDriveFirmware {
    if (wasCalled()) { return }
    checkDriveSupportPackage();
    checkFCOF042415EE();
    checkFCOF022318EE();
    checkFCOF031617FC();
    checkCM6FWBug();
} # End checkDriveFirmware
$CHECKS{'checkDriveSupportPackage'} = {
    'Description' => 'Checks for drive firmware updates available in the Drive Support Package',
    'Reference' => 'https://www.dell.com/support/kbdoc/000037967',
	'Parameters' => { 'target_version' => $RPS{'DSP'}, },
};
$MSG{'checkDriveSupportPackage_VAR_1'} = 'To deploy the Drive Support Package there must be at least 200MB of available space in the /var partition and the following nodes do not meet this requirement: %s';
$MSG{'checkDriveSupportPackage_OOD_2'} = '%s nodes have out of date drive firmware: %s';
$MSG{'checkDriveSupportPackage_RISK_2'} = 'Drives on the following nodes are running firmware versions at risk for %s: %s';
$MSG{'checkDriveSupportPackage_NO_DSP_2'} = 'Drive Support Package %s requires OneFS %s or later. Consider performing a OneFS upgrade to a version which supports the latest Drive Support Package';
$MSG{'checkDriveSupportPackage_ONEFS_EVENT_1'} = 'Following the commit of the upgrade to OneFS %s, the Drive Support Package should be installed and events may be triggered for each drive which has an update available.  These events may be ignored and will be resolved once the firmware on the drives have been updated.';
$MSG{'checkDriveSupportPackage_SIGNED'} = 'OneFS 9.4 and later releases require signed Drive Support Packages and the requested Drive Support Package is not a signed release. Consider updating to the latest available Drive Support Package';
sub checkDriveSupportPackage {
    if (wasCalled()) { return }

    # Skip if no DSP version
    if (!$RPS{'DSP'} or $RPS{'DSP'} eq 'None') {
	return unsupported('UNSUP_NO_FW_1', 'Drive Support Package');
    }

    # Skip if virtual
    if (isVirtual()) { return unsupported('UNSUP_VIRT') }

    # Skip if no bootflash nodes
    checkPartitions();
    if (!$CLUSTER_INFO{'Bootflash'}) {
	return unsupported('UNSUP_DSP_OLD');
    }
    # Flag that OneFS 9.4 requires signed packages
    if ((isCurrentOneFS('>=9.4') or isDestinationOneFS('>=9.4')) and
	compareVersions($RPS{'DSP'}, '1.40.1') < 0) {
	fail($MSG{'checkDriveSupportPackage_SIGNED'});
    }

    printCheck("Drive Support Package (Target: $RPS{'DSP'})");
    gatherDriveFirmware();

    our %DriveSupport;

    # Generate a sorted list of drive support packages
    my @dsps = sort {compareVersions($b, $a)} keys %DriveSupport;

    # Analyze for out of date firmware
    my $requires_91 = 0;
    my $max_model_length = 30;
	
	foreach my $model (keys %{$CLUSTER_INFO{'Drive Firmware'}}) {
	my $model_length = length($model);
	if ($model_length > $max_model_length) { $max_model_length = $model_length }
	foreach my $firmware (keys %{$CLUSTER_INFO{'Drive Firmware'}{$model}}) {
	    next unless $CLUSTER_INFO{'Drive Firmware'}{$model}{$firmware};
	    my $fw = $CLUSTER_INFO{'Drive Firmware'}{$model}{$firmware};
	    my $dsp_target = 0;

		if ($firmware eq '9L77' or $firmware eq '9P73'){
			if (compareVersions($RPS{'DSP'}, '1.60.1') < 0 or $RPS{'DSP'} eq '1.60'){
				$RPS{'DSP_000456894'} = 1;
				debug("Found Seagate Drive with FW $firmware and Target DSP as being less than required 1.60.1 to enable updates, target DSP currently set to: $RPS{'DSP'}");
			}
		}

	    foreach my $pkg (@dsps) {
		# Stop if we found a target
		last if $dsp_target;

		# Skip this package if no updates for this model
		next unless $DriveSupport{$pkg}{$model};

		# There may be multiple target versions for each model
		my @targets = keys %{$DriveSupport{$pkg}{$model}};
		foreach my $t (@targets){
			debug("Res for $pkg , Drive $model  = $t\tFirmware = $firmware");
		}

		# Check to see if the firmware is already at a target version
		my @results = grep {$_ eq $firmware} @targets;
		my @res;
        foreach my $t (@results){
            foreach my $p (@targets){
				debug("Checking results $t, targets $p");
                if($p gt $t and $p ne $t){
                    push(@res, $p);
                    debug("$p greater than $t");
                }
            }
        }
        #last if @res;
        last if (@res and !@results);


		# Skip this package if newer than requested
		next if compareVersions($pkg, $RPS{'DSP'}) > 0;

		foreach my $target (@targets)
		{   
			debug("Skipped FW at current version, proceeding to applicable FW checks");
			# Check for firmware updates
		    # Check if the current version is supported
		    # Unqualified upgrade is allowed, skip current version check
		    my @results = grep {$_ eq $firmware or $_ eq 'allow'} @{$DriveSupport{$pkg}{$model}{$target}};
		    if (@results)
		    {	
				debug("results returned for Drive update");
				# Firmware updates available
				$dsp_target = $target;

				# To perform unqualified upgrades requires OneFS 9.1+
				if ($results[0] eq 'allow') { $requires_91 = 1 }
				last;
		    }
		}
	    }

	    ###
	    # Check for nodes that support DSP
	    foreach my $lnn (keys %{$fw->{'Counts'}}) {

		# Only check on Bootflash nodes
		next unless $NODE_INFO{$lnn}{'Bootflash'};
		#if($NODE_INFO{$lnn}{'Accelerator'}){
		#	delete($fw->{'DSP'}{'Nodes'});
		#	delete($fw->{'DSP'}{'Count'});
		#	debug("SKIPPING NODE $lnn AS IT IS PRESENT AS ACCELORATOR.");
		#	next;
		#};
		# Add to the list of nodes
		push(@{$fw->{'DSP'}{'Nodes'}}, $lnn) unless $NODE_INFO{$lnn}{'Accelerator'};

		# Add to the count of devices
		$fw->{'DSP'}{'Count'} += $fw->{'Counts'}{$lnn} unless $NODE_INFO{$lnn}{'Accelerator'};

	    }

	    # Next firmware if no updates were found
	    next unless $dsp_target and
		$fw->{'DSP'}{'Nodes'} and
		$fw->{'DSP'}{'Count'};
		next if $firmware eq $dsp_target;
		
	    ###
	    # Beyond this point drive firmware updates are available.
		debug("checkDriveSupportPackage: DSP $RPS{'DSP'} supports ".
		  "updating from '$firmware' to '$dsp_target'");

	    # Set target firmware version
	    $fw->{'DSP'}{'Target'} = $dsp_target;

	    # Add to the list of nodes to be updated
	    @{$RPS{'Drive Support Package'}{'Nodes'}} =
		merge(@{$RPS{'Drive Support Package'}{'Nodes'}},
		      @{$fw->{'DSP'}{'Nodes'}});

	    # Add to the count of drives the drives to be updated
	    $RPS{'Drive Support Package'}{'Drives'} += $fw->{'DSP'}{'Count'};
	}
    }

    # Print drive firmware information
    my $SPF_SUMMARY = "%-${max_model_length}s %-10s %-10s %-5s %s";
    details(sprintf($SPF_SUMMARY,
		    'Model',
		    'Current',
		    'Desired',
		    'Count',
		    'Nodes'));
    foreach my $model (sort keys %{$CLUSTER_INFO{'Drive Firmware'}}) {
        foreach my $firmware (keys %{$CLUSTER_INFO{'Drive Firmware'}{$model}}) {
	    my $fw = $CLUSTER_INFO{'Drive Firmware'}{$model}{$firmware};
	    next unless ($fw->{'DSP'}{'Nodes'} and
			 $fw->{'DSP'}{'Count'});
            details(sprintf($SPF_SUMMARY,
			    $model,
			    $firmware,
			    ($fw->{'DSP'}{'Target'} or '-'),
			    $fw->{'DSP'}{'Count'},
			    compressRange(@{$fw->{'DSP'}{'Nodes'}})));
        }
    }

    if (!$RPS{'Drive Support Package'}{'Nodes'})
    { # Drive firmware is up to date
		details("Installed drives are showing latest available firmware installed for Drive Support Pacakage v".$RPS{'DSP'}.".");
	if (isOneFSUpgrade() and
	    isDestinationOneFS('>=9.1'))
	{ # Add the latest Drive Support Package to be used post OneFS upgrade
	    addRequiredFiles("Isilon Drive Support Package $RPS{'DSP'}");
	    if ($RPS{'RQR_DSP'}) {
		# var partition available free space check
		fail(sprintf($MSG{'checkDriveSupportPackage_VAR_1'},
			     compressRange(@{$RPS{'RQR_DSP'}})));
	    }
	}
	if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000037967')) }
	printResults();
	return;
    }
	
    # Flag out of date drive firmware
    warning(sprintf($MSG{'checkDriveSupportPackage_OOD_2'},
		    scalar(@{$RPS{'Drive Support Package'}{'Nodes'}}),
		    compressRange(@{$RPS{'Drive Support Package'}{'Nodes'}})));

    # Flag reasons why drive firmware updates should be a priority
    if ($RPS{'ETA'}{'193819'}{'Nodes'}) {
	my @nodes = ();
	foreach my $lnn (@{$RPS{'ETA'}{'193819'}{'Nodes'}}) {
	    my @results = grep {$_ == $lnn} @{$RPS{'Drive Support Package'}{'Nodes'}};
	    @nodes = merge(@nodes, @results);
	}
	if (@nodes) {
	    push(@{$RPS{'Drive Support Package'}{'Priority'}},
		 warning(sprintf($MSG{'checkDriveSupportPackage_RISK_2'},
				 'KB 000003101', compressRange(@nodes))));
	}
    }
    if ($RPS{'Drive Firmware'}{'KB88930'}) {
	my @nodes = ();
	foreach my $lnn (@{$RPS{'Drive Firmware'}{'KB88930'}}) {
	    my @results = grep {$_ == $lnn} @{$RPS{'Drive Support Package'}{'Nodes'}};
	    @nodes = merge(@nodes, @results);
	}
	if (@nodes) {
	    push(@{$RPS{'Drive Support Package'}{'Priority'}},
		 warning(sprintf($MSG{'checkDriveSupportPackage_RISK_2'},
				 'KB 000116987', compressRange(@nodes))));
	}
    }
    if ($RPS{'Drive Firmware'}{'KB502084'}) {
	my @nodes = ();
	foreach my $lnn (@{$RPS{'Drive Firmware'}{'KB502084'}}) {
	    my @results = grep {$_ == $lnn} @{$RPS{'Drive Support Package'}{'Nodes'}};
	    @nodes = merge(@nodes, @results);
	}
	if (@nodes) {
	    push(@{$RPS{'Drive Support Package'}{'Priority'}},
		 warning(sprintf($MSG{'checkDriveSupportPackage_RISK_2'},
				 'KB 000029393', compressRange(@nodes))));
	}
    }

    # Flag when not running or upgrading to a OneFS version which supports the DSP being requested
    my $dsp_unsupported = 0;
    if ($requires_91 and
	isCurrentOneFS('<9.1') and
	(!isDestinationOneFS() or isDestinationOneFS('<9.1')))
    { # When DSP requires OneFS 9.1 or later
	addCustomerInformation('Upgrade Plan', critical(sprintf($MSG{'checkDriveSupportPackage_NO_DSP_2'}, $RPS{'DSP'}, '9.1')));
	$dsp_unsupported = 1;
    } elsif ($RPS{'DSP'} ne '1.41' and
	     compareVersions($RPS{'DSP'}, '1.41.1') >= 0 and
	     isCurrentOneFS('<8.2.2') and
	     (!isDestinationOneFS() or isDestinationOneFS('<8.2.2')))
    { # When DSP requires OneFS 8.2.2 or later
	addCustomerInformation('Upgrade Plan', critical(sprintf($MSG{'checkDriveSupportPackage_NO_DSP_2'}, $RPS{'DSP'}, '8.2.2')));
	$dsp_unsupported = 1;
    } elsif ((isCurrentOneFS('<7.1.1.4') or
	      isCurrentOneFS(qr/^7\.2\.0\.[01]$/)) and
	     (!isDestinationOneFS() or
	      isDestinationOneFS('<7.1.1.4') or
	      isDestinationOneFS(qr/^7\.2\.0\.[01]$/)))
    { # When DSP requires OneFS 7.1.1.4/7.2.0.2 or later
	addCustomerInformation('Upgrade Plan', critical(sprintf($MSG{'checkDriveSupportPackage_NO_DSP_2'}, $RPS{'DSP'}, '7.1.1.4/7.2.0.2')));
	$dsp_unsupported = 1;
    }

    if ($dsp_unsupported or
	($RPS{'Priority DF Only'} and !$RPS{'Drive Support Package'}{'Priority'}))
    { # Remove drive firmware updates
	delete $RPS{'Drive Support Package'};
    } else {
	# var partition available free space check
	if ($RPS{'RQR_DSP'} and $RPS{'Drive Support Package'}) {
	    fail(sprintf($MSG{'checkDriveSupportPackage_VAR_1'},
			 compressRange(@{$RPS{'RQR_DSP'}})));
	}

	if($RPS{'DSP_000456894'}){
		addCustomerInformation(critical("Seagate drives found with firmware versions that are not supported for update with package $RPS{'DSP'}, please ensure updates are done using Drive Support Package 1.60.1 or higher."));
		info("Please refer to KB 000456894 https://www.dell.com/support/kbdoc/000456894 for additional information.");
	}

	# Set the package to use
	$RPS{'Drive Support Package'}{'Target'} = "Drive Support Package $RPS{'DSP'}";

	# Set the position in the upgrade plan
	$RPS{'Drive Support Package'}{'Position'} = 3;

	# Append files required for the updates
	push(@{$RPS{'Drive Support Package'}{'Files'}},
	     "Isilon $RPS{'Drive Support Package'}{'Target'}");

	# Flag that events will be triggered when the DSP is installed
	if (isOneFSUpgrade()) {
	    addCustomerInformation('Upgrade Plan', sprintf($MSG{'checkDriveSupportPackage_ONEFS_EVENT_1'}, isDestinationOneFS()));
	}
    }

    # Output
	if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000037967')) }
	printResults();
} # End checkDriveSupportPackage
$CHECKS{'checkFCOF022318EE'} = {
    'Description' => 'Checks drive firmware versions for the criteria of FCO F022318EE',
    'Reference' => 'https://www.dell.com/support/kbdoc/000030345',
};
$MSG{'checkFCOF022318EE_FW_2'} = 'There are %s drives(s) that are running firmware versions affected by F022318EE: %s';
$MSG{'checkFCOF022318EE_ACTION'} = 'The identified drives should be updated, smartfailed, and then formatted';
sub checkFCOF022318EE {
    if (wasCalled()) { return }
    if (isVirtual()) { return unsupported('UNSUP_VIRT') }
    printCheck('FCO F022318EE/KB 000030345');
    gatherDriveFirmware();
    if ($RPS{'Drive Firmware'}{'F022318EE'}) {
	warning(sprintf($MSG{'checkFCOF022318EE_FW_2'},
			scalar(@{$RPS{'Drive Firmware'}{'F022318EE'}}),
			join(',', @{$RPS{'Drive Firmware'}{'F022318EE'}})));
	warning($MSG{'checkFCOF022318EE_ACTION'});
	info(sprintf($MSG{'KB_1'}, '000030345'));
    }
    printResults();
} # End checkFCOF022318EE
$CHECKS{'checkFCOF031617FC'} = {
    'Description' => 'Checks drive firmware versions for the criteria of KB 000024620',
    'Reference' => 'https://www.dell.com/support/kbdoc/000024620',
};
$MSG{'checkFCOF031617FC_FW_2'} = 'There are %s drive(s) that are running firmware versions affected by FCO F031617FC/KB 000024620: %s';
$MSG{'checkFCOF031617FC_SF'} = 'The identified drives should be smartfailed and replaced';
sub checkFCOF031617FC {
    if (wasCalled()) { return }
    if (isVirtual()) { return unsupported('UNSUP_VIRT') }
    printCheck('FCO F031617FC/KB 000024620');
    gatherDriveFirmware();
    if ($RPS{'Drive Firmware'}{'KB469133'}) {
	warning(sprintf($MSG{'checkFCOF031617FC_FW_2'},
			scalar(@{$RPS{'Drive Firmware'}{'KB469133'}}),
			join(', ', @{$RPS{'Drive Firmware'}{'KB469133'}})));
	warning($MSG{'checkFCOF031617FC_SF'});
	info(sprintf($MSG{'KB_1'}, '000024620'));
    }
    printResults();
}
$CHECKS{'checkFCOF042415EE'} = {
    'Description' => 'Checks the cluster to see if it meets criteria for FCO F042415EE/KB 000051631',
    'Reference' => 'https://www.dell.com/support/kbdoc/000051631',
};
$MSG{'checkFCOF042415EE_FW_2'} = 'There are %s drive(s) that are running firmware versions affected by F042415EE: %s';
$MSG{'checkFCOF042415EE_ACTION'} = 'The identified drives should be updated by Dell EMC';
sub checkFCOF042415EE {
    if (wasCalled()) { return }
    if (isVirtual()) { return unsupported('UNSUP_VIRT') }
    printCheck('FCO F042415EE/KB 000051631');
    gatherDriveFirmware();
    if ($RPS{'Drive Firmware'}{'F042415EE'}) {
	warning(sprintf($MSG{'checkFCOF042415EE_FW_2'},
			scalar(@{$RPS{'Drive Firmware'}{'F042415EE'}}),
			join(',', @{$RPS{'Drive Firmware'}{'F042415EE'}})));
	warning($MSG{'checkFCOF042415EE_ACTION'});
	info(sprintf($MSG{'KB_1'}, '000051631'));
    }
    if ($RPS{'FCO'}{'F042415EE'}{'Nodes'}) {
	@{$RPS{'FCO F042415EE'}{'Nodes'}} =
	    @{$RPS{'FCO'}{'F042415EE'}{'Nodes'}};
    }
    printResults();
} # End checkFCOF042415EE
$CHECKS{'checkKB490849'} = {
    'Description' => 'Checks if at risk for KB 000052089',
    'Reference' => 'https://www.dell.com/support/kbdoc/000052089',
    'Exclude' => 1,
};
$MSG{'checkKB490849_AFFECTED_1'} = 'Drives on the following nodes may be affected by KB 000052089 during drive firmware updates: %s';
$MSG{'checkKB490849_REBOOT'} = 'Following drive firmware updates, the drives may continue to report out-of-date. To show the correct firmware version, reboot the affected nodes.  The reboot may be done as a standalone reboot or by any activity that reboots the nodes.';
sub checkKB490849 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<=8.0.0.4') or isCurrentOneFS(qr/^(8\.0\.1\.[01])$/)) {
	return unsupported('UNSUP_CUR');
    }

    checkDriveFirmware();

    my $MSG_ADDED_REBOOT = 'An additional reboot activity was added to accommodate for KB 000052089';
    my $MSG_ACTIVITY = 'It is recommended to perform the subsequent reboot activity in the same maintenance window as the drive firmware updates due to drives running firmware versions at risk for KB 000052089';
    my $SPF_ACTIVITY = 'Only the following nodes require rebooting: %s';

    printCheck('KB 490849');

    # Stop if no affected drives
    if (!$RPS{'Drive Firmware'}{'KB490849'}) {
	printResults();
	return;
    }

    # Flag nodes with affected drives
    critical(sprintf($MSG{'checkKB490849_AFFECTED_1'},
		     compressRange(@{$RPS{'Drive Firmware'}{'KB490849'}})));
    info(sprintf($MSG{'KB_1'}, '000052089'));

    # Stop if drive firmware updates are not being performed
    if (!$RPS{'Drive Support Package'}{'Nodes'}) {
	printResults();
	return;
    }

    # Stop if performing drive firmware updates after performing OneFS upgrade to a version including fixes
    if ($RPS{'Drive Support Package'}{'Position'} == 3 and
	isDestinationOneFS() and
	!(isDestinationOneFS('<=8.0.0.4') or
	  isDestinationOneFS(qr/^8\.0\.1\.[01]$/))) {
	printResults();
	return;
    }

    # Add an additional reboot after drive firmware updates for affected nodes
    $RPS{'Drive Firmware Reboot'}{'Position'} =	$RPS{'Drive Support Package'}{'Position'};
    $RPS{'Drive Firmware Reboot'}{'Nodes'} = $RPS{'Drive Firmware'}{'KB490849'};
    addCustomerInformation('Upgrade Plan', critical($MSG{'checkKB490849_REBOOT'}));
    addCustomerInformation('Upgrade Plan', $MSG_ADDED_REBOOT);
    push(@{$RPS{'Drive Support Package'}{'Additional Comments'}}, $MSG_ACTIVITY);
    push(@{$RPS{'Drive Firmware Reboot'}{'Additional Comments'}},
	 sprintf($SPF_ACTIVITY, compressRange(@{$RPS{'Drive Firmware'}{'KB490849'}})));

    # Output
    printResults();
}
$CHECKS{'checkKB535582'} = {
    'Description' => 'Checks if at risk for KB 000060471',
    'Reference' => 'https://www.dell.com/support/kbdoc/000060471',
};
$MSG{'checkKB535582_AFFECTED_1'} = 'Drives on the following nodes may be affected by KB 000060471 during drive firmware updates: %s';
$MSG{'checkKB535582_REBOOT'} = 'Following drive firmware updates, the drives may continue to report out-of-date. To show the correct firmware version, reboot the affected nodes.  This reboot may be done as a standalone reboot or by any activity that reboots the nodes.';
sub checkKB535582 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=9.1')) { return unsupported('UNSUP_CUR') }

    checkDriveFirmware();

    my $MSG_ADDED_REBOOT = 'An additional reboot activity was added to accommodate for KB 000060471';
    my $MSG_ACTIVITY = 'It is recommended to perform the subsequent reboot activity in the same maintenance window as the drive firmware updates due to drives running firmware versions at risk for KB 000060471';
    my $SPF_ACTIVITY = 'Only the following nodes require rebooting: %s';

    printCheck('KB 535582');

    # Stop if no affected drives
    if (!$RPS{'Drive Firmware'}{'KB535582'}) {
	printResults();
	return;
    }

    # Flag nodes with affected drives
    critical(sprintf($MSG{'checkKB535582_AFFECTED_1'},
		     compressRange(@{$RPS{'Drive Firmware'}{'KB535582'}})));
    info(sprintf($MSG{'KB_1'}, '000060471'));

    # Stop if drive firmware updates are not being performed
    if (!$RPS{'Drive Support Package'}{'Nodes'}) {
	printResults();
	return;
    }

    # Stop if performing drive firmware updates after performing OneFS upgrade to a version including fixes
    if ($RPS{'Drive Support Package'}{'Position'} == 3 and
	isDestinationOneFS('>=9.1')) {
	printResults();
	return;
    }

    # Add an additional reboot after drive firmware updates for affected nodes
    $RPS{'Drive Firmware Reboot'}{'Nodes'} = $RPS{'Drive Firmware'}{'KB535582'};
    $RPS{'Drive Firmware Reboot'}{'Position'} =	$RPS{'Drive Support Package'}{'Position'};
    push(@{$RPS{'Drive Firmware Reboot'}{'Additional Comments'}},
	 sprintf($SPF_ACTIVITY, compressRange(@{$RPS{'Drive Firmware'}{'KB535582'}})));
    push(@{$RPS{'Drive Support Package'}{'Additional Comments'}}, $MSG_ACTIVITY);
    addCustomerInformation('Upgrade Plan', $MSG_ADDED_REBOOT);
    addCustomerInformation('Upgrade Plan', critical($MSG{'checkKB535582_REBOOT'}));

    # Output
    printResults();
}
###
# gatherDriveFirmware: Gathers drive firmware information from each
#  node and stores it in $CLUSTER_INFO{'Drive Firmware'}
sub gatherDriveFirmware {
    if (wasCalled()) { return }
    if (isVirtual()) { return }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'isi_radish -q'];
    my $CMD_LOGS = q[2>&1 grep -H '^Bay' */isi_radish];
    my $RE_DRIVE = qr/Bay\s+(\S+)\/\S+\s+is\s+(\S.*?)\s+FW:(\S+)\s+SN:/;

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Compile
    my %fw = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { missing($lnn); next; }
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line !~ $RE_DRIVE) { next }
	    my ($bay, $model, $firmware) = ($1, $2, $3);

	    # Compile %fw to contain:
	    #   Model -> Firmware -> Count/Nodes
	    $fw{$model}{$firmware}{'Counts'}{$lnn}++;
	    $fw{$model}{$firmware}{'Count'}++;
	    @{$fw{$model}{$firmware}{'Nodes'}} =
		merge(@{$fw{$model}{$firmware}{'Nodes'}}, $lnn);

	    # Firmware at risk for FCO F022318EE
	    if ($model =~ /H5472602CLAR2000|H5472604CLAR4000/ and
		$firmware =~ /D930/) {
		push(@{$RPS{'Drive Firmware'}{'F022318EE'}}, "$lnn:bay$bay");
	    }

		# CM6 drives on F900 FW check	
		if ($model =~ /NVMe FIPS/ and compareVersions($firmware, '3.1.0') < 0 and $CLUSTER_INFO{'F900'} and $NODE_INFO{$lnn}{'Product'} =~ /SSD-SED/){
			@{$RPS{'Drive Firmware'}{'NVMe Bug'}} =
		    merge(@{$RPS{'Drive Firmware'}{'NVMe Bug'}}, $lnn);
		}		

	    # Firmware update may be at risk for KB 535582
	    if (($model =~ /ST8000NM0045-1RL112/ and
		 $firmware =~ /UG05/) or
		($model =~ /HUS726020ALN610/ and
		 $firmware =~ /APGNT840|APGNTDCH/) or
		($model =~ /HUS726040ALN610/ and
		 $firmware =~ /APGNT840|APGNTDCH/)) {
		@{$RPS{'Drive Firmware'}{'KB535582'}} =
		    merge(@{$RPS{'Drive Firmware'}{'KB535582'}}, $lnn);
	    }

	    # Firmware impacted by KB 502084
	    if (($model =~ /HITACHI HUSMM151CLAR1600/ and
		 $firmware =~ /D344/) or
		($model =~ /HGST HUSMM1680ASS205/ and
		 $firmware =~ /D252|d322|D326/)) {
		@{$RPS{'Drive Firmware'}{'KB502084'}} =
		    merge(@{$RPS{'Drive Firmware'}{'KB502084'}}, $lnn);
	    }

	    # Firmware impacted by KB 490849
	    if ($model =~ /ST.000NM00.3/ and
		$firmware =~ /^SN0[23]$|^SNG4$|^000[34]$/) {
		@{$RPS{'Drive Firmware'}{'KB490849'}} =
		    merge(@{$RPS{'Drive Firmware'}{'KB490849'}}, $lnn);
	    }

	    # Firmware impacted by KB 469133
	    if ($firmware =~ /^SN0[46]$/) {
		push(@{$RPS{'Drive Firmware'}{'KB469133'}}, "$lnn:bay$bay");
	    }

	    # Firmware impacted by KB 88930
	    if ($model =~ /HUA723030ALA640/ and
		$firmware =~ /MKAOA[35][8BV]0/) {
		@{$RPS{'Drive Firmware'}{'KB88930'}} =
		    merge(@{$RPS{'Drive Firmware'}{'KB88930'}}, $lnn);
	    }

	    # Firmware impacted by FCO F042415EE
	    if ($model =~ /HUSMM8080ASS205/ and $firmware eq 'D210') {
		push(@{$RPS{'Drive Firmware'}{'F042415EE'}}, "$lnn:bay$bay");
		@{$RPS{'FCO'}{'F042415EE'}{'Nodes'}} =
		    merge(@{$RPS{'FCO'}{'F042415EE'}{'Nodes'}}, $lnn);
	    }

	    # Firmware impacted by ETA 193819
	    if ($model =~ /HUS7240[234]0ALA640/ and
		$firmware =~ /A[8AB][BCW]0/) {
		@{$RPS{'ETA'}{'193819'}{'Nodes'}} =
		    merge(@{$RPS{'ETA'}{'193819'}{'Nodes'}}, $lnn);
	    }
	}
    }

    # Save
    if (%fw) { debug(toJSON($CLUSTER_INFO{'Drive Firmware'} = \%fw)) }

    # Add FCO/ETA messages
    if ($RPS{'FCO'}{'F042415EE'}{'Nodes'}) {
	$RPS{'FCO'}{'F042415EE'}{'Severity'} = 'WARN';
	$RPS{'FCO'}{'F042415EE'}{'Message'} =
	    scalar(@{$RPS{'FCO'}{'F042415EE'}{'Nodes'}}).
	    ' nodes are affected by FCO F042415EE: '.
	    compressRange(@{$RPS{'FCO'}{'F042415EE'}{'Nodes'}});
    }
    if ($RPS{'ETA'}{'193819'}{'Nodes'}) {
	$RPS{'ETA'}{'193819'}{'Severity'} = 'INFO';
	$RPS{'ETA'}{'193819'}{'Message'} =
	    scalar(@{$RPS{'ETA'}{'193819'}{'Nodes'}}).
	    ' nodes are affected by ETA 193819: '.
	    compressRange(@{$RPS{'ETA'}{'193819'}{'Nodes'}});
    }

} # End gatherDriveFirmware
$CHECKS{'checkDTA000194434'} = {
    'Description' => 'Checks for criteria of KB 000194434',
    'Reference' => 'https://www.dell.com/support/kbdoc/000194434',
};
$MSG{'checkDTA000194434_SSD_SEDS_2'} = 'During an upgrade to OneFS %s, an issue exists that may lead to failures of the Self-Encrypting Solid State Drives on the following nodes: %s';
$MSG{'checkDTA000194434_NEXT_ACTION_93'} = 'This issue has been addressed in OneFS 9.3.0.3 and newer releases, consider instead updating to the latest available patch release for OneFS 9.3';
$MSG{'checkDTA000194434_NEXT_ACTION_921'} = 'This issue has been addressed in OneFS 9.2.1.7 and newer OneFS 9.2.1 releases, consider instead updating to the latest available patch release for OneFS 9.2.1';
sub checkDTA000194434 {
    if (wasCalled()) { return }
    if (!isDestinationOneFS()) { return unsupported('UNSUP_TGT') }
    if (!isDestinationOneFS(qr/^9\.2\.1\.[56]$|^9\.3\.0\.[012]$/)) {
	return unsupported('UNSUP_TGT_1', '9.2.1.5, 9.2.1.6, 9.3.0.0, 9.3.0.1, or 9.3.0.2');
    }
    if (isCurrentOneFS('>=9.2.1.5')) { return unsupported('UNSUP_CUR') }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen5'} and !$CLUSTER_INFO{'Gen6'}) {
	return unsupported('UNSUP_GEN56');
    }

    # Setup
    printCheck('DTA 000194434');

    # Analyze
    my @ssd_sed_nodes = ();
    foreach my $lnn (@LNNS) {
	if (!$NODE_INFO{$lnn}{'Product'}) { missing($lnn); next; }
	if ($NODE_INFO{$lnn}{'Generation'} ne 'Gen5' and
	    $NODE_INFO{$lnn}{'Generation'} ne 'Gen6') { next }
	if ($NODE_INFO{$lnn}{'Product'} =~ /SSD-SED/) {
	    push(@ssd_sed_nodes, $lnn);
	}
    }

    # Flag
    if (@ssd_sed_nodes) {
	fail(sprintf($MSG{'checkDTA000194434_SSD_SEDS_2'},
		     isDestinationOneFS(),
		     compressRange(@ssd_sed_nodes)));
	if (isDestinationOneFS('>=9.3')) {
	    fail($MSG{'checkDTA000194434_NEXT_ACTION_93'});
	} else {
	    fail($MSG{'checkDTA000194434_NEXT_ACTION_921'});
	}
	info(sprintf($MSG{'KB_1'}, '000194434'));
    }

    # Output
    printResults();
} # End checkDTA000194434
$CHECKS{'checkDTA000221299'} = {
    'Description' => 'Checks for criteria of KB 000221299',
    'Reference' => 'https://www.dell.com/support/kbdoc/000221299',
};
sub checkDTA000221299 {
    if (wasCalled()) { return }
    
    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'sysctl kern.proc.all_stacks | grep cfifo'];

    my $gather;
    if(isLive()){
        $gather = gatherCluster($CMD_LIVE);
    }

    printCheck('DTA 000221299');

    # Analyze

    my @nodes = ();
    foreach my $lnn (@LNNS) {
        foreach $line (@{$gather{$lnn}}){
            if($line =~ /.*\"cfifo\"\s*for\s*(\d+)\s*ticks/){
                if($1 > 100000){
                    push(@nodes, $lnn);
                }
            }
        }    
    }

    # Flag
    if (isLive()) {
        if(@nodes){
            fail("Checks have identified CFIFO waiting over 100k ticks on the following nodes: ".compressRange(@nodes));
        }
    }
    if(
        ((isCurrentOneFS('9.5') and isCurrentOneFS('<9.5.0.7')) or isCurrentOneFS('<9.4.0.17'))    
        or 
        ((isDestinationOneFS('9.5') and isDestinationOneFS('<9.5.0.7')) or isDestinationOneFS('<9.4.0.17'))
    ){
        warning("This cluster may be affected by DTA 000221299 and may lead to unresponsive ISI commands or affected performance issues.");
        info("Please consider upgrading to a patched destination for this DTA (current versions addressing this DTA are 9.4.0.17 or higher and 9.5.0.7).");
    }
    
    if(@nodes or ((isCurrentOneFS('9.5') and isCurrentOneFS('<9.5.0.7')) or isCurrentOneFS('<9.4.0.17'))    
        or 
        ((isDestinationOneFS('9.5') and isDestinationOneFS('<9.5.0.7')) or isDestinationOneFS('<9.4.0.17'))){
            info("Please refer to DTA article https://www.dell.com/support/kbdoc/000221299 (DTA 000221299) for further information.");
    }

    # Output
    printResults();
} # End checkDTA000221299
$CHECKS{'checkDTA219904'} = {
    'Description' => 'Checks for criteria of DTA 219904',
    'Reference' => '',
};
sub checkDTA219904 {
    if (wasCalled()) { return }
    #if ((isCurrentOneFS('<9.5') || isCurrentOneFS('>9.5.0.5'))) { return }
    
    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -s 'ps auxww | grep _lldpd | grep -v grep | awk '"'"'{print \$2}'"'"' | while read x; do procstat -r \$x | grep RSS; done'];
    my $CMD_LOGS = q[2>&1 grep -H '^_lldpd.*' */ps | awk '{print $1,$6,$14}'];
    
    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    my $sysctl_value = -1;
    my @nodes_fail = ();
    my @nodes_warn = ();

    printCheck('DTA 000219904');
    foreach my $lnn (@LNNS) {
            #debug("node = $lnn\t|\tchecking @{$gather->{$lnn}}");
            if (!@{$gather->{$lnn}}) { next }
            my $cur_val = '';
        foreach my $line (@{$gather->{$lnn}}) {
                debug("CHECKING: $line");
            if ($line =~ /^\s*\d*\D*(\d+)\D*$/) {
            $cur_val = int($1);
            debug("checking $cur_val");
            if ($cur_val) { 
                # WARNING
                push(@nodes_warn,$lnn);
            }
            if ($cur_val >= 900000) { 
                #FAIL CRITICAL    
                push(@nodes_fail,$lnn);
            }
	    }
	}
    }
    if ((@nodes_fail or @nodes_warn) and 
    (
        (
            isCurrentOneFS('>=9.5.0.0') and isCurrentOneFS('<=9.5.0.5')
        ) 
    or 
        (
            isDestinationOneFS('>=9.5.0.0') and isDestinationOneFS('<=9.5.0.5')
        )
    )
    ) {
        if(@nodes_fail){
        critical("Checks have identified _lldpd process running on the following nodes that have 900mb or more of consumed memory: ".compressRange(@nodes_fail));
        critical("Prior to any maintenance activity, please ensure the _lldpd process is restarted using the workaround provided in https://www.dell.com/support/kbdoc/en-ie/000219904.");
        }
        else{
        warning("Checks have identified _lldpd process running on the following nodes and the process should be restarted prior to any upgrade activity: ".compressRange(@nodes_warn));
        warning("It is recommended to run the workaround provided on https://www.dell.com/support/kbdoc/en-ie/000219904 to avoid issues being encountered.");
        }
        warning("Current OneFS version ".isCurrentOneFS()." is affected by DTA219904, please consider upgrading to a patched version of 9.5.0.6 or higher.") if (isCurrentOneFS('>=9.5.0.0') and isCurrentOneFS('<=9.5.0.5'));
        addCustomerInformation('DTA 219904', 'This cluster is running a OneFS version that is at risk for DTA 219904, https://www.dell.com/support/kbdoc/000219904. The cluster will remain at risk until an upgrade to a version which includes fixes for the DTA (9.5.0.6 or higher) has been completed.') if (isCurrentOneFS('>=9.5.0.0') and isCurrentOneFS('<=9.5.0.5'));
        warning("Destination OneFS version ".isDestinationOneFS()." is affected by DTA219904, please consider upgrading to a patched version of 9.5.0.6 or higher.") if (isDestinationOneFS('>=9.5.0.0') and isDestinationOneFS('<=9.5.0.5'));
        addCustomerInformation('DTA 219904', 'This cluster is upgrading to a OneFS version that is at risk for DTA 219904, https://www.dell.com/support/kbdoc/000219904. The cluster will remain at risk until an upgrade to a version which includes fixes for the DTA (9.5.0.6 or higher) has been completed.') if (isDestinationOneFS('>=9.5.0.0') and isDestinationOneFS('<=9.5.0.5'));
        info("Please refer to DTA219904 document https://www.dell.com/support/kbdoc/en-ie/000219904 for further information.");
    }
printResults();
} # End checkDTA219904
###
# checkDTA225116: Checks to see if the cluster meets criteria for DTA 540272
$MSG{'checkETAs_DTA225116'} = 'Cluster at risk for DTA 540272';
sub checkDTA225116 {
    if (wasCalled()) { return }

if((isCurrentOneFS('<9.7.1') and !isOneFSUpgrade()) or isDestinationOneFS('<9.7.1')){
    warning('Checks have identified your cluster is at risk of DTA 225116 and an upgrade to 9.7.1 or higher is recommended.');
    info('Please review DTA 225116 (https://www.dell.com/support/kbdoc/000225116) for further details.');
    addCustomerInformation('DTA 225116', 'PowerScale OneFS: Code Upgrade Recommendation to OneFS 9.7.1.0. Review DTA 225116 (https://www.dell.com/support/kbdoc/000225116) for further information.');
}
printResults();
} # End checkDTA225116
$CHECKS{'checkET004252'} = {
    'Description' => 'Checks for criteria of ET004252',
    'Exclude' => 1,
};
sub checkET004252 {
    if (wasCalled()) { return }
    if (!hasDellSwitch()) { return unsupported('UNSUP_BE_DN') }

    # Setup
    printCheck('ET004252');
    my $MSG_AT_RISK_MAJOR = 'The Dell Networking switches used for backend'.
	' connectivity are running DNOS versions that are at risk for ET004252.';
    my $MSG_REM = 'The Dell Networking switch updates are eligible for'.
	' remote updates to 10.5.0.6C1';
    my $MSG_NOT_REM = 'The Dell Networking switch updates are NOT eligible for'.
	' remote updates to 10.5.0.6C1 and must be updated on-site';
    my $MSG_NO_SWITCHES = 'Could not discover switch details.'.
	' It may be necessary to update the list of recognized Dell switch'.
	' OUIs on the Isilon cluster. '.sprintf($MSG{'KB_INT_1'}, '000071455');
    my $MSG_ODD_SWITCHES = 'An odd number of switches were found, there could'.
	' be issues with one of the two backend networks preventing gathering'.
	' of switch details';
    my $RE_DELL = qr/(Dell\s+\S+)\s+-\s+v(\d+\.\d+\.\S+)\s/;

    # Analyze
    my $count_switches = 0;
    my $count_updates = 0;
    my $remote_eligible = 0;
    if (defined $CLUSTER_INFO{'Switches'}) {
	foreach my $switches (values %{$CLUSTER_INFO{'Switches'}}) {
	    foreach my $switch (@$switches) {
		debug("Switch: $switch");
		$count_switches++;
		if ($switch !~ $RE_DELL) { next }
		my ($switch_type, $switch_os) = ($1, $2);
		debug("Switch Type: $switch_type, Switch OS: $switch_os");
		if ($switch_os eq '10.5.0.6C1') {
		    # DNOS update already performed
		    next;
		} elsif ($switch_os eq '10.5.0.6') {
		    # DNOS update remote eligible
		    $count_updates++;
		    $remote_eligible++;
		} elsif (compareVersions($switch_os, '10.5.0.6') < 0) {
		    # DNOS update not remote eligible
		    $count_updates++;
		}
	    }
	}
    }

    # Flag if at risk and remote eligibility
    if ($count_updates) {
	critical($MSG_AT_RISK_MAJOR);

	# Flag if eligible or not for remote execution
	if ($count_updates == $remote_eligible) {
	    # Eligble for remote execution
	    info($MSG_REM);
	} else {
	    # Not eligible for remote execution
	    info($MSG_NOT_REM);
	}
    }

    # Flag issues in data
    if (!$count_switches) {
	# No switches?
	warning($MSG_NO_SWITCHES);
    }
    if ($count_switches > 0 and ($count_switches % 2) != 0) {
	# Odd number of switches reported
	warning($MSG_ODD_SWITCHES);
    }
    if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000185548')) }

    # Output
    printResults();
} # End checkET004252
###
# checkETAs: Checks for ETAs, version specific ETAs.
$CHECKS{'checkETAs'} = {
    'Description' => 'Checks for Technical Advisories',
    'Reference' => 'https://www.dell.com/support/home/product-support/product/isilon-onefs/advisories',
};
$MSG{'checkETAs_CUR_2'} = 'Current OneFS %s affected by %s';
$MSG{'checkETAs_DES_2'} = 'Destination OneFS %s affected by %s';
$MSG{'checkETAs_UPG_3'} = 'Upgrade from OneFS %s to OneFS %s affected by %s';
sub checkETAs {
    return if wasCalled();
    checkNodeFirmware();
    checkDTA000194434();
    #checkDTA000207476();

    gatherDriveFirmware();
    checkDTA225116();
    gatherDTA194319();
    gatherDTA540272();
    gatherDTA533516();
    gatherDTA526050();
    gatherDTA525928();
    gatherETA519384();
    gatherETA518924();
    gatherETA491747();
    gatherETA483840();
    gatherETA209918();
    gatherETA207785();
    gatherETA205085();
    gatherETA202452();
    gatherETA198924();
    gatherETA194692();
    gatherETA180317();

    printCheck("ETAs");

    # ETA 208773
    if (isCurrentOneFS(qr/^7\.1\.1\.[67]$|^7\.2\.0\.[34]$|^7\.2\.1\.0$/)) {
	$RPS{'ETA'}{'208773'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'208773'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 208773'));
    }
    if (isDestinationOneFS(qr/^7\.1\.1\.[67]$|^7\.2\.0\.[34]$|^7\.2\.1\.0$/)) {
	$RPS{'ETA'}{'208773'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'208773'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 208773'));
    }

    # ETA 208772
    if (isCurrentOneFS(qr/^7\.2\.1\.0$|^7\.2\.0\.[01234]$|^7\.1\.1\.[01234567]$|^7\.1\.0\.[0123456]$/)) {
	$RPS{'ETA'}{'208772'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'208772'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 208772'));
    }
    if (isDestinationOneFS(qr/^7\.2\.1\.0$|^7\.2\.0\.[01234]$|^7\.1\.1\.[01234567]$|^7\.1\.0\.[0123456]$/)) {
	$RPS{'ETA'}{'208772'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'208772'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 208772'));
    }

    # ETA 207782
    if (isCurrentOneFS(qr/^7\.2\.0\.[012]$|^7\.2\.1\.0$/)) {
	$RPS{'ETA'}{'207782'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'207782'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 207782'));
    }
    if (isDestinationOneFS(qr/^7\.2\.0\.[012]$|^7\.2\.1\.0$/)) {
	$RPS{'ETA'}{'207782'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'207782'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 207782'));
    }

    # ETA 204898
    if (isCurrentOneFS(qr/^7\.2\.0\.[0123]$|^7\.2\.1\.0$/)) {
	$RPS{'ETA'}{'204898'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'204898'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 204898'));
    }
    if (isDestinationOneFS(qr/^7\.2\.0\.[0123]$|^7\.2\.1\.0$/)) {
	$RPS{'ETA'}{'204898'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'204898'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 204898'));
    }

    # ETA 203815
    if (isCurrentOneFS(qr/^7\.1\.1\.[012345]$|^7\.2\.0\.[0123]$|^7\.2\.1\.0$/)) {
	$RPS{'ETA'}{'203815'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'203815'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 203815'));
    }
    if (isDestinationOneFS(qr/^7\.1\.1\.[012345]$|^7\.2\.0\.[012]$|^7\.2\.1\.0$/)) {
	$RPS{'ETA'}{'203815'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'203815'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 203815'));
    }

    # ETA 200097
    if (isCurrentOneFS(qr/^7\.1\.1\.[0123]$|^7\.2\.0\.[01]$/)) {
	$RPS{'ETA'}{'200097'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'200097'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 200097'));
    }
    if (isDestinationOneFS(qr/^7\.1\.1\.[0123]$|^7\.2\.0\.[01]$/)) {
	$RPS{'ETA'}{'200097'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'200097'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 200097'));
    }

    # ETA 200096
    if (isCurrentOneFS(qr/^7\.1\.1\.[345]$|^7\.2\.0\.[12]$|^7\.2\.1\.1$/)) {
	$RPS{'ETA'}{'200096'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'200096'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 200096'));
    }
    if (isDestinationOneFS(qr/^7\.1\.1\.[345]$|^7\.2\.0\.[12]$|^7\.2\.1\.1$/)) {
	$RPS{'ETA'}{'200096'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'200096'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 200096'));
    }

    # ETA 199379
    if (isCurrentOneFS(qr/^6|^7\.0|^7\.1\.0|^7\.1\.1\.[0123]$|^7\.2\.0\.[01]$/)) {
	$RPS{'ETA'}{'199379'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'199379'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 199379'));
    }
    if (isDestinationOneFS(qr/^6|^7\.0|^7\.1\.0|^7\.1\.1\.[0123]$|^7\.2\.0\.[01]$/)) {
	$RPS{'ETA'}{'199379'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'199379'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 199379'));
    }

    # ETA 198187
    if (isCurrentOneFS('7.2.0.0')) {
	$RPS{'ETA'}{'198187'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'198187'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 198187'));
    }
    if (isDestinationOneFS('7.2.0.0')) {
	$RPS{'ETA'}{'198187'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'198187'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 198187'));
    }

    # ETA 197460
    if (isCurrentOneFS('7.2.0.0')) {
	$RPS{'ETA'}{'197460'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'197460'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 197460'));
    }
    if (isDestinationOneFS('7.2.0.0')) {
	$RPS{'ETA'}{'197460'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'197460'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 197460'));
    }

    # ETA 190094
    if (isCurrentOneFS(qr/^7\.1\.0\.[0123]|^7\.1\.1\.0$/)) {
	$RPS{'ETA'}{'190094'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'190094'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 190094'));
    }
    if (isDestinationOneFS(qr/^7\.1\.0\.[0123]|^7\.1\.1\.0$/)) {
	$RPS{'ETA'}{'190094'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'190094'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 190094'));
    }

    # ETA 189234
    if (isCurrentOneFS(qr/^7\.1\.0\.[0123]|^7\.1\.1\.0$/)) {
	$RPS{'ETA'}{'189234'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'189234'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 189234'));
    }
    if (isDestinationOneFS(qr/^7\.1\.0\.[0123]|^7\.1\.1\.0$/)) {
	$RPS{'ETA'}{'189234'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'189234'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 189234'));
    }

    # ETA 188835
    if (isCurrentOneFS('7.1.0.3')) {
	$RPS{'ETA'}{'188835'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'188835'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 188835'));
    }
    if (isDestinationOneFS('7.1.0.3')) {
	$RPS{'ETA'}{'188835'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'188835'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 188835'));
    }

    # ETA 185153
    if (isCurrentOneFS(qr/^6\.5\.5\.[01][0123456789]|^6\.5\.5\.2[01]$|^7\.0\.0\.0$|^7\.0\.1\.[012345]$/)) {
	$RPS{'ETA'}{'185153'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'185153'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 185153'));
    }
    if (isDestinationOneFS(qr/^6\.5\.5\.[01][0123456789]|^6\.5\.5\.2[01]$|^7\.0\.0\.0$|^7\.0\.1\.[012345]$/)) {
	$RPS{'ETA'}{'185153'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'185153'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 185153'));
    }

    if ($RPS{'ETA'}) {
	foreach my $eta (sort {$a <=> $b} keys %{$RPS{'ETA'}}) {
	    next unless $RPS{'ETA'}{$eta}{'Message'} and $RPS{'ETA'}{$eta}{'Severity'};
	    if ($RPS{'ETA'}{$eta}{'Severity'} eq 'INFO') {
		info($_) foreach @{$RPS{'ETA'}{$eta}{'Message'}};
	    }
	    if ($RPS{'ETA'}{$eta}{'Severity'} eq 'WARN') {
		warning($_) foreach @{$RPS{'ETA'}{$eta}{'Message'}};
	    }
	    if ($RPS{'ETA'}{$eta}{'Severity'} eq 'FAIL') {
		fail($_) foreach @{$RPS{'ETA'}{$eta}{'Message'}};
	    }
	}
    }
    printResults();
} # End checkETAs
$CHECKS{'checkKB533516'} = {
    'Description' => 'Checks if cluster uses an IP for AWS CloudPool accounts putting it at risk for DTA 533516',
    'Exclude' => 1,
};
sub checkKB533516 {
    return if wasCalled();
    if (!hasLicense('CloudPools')) {
	return unsupported('UNSUP_LIC_1', 'CloudPools');
    }
    if (isCurrentOneFS('<8')) { return unsupported('UNSUP_CUR') }
    if (isCurrentOneFS('>=8.2')) { return unsupported('UNSUP_CUR') }

    printCheck("KB 533516");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal(qq[2>&1 ${SUDO} isi_gconfig -t cpool_cfg].
			      q[ | grep -E '\.uri |\.account_name |\.ptype ']);
    } else {
	$gather = gatherLocal(qq[2>&1 sqlite3 local/ifsvar_modules_tardis.tar/modules/tardis/namespaces/Gconfig.cpool_config.sqlite 'select * from kv_table'].
			      q[ | grep -E '\.uri |\.account_name |\.ptype ']);
    }

    # Analyze information for issues.
    my %accounts = ();
    my $re_line = qr[^account\.(\d+)\.(account_name|ptype|uri) .*= "?(.*?)"?$];
    foreach my $line (@$gather) {
	$accounts{$1}{$2} = $3 if $line =~ $re_line;
    }
    my @accounts_with_ip = ();
    foreach my $id (keys %accounts) {
	if ($accounts{$id}{'uri'} and $accounts{$id}{'uri'} =~ qr[//\d+\.\d+\.\d+\.\d+] and
	    $accounts{$id}{'ptype'} and $accounts{$id}{'ptype'} =~ qr[CPT_AWS] and
	    $accounts{$id}{'account_name'}) {
	    push(@accounts_with_ip, $accounts{$id}{'account_name'});
	}
    }

    if (@accounts_with_ip) {
	critical("The following CloudPool accounts are configured for AWS using an IP for the URI: ".join(",", @accounts_with_ip));
	critical("  For V4 signatures a FQDN is required.");
	info(sprintf($MSG{'KB_1'}, '000154599'));
    }

    # Output Check result
    printResults();
} # End checkKB533516
###
# gatherDTA194319: Checks to see if the cluster meets criteria for DTA 194319
sub gatherDTA194319 {
    if (wasCalled()) { return }
    gatherFlexNet();
    my $has_dynamic = 1;
    if ($CLUSTER_INFO{'FlexNet'} and
	$CLUSTER_INFO{'FlexNet'}{'Pools'}) {
	my ($match) = grep {$_->{'Allocation Method'} and $_->{'Allocation Method'} eq 'dynamic'} values %{$CLUSTER_INFO{'FlexNet'}{'Pools'}};
	# No dynamic pools means no risk
	if (!$match) { return }
    }
    if (hasPatch('PSP-(1150|1247|1249|1251|1395|1397|1399|1433|1491|1493|1497|1526|1549|1572|1611|1641|1642|1651|1687|1688|1694|1698|1699|1722|1723|1727|1775|1778|1816|1817|1822|1860|1861|1964|1965|1994|1995|2015|2075|2076|2077|2078|2105|2126|2127|2155|2156|2192|2217|2218|2240|2241|2242|2243|2365|2366|2367)$')) {
	# Has patch applied which includes fixes for DTA 194319
    } elsif (isCurrentOneFS(qr/^(?:8\.2|9\.0|9\.1\.1|9\.2\.0)\./) or
	isCurrentOneFS(qr/^9\.1\.0\.(?:1[0123]|[0123456789])$/) or
	isCurrentOneFS(qr/^9\.2\.1\.[0123456]$/)) {
	$RPS{'KB'}{'194319'} = 1;
	$RPS{'ETA'}{'194319'}{'Severity'} = 'WARN';
	push(@{$RPS{'ETA'}{'194319'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'DTA 194319'));
	addCustomerInformation('DTA 194319', 'This cluster is running a version that is at risk for DTA 194319, https://www.dell.com/support/kbdoc/000194319. When any activity requiring a reboot of nodes is performed, you may encounter the issues outlined in the DTA. The cluster will remain at risk until an upgrade to a version which includes fixes for the DTA has been completed.');
    }
    if (isDestinationOneFS(qr/^(?:8\.2|9\.0|9\.1\.1|9\.2\.0)\./) or
	isDestinationOneFS(qr/^9\.1\.0\.(?:1[0123]|[0123456789])$/) or
	isDestinationOneFS(qr/^9\.2\.1\.[0123456]$/)) {
	$RPS{'ETA'}{'194319'}{'Severity'} = 'WARN';
	push(@{$RPS{'ETA'}{'194319'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'DTA 194319'));
    }
} # End gatherDTA194319
###
# gatherDTA525928: Checks to see if the cluster meets criteria for DTA 525928.
sub gatherDTA525928 {
    return if wasCalled();
    if (isCurrentOneFS(qr/^(8.0.0.7|8.1.0.4)$/)) {
	$RPS{'ETA'}{'525928'}{'Severity'} = "INFO";
	push(@{$RPS{'ETA'}{'525928'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'DTA 525928'));
    }
    if (isDestinationOneFS(qr/^(8.0.0.7|8.1.0.4)$/)) {
	$RPS{'ETA'}{'525928'}{'Severity'} = "INFO";
	push(@{$RPS{'ETA'}{'525928'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'DTA 525928'));
    }
} # End gatherDTA525928
###
# gatherDTA526050: Checks to see if the cluster meets criteria for ETA 526050.
sub gatherDTA526050 {
    return if wasCalled();
    if (isCurrentOneFS(qr/^7\.[12]/)) {
	$RPS{'ETA'}{'526050'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'526050'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'DTA 526050'));
    }
    if (isDestinationOneFS(qr/^7\.[12]/)) {
	$RPS{'ETA'}{'526050'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'526050'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'DTA 526050'));
    }
} # End gatherDTA526050
sub gatherDTA533516 {
    return if wasCalled();
    if (isCurrentOneFS(qr/^8\.[01]/)) {
	$RPS{'ETA'}{'533516'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'533516'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'DTA 533516'));
    }
    if (isDestinationOneFS(qr/^8\.[01]/)) {
	$RPS{'ETA'}{'533516'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'533516'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'DTA 533516'));
    }
}
###
# gatherDTA540272: Checks to see if the cluster meets criteria for DTA 540272
$MSG{'checkETAs_DTA540272'} = 'Cluster at risk for DTA 540272';
sub gatherDTA540272 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=9')) { return }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'sysctl efs.bam.rename_event_coherency'];
    my $CMD_LOGS = q[2>&1 grep -H efs.bam.rename_event_coherency */etc.tar/etc/sysctl.conf];
    my $RE_SYSCTL = qr/efs.bam.rename_event_coherency(?:=|: )(\d+)/;

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    my $sysctl_value = -1;
    foreach my $lnn (@LNNS) {
	if (!$gather{$lnn}) { next }
	foreach my $line (@{$gather{$lnn}}) {
	    if ($line =~ $RE_SYSCTL) {
		if ($1 > $sysctl_value) { $sysctl_value = $1 }
	    }
	}
    }
    if ($sysctl_value == 1) {
	$RPS{'ETA'}{'540272'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'540272'}{'Message'}},
	     $MSG{'checkETAs_DTA540272'});
    }
} # End gatherDTA540272
###
# gatherETA180317: Gathers whether the cluster is running versions affected by ETA 180317
$MSG{'checkETAs_ETA180317'} = 'InfiniBand switch firmware may be running a version affected by ETA 180317';
sub gatherETA180317 {
    return if wasCalled();
    gatherNetworkInterfaces();
    if (!$CLUSTER_INFO{'Active IB Interfaces'}) { return }
    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array 'grep -m1 "QLogic 12[38]00" /var/log/opensm-[12].topo']);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -m1 -H "QLogic 12[38]00" */varlog.tar/log/opensm-[12].topo]);
    }

    # Analyze information for issues.
    my $meets_eta180317 = 0;
    foreach my $lnn (@LNNS) {
	next unless ($gather->{$lnn});
	last if $meets_eta180317;
	foreach my $line (@{$gather->{$lnn}}) {
	    last if $meets_eta180317;
	    $meets_eta180317 = 1 if $line =~ /QLogic 12[38]00/;
	}
    }
    if ($meets_eta180317) {
	$RPS{'ETA'}{'180317'}{'Severity'} = 'WARN';
	push(@{$RPS{'ETA'}{'180317'}{'Message'}}, $MSG{'checkETAs_ETA180317'});
    }
} # End gatherETA180317
###
# gatherETA194692: Gathers whether the cluster is running versions affected by ETA 194692
sub gatherETA194692 {
    return if wasCalled();

    # ETA 194692
    my $regex = qr/^6\.5\.5\.\d$|^6\.5\.5\.1\d$|^6\.5\.5\.2[012]$|^7\.0\.2\.[01]$|^7\.1\.0\.[012345]$|^7\.1\.1\.1$|^7\.2\.0\.0$/;
    if (isCurrentOneFS($regex)) {
	$RPS{'ETA'}{'194692'}{'Severity'} = "INFO";
	push(@{$RPS{'ETA'}{'194692'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), ' ETA 194692'));
    }
    if (isDestinationOneFS($regex)) {
	$RPS{'ETA'}{'194692'}{'Severity'} = "INFO";
	push(@{$RPS{'ETA'}{'194692'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 194692'));
    }
} # End gatherETA194692
###
# gatherETA198924: Gathers whether the cluster is running versions affected by ETA 198924
sub gatherETA198924 {
    return if wasCalled();
    gatherProductInfo();

    # ETA 198924
    my $has_hd400_nodes = 0;
    foreach my $lnn (@LNNS) {
	next unless $NODE_INFO{$lnn}{'Product'};
	$has_hd400_nodes = 1 if $NODE_INFO{$lnn}{'Product'} =~ /^HD400/;
    }
    if ($has_hd400_nodes) {
	if (isCurrentOneFS(qr/^7\.2\.0\.[01]$/)) {
	    $RPS{'ETA'}{'198924'}{'Severity'} = "INFO";
	    push(@{$RPS{'ETA'}{'198924'}{'Message'}},
		 sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 198924'));
	}
	if (isDestinationOneFS(qr/^7\.2\.0\.[01]$/)) {
	    $RPS{'ETA'}{'198924'}{'Severity'} = "INFO";
	    push(@{$RPS{'ETA'}{'198924'}{'Message'}},
		 sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 198924'));
	}
    }
} # End gatherETA198924
###
# gatherETA202452: Gathers whether the cluster is running versions affected by ETA 202452
sub gatherETA202452 {
    return if wasCalled();

    # ETA 202452
    my $regex = qr/^7\.1\.0\.[0123456]$|^7\.1\.1\.[0123456]$|^7\.2\.0\.[0123]$|^7\.2\.1\.0$/;
    if (isCurrentOneFS($regex)) {
	$RPS{'ETA'}{'202452'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'202452'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 202452'));
    }
    if (isDestinationOneFS($regex)) {
	$RPS{'ETA'}{'202452'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'202452'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 202452'));
    }
} # End gatherETA202452
###
# gatherETA205085: checks for conditions for ETA 205085.
#  ETA 205085: Isilon OneFS: Legacy NFSv3 clients that require 32-bit
#  cookies may encounter data unavailability in OneFS 7.2.1.0 and 7.2.0.0 - 7.2.0.2
sub gatherETA205085 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=7.2') or isDestinationOneFS('<7.2')) { return }

    # ETA 205085
    $RPS{'ETA'}{'205085'}{'Severity'} = 'WARN';
    push(@{$RPS{'ETA'}{'205085'}{'Message'}},
	 sprintf($MSG{'checkETAs_UPG_3'}, isCurrentOneFS(),
		 isDestinationOneFS(), 'ETA 205085'));

    addCustomerInformation('ETA 205085', 'NFSv3 clients that require 32-bit cookies may be unable to access data or view files that are contained in an NFS export due to a change in the way OneFS returns read directory (READDIR) cookies. This situation may result in data unavailability.  If you have NFSv3 clients that require 32-bit cookies, review ETA 205085 (https://www.dell.com/support/kbdoc/000002554) and take any appropriate action prior to the upgrade.');

} # End gatherETA205085
###
# gatherETA207785: Gathers whether the cluster is running versions affected by ETA 207785
sub gatherETA207785 {
    return if wasCalled();

    # ETA 207785
    if (isCurrentOneFS(qr/^7\.1\.1\.6$/)) {
	$RPS{'ETA'}{'207785'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'207785'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 207785'));
    }
    if (isDestinationOneFS(qr/^7\.1\.1\.6$/)) {
	$RPS{'ETA'}{'207785'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'207785'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 207785'));
    }
} # End gatherETA207785
###
# gatherETA209918: Gathers whether the cluster is running versions affected by ETA 209918
sub gatherETA209918 {
    return if wasCalled();

    # ETA 209918
    my $regex = qr/^7\.1\.0\.[0123456]$|^7\.1\.1\.[012345]$|^7\.2\.0\.[012]$|^7\.2\.1\.0$/;
    if (isCurrentOneFS($regex)) {
	$RPS{'ETA'}{'209918'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'209918'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 209918'));
    }
    if (isDestinationOneFS($regex)) {
	$RPS{'ETA'}{'209918'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'209918'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 209918'));
    }
} # End gatherETA209918
###
# gatherETA483840: checks for conditions for ETA 483840.
#  ETA 483840: Isilon OneFS: Isilon OneFS: Non-UTF-8 encoded files or
#  directories may be inaccessible to NFSv3 and NFSv4 clients after
#  upgrading OneFS clusters from version 7.1.x or earlier to 7.2.0.0 or later
sub gatherETA483840 {
    if (wasCalled()) { return }

    # Must be for a OneFS upgrade
    if (!isDestinationOneFS()) { return }

    # Does not apply if already at OneFS 7.2 or higher
    return if isCurrentOneFS('>=7.2');

    # Does not apply if upgrade is not to OneFS 7.2 or higher
    if (isDestinationOneFS('<7.2')) { return }

    # Does not apply if upgrade is to OneFS 8.1.1 or higher
    if (isDestinationOneFS('>=8.1.1')) { return }

    # ETA 483840
    $RPS{'ETA'}{'483840'}{'Severity'} = 'WARN';
    push(@{$RPS{'ETA'}{'483840'}{'Message'}},
	 sprintf($MSG{'checkETAs_UPG_3'}, isCurrentOneFS(),
		 isDestinationOneFS(), 'ETA 483840'));

    addCustomerInformation('ETA 483840', 'OneFS may inaccurately translate non-UTF-8 encoded file and directory names that are accessed by NFS clients on a small percentage of clusters that have been upgraded from OneFS 7.1.x or earlier to OneFS 7.2.0.0 or later.  If you have NFS clients, review ETA 483840 (https://www.dell.com/support/kbdoc/000002524) and complete steps in the resolution to determine if this may put access to your data at risk.');

} # End gatherETA483840
###
# gatherETA491747: Checks to see if the cluster meets criteria for ETA 491747.
sub gatherETA491747 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=7.2.1.4') or isCurrentOneFS('7.1.1.11')) { return }

    gatherProductInfo();
    gatherNodeFirmware();

    my @ETA491747 = ();
    foreach my $lnn (@LNNS) {
	if ($NODE_INFO{$lnn}{'Product'} and
	    $NODE_INFO{$lnn}{'Product'} =~ /^(A100|X410|X210|S210|HD400|NL410)/ and
	    $NODE_INFO{$lnn}{'Firmware'}{'Mellanox'}) {
	    push(@ETA491747, $lnn);
	}
    }

    if (@ETA491747) {
	if (isCurrentOneFS(qr/^7\.2\.1\.[0123]$/) ||
	    isCurrentOneFS(qr/^7\.1\.1\.([0123456789]|10)$/)) {
	    $RPS{'ETA'}{'491747'}{'Severity'} = 'INFO';
	    push(@{$RPS{'ETA'}{'491747'}{'Message'}},
		 sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 491747'));
	}
	if (isDestinationOneFS(qr/^7\.2\.1\.[0123]$/) ||
	    isDestinationOneFS(qr/^7\.1\.1\.([0123456789]|10)$/)) {
	    $RPS{'ETA'}{'491747'}{'Severity'} = 'INFO';
	    push(@{$RPS{'ETA'}{'491747'}{'Message'}},
		 sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 491747'));
	}
    }
} # End gatherETA491747
###
# gatherETA518924: Checks to see if the cluster meets criteria for ETA 518924
$MSG{'checkETAs_ETA518924_2'} = 'ETA 518924: Locking contention seen in the last 30 days, most recent occurrence seen on node %s at %s';
sub gatherETA518924 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8.1.1')) { return }

    # Setup
    my @ERRORS = ('bam_mark_blocks',
		  'dfm_mark_directory',
		  'ifm_mark_metatree',
		  'qdb_mark_inner',
		  'sbt_mark_tree',
		  'stf_mark_metatree');
    my $GREP_ERRORS = join('|', @ERRORS);
    my $GREP_IGNORE = 'sudo: ';
    my $CMD_LOGS = qq[grep -a -h -E "$GREP_ERRORS" */varlog.tar/log/messages].
	qq[ | grep -v "$GREP_IGNORE" | sort | tail -1];
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -Q].
	qq[ 'grep -a -h -E "$GREP_ERRORS" /var/log/messages'].
	qq[ | grep -v "$GREP_IGNORE" | sort | tail -1];
    my $RE_LNN = '^\s*(\S+)\s+\S+\s+\S+-(\d+)';

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my $epoch = 0;
    my $lnn = 0;
    foreach (@$gather) {
	if (!/$RE_LNN/) { next }
	$epoch = parseTime($1);
	$lnn = $2;
	if ($epoch < $RPS{'Timestamp'} - 30 * 24 * 60 * 60) {
	    $epoch = 0;
	}
    }

    # Flag
    $RPS{'ETA'}{'518924'}{'Severity'} = 'INFO';
    push(@{$RPS{'ETA'}{'518924'}{'Message'}},
	 sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 518924'));
    if (isDestinationOneFS('<8.1.1')) {
	$RPS{'ETA'}{'518924'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'518924'}{'Message'}},
	     sprintf($MSG{'checkETAs_DES_2'}, isDestinationOneFS(), 'ETA 518924'));
    }
    if ($epoch and $lnn) {
	$RPS{'ETA'}{'518924'}{'Severity'} = 'WARN';
	push(@{$RPS{'ETA'}{'518924'}{'Message'}},
	     sprintf($MSG{'checkETAs_ETA518924_2'}, $lnn, scalar(localtime($epoch))));
    }
} # End gatherETA518924
sub gatherETA519384 {
    return if wasCalled();
    if (isCurrentOneFS('<8.0.0.6') or
	isCurrentOneFS(qr/^(8\.0\.1\.[01]|8\.1\.0\.0)$/)) {
	$RPS{'ETA'}{'519384'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'519384'}{'Message'}},
	     sprintf($MSG{'checkETAs_CUR_2'}, isCurrentOneFS(), 'ETA 519384'));
    }
    if (isDestinationOneFS('<8.0.0.6') or
	isDestinationOneFS(qr/^(8\.0\.1\.[01]|8\.1\.0\.0)$/)) {
	$RPS{'ETA'}{'519384'}{'Severity'} = 'INFO';
	push(@{$RPS{'ETA'}{'519384'}{'Message'}},
	     sprintf($MSG{'checkETAs_UPG_3'}, isDestinationOneFS(), 'ETA 519384'));
    }
} # End gatherETA519384
###
# checkBMC: checks for BMC related issues
$CHECKS{'checkBMCandCMC'} = {
    'Description' => 'Checks for BMC/CMC related issues',
    'Reference' => 'https://www.dell.com/support/kbdoc/000041523',
};
$MSG{'checkBMCandCMC_UNRESPONSIVE'} = 'Be advised, nodes with unresponsive BMC/CMC or not reporting a BMC/CMC firmware version are assumed to be out of date';
$MSG{'checkBMCandCMC_LOG_BMC'} = 'Be advised, log gathers in OneFS 7.1.1 do not include BMC firmware versions, BMC firmware versions are assumed to be out of date';
$MSG{'checkBMCandCMC_FCO_BMC_2'} = '%s nodes are running BMC firmware versions affected by FCO F010517EE: %s';
$MSG{'checkBMCandCMC_RBT_BMC'} = 'Nodes with out of date BMC firmware have an increased risk of encountering hardware monitoring issues and may encounter issues when rebooted.';
$MSG{'checkBMCandCMC_UPG_BMC'} = 'BMC firmware updates cannot be performed remotely by RPS on OneFS versions prior to 8.0.0.2.';
$MSG{'checkBMCandCMC_FCO_CMC_2'} = '%s nodes are running CMC firmware versions affected by FCO F010517EE: %s';
$MSG{'checkBMCandCMC_NONE_2'} = 'The %s device is reporting None on the following nodes: %s';
$MSG{'checkBMCandCMC_HWMON_1'} = 'Hardware monitoring issues detected on nodes: %s';
$MSG{'checkBMCandCMC_SIGNED'} = 'BMC firmware updates must be performed after updating to a OneFS 8 release and prior to updating to OneFS 9.4 and later releases.  The provided upgrade plan must be modified manually to meet these requirements';
sub checkBMCandCMC {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen5'}) { return unsupported('UNSUP_GEN5') }

    checkHardwareStatus();
    printCheck('BMC/CMC Hardware Monitoring');
    gatherBMCInfo();
    gatherCMCInfo();
    gatherNodeFirmware();

    my $SPF_PRI_BMC = 'BMC firmware updates are strongly recommended because'.
	' %s nodes are affected by F010517EE';

    # Look for BMC/CMC issues
    my @node_list = ();
    my %none = ();
    foreach my $lnn (@{$CLUSTER_INFO{'Gen5'}}) {
	if (!$NODE_INFO{$lnn}{'Firmware'}) { next }
	my %fw = %{$NODE_INFO{$lnn}{'Firmware'}};

	# If the CMC firmware is not up to 02.05 or unresponsive
	if (!$fw{'CMC'} or compareVersions($fw{'CMC'}, '02.05') < 0) {
	    push(@{$RPS{'CMC Firmware'}{'Nodes'}}, $lnn);
	}

	# If the BMC firmware is not up to 01.25 or unresponsive
	if (!$fw{'BMC'} or compareVersions($fw{'BMC'}, '01.25') < 0) {
	    push(@{$RPS{'BMC Firmware'}{'Nodes'}}, $lnn);
	}

	# Check each node for hardware monitoring issues
	my $hardware_monitoring_issue = 0;

	# BMC or CMC is not found/unresponsive
	if (isLive() or isCurrentOneFS('>7.2')) {
	    if (!$fw{'BMC'}) {
		debug("checkBMCandCMC: !!! Node $lnn BMC not responsive !!!");
		$hardware_monitoring_issue = 1;
	    }
	    if (!$fw{'CMC'}) {
		debug("checkBMCandCMC: !!! Node $lnn CMC not responsive !!!");
		$hardware_monitoring_issue = 1;
	    }
	}

	# Both power supplies don't report FW versions
	if (!$fw{'PS1'} and !$fw{'PS2'}) {
	    debug("checkBMCandCMC: !!! Node $lnn both PS not reporting !!!");
	    $hardware_monitoring_issue = 1;
	}

	# Any ^(BMC/CAR/CFFPS/CMC/CMD) component reports None
	my @results = grep {$_ =~ /^(?:BMC|CAR|CMC|CMD)_/} keys %fw;
	foreach my $device (@results) {
	    if ($fw{$device} eq 'None') {
		$hardware_monitoring_issue = 1;
		push(@{$none{$device}}, $lnn);
		debug("checkBMCandCMC: !!! Node $lnn Device $device is".
		      " reporting None");
	    }
	}

	# Add to list of nodes with hardware monitoring issues
	if ($hardware_monitoring_issue) {
	    debug("checkBMCandCMC: Node $lnn found hardware monitoring issue");
	    push(@node_list, $lnn);
	}
    }

    if ($RPS{'BMC Firmware'}{'Nodes'} and $RPS{'CMC Firmware'}{'Nodes'}) {
	# BMC firmware updates must be performed before node firmware updates
	$RPS{'BMC First'} = 1;
    }

    if ($RPS{'BMC Firmware'}{'Nodes'} or $RPS{'CMC Firmware'}{'Nodes'}) {
	# BMC/CMC is assumed out of date if not responsive/gathered
	if (@node_list) {
	    info($MSG{'checkBMCandCMC_UNRESPONSIVE'});
	}

	# BMC is assumed to be out of date for log gathers in OneFS 7.1.1
	if (!isLive() and isCurrentOneFS(qr/^7\.1\.1\./)) {
	    info($MSG{'checkBMCandCMC_LOG_BMC'});
	}
    }

    # BMC firmware out of date on some nodes
    if ($RPS{'BMC Firmware'}{'Nodes'}) {

	# Flag BMC firmware FCO details
	$RPS{'FCO'}{'F010517EEB'}{'Severity'} = 'INFO';
	$RPS{'FCO'}{'F010517EEB'}{'Message'} =
	    sprintf($MSG{'checkBMCandCMC_FCO_BMC_2'},
		    scalar(@{$RPS{'BMC Firmware'}{'Nodes'}}),
		    compressRange(@{$RPS{'BMC Firmware'}{'Nodes'}}));
	critical($RPS{'FCO'}{'F010517EEB'}{'Message'});
	critical($MSG{'checkBMCandCMC_RBT_BMC'});

	# BMC Firmware Activity
	$RPS{'BMC Firmware'}{'Target'} = 'BMC Firmware Package 1.0';
	push(@{$RPS{'BMC Firmware'}{'Files'}},
	     'Isilon BMC Firmware Package 1.0');
	push(@{$RPS{'BMC Firmware'}{'Priority'}},
	    sprintf($SPF_PRI_BMC, scalar(@{$RPS{'BMC Firmware'}{'Nodes'}})));

	# Set the position of where the BMC firmware updates will be completed
	if (isCurrentOneFS('>=8.0.0.2')) {
	    $RPS{'BMC Firmware'}{'Position'} = 1;
	} elsif (isDestinationOneFS('>=8.0.0.2')) {
	    $RPS{'BMC Firmware'}{'Position'} = 2;
	    if (isDestinationOneFS('>=9.4')) {
		critical($MSG{'checkBMCandCMC_SIGNED'});
	    }
	} else {
	    warning($MSG{'checkBMCandCMC_UPG_BMC'});
	}
    }

    # Flag CMC firmware that is lower than required for FCO F010517EEC
    if ($RPS{'CMC Firmware'}{'Nodes'}) {
	$RPS{'FCO'}{'F010517EEC'}{'Severity'} = 'INFO';
	$RPS{'FCO'}{'F010517EEC'}{'Message'} =
	    sprintf($MSG{'checkBMCandCMC_FCO_CMC_2'},
		    scalar(@{$RPS{'CMC Firmware'}{'Nodes'}}),
		    compressRange(@{$RPS{'CMC Firmware'}{'Nodes'}}));
	info($RPS{'FCO'}{'F010517EEC'}{'Message'});
    }

    # Flag CMC/BMC components/devices reporting None
    foreach my $device (keys %none) {
	fail(sprintf($MSG{'checkBMCandCMC_NONE_2'}, $device, compressRange(@{$none{$device}})));
    }

    # Flag hardware monitoring issues
    if (@node_list) { fail(sprintf($MSG{'checkBMCandCMC_HWMON_1'}, compressRange(@node_list))) }

    # Output
    if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000041523')) }
    printResults();
} # End checkBMCandCMC
$CHECKS{'checkBXENodes'} = {
    'Description' => 'Checks for existence of nodes with BXE interfaces, also checks for KB 000048172 & KB 000064027 known issues',
    'Exclude' => 1,
};
sub checkBXENodes {
    if (wasCalled()) { return }

    # Pre-requisite version checks
    if (isCurrentOneFS('<7.1.1')) {
	debug("checkBXENodes: Skipped..");
	return;
    }
    if (isVirtual()) { return unsupported('UNSUP_VIRT') }

    # Print showing check running
    printCheck("BXE Nodes");

    # Gather required information
    gatherNetworkInterfaces();

    # Flag nodes with BXE interfaces or that no nodes have BXE interfaces
    my $num_bxe_nodes = 0;
    if ($CLUSTER_INFO{'BXE Nodes'}) {
	$num_bxe_nodes = scalar(@{$CLUSTER_INFO{'BXE Nodes'}});
	details("$num_bxe_nodes nodes have BXE interfaces: ".compressRange(@{$CLUSTER_INFO{'BXE Nodes'}}));
    }

    # Flag issue in KB 000048172
    checkKB493493();

    # Flag issue in KB 000064027
    checkKB511208();

    # Output results
    printResults();

} # End checkBXENodes
$CHECKS{'checkIBPCIeSlot'} = {
    'Description' => 'Checks if the InfiniBand card is installed in the wrong slot which may lead node start up issues during an upgrade to OneFS 9 and later releases',
    'Exclude' => 1,
};
$MSG{'checkIBPCIeSlot_FAIL_2'} = 'The following nodes have the InfiniBand card installed into the wrong PCIe slot and will encounter issues during an upgrade to OneFS %s: %s';
sub checkIBPCIeSlot {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=9')) { return unsupported('UNSUP_CUR') }
    if ($RPS{'CheckList'} ne 'healthcheck' and
	(!isOneFSUpgrade() or isDestinationOneFS('<9'))) {
	return unsupported('UNSUP_TGT_1', '9 and later');
    }

    # Setup
    gatherProductInfo();
    my %RE_EXPECTED_BY_TYPE = (
	'X210' => qr/pci0:[34]:0:0:.*Mellanox/,
	'X410' => qr/pci0:[56]:0:0:.*Mellanox/,
	'S210' => qr/pci0:[56]:0:0:.*Mellanox/,
	'NL410' => qr/pci0:[34]:0:0:.*Mellanox/,
	'HD400' => qr/pci0:[34]:0:0:.*Mellanox/,
	'A100' => qr/pci0:[67]:0:0:.*Mellanox/,
    );
    my $SUPPORTED_TYPES = join('|', keys %RE_EXPECTED_BY_TYPE);
    my $RE_SUPPORTED_TYPES = qr/^($SUPPORTED_TYPES)/;
    my %nodes_supported;
    foreach my $lnn (@LNNS) {
	if ($NODE_INFO{$lnn}{'Product'} =~ $RE_SUPPORTED_TYPES) {
	    $nodes_supported{$lnn} = $1;
	}
    }
    if (!%nodes_supported) { return unsupported('UNSUP_GEN5') }
    my $NODE_LIST = compressRange(keys %nodes_supported);
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -s -n $NODE_LIST 'pciconf -lv' | grep -E -A1 "pci0" | grep -v -e -- | paste - - | egrep "mlx4_core|Mellanox"];
    my $CMD_LOGS = q[for i in */pciconf; do grep -E -h -A1 "pci0" $i | grep -v -e – | paste - - | while read line; do echo "$i: $line"; done; done | egrep "mlx4_core|Mellanox" 2>&1];

    # Start
    printCheck('InfiniBand PCIe Slot');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my @nodes_at_risk = ();
  LNN:
    foreach my $lnn (@LNNS) {
	if (!$nodes_supported{$lnn}) { next }
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	my ($match) = grep {$_ =~ $RE_EXPECTED_BY_TYPE{$nodes_supported{$lnn}}} @{$gather->{$lnn}};
	if ($match) { next LNN }
	push(@nodes_at_risk, $lnn);
    }

    # Flag
    if (@nodes_at_risk) {
	info(sprintf($MSG{'KB_INT_1'}, '201524'));
	if ($RPS{'CheckList'} eq 'healthcheck') {
	    fail(sprintf($MSG{'checkIBPCIeSlot_FAIL_2'}, '9 and later releases', compressRange(@nodes_at_risk)));
	} else {
	    fail(sprintf($MSG{'checkIBPCIeSlot_FAIL_2'}, isDestinationOneFS(), compressRange(@nodes_at_risk)));
	}
    }

    # Output
    printResults();
} # End checkIBPCIeSlot
$CHECKS{'checkKB462202'} = {
    'Description' => 'Checks BootOrder in bios_settings.ini on Generation 5 nodes to determine if at risk for KB 000025523',
    'Reference' => 'https://www.dell.com/support/kbdoc/000025523',
};
sub checkKB462202 {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen5'}) { return unsupported('UNSUP_GEN5') }

    printCheck('KB 462202');
    my $HDD = 'Hard Drive';
    my $EFI = 'Internal EFI Shell';
    my $SPF_NODES_AT_RISK = 'The boot order has "Internal EFI Shell" prior to "Hard Drive" on the following nodes: %s';
    my $CMD_NODE = qq[grep -E "$HDD|$EFI" /var/log/bios_settings.ini];
    my $CMD_LOGS = qq[grep -H -E "$HDD|$EFI" */varlog.tar/log/bios_settings.ini];

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array '$CMD_NODE']);
    } else {
	$gather = gatherCluster(qq[2>&1 $CMD_LOGS]);
    }

    # Analyze information for issues.
    my %boot_order = ();
    my @nodes_at_risk = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	foreach (@{$gather->{$lnn}}) {
	    if (/($HDD|$EFI)=(\d+)$/) {
		$boot_order{$lnn}{$1} = $2;
	    }
	}
	if (!$boot_order{$lnn}{$HDD} or !$boot_order{$lnn}{$EFI}) {
	    next;
	}
	if ($boot_order{$lnn}{$HDD} > $boot_order{$lnn}{$EFI}) {
	    push(@nodes_at_risk, $lnn);
	}
    }

    # Flag any issues identified
    if (@nodes_at_risk) {
	fail(sprintf($SPF_NODES_AT_RISK, compressRange(@nodes_at_risk)));
	info(sprintf($MSG{'KB_INT_1'}, '000025523'));
    }

    # Output Check result
    printResults();
} # End checkKB462202
sub checkKB493493 {
    if (wasCalled()) { return }
    if (!isCurrentOneFS(qr/^8\.0\.0\.[01234]$/)) { return }

    # Gather NIC driver if available and patches information
    gatherPatches();
    gatherBXEDriver();
    gatherNetworkInterfaces();
    my $SPF_INTERFACES = '%s interface affected by KB 000048172 on nodes: %s';
    my $SPF_PATCH_REBOOT = 'Patch 188239 is installed, but the following nodes were detected as running old NIC drivers which need to be rebooted with lagg interfaces first being taken down on each node prior to reboot: %s';
    my $SPF_PATCH_CONFLICT = 'Prior to installing patch-188239, the conflicting patch, %s, must be uninstalled.  A reboot of nodes is not necessary after the uninstall of the conflicting patch, but is neccesary after the install of patch-188239.';
    my $MSG_REASON = 'Installation of patch-188239 is required to avoid node panics caused by issues outlined in KB 000048172.';
    my $MSG_LAGG = 'Prior to rebooting nodes for the installation of patch-188239, the lagg interfaces on the node to be rebooted must be taken down to avoid node panics during the reboots. If rebooting all nodes simultaneously, avoid taking down all lagg interfaces as this may leave the cluster in an inaccessible state.';

    # Based on the code level, try to make assumptions on the BXE NIC driver
    my $safe_driver_codelevel = 0;
    if (isCurrentOneFS(qr/^8\.0\.0\.[012]$/)) {
	$safe_driver_codelevel = 1;
    }
    if (hasPatch('188239')) {
	$safe_driver_codelevel = 1;
    }
    if (hasPatch('182721')) {
	$safe_driver_codelevel = 0;
    }

    # Check for conflicting patch installs
    my ($patch_conflict) = hasPatch('182721|175450|171054');

    # Check to see if any interfaces are impacted
    my %lagg_interfaces = ();
    foreach my $lnn (@LNNS) {

	# Not running an impacted NIC driver version
	if ($NODE_INFO{$lnn}{'dev.bxe.0.version'} and
	    $NODE_INFO{$lnn}{'dev.bxe.0.version'} !~ /^1\.78\.89\.ISILON\.0[234]$/) {
	    next;
	}

	# Unable to gather BXE nic information due to running on a log gather.
	# Make assumptions from current code level.
	if (!$NODE_INFO{$lnn}{'dev.bxe.0.version'} and $safe_driver_codelevel) {
	    next;
	}

	my @affected_interfaces = ();
	foreach my $interface (keys %{$NODE_INFO{$lnn}{'Network'}}) {
	    if ($interface !~ /lagg/i) { next }
	    if (!$NODE_INFO{$lnn}{'Network'}{$interface}{'laggproto'}) { next }
	    if ($NODE_INFO{$lnn}{'Network'}{$interface}{'laggproto'} !~ /lacp/i) { next }
	    if (!$NODE_INFO{$lnn}{'Network'}{$interface}{'laggport'}) { next }
	    my @results = grep {$_ =~ /^bxe/} @{$NODE_INFO{$lnn}{'Network'}{$interface}{'laggport'}};
	    if (@results) { push(@affected_interfaces, $interface) }
	}
	foreach (@affected_interfaces) {
	    push(@{$lagg_interfaces{$_}}, $lnn);
	}
	if (@affected_interfaces) {
	    push(@{$RPS{'KB493493'}}, $lnn);
	}
    }
    foreach (keys %lagg_interfaces) {
	warning(sprintf($SPF_INTERFACES, $_, compressRange(@{$lagg_interfaces{$_}})));
    }

    # If identified as impacted by KB 000048172, patch 188239 should be performed prior to any node reboots
    if ($RPS{'KB493493'}) {
	if (hasPatch('188239')) {
	    @{$RPS{'Proactive Reboot'}{'Nodes'}} = @{$RPS{'KB493493'}};
	    addCustomerInformation('Upgrade Plan', warning(sprintf($SPF_PATCH_REBOOT, compressRange(@{$RPS{'KB493493'}}))));
	} else {
	    # Provide a reason why we are doing the patch/reboot
	    addCustomerInformation('Upgrade Plan', $MSG_REASON);

	    # This will make sure it gets into the upgrade plan
	    $RPS{'Pre Upgrade Patches'}{'patch-188239'}{'Impacts'} = '';
	    @{$RPS{'Pre Upgrade Patches'}{'patch-188239'}{'Reboot'}} = @{$CLUSTER_INFO{'BXE Nodes'}};
	    if ($patch_conflict) {
		push(@{$RPS{'Pre Upgrade Patches'}{'patch-188239'}{'Additional Comments'}},
		     warning(sprintf($SPF_PATCH_CONFLICT, $patch_conflict)));
	    }
	    push(@{$RPS{'Pre Upgrade Patches'}{'patch-188239'}{'Additional Comments'}},
		 warning($MSG_LAGG));
	}
    }
} # End checkKB493493
sub checkKB511208 {
    return if wasCalled();
    return if
	(isCurrentOneFS(qr/^8\.0\.0/) and isCurrentOneFS('>=8.0.0.7')) or
	(isCurrentOneFS(qr/^8\.1\.0/) and isCurrentOneFS('>=8.1.0.4')) or
	isCurrentOneFS('>=8.1.1.1');
    gatherNetworkInterfaces();

    # Analyze information for issues.
    my @impacted = ();
    foreach my $lnn (@LNNS) {
	next unless $NODE_INFO{$lnn}{'Network'};
	my @lagg_interfaces = grep {$_ =~ /^lagg/} keys %{$NODE_INFO{$lnn}{'Network'}};
	foreach my $lagg_interface (@lagg_interfaces) {
	    next unless $NODE_INFO{$lnn}{'Network'}{$lagg_interface}{'laggport'};
	    next unless $NODE_INFO{$lnn}{'Network'}{$lagg_interface}{'laggproto'};
	    next unless $NODE_INFO{$lnn}{'Network'}{$lagg_interface}{'laggproto'} =~ /lacp/;
	    foreach my $laggport (@{$NODE_INFO{$lnn}{'Network'}{$lagg_interface}{'laggport'}}) {
		my $interface = "";
		my $flags = "";
		$interface = $1 if $laggport =~ /^(\S+)/;
		$flags = $1 if $laggport =~ /flags=(\S+)/;
		next unless $interface and $interface =~ /bxe/;
		next unless $flags;
		next unless $NODE_INFO{$lnn}{'Network'}{$interface}{'status'} and $NODE_INFO{$lnn}{'Network'}{$interface}{'status'} eq "active";
		next unless $flags =~ /0<>/;
		# next if $flags =~ /ACTIVE/ and $flags =~ /COLLECTING/ and $flags =~ /DISTRIBUTING/;
		push(@impacted, "Node $lnn, aggregate interface $lagg_interface is reporting 'flags=0<>' for the interface $interface");
	    }
	}
    }

    # Skip unless identified as impacted
    return unless @impacted;

    if (isDestinationOneFS('8.0.0.7') or
	isDestinationOneFS('8.1.0.4') or
	isDestinationOneFS('>=8.1.1.1')) {
	warning(@impacted, "The above listed interfaces are currently impacted by KB 000064027.  This issue is addressed in OneFS 8.0.0.7, OneFS 8.1.0.4, OneFS 8.1.1.1 and later codes.");
    } else {
	fail(@impacted, "The above listed interfaces are currently impacted by KB 000064027.  This issue is addressed in OneFS 8.0.0.7, OneFS 8.1.0.4, OneFS 8.1.1.1 and later codes.");
    }

}
# gatherBMCInfo: Gather BMC firmware information
sub gatherBMCInfo {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen5'}) { return }
    my $nodes = compressRange(@{$CLUSTER_INFO{'Gen5'}});
    if (!$nodes) { return }

    # Setup
    my $CMD_LIVE = qq[ 2>&1 ${SUDO} isi_for_array -n ${nodes} '${SUDO} /usr/bin/isi_hwtools/isi_ipmicmc -d -V -a bmc' | grep firmware ];
    my $CMD_LOGS = q[ 2>&1 grep -H firmware */bmc_info ];
    my $RE_VERSION = qr/IPMI firmware version = (.*)$/;

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Store
    foreach my $lnn (@{$CLUSTER_INFO{'Gen5'}}) {
	if (!$gather->{$lnn}) { next }
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ $RE_VERSION) {
		$NODE_INFO{$lnn}{'Firmware'}{'BMC'} = $1;
		debug("NODE_INFO->${lnn}->Firmware->BMC=$1");
	    }
	}
    }
}
###
# gatherBXEDriver: Gather BXE NIC driver version
sub gatherBXEDriver {
    if (wasCalled()) { return }
    if (!$CLUSTER_INFO{'Gen5'}) { return }
    my $nodes = compressRange(@{$CLUSTER_INFO{'Gen5'}});
    if (!$nodes) { return }

    my $CMD_LIVE =
	qq[ 2>&1 ${SUDO} isi_for_array -n $nodes ].
	q[ 'sysctl dev.bxe.0.version' ];
    my $CMD_LOGS =
	q[ 2>&1 zgrep -H 'bxe0.*BXE.*ISILON' ].
	q[ */varlog.tar/log/messages* ];

    # Gather
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster($CMD_LIVE);
    } else {
	$gather = gatherCluster($CMD_LOGS);
    }

    # Store
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	foreach my $line (@{$gather->{$lnn}}) {
	    my $found = '';
	    if ($line =~ /^dev.bxe.0.version:\s*(\S+)$/) {
		$found = $1;
	    } elsif ($line =~ /BXE v:(.*?)>/) {
		$found = $1;
	    }
	    if (!$found) { next }
	    if ($NODE_INFO{$lnn}{'dev.bxe.0.version'}) {
		my $existing = $NODE_INFO{$lnn}{'dev.bxe.0.version'};
		if (compareVersions($existing, $found) >= 0) { next }
	    }
	    $NODE_INFO{$lnn}{'dev.bxe.0.version'} = $found;
	    debug("NODE_INFO->${lnn}->dev.bxe.0.version=$found");
	}
    }
} # End gatherBXEDriver
# gatherCMCInfo: Gather CMC firmware information
sub gatherCMCInfo {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen5'}) { return }
    my $nodes = compressRange(@{$CLUSTER_INFO{'Gen5'}});
    if (!$nodes) { return }

    # Setup
    my $CMD_LIVE = qq[ 2>&1 ${SUDO} isi_for_array -n $nodes '${SUDO} /usr/bin/isi_hwtools/isi_ipmicmc -d -V -a cmc' | grep firmware ];
    my $CMD_LOGS = q[ 2>&1 grep -H firmware */cmc_info ];
    my $RE_VERSION = qr/IPMI firmware version = (.*)$/;

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Store
    foreach my $lnn (@{$CLUSTER_INFO{'Gen5'}}) {
	if (!$gather->{$lnn}) { next }
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ $RE_VERSION) {
		$NODE_INFO{$lnn}{'Firmware'}{'CMC'} = $1;
		debug("NODE_INFO->${lnn}->Firmware->CMC=$1");
	    }
	}
    }
}
$CHECKS{'checkKB519119'}{'Description'} = "Checks if nodes may be impacted by KB 519119";
sub checkKB519119 {
    return if wasCalled();
    return unless isLive();
    gatherProductInfo();
    return unless $CLUSTER_INFO{'Gen6_NMLK'};
    printCheck("KB519119");

	

    # Gather information from Gen6 nodes
	#my $gen6_node = compressRange(@{$CLUSTER_INFO{'Gen6'}});
    my $gather = gatherCluster
	(qq[2>&1 ${SUDO} isi_for_array -n ].compressRange(@{$CLUSTER_INFO{'Gen6_NMLK'}}).
	 qq[ 'echo; ].
	 qq[ echo PS0-A; 2>&1 ${SUDO} /usr/bin/isi_hwtools/isi_ipmicmc -V -a bmc -R 30b005020000ff10f50050; ].
	 qq[ echo PS0-B; 2>&1 ${SUDO} /usr/bin/isi_hwtools/isi_ipmicmc -V -a bmc -R 30b005020000ff10f50060; ].
	 qq[ echo PS0-C; 2>&1 ${SUDO} /usr/bin/isi_hwtools/isi_ipmicmc -V -a bmc -R 30b005020000ff10f50070; ].
	 qq[ echo PS1-A; 2>&1 ${SUDO} /usr/bin/isi_hwtools/isi_ipmicmc -V -a bmc -R 30b006020000ff10f50050; ].
	 qq[ echo PS1-B; 2>&1 ${SUDO} /usr/bin/isi_hwtools/isi_ipmicmc -V -a bmc -R 30b006020000ff10f50060; ].
	 qq[ echo PS1-C; 2>&1 ${SUDO} /usr/bin/isi_hwtools/isi_ipmicmc -V -a bmc -R 30b006020000ff10f50070 ].
	 qq[ ' ]);

    # Expected RAW IPMI responses
    my %expected =
	("PS0-A" => "b0 00 10 4d 46 52 5f 52 45 53 50 2a 00 00 40 00 00 00 18",
	 "PS0-B" => "b0 00 10 4d 46 52 5f 43 41 4c 90 2a 00 00 00 01 00 00 31",
	 "PS0-C" => "b0 00 10 4d 46 52 5f 46 57 55 90 2b 00 00 20 d4 00 00 1b",
	 "PS1-A" => "b0 00 10 4d 46 52 5f 52 45 53 50 2a 00 00 40 00 00 00 18",
	 "PS1-B" => "b0 00 10 4d 46 52 5f 43 41 4c 90 2a 00 00 00 01 00 00 31",
	 "PS1-C" => "b0 00 10 4d 46 52 5f 46 57 55 90 2b 00 00 20 d4 00 00 1b");

    # Analyze each node for issues
    my %nodes = ();
    foreach my $lnn (@{$CLUSTER_INFO{'Gen6_NMLK'}}) {
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	my $cmd = "";
	my $response = "";
	my $valid_responses = 0;
	foreach my $line (@{$gather->{$lnn}}) {
	    # Gather which PS is being checked
	    $cmd = $1 if $line =~ /(PS[01]-[ABC])/;
	    next unless $cmd and $expected{$cmd};

	    # Gather raw ipmi response and compare to expected response
	    if ($line =~ /^\s*((?:[0-9a-f]{2}\s)*[0-9a-f]{2})$/) {
		$response .= ($response?" $1":$1);
	    } else {
		$response = "";
	    }
	    next unless $response and $expected{$cmd} eq $response;

	    # Increment the number of valid responses found for this node
	    $cmd = "";
	    $response = "";
	    $valid_responses++;
	}

	# If there was not 6 responses, the EEPROM may be corrupted
	debug("checkKB519119: Number of valid responses found on node $lnn: $valid_responses");
	if ($valid_responses ne 6) {
	    $nodes{$lnn} = 1;
	}
    }

    # Flag if issues detected
    if (%nodes) {
	fail("The following nodes may be impacted by KB000057263 (https://www.dell.com/support/kbdoc/000057263): ".compressRange(keys %nodes));
	fail("Please open a service request for replacement of affected Power Supplies");
	#fail("Nodes idenfitied as affected by KB 519119 should have the impacted power supply replaced prior to performing any node firmware updates.");
    }

    # Output Check result
    printResults();
} # End checkKB519119
$CHECKS{'checkKB540513'} = {
    'Description' => 'Checks for criteria of KB 000174074',
    'Exclude' => 1,
};
$MSG{'checkKB540513_NODES_1'} = 'The following nodes have serial numbers which will cause OneFS upgrades to OneFS 8.1 releases to fail during the upgrade commit: %s';
$MSG{'checkKB540513_FIXED'} = 'This issue has been addressed in the February 2020 pre-patched installation file for OneFS 8.1.2.0. If performing an upgrade to OneFS 8.1.2.0, ensure you utilize a pre-patched installation file dated after February 2020';
sub checkKB540513 {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen6'}) { return unsupported('UNSUP_GEN6') }
    if (isCurrentOneFS('>=8.2') or isCurrentOneFS('<8.1')) {
	return unsupported('UNSUP_CUR');
    }
    if (!isDestinationOneFS('<8.2')) {
	return unsupported('UNSUP_TGT_1', 'OneFS 8.1 releases');
    }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'cat /etc/isilon_serial_number'];
    my $CMD_LOGS = q[2>&1 grep -H . */etc.tar/etc/isilon_serial_number];
    my $RE_SN_GOOD = qr/^\S{5}[0-9]{9}$/;
    printCheck('KB 540513');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my @nodes = ();
    foreach my $lnn (@{$CLUSTER_INFO{'Gen6'}}) {
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	my ($match) = grep {/$RE_SN_GOOD/} @{$gather->{$lnn}};
	if (!$match) { push(@nodes, $lnn) }
    }

    # Flag
    if (@nodes) {
	if (isDestinationOneFS('8.1.2.0')) {
	    critical(sprintf($MSG{'checkKB540513_NODES_1'}, compressRange(@nodes)));
	    critical($MSG{'checkKB540513_FIXED'});
	} else {
	    fail(sprintf($MSG{'checkKB540513_NODES_1'}, compressRange(@nodes)));
	}
	info(sprintf($MSG{'KB_1'}, '000174074'));
    }

    # Output
    printResults();
}
$CHECKS{'checkKB540901'} = {
    'Description' => 'Checks boot disk partitions for any mismatches in uuids which may lead to boot failures',
    'Reference' => 'https://www.dell.com/support/kbdoc/000171641',
};
sub checkKB540901 {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen6'}) { return unsupported('UNSUP_GEN6') }
    printCheck('KB 540901');
    my $CMD_LOGS = 'grep -E "<(name|type|rawuuid)>" */kern.geom | grep -C1 bootdiskid';
    my $CMD_NODE = 'sysctl kern.geom | grep -E "<(name|type|rawuuid)>" | grep -C1 bootdiskid';
    my $SPF_NODES_AT_RISK = 'The following nodes have boot disk partitions with different uuids which may lead to boot failures: %s';

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array '$CMD_NODE']);
    } else {
	$gather = gatherCluster(qq[2>&1 $CMD_LOGS]);
    }

    # Analyze information for issues.
    my @nodes_at_risk = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	my %uuids = ();
	foreach (@{$gather->{$lnn}}) {
	    if (/<rawuuid>.*?-([^-]+)</) { $uuids{$1}++ }
	}
	if (scalar(keys %uuids) > 1) {
	    push(@nodes_at_risk, $lnn);
	}
    }

    # Flag any issues identified
    if (@nodes_at_risk) {
	fail(sprintf($SPF_NODES_AT_RISK, compressRange(@nodes_at_risk)));
	info(sprintf($MSG{'KB_INT_1'}, '000171641'));
    }

    # Output check results
    printResults();
}
$CHECKS{'checkKB546604'} = {
    'Description' => 'Checks for criteria of KB 000180866',
    'Reference' => 'https://www.dell.com/support/kbdoc/000180866',
};
sub checkKB546604 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8.2')) { return unsupported('UNSUP_CUR') }
    if (!isDestinationOneFS('>=9') or isDestinationOneFS('>=9.1.0.5')) {
	return unsupported('UNSUP_TGT_1', '9.0.0.0, 9.1.0.0, 9.1.0.1, 9.1.0.2, 9.1.0.3, or 9.1.0.4');
    }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen6'}) { return unsupported('UNSUP_GEN6') }
    gatherNetworkInterfaces();
    if (!$CLUSTER_INFO{'Active IB Interfaces'}) {
	return unsupported('UNSUP_BE_IB');
    }

    # Setup
    printCheck('KB 546604');
    my $SPF_NODES = "During the upgrade from $CLUSTER_INFO{'OneFS'} to".
	" $RPS{'Target'}, the following nodes will lose front end connectivity".
	' on the mlxen network interfaces until the upgrade has been'.
	' committed: %s. To avoid this, it is recommended to upgrade to a'.
	" OneFS 8.2 release as an interim jump to get to $RPS{'Target'}.".
	' This issue has also been addressed in OneFS 9.1.0.5. Consider'.
	' instead upgrading to the latest OneFS 9.1.0 patch release.';

    # Analyze
    my @nodes = ();
    foreach my $lnn (@{$CLUSTER_INFO{'Gen6'}}) {
	if (!$NODE_INFO{$lnn}{'Network'}) { next }
	my ($match) = grep(/mlxen[01]/, keys %{$NODE_INFO{$lnn}{'Network'}});
	if ($match) { push(@nodes, $lnn) }
    }

    # Flag
    if (@nodes) {
	my $msg = sprintf($SPF_NODES, compressRange(@nodes));
	addCustomerInformation('Upgrade Plan', critical($msg));
	$RPS{'KB546604'} = 1;
    }

    # Output
    printResults();
}
$CHECKS{'checkVaultCard'} = {
    'Description' => 'Checks for the presence of the M.2 vault card in Generation 6 nodes and confirms SMART status threshold has not been exceeded on that device',
    'Reference' => 'https://www.dell.com/support/kbdoc/000082048',
};
sub checkVaultCard {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen6'}) { return unsupported('UNSUP_GEN6') }
    printCheck('Vault Card');
    checkVaultCardCamcontrol();
    checkVaultCardSMARTStatus();
    if (isResults(&INFO)) { info(sprintf($MSG{'KB_1'}, '000082048')) }
    printResults();
} # End checkVaultDrive
$MSG{'checkVaultCard_CAMCONTROL_1'} = 'The vault card located at ada0 in the following Generation 6 nodes are not reporting in the output of camcontrol devlist: %s';
sub checkVaultCardCamcontrol {
    if (wasCalled()) { return }
    if (isLive() and isCompliance() and !$RPS{'JSON'}) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen6'}) { return }
    my $NODES = compressRange(@{$CLUSTER_INFO{'Gen6'}});
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -n $NODES '${SUDO} camcontrol devlist' | grep ada0];
    my $CMD_LOGS = q[2>&1 grep -H ada0 */camcontrol];

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my @nodes = ();
    foreach my $lnn (@LNNS) {
	if ($NODE_INFO{$lnn}{'Generation'} ne 'Gen6') { next }
	if ($gather->{$lnn}) {
	    my @results = grep /\(.*ada0.*\)$/, @{$gather->{$lnn}};
	    if (@results) { next }
	}
	push(@nodes, $lnn);
    }
    if (@nodes) { fail(sprintf($MSG{'checkVaultCard_CAMCONTROL_1'}, compressRange(@nodes))) }
}
$MSG{'checkVaultCard_SMART_1'} = 'The vault card located at ada0 in the following Generation 6 nodes are reporting SMART status is threshold exceeded: %s';
sub checkVaultCardSMARTStatus {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen6'}) { return }
    my $NODES = compressRange(@{$CLUSTER_INFO{'Gen6'}});
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -n $NODES '${SUDO} isi_radish -a ada0' | grep 'SMART status'];
    my $CMD_LOGS = q[2>&1 grep -H 'SMART status' */isi_radish_bootdisk];

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my @nodes = ();
    foreach my $lnn (@LNNS) {
	if ($NODE_INFO{$lnn}{'Generation'} ne 'Gen6') { next }
	if (!$gather->{$lnn}) { next }
	my @results = grep /threshold exceeded.*\(.*ada0.*\)$/, @{$gather->{$lnn}};
	if (@results) { push(@nodes, $lnn) }
    }
    if (@nodes) { fail(sprintf($MSG{'checkVaultCard_SMART_1'}, compressRange(@nodes))) }
}
$CHECKS{'checkKB000081658'} = {
    'Description' => 'Checks for criteria of KB 000081658',
    'Reference' => 'https://www.dell.com/support/kbdoc/000081658',
};
$MSG{'checkKB000081658_FAIL_1'} = 'This cluster will remain at risk for KB 000081658 following an upgrade to OneFS %s. Consider upgrading to a OneFS release that is not at risk.';
$MSG{'checkKB000081658_WARN'} = 'The node types and OneFS version put this cluster at risk for KB 000081658';
sub checkKB000081658 {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen6.5'}) {
	return unsupported('UNSUP_HW_1', 'F200, F600, and F900');
    }
    my $RE_AFFECTED = qr/^9\.0\.0\.0$|^9\.1\.0\.(?:[0-9]|1[0-9]|2[0-1])$|^9\.1\.1\.[0-1]$|^9\.2\.0\.[01]$|^9\.2\.1\.(?:[0-9]|1[0-2])$|^9\.3\.0\.[0-6]$|^9\.4\.0\.[0-2]$/;
    if (!isCurrentOneFS($RE_AFFECTED) and !isDestinationOneFS($RE_AFFECTED)) {
	return unsupported('UNSUP_NO_RISK');
    }

    # Start
    printCheck('KB 000081658');

    # Flag
    if (isCurrentOneFS($RE_AFFECTED)) {
	warning($MSG{'checkKB000081658_WARN'});
    }
    if (isDestinationOneFS($RE_AFFECTED)) {
	fail(sprintf($MSG{'checkKB000081658_FAIL_1'}, isDestinationOneFS()));
    }
    info(sprintf($MSG{'KB_1'}, '000081658'));

    # Output
    printResults();
}
$CHECKS{'checkKB000314255'} = {
    'Description' => 'Checks 14G and 16G nodes that contain P5316 drives, if so then node firmware must be updated to 12.4.2 or higher prior to updating drive fimrware.',
    'Reference' => '',
};

sub checkKB000314255 {
    if (wasCalled()) { return }
    gatherProductInfo();
    checkNodeFirmware();
    checkDriveFirmware();
    #if(!$CLUSTER_INFO{'14G_NODES'} and !$CLUSTER_INFO{'16G_NODES'}){ return }

    # Setup
    printCheck('KB 000314255');
    gatherKB000314255();
    
    # Flag
    if($CLUSTER_INFO{'P5316_SED'} and ($RPS{'Node Firmware'}{'Nodes'} or $RPS{'Drive Support Package'}{'Nodes'})){
        $CLUSTER_INFO{'000314255'} = 1;
        addCustomerInformation(critical("Please ensure you update node firmware to 12.4.2 or higher prior to performing any drive updates on the following nodes which have Solidigm P5316 drives: ".compressRange(@{$CLUSTER_INFO{'P5316_SED'}})));
        info("Please reference https://www.dell.com/support/kbdoc/000314255 for additional information.");
    }

    printResults();
} # End checkKB000314255


$CHECKS{'checkKB201933'} = {
    'Description' => 'Checks for criteria of KB 000201933',
    'Reference' => 'https://www.dell.com/support/kbdoc/000201933',
};
$MSG{'checkKB201933_FAIL'} = 'This cluster is at risk for encountering KB 000201933 following an upgrade to OneFS 9.1.0.19, 9.1.0.20, or 9.1.0.21 releases.  Consider upgrading to OneFS 9.2.1 or newer releases instead';
sub checkKB201933 {
    if (wasCalled()) { return }
    my $RE_AFFECTED = qr/^9\.1\.0\.(?:19|20|21)$/;
    if (isCurrentOneFS('>=9.1.0.22')) {	return unsupported('UNSUP_CUR') }
    if (!isDestinationOneFS($RE_AFFECTED)) {
	return unsupported('UNSUP_TGT_1', '9.1.0.19, 9.1.0.20, or 9.1.0.21');
    }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen6.5'}) {
	return unsupported('UNSUP_HW_1', 'F600 and F200');
    }

    # Start
    printCheck('KB 201933');

    # Flag
    fail($MSG{'checkKB201933_FAIL'});
    info(sprintf($MSG{'KB_1'}, '000201933'));

    # Output
    printResults();
} # End checkKB201933
sub gatherKB000314255 {
    if (wasCalled()) { return }
    gatherProductInfo();
    checkNodeFirmware();
    checkDriveFirmware();
    #if(!$CLUSTER_INFO{'14G_NODES'} and !$CLUSTER_INFO{'16G_NODES'}){ return }

    # Setup
    my @nts;
    push(@nts, @{$CLUSTER_INFO{'16G_NODES'}});
    push(@nts, @{$CLUSTER_INFO{'14G_NODES'}});
    debug("14G & 16G combined node list: ".compressRange(@nts));
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array isi_drivenum -A];
    my $CMD_LOGS = q[2>&1; grep -H '.*' */isi_drivenum];
  #  printCheck('KB 000314255');

    my @nodes;
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    foreach my $lnn (@LNNS) {
        debug("checking $lnn with result: @{$gather->{$lnn}}");
        if(defined($RPS{'Current_FW_revisions'}{$lnn}{'Integrated Remote Access Controller'}) and $RPS{'Current_FW_revisions'}{$lnn}{'Integrated Remote Access Controller'} ne ''){
           if(($NODE_INFO{$lnn}{'Node Type'} =~ /^(F[279]10)$/ and compareVersions($RPS{'Current_FW_revisions'}{$lnn}{'Integrated Remote Access Controller'}, '7.10.90.00') >= 0)
           or
           ($NODE_INFO{$lnn}{'Node Type'} =~ /^(F[69]00)$/ and compareVersions($RPS{'Current_FW_revisions'}{$lnn}{'Integrated Remote Access Controller'}, '7.00.00.174') >= 0)){
            debug("Node Type $1 has current iDRAC version of $RPS{'Current_FW_revisions'}{$lnn}{'Integrated Remote Access Controller'} - Skipping as the current version is >= the minimum required for firmware updates to P5316 SED drives.");
            next;
           }
        }
        foreach $l (@{$gather->{$lnn}}){
                my $bay;
            if($l =~ /^\s*Bay\s+(\S+).*P5316.*$/){
                    $bay = $1;
                debug("AFFECTED DRIVE FOUND: P5316 Type: $2 LNN: $lnn BAY: $1 SIZE: ".int($3));
                if(int($3) >= 15){
                    debug("AFFECTED DRIVE FOUND: P5316 LNN: $lnn BAY: $1 SIZE: ".int($3)."TB");
                    push(@nodes, $lnn);
                }
                if($l =~ /^.*SED\s+P5316.*$/){
                        debug("Drive Type is SED, adding to list for additional checks");
                    push(@{$CLUSTER_INFO{'P5316_SED'}}, $lnn) unless grep { $_ eq $lnn } @{$CLUSTER_INFO{'P5316_SED'}};
                    debug("pushing bay $bay");
                    push(@{$NODE_INFO{$lnn}{'P5316_SED_bays'}}, $bay);
                    if($l =~ /^.*(Dell.*)\s*FW:(\S+).*$/){
                        debug("Found Dell Drive \'$1\'with firmware $2");
                        push(@{$NODE_INFO{$lnn}{'P5316_SED_types'}{$bay}}, $1);
                        push(@{$NODE_INFO{$lnn}{'P5316_SED_fw'}{$bay}}, $2);
                    }
                }
            }

        }
    }

#    printResults();
} # End gatherKB000314255


# gatherm2
sub gatherm2 {
    if (wasCalled()) { return }
    if (!$CLUSTER_INFO{'Gen6'} and !$CLUSTER_INFO{'14G_NODES'} and !$CLUSTER_INFO{'16G_NODES'} and !$CLUSTER_INFO{'Pifano'}) { return }

    my @combined = (
        @{ $CLUSTER_INFO{'Gen6'} },
        @{ $CLUSTER_INFO{'14G_NODES'} },
        @{ $CLUSTER_INFO{'16G_NODES'} },
        @{ $CLUSTER_INFO{'Pifano'} },
    );
    my %seen;
    my @nodes = grep { !$seen{$_}++ } @combined;
    my $nodes = compressRange(@nodes);
    if (!$nodes) { return }

    # Setup
    my $CMD_LIVE = qq[ 2>&1 ${SUDO} isi_for_array sysctl kern.geom | grep 'M\\.2' ];
    my $CMD_LOGS = q[ 2>&1 grep -H 'M\.2' */kern.geom ];

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Store
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
    foreach my $line (@{$gather->{$lnn}}) {
        debug("Checking result: $line");
	    if ($line =~ /\s*<descr>.*(NVMe)\s*(FIPS|ISE)\s*(\d+).*<\/descr>\s*/) {
		my $m2_v_card = 'M.2-'.$1.'-'.$3.'-'.$2;
        $NODE_INFO{$lnn}{'m.2'} = $m2_v_card;
        debug("Storing vault card <$m2_v_card> from kernel info for node $lnn");
	    }
	}
    }
}
# End gatherm2
$CHECKS{'checkA100Root'} = {
    'Description' => 'Checks if A100 node root mirrors need to be resized to 2GB',
    'Reference' => 'https://www.dell.com/support/kbdoc/000196854',
};
$MSG{'checkA100Root_SCRIPT_1'} = 'The following A100 nodes have 1GB root mirrors that need to be resized to 2GB using the scripted solution: %s';
$MSG{'checkA100Root_MANUAL_1'} = 'The following A100 nodes have 1GB root mirrors that need to be manually resized to 2GB: %s';
$MSG{'checkA100Root_COMPLIANCE_1'} = 'The following A100 nodes have 1GB root mirrors that need to be manually resized to 2GB due to the cluster being in Compliance mode: %s';
sub checkA100Root {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'A100'}) { return unsupported('UNSUP_HW_1', 'A100') }
    if (isCurrentOneFS('<8')) {	return unsupported('UNSUP_CUR') }
    checkPartitions();

    # Setup
    my $CMD_LOGS = q[2>&1 grep -h . *-%s/gpart_show];
    my $CMD_LIVE = q[2>&1 isi_for_array -Q -n%s 'gpart show'];

    printCheck('A100 Node Root Mirrors');

    # Check which nodes require resizing
    my @needs_resizing = ();
    foreach my $lnn (@{$CLUSTER_INFO{'A100'}}) {
	if ($RPS{'Root less than 1GB'}{$lnn}) {
	    push(@needs_resizing, $lnn);
	}
    }

    # Pass if no nodes needs to be resized
    if (!@needs_resizing) {
	printResults();
	return;
    }

    # Require manual fix on compliance mode clusters
    if (isCompliance()) {
	fail(sprintf($MSG{'checkA100Root_COMPLIANCE_1'}, compressRange(@needs_resizing)));
	printResults();
	return;
    }

    # Gather and Analyze each node
    my @require_manual = ();
    my @support_script = ();
    foreach my $lnn (@needs_resizing) {
	# Gather
	my $gather = gatherLocal(sprintf(isLive()?$CMD_LIVE:$CMD_LOGS, $lnn));
	if (!$gather) {
	    missing($lnn);
	    next;
	}

	# Check if the node has a valid number of root partitions
	my ($aref_root0, $aref_root1) = checkA100RootPartitions($lnn);
	if ($aref_root0 == -1) {
	    missing($lnn);
	    next;
	}
	if (scalar(@$aref_root0) != 2 or scalar(@$aref_root1) != 2) {
	    push(@require_manual, $lnn);
	    next;
	}

	# Check if the existing root partitions are index 4 and 5
	if (!checkA100RootIndexes($aref_root0, $aref_root1)) {
	    push(@require_manual, $lnn);
	    next;
	}

	# Check if the partition indexes 13/14 are available
	if (!checkA100RootFreeIndexes($gather)) {
	    push(@require_manual, $lnn);
	    next;
	}

	# Check if the node has sufficient free space for scripted solution
	if (!checkA100RootFreeSpace($gather)) {
	    push(@require_manual, $lnn);
	    next;
	}
	push(@support_script, $lnn);
    }
    if (@require_manual) {
	fail(sprintf($MSG{'checkA100Root_MANUAL_1'}, compressRange(@require_manual)));
    }
    if (@support_script) {
	critical(sprintf($MSG{'checkA100Root_SCRIPT_1'}, compressRange(@support_script)));
	addRequiredFiles('A100 Root Resize Script');
    }

    # Output
    if (isResults()) { info(sprintf($MSG{'KB_INT_1'}, '000196854')) }
    printResults();
} # End checkA100Root
sub checkA100RootPartitions {
    my ($lnn) = @_;
    if (!$lnn) { return -1 }
    my $CMD_LOGS = qq[2>&1 grep -h ^root *-${lnn}/isi_bootdisk_status];
    my $CMD_LIVE = qq[2>&1 isi_for_array -Q -n${lnn} 'isi_bootdisk_status'];
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);
    if (!$gather) { return -1 }
    my @root0 = ();
    my @root1 = ();
    foreach (@$gather) {
	if (/^root0\s+(da[12]p\d+)\s+(da[12]p\d+)$/) {
	    push(@root0, $1, $2);
	}
	if (/^root1\s+(da[12]p\d+)\s+(da[12]p\d+)$/) {
	    push(@root1, $1, $2);
	}
    }
    return \@root0, \@root1;
}
sub checkA100RootFreeSpace {
    my ($aref_output) = @_;
    my $SAFE_START = 18130944;
    my $SAFE_END = 26519552;
    my $is_da1_safe = 0;
    my $is_da2_safe = 0;
    my $disk = '';
    foreach (@$aref_output) {
	if (/\s+(da[12])\s+GPT/) { $disk = $1 }
	if (!/^\s*(\d+)\s+(\d+)\s+- free -/) { next }
	my ($start, $end) = ($1, $1 + $2);
	if ($start <= $SAFE_START and $end >= $SAFE_END) {
	    if ($disk eq 'da1') { $is_da1_safe = 1 }
	    if ($disk eq 'da2') { $is_da2_safe = 1 }
	}
    }
    if ($is_da1_safe and $is_da2_safe) { return 1 }
    return 0;
}
sub checkA100RootFreeIndexes {
    my ($aref_output) = @_;
    my ($results) = grep {/^\s*\d+\s+\d+\s+(?:13|14)\s+/} @$aref_output;
    if (!$results) { return 1 }
    return 0;
}
sub checkA100RootIndexes {
    my ($aref_root0, $aref_root1) = @_;
    my ($has_da1p4) = grep {$_ eq 'da1p4'} @$aref_root0;
    my ($has_da2p4) = grep {$_ eq 'da2p4'} @$aref_root0;
    my ($has_da1p5) = grep {$_ eq 'da1p5'} @$aref_root1;
    my ($has_da2p5) = grep {$_ eq 'da2p5'} @$aref_root1;
    if ($has_da1p4 and $has_da1p5 and $has_da2p4 and $has_da2p5) { return 1 }
    return 0;
}
$CHECKS{'checkBBUDegCap'} = {
    'Description' => 'Checks for the level of BBU degradation on Gen6 nodes and flags any nodes with excessive degradation which would increase the risk of the node going into an RO state.',
    'Reference' => '',
};
# CRITICAL MSG
$MSG{'checkBBUCapDeg_1'} = 'Do not proceed with any update to NFP >= 11.6 or 10.3.9. The following nodes have BBU units degraded and in need of a proactive replacement: %s';
# FAIL MSG
$MSG{'checkBBUCapDeg_2'} = 'The following nodes have BBU units degraded and in need of a proactive replacement: %s';

sub checkBBUDegCap {
    if (wasCalled()) { return }
    gatherProductInfo();
    gatherStat();
    gatherNodeFirmware();
    checkNodeFirmware();
    if (!$CLUSTER_INFO{'Gen6'}) { return unsupported('UNSUP_GEN6') }

    # Setup
    my $SPF = '%5s  %-10s';
    my $gen6_node = compressRange(@{$CLUSTER_INFO{'Gen6'}});
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -n $gen6_node "{ /usr/bin/isi_hwtools/isi_bbu_util | grep 'Cap deg' | tr '\\n' '\\t'; uname -n }"];
    my $CMD_LOGS = q[2>&1; for d in *-*/ ; do { echo "${d}bbu_log:"; bbu_opt=$(egrep -h 'Cap\sdeg.*$' ${d}bbu_log | awk '{print $4}' | sed 's/^[ \t]*//;s/[ \t]*$//' | grep -ve [a-zA-Z]); echo "$bbu_opt"; } | sed ':a;N;s/\n/ /;ba'; done];
    # increase command timeout for large number of nodes being checked
    printCheck('BBU Capacity');
    if(scalar(@{$CLUSTER_INFO{'Gen6'}}) > 20 && scalar(@{$CLUSTER_INFO{'Gen6'}}) < 30){
        debug("Gen6 count = ".scalar(@{$CLUSTER_INFO{'Gen6'}})." - increasing timeout.");
        $ALRM_TMOUT = 900;
    }
    my $fail_list;
    my @vrbse_lnn_lst = ();
    my @nodes;
    
    my $w_lnn = length 'LNN';
    my $w_ser = length 'Serial';
    my $w_fw  = length 'BBU Firmware';
    my $w_pct = length '% Degraded';

    my $lwr_thrshld = 435750;
    my $hgr_thrshld = 514500;

    if((isLive() && scalar(@{$CLUSTER_INFO{'Gen6'}}) < 30) || !isLive()){
        my $gather = gatherCustom(isLive()?$CMD_LIVE:$CMD_LOGS);

        # reset timeout back to default prior to proceeding with check verification
        $ALRM_TMOUT = 600;
        $fail_list.= "==============================\n"."  Degraded Backup Battery Units\n"."  ==============================\n";
        my $SPF1 = '%5s %1s %-10s %1s %-10s %1s %-10s';
        $fail_list.= sprintf($SPF1, 'LNN', ' ', 'Serial', ' ', 'BBU Firmware', ' ', '% Degraded')."\n";
        my $SPF2 = '%1s %5s %1s %-10s %1s %-10s %1s %-10s';
        $fail_list.= sprintf($SPF2, ' ', '-'x5, '-'x10,' ','-'x10,' ','-'x10)."\n";
        my $cap_deg_per;
        my $missing_node = 0;
        my %bbu_over_threshold = ();
        foreach my $lnn (@{$CLUSTER_INFO{'Gen6'}}) {
            my $cur_deg_percent = '';
            debug("checking $gather->{$lnn}");
            if (!$gather->{$lnn} || $gather->{$lnn} eq '') {
                missing($lnn);
                $missing_node = 1;
                next;
            }
            else {
                foreach $l (@{$gather->{$lnn}}){
                    debug("Node ".$lnn." Result:\t".$l);
                    my $BBU_CAP_DEG = $1 if $l =~ /^(\s?\d+\s?|\s?\d?\s*[a-zA-Z0-9_\-]+-\d+\s?)$/;
                    my $val = $BBU_CAP_DEG;
                    $val =~ s/^\s+|\s+$//g;
                    debug("Node ".$lnn." Regex Result:\t".$BBU_CAP_DEG);
                    if (!$BBU_CAP_DEG || $BBU_CAP_DEG eq ''){
                        missing($lnn);
                        $missing_node = 1;
                        push(@{$CLUSTER_INFO{'BBUCAPFAIL'}}, '1');
                        next;
                    }
                    if ($val !~ /^\s*\d+\s*$/) { debug("Skipping:\t".$val); next;}
                    $cap_deg_per = $val/1050000;
                    $cap_deg_per = $cap_deg_per*100;
                    $cap_deg_per = sprintf("%.2f", $cap_deg_per).'%';                  
                    $cur_deg_percent = $cap_deg_per;
                    if ($val >= $lwr_thrshld){
                        #BBU's exceed 41.5%
                        if($val >= $hgr_thrshld){
                            # BBU's exceed 47.5%
                            debug("FAIL (Exceeding 49%): Node $lnn\t:\t$cap_deg_per");
                            push(@nodes, $lnn);
                            push(@{$CLUSTER_INFO{'BBUCAPFAIL'}}, '1');
                        }
                        else{
                            # BBU's between 41.5% and 47.5% and FW is less that 02.40 for BBU's
                            if((exists($NODE_INFO{$lnn}{'Firmware'}{'DEeth_infinity'}) and compareVersions($NODE_INFO{$lnn}{'Firmware'}{'DEeth_infinity'}, '01.20') <= 0) or 
                            (exists($NODE_INFO{$lnn}{'Firmware'}{'EPeth_infinity'}) and compareVersions($NODE_INFO{$lnn}{'Firmware'}{'EPeth_infinity'}, '01.20') <= 0) or
                            (exists($NODE_INFO{$lnn}{'Firmware'}{'DEbem_mongoose'}) and compareVersions($NODE_INFO{$lnn}{'Firmware'}{'DEbem_mongoose'}, '01.20') <= 0) or
                            (exists($NODE_INFO{$lnn}{'Firmware'}{'EPbem_mongoose'}) and compareVersions($NODE_INFO{$lnn}{'Firmware'}{'EPbem_mongoose'}, '01.20') <= 0) or
                            (exists($NODE_INFO{$lnn}{'Firmware'}{'MLKbem_mongoose'}) and compareVersions($NODE_INFO{$lnn}{'Firmware'}{'MLKbem_mongoose'}, '01.20') <= 0) or
                            (exists($NODE_INFO{$lnn}{'Firmware'}{'MLKbem_mongoose_lw_m1b'}) and compareVersions($NODE_INFO{$lnn}{'Firmware'}{'MLKbem_mongoose_lw_m1b'}, '01.20') <= 0)){
                                # Replacement advised, firmware below min ver recommended
                                debug("FAIL (Exceeding 41.5% and FW below 02.40): Node $lnn\t:\t$cap_deg_per");
                                push(@nodes, $lnn);
                                push(@{$CLUSTER_INFO{'BBUCAPFAIL'}}, '1');
                            }
                            else{
                                # Firmware update advised as FW is at minimum of 02.40
                                debug("Update FW where needed, BBU's are on FW 02.40 or higher so current BBU degradation is ok: Node $lnn\t:\t$cap_deg_per");
                            }
                        }
                    }
                }
            }
                my $bbu_fw = $NODE_INFO{$lnn}{'Firmware'}{'EPbem_mongoose'}
                // $NODE_INFO{$lnn}{'Firmware'}{'DEbem_mongoose'}
                // $NODE_INFO{$lnn}{'Firmware'}{'EPeth_infinity'}
                // $NODE_INFO{$lnn}{'Firmware'}{'DEeth_infinity'}
                // $NODE_INFO{$lnn}{'Firmware'}{'MLKbem_mongoose'}
                // $NODE_INFO{$lnn}{'Firmware'}{'MLKbem_mongoose_lw_m1b'};
                push(@vrbse_lnn_lst, [$lnn, $NODE_INFO{$lnn}{'Serial'}, $bbu_fw, $cur_deg_percent]);
                $w_lnn = length($lnn) if length($lnn) > $w_lnn;
                $w_ser = length($NODE_INFO{$lnn}{'Serial'}) if length($NODE_INFO{$lnn}{'Serial'}) > $w_ser;
                $w_fw  = length($bbu_fw) if length($bbu_fw) > $w_fw;
                $w_pct  = length($cur_deg_percent) if length($cur_deg_percent) > $w_pct;
        }
    
        # Flag
        if($missing_node){
            info("Please reference KB 000206651 (https://www.dell.com/support/kbdoc/000206651) for instructions on how to manually run a check test.");
        }
        if (@nodes || @{$CLUSTER_INFO{'BBUCAPFAIL'}}) {
            debug('FAIL TRIGGERED FOR GENERIC');
            if ($RPS{'Node Firmware'}{'Nodes'} and (compareVersions($RPS{'IsiFw'}, '11.6') >= 0 || (compareVersions($RPS{'IsiFw'}, '11.0') < 0 && compareVersions($RPS{'IsiFw'}, '10.3.9') >= 0))){
                fail('There are nodes affected by DTA 000207476 (https://www.dell.com/support/kbdoc/en-ie/000207476). Checks have identified degraded batteries on nodes '.compressRange(@nodes).'. Firmware upgrade to 10.3.9 or 11.6 or higher cannot proceed until degraded BBU\'s are replaced.');
                addCustomerInformation('Degraded batteries on nodes '.compressRange(@nodes).' need to be replaced prior to updating node firmware.');
                info("Additional commands that can be used to onbtain structured output: isi_for_array -Q 'isi_nodes -L \"%{name},\$(isi_hw_status -i | grep Prod | cut -d \" \" -f2  ),%{serialno},\$(isi upgrade node firmware devices view \$( isi_nodes -L %{lnn} ) | grep -e mongoose -e bcc | tr -s \" \" | cut -d\" \"  -f 3),\$(/usr/bin/isi_hwtools/isi_bbu_util | grep -oE \"Cap deg.*\") \" '  | awk -F: ' { print $1 $2/1050000*100.\"%\" } '");
                info('If the manual checks fail, please open a Technical Support Service Request and reference this failure within the description.');
            }
        }
    }
    else{
        if(isLive()){
        debug("Gen6 count = ".scalar(@{$CLUSTER_INFO{'Gen6'}})." - disabling this check on live systems and manual steps required.");
        push(@{$CLUSTER_INFO{'BBUCAPFAIL'}}, '1');
        critical('Please run checks outlined on DTA 000207476 (https://www.dell.com/support/kbdoc/en-ie/000207476) to verify BBU capacity health for your Gen6 nodes '.$gen6_node.'.');
        info("Additional commands that can be used to onbtain structured output: isi_for_array -Q 'isi_nodes -L \"%{name},\$(isi_hw_status -i | grep Prod | cut -d \" \" -f2  ),%{serialno},\$(isi upgrade node firmware devices view \$( isi_nodes -L %{lnn} ) | grep -e mongoose -e bcc | tr -s \" \" | cut -d\" \"  -f 3),\$(/usr/bin/isi_hwtools/isi_bbu_util | grep -oE \"Cap deg.*\") \" '  | awk -F: ' { print $1 $2/1050000*100.\"%\" } '");
        addCustomerInformation('Gen 6 nodes in this cluster may be affected by DTA 000207476 (https://www.dell.com/support/kbdoc/en-ie/000207476). Please run checks documented in the DTA to verify the BBU capacity health on nodes '.$gen6_node.'.');
        }
    }

    #printResults();

    verbose("==============================\n"."  Backup Battery Unit Summary\n"."  ==============================\n") if @vrbse_lnn_lst;

my $hdr_fmt = "  %-*s  %-*s  %-*s  %-*s\n";

verbose(sprintf(
    $hdr_fmt,
    $w_lnn, 'LNN',
    $w_ser, 'Serial',
    $w_fw,  'BBU Firmware',
    $w_pct, '% Degraded',
));

verbose(sprintf(
    $hdr_fmt,
    $w_lnn, '-' x $w_lnn,
    $w_ser, '-' x $w_ser,
    $w_fw,  '-' x $w_fw,
    $w_pct, '-' x $w_pct,
));

my $row_fmt = "  %-*s  %-*s  %-*s  %-*s\n";

foreach my $r (@vrbse_lnn_lst) {
    
my $pct = defined $r->[3]
        ? sprintf('%.1f%%', $r->[3])
        : '';

    verbose(sprintf(
        $row_fmt,
        $w_lnn, $r->[0],
        $w_ser, $r->[1],
        $w_fw,  $r->[2],
        $w_pct, $pct,
    ));
}



printResults();

    #print $fail_list ;# if @nodes;
} # End checkBBUCapDeg
$CHECKS{'checkBootDisks'} = {
    'Description' => 'Checks boot disk wear life remaining, firmware level, and historic error count',
    'Exclude' => 1,
};
sub checkBootDisks {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen5'} and !$CLUSTER_INFO{'Gen4'}) {
	return unsupported('UNSUP_GEN45');
    }

    # Setup
    printCheck('Boot Disks');
    my $CMD_LOGS = q[2>&1 grep -H . */isi_radish_bootdisk];
    my $CMD_LIVEOLD = qq[2>&1 ${SUDO} isi_for_array 'isi_radish -a].
	q[ "Internal J3" "Internal J4" "Carrier board J3" "Carrier board J4"'];
    my $CMD_LIVENEW = qq[2>&1 ${SUDO} isi_for_array 'isi_radish -a ada0 ada1'];
    my $RE_BD = qr/^\s*(?:Internal|Carrier board)?\s*(\S+\/\S+)\s+is\s+(.*)$/i;
    my $RE_LIFE =
	qr/(?:Percent Life Remaining|Percent Lifetime Left).*raw (\S+)\)/i;
    my $RE_ERROR = qr/error index\s+\d+\s+historic error count\s+(\d+)$/i;
    my $SPF_LIFE = 'Boot disk %s on node %s has %s life remaining';
    my $SPF_ERROR = 'Boot disk %s on node %s has a historic error count of %s';
    my $SPF_FW = 'The following boot devices are running firmware version'.
	' 7.02k which is impacted by KB 000062979: %s';

    # Gather
    my $gather = ();
    if (isLive()) {
	if (isCurrentOneFS('<8.1')) {
	    $gather = gatherCluster($CMD_LIVEOLD);
	} else {
	    $gather = gatherCluster($CMD_LIVENEW);
	}
    } else {
	$gather = gatherCluster($CMD_LOGS);
    }

    # Analyze
    my @fw_702k = ();
    foreach my $lnn (@LNNS) {
	if (!$NODE_INFO{$lnn}{'Generation'} or
	    ($NODE_INFO{$lnn}{'Generation'} ne 'Gen5' and
	     $NODE_INFO{$lnn}{'Generation'} ne 'Gen4')) {
	    next;
	}
	verboseHeader("$NODE_INFO{$lnn}{'Name'} - Boot Disks");
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    verbose('Missing');
	    next;
	}
	my $bd_location = '';
	my $bd_details = '';
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ $RE_BD) {
		($bd_location, $bd_details) = ($1, $2);
		verbose("Boot Device $bd_location:");
		if ($bd_details =~ /^(.*?) FW:/) {
		    verbose("    Model: $1");
		}
		if ($bd_details =~ /SN:([^,]+)/) {
		    verbose("    Serial Number: $1");
		}
		if ($bd_details =~ /FW:(.*?) SN/) {
		    verbose("    Firmware: $1");
		}
		if ($bd_details =~ /FW:.*7\.02k/) {
		    push(@fw_702k, "Node $lnn $bd_location");
		}
	    }
	    if ($line =~ $RE_LIFE) {
		my $life = hex $1;
		verbose("    Wear Life Remaining: $life\%");
		if ($life <= 5) {
		    fail(sprintf($SPF_LIFE, $bd_location, $lnn, "$life%"));
		} elsif ($life <= 10) {
		    warning(sprintf($SPF_LIFE, $bd_location, $lnn, "$life%"));
		}
	    }
	    if ($line =~ $RE_ERROR) {
		verbose("    Historic Error Count: $1");
		if ($1 ne '0000') {
		    warning(sprintf($SPF_ERROR, $bd_location, $lnn, $1));
		}
	    }
	}
    }

    # Flag
    if (@fw_702k) { warning(sprintf($SPF_FW, join(', ', @fw_702k))) }
    if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000045857')) }

    # Output
    printResults();
} # End checkBootDisks
$CHECKS{'checkCMOSTimeCentury'} = {
    'Description' => 'Check if the century configured in CMOS time matches the current century',
    'Reference' => 'https://www.dell.com/support/kbdoc/000198655',
};
$MSG{'checkCMOSCentury_FAIL_1'} = 'The century in the CMOS time does not match the current century on the following nodes: %s';
sub checkCMOSTimeCentury {
    if (wasCalled()) { return }
    if (!isLive()) { return unsupported('UNSUP_LOGS') }
    if (isCompliance() and !$RPS{'JSON'}) { return unsupported('UNSUP_COMP') }
    if (isCurrentOneFS('<8')) {	return unsupported('UNSUP_CUR') }

    # Setup
    my $CMD_LIVE = q[isi_for_array '/usr/bin/isi_hwtools/bioscmos_dump -Hc2 | paste -s - | tr -d "[:blank:]" | cut -c 67-68'];
    my $CENTURY = substr((localtime())[5] + 1900, 0, 2);
    my $RE_CENTURY = qr/^\s*(\d{2})\s*$/;

    # Check start
    printCheck('CMOS Time - Century');

    # Gather
    my $gather = gatherCluster($CMD_LIVE);

    # Analyze
    my @nodes = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	my $century;
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ $RE_CENTURY) { $century = $1 }
	}
	if (!$century) {
	    missing($lnn);
	} elsif ($century ne $CENTURY) {
	    push(@nodes, $lnn);
	}
    }

    # Flag
    if (@nodes) {
	fail(sprintf($MSG{'checkCMOSCentury_FAIL_1'}, compressRange(@nodes)));
	info(sprintf($MSG{'KB_INT_1'}, '000198655'));
    }

    # Output
    printResults();
} # End checkCMOSTimeCentury
$CHECKS{'checkDriveLoad'} = {
    'Description' => 'Checks the current load on the drives',
    'Reference' => 'https://www.dell.com/support/kbdoc/000015384',
};
$MSG{'checkDriveLoad_HIGHLOAD_1'} = 'High disk load detected on the following devices: %s';
sub checkDriveLoad {
    if (wasCalled()) { return }
    printCheck("Drive Load");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	if (isCurrentOneFS('<8.0')) {
	    $gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array isi statistics drive --degraded]);
	} else {
	    $gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array isi statistics drive --degraded --long]);
	}
    } else {
	$gather = gatherCluster(qq[2>&1 grep -H . */isi_statistics_drive]);
    }

    # Analyze information for issues.
    my ($bay, $typ, $tiq, $que, $max);
    if (isCurrentOneFS('<8.0')) {
	$bay = 0;
	$typ = 1;
	$tiq = 7;
	$que = 8;
	$max = 9;
    } else {
	$bay = 1;
	$typ = 2;
	$tiq = 11;
	$que = 12;
	$max = 15;
    }

    my @devices_highload = ();
    foreach my $lnn (@LNNS) {
	verboseHeader("$NODE_INFO{$lnn}{'Name'} - Drive Load");
	verbose(sprintf("%-10s %-10s %-15s %-10s", "Location", "Type", "Time-in-queue", "Queue"));
	verbose(sprintf("%-10s %-10s %-15s %-10s", "-"x10, "-"x10, "-"x15, "-"x10));
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	foreach my $line (@{$gather->{$lnn}}) {
	    $line =~ s/^\s+|\s+$//g;
	    next if $line =~ /^(LNN|Drive|Timestamp|-|Total)/;
	    my @device = split(/\s+/, $line);
	    next unless @device and $#device == $max;
	    $device[$tiq] =~ s/ms//;
	    verbose(sprintf("%-10s %-10s %-15s %-10s", $device[$bay], $device[$typ], $device[$tiq], $device[$que]));
	    if (($device[$typ] eq 'SAS' and ($device[$tiq] >= 80 or $device[$que] >= 10))
		or ($device[$typ] eq 'SATA' and ($device[$tiq] >= 100 or $device[$que] >= 5))
		or ($device[$typ] eq 'SSD' and ($device[$tiq] >= 5 or $device[$que] >= 1))) {
		push(@devices_highload, $device[$bay]);
	    }
	}
    }
    warning(sprintf($MSG{'checkDriveLoad_HIGHLOAD_1'}, join(',', @devices_highload))) if @devices_highload;

    # Output Check result
    printResults();
} # End checkDriveLoad
$CHECKS{'checkDriveStallTimeout'} = {
    'Description' => 'Checks current Drive Stall Timeout setting, recommend value is 3.5 seconds (3500000 microseconds) or higher',
    'Exclude' => 1,
};
sub checkDriveStallTimeout {
    return if wasCalled();
    printCheck("Drive Stall Timeout");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array /sbin/sysctl hw.disk_event.thresh.slowacc_usec]);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -H hw.disk_event.thresh.slowacc_usec */hw.disk_event]);
    }

    # Analyze information for issues.
    my $min_recommended_threshold = 3500000;
    my @nodes_less_than_threshold = ();
    verboseHeader("Drive Stall Timeout");
    verbose(sprintf("%-10s %-10s", "Node", "Drive Stall Timeout Setting (microseconds)"));
    verbose(sprintf("%-10s %-10s", "-"x10, "-"x10));
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	my $threshold = 0;
	foreach my $line (@{$gather->{$lnn}}) {
	    next unless $line =~ /hw.disk_event.thresh.slowacc_usec: (\d+)$/;
	    $threshold = $1;
	    verbose(sprintf("%-10s %-10s", $lnn, $threshold));
	}
	if ($threshold and $threshold < $min_recommended_threshold) {
	    push(@nodes_less_than_threshold, $lnn);
	}
    }
    if (@nodes_less_than_threshold) {
	warning("Drive stall timeout is not set to 3.5 seconds on the following nodes: ".compressRange(@nodes_less_than_threshold),
		sprintf($MSG{'KB_1'}, '000052229'));
    }
    printResults();
} # End checkDriveStallTimeout
$CHECKS{'checkDrivesHealth'} = {
    'Description' => 'Checks health of drives and the drive stall timeout setting in sysctl',
    'Reference' => 'https://www.dell.com/support/kbdoc/000035312',
};
$MSG{'checkDrivesHealth_DRIVE_3'} = 'Node %s %s is %s';
sub checkDrivesHealth {
    if (wasCalled()) { return }
    printCheck("Drives Health");
    gatherProductInfo();

    # Gather device information
    my $gather = ();
    if (isCurrentOneFS('<8.0') and isLive()) {
	$gather = gatherCluster("2>&1 ${SUDO} isi_for_array isi devices|grep -v HEALTHY");
    } elsif (isCurrentOneFS('<8.0')) {
	$gather = gatherCluster("2>&1 grep -H -v HEALTHY */isi_devices");
    } elsif (isLive()) {
	$gather = gatherCluster("2>&1 ${SUDO} isi_for_array 'isi devices drive list|grep -v HEALTHY'");
    } else {
	$gather = gatherCluster("2>&1 grep -H -v HEALTHY */isi_devices_drive_list");
    }
    verboseHeader("Drives Health - Drives in a HEALTHY state are not reported");
    # Analyze information for issues.
    foreach my $lnn (@LNNS) {
	verboseHeader("$NODE_INFO{$lnn}{'Name'} - Drives Health");
	verbose(sprintf("%-10s %-10s", "Location", "State"));
	verbose(sprintf("%-10s %-10s", "-"x10, "-"x10));
	if (!$gather->{$lnn}) {
	    missing($lnn) unless $NODE_INFO{$lnn}{'Accelerator'};
	    next;
	}
	my $unpack_template = q[];
	my $min_length = q[];
	foreach my $line (@{$gather->{$lnn}}) {
	    next if ($line =~ /^[\s-]*$/);
	    next if ($line =~ /^\s*Total/);
	    # Strip leading whitespace
	    $line =~ s/^\s*//;
	    my ($bay, $lnum, $state) = "";
	    my @unpacked_line = ();
	    if ($line =~ /^\s*Lnn/) {
		$unpack_template = generateUnpackTemplate($line, "Lnn", "Location", "Device", "Lnum", "State", "Serial");
		$min_length = length($line);
		next;
	    } elsif ($unpack_template) {
		eval {
		    $line = sprintf("%-${min_length}s", $line);
		    @unpacked_line = unpack($unpack_template, $line);
		    $bay = $1 if ($unpacked_line[1] =~ /Bay\s+(\S+)/);
		    $lnum = $1 if ($unpacked_line[3] =~ /(\d+)/);
		    $state = $1 if ($unpacked_line[4] =~ /(\S+)/);
		};
	    } else {
		$bay = $1 if ($line =~ /Bay\s+(\S+)/);
		$lnum = $1 if ($line =~ /Lnum\s+(\S+)/);
		$state = $1 if ($line =~ /\[(.*)\]/);
	    }
	    next unless ($lnum or $bay) and $state;
	    my $identifier = ($bay?"Bay $bay":"Lnum $lnum");
	    verbose(sprintf("%-10s %-10s", $identifier, $state));

	    # Flag nodes if the SSDs are L3, this will be used for drive firmware and FCO activity scheduling
	    if ($state =~ /L3/) {
		$NODE_INFO{$lnn}{'L3'} = 1;
		debug("NODE_INFO->${lnn}->L3=1");
	    }

	    # Healthy states
	    next if ($state =~ /HEALTHY|L3|BOOT_DRIVE|JOURNAL|EMPTY/);

	    # Not healthy, but the upgrade can proceed
	    if ($state =~ /STALLED|REPLACE|EMPTY|ERASE|USED/) {
		warning(sprintf($MSG{'checkDrivesHealth_DRIVE_3'},
				$lnn, $identifier, $state));
	    }
	    # Unexpected state
	    else {
		fail(sprintf($MSG{'checkDrivesHealth_DRIVE_3'},
			     $lnn, $identifier, $state));
	    }
	}
    }
    info(sprintf($MSG{'KB_1'}, '000035312')) if isResults(&WARN);

    # Output Check result
    printResults();
} # End checkDrivesHealth
sub checkExpectedMemory {
    if (wasCalled()) { return }
    checkHardwareStatus();

    # Setup
    my $SPF_VERBOSE = '%5s %10s %10s';
    my $SPF_LESS = 'Memory mismatch on node %s, expected %s, found %s';

    # Analyze
    verboseHeader('Expected Memory');
    verbose(sprintf($SPF_VERBOSE, 'LNN', 'Actual', 'Expected'));
	my @nodes;
	foreach my $lnn (@LNNS) {
	if (!$NODE_INFO{$lnn}{'Product'} or !$NODE_INFO{$lnn}{'RAM'}) { next }

	# Current amount of RAM reported in isi_hw_status
	my $num_current = $NODE_INFO{$lnn}{'RAM'};
	my $str_current = convertFromNumeric($num_current, 'G');
	# Expected amount of RAM from the product info
	my @product_info = split(/-/, $NODE_INFO{$lnn}{'Product'});
	if (scalar(@product_info) < 4) { next }
	my $str_expected = $product_info[3];
	my $num_expected = convertToNumeric($str_expected);
	push(@{$CLUSTER_INFO{'nodes_memory'}{$str_expected}}, $lnn);
	# Fail when the expected is over 1GB more than the current memory
	my $mem_diff = $num_expected - $num_current;
	if ($mem_diff > 2147483648) {
	    fail(sprintf($SPF_LESS, $lnn, $str_expected, $str_current));
	}

	# Verbose
	verbose(sprintf($SPF_VERBOSE, $lnn, $str_current, $str_expected));
    }
	
} # End checkExpectedMemory
$CHECKS{'checkHardwareStatus'} = {
    'Description' => 'Checks battery health, power supplies, and gathers hardware details for use elsewhere',
    'Exclude' => 1,
};
sub checkHardwareStatus {
    return if wasCalled();
    printCheck('Hardware Status');

    # Gather information
    my $gather = ();
    #if (isLive()) {
	#$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array 'isi_hw_status -Lcisfvtbg|grep -v test']);
    #} else {
	#$gather = gatherCluster(q[2>&1 grep -H -v test */isi_hw_status]);
    #}
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'isi_hw_status -Lcisfvtbg|grep -v test'];
    my $CMD_LOGS = q[2>&1 grep -H -v test */isi_hw_status];
    
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze information
    foreach my $lnn (@LNNS) {
	verboseHeader("$NODE_INFO{$lnn}{'Name'} - Hardware Status");
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	foreach my $line (@{$gather->{$lnn}}) {
	    verbose($line);
	    if ($line =~ /Write to lm96000 fan2 configuration register failed/) {
		fail("Node $lnn is reporting '${line}'");
	    } elsif ($line =~ /hal_get_power_supply_type/) {
		fail("Node $lnn is reporting '${line}'");
	    } elsif ($line =~ /^\s*Product: (.*)$/) {
		$NODE_INFO{$lnn}{'Product'} = $1;
		debug("NODE_INFO->${lnn}->Product=$1");
	    } elsif ($line =~ /^\s*PROC: (.*)$/) {
		$NODE_INFO{$lnn}{'Cores'} = parseCoreCount($1);
		debug("NODE_INFO->${lnn}->Cores=$NODE_INFO{$lnn}{'Cores'}");
	    } elsif ($line =~ /^\s*PwrSupl: (\S+)/) {
		my $location = $1;
		my $firmware = "";
		my $type = "";
		$firmware = $1 if $line =~ /fw=(\S+)/;
		$firmware =~ s/\)//;
		$type = $1 if $line =~ /type=(\S+)/;
		$type =~ s/,//;
		if ($firmware ne "N/S") {
		    $NODE_INFO{$lnn}{'Firmware'}{$location} = $firmware;
		    debug("NODE_INFO->${lnn}->Firmware->${location}=$firmware");
		}
	    }elsif ($line =~ /^PwrSupl:.*(PS[01])type=(\S+),.*$/){
			my $lc_dvc = lc($2);
			push(@{$RPS{$lnn}{$1}},$lc_dvc);
		}elsif ($line =~ /^\s* Supply\s+(\S+)\s+Power(.*)$/) {
		next if $line =~ /=/;
		if ($2 ne "good") {
		    fail("Power Supply $1 on node $lnn is $2");
		}
	    } elsif ($line =~ /^\s*Battery1:\s*\((\S*)\)\s*Battery2:\s*\((\S*)\)\s*$/) {
		if ($1 ne "okay") {
		    fail("Battery 1 on node $lnn is $1");
		}
		if ($2 ne "okay") {
		    fail("Battery 2 on node $lnn is $2");
		}
	    } elsif ($line =~ /^\s*RAM:\s*(\d+)\s*Bytes$/) {
		$NODE_INFO{$lnn}{'RAM'} = $1;
		debug("NODE_INFO->${lnn}->RAM=$1");
		if ($1 < 17000000000) {
		    push(@{$CLUSTER_INFO{'16GB Nodes'}}, $lnn);
		    debug("CLUSTER_INFO->16GB Nodes=$lnn");
		}
		if ($1 < 68719476736){
			push(@{$CLUSTER_INFO{'less than 64GB Nodes'}}, $lnn);
		    debug("CLUSTER_INFO->less than 64GB Nodes=$lnn");	
		}
	    }
	}
    }
    info(sprintf($MSG{'KB_1'}, '000035311'))
	if isResults(&WARN);

    # Output Check result
    printResults();
} # End checkHardwareStatus

$CHECKS{'checkKB000213188'} = {
    'Description' => 'Checks for SED hardware where current version is less than 9.2 and destination version is 9.5 or higher.',
    'Reference' => '',
};

sub checkKB000213188 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=9.2')){ return };
    if (!isOneFSUpgrade()){ return };
    if (isDestinationOneFS('<9.5') || ($RPS{'Target'} and compareVersions($RPS{'Target'}, '9.5') < 0) || !$RPS{'Target'}){ return };

    printCheck('KB 213188');

    my $ssd_sed_nodes_95_issue = 0;
    foreach my $lnn (@LNNS) {
        if (!$NODE_INFO{$lnn}{'Product'}) { 
            missing($lnn); 
            }
        else{
            if ($NODE_INFO{$lnn}{'Product'} =~ /SSD-SED/) {
                debug("SED NODE DETECTED - $NODE_INFO{$lnn}{'Product'}");
                $ssd_sed_nodes_95_issue = 1;
            }
        }
    }

    if($ssd_sed_nodes_95_issue){
    if(((isDestinationOneFS('>=9.5') and isDestinationOneFS('<9.5.0.4')) || ($RPS{'Target'} and compareVersions($RPS{'Target'}, '9.5.0.4') < 0)) and isCurrentOneFS('<9.5.0.4')){
    $RPS{'000213188'} = 1;
    critical("SED Nodes detected and current version is less than 9.2, Current Version is ".$CLUSTER_INFO{'OneFS'}.", Destination version is ".$RPS{'Target'}.".");
    info("Please ensure you upgrade to an intermediate code version of either 9.2 or 9.4 prior to upgrading to 9.5 - the build plan has been altered to provide 9.4 as the intermediate version.");
    info("Please refer to KB 000213188 (https://www.dell.com/support/kbdoc/en-us/000213188) for further information.")
    }
    }

    printResults();
} # End checkKB000213188
###
# checkKB000290094: Checks for bootdisk reservation value mismatch from expected value
$CHECKS{'checkKB000290094'} = {
    'Description' => 'Checks if the cluster is at risk for KB 000293383',
    'Exclude' => 0,
};
sub checkKB000290094 {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'KB_000293383'}) { return unsupported('KB_000293383') }
    
    debug("16G NODES = compressRange(@{$CLUSTER_INFO{'KB_000293383'}})");

    my $sixteenG_Node = compressRange(@{$CLUSTER_INFO{'KB_000293383'}});
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -n $sixteenG_Node isi_usysctl -a -v | grep 'partition_reserve'];
    my $CMD_LOGS = q[2>&1; for dir in $(egrep -rl 'Product: F710' */isi_hw_status | awk -F '/' '{print $1}'); do grep -H 'installation.devices.bootdisk.partition_reserve' $dir/isi_usysctl_psi; done];
    
    printCheck('KB 000293383');

    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Gather
        my @nodes = ();
        my @nodes_unkown = ();
        foreach my $lnn (@{$CLUSTER_INFO{'KB_000293383'}}) {
            #debug($gather->{$lnn});
            if (!$gather->{$lnn} || $gather->{$lnn} eq '') {
                missing($lnn);
                next;
            }
            else {
                foreach $l (@{$gather->{$lnn}}){
                    debug("Node ".$lnn." Result:\t".$l);
                    my $allocated_partition_res = $1 if $l =~ /^\s*.*:\s*(\d+)\s*$/;
                    my $val = $allocated_partition_res;
                    debug("Node ".$lnn." Regex Result:\t".$allocated_partition_res);
                    if(int($val) != 42949672960 and int($val) != 206158430208){
                        #Fail
                        push(@nodes, $lnn);
                        debug("Result for node $lnn is $val");
                    }
                };
            }
        };

if(@nodes){
fail('The following nodes have values set for bootdisk.partition.reserve which are not the expected value: '.compressRange(@nodes));
info('Please open a Technical Support case referencing this failure and associated KB (https://www.dell.com/support/kbdoc/000293383).');
}
printResults();
} # End checkKB000290094

$CHECKS{'checkKB000421406'} = {
    'Description' => 'Checks if Secure Boot is enabled on any node, if Secure Boot is enabled, it may pose an issue when the node reboots during an upgrade to 9.5.1.0.',
    'Reference' => '',
};

sub checkKB000421406 {
    if (wasCalled()) { return }
    if (!isOneFSUpgrade() || isDestinationOneFS('>=9.7.1.12') || isDestinationOneFS('<9.7.1.11')) { return } 
    gatherProductInfo();
    
    printCheck('KB 000421406');
    
    my @nodes;
    
    if(@{$CLUSTER_INFO{'14G_NODES'}}){ push(@nodes,@{$CLUSTER_INFO{'14G_NODES'}}); }

    # Flag
    if(@nodes){
        addCustomerInformation(fail("This cluster has 14G nodes installed and may encounter an issue when upgrading to 9.7.1.11 or 9.7.1.12. The following nodes are 14G: ".compressRange(@nodes)));
        info("You should upgrade to 9.7.1.13 or higher to avoid issues outlined in the below article.");
        info("Please reference https://www.dell.com/support/kbdoc/000421406 for additional information.");
    }

    printResults();
} # End checkKB000421406
$CHECKS{'checkMemory'} = {
    'Description' => 'Checks each DIMM to meet criteria outlined in KB 000041666 and if the expected (per product info line) matches closely the reported RAM',
    'Exclude' => 1,
};
sub checkMemory {
    return if wasCalled();
    checkHardwareStatus();
    gatherProductInfo();
    printCheck("Memory");
    checkExpectedMemory();
	 my $SPF_VERBOSE = '%5s %10s %10s';
	
    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array '${SUDO} isi_dmilog  |grep DIMM'; ].
				qq[sleep 2; ].
				qq[2>&1 ${SUDO} isi_for_array '${SUDO} isi_hwmon -l|grep DIMM'; ].
				qq[sleep 2; ].
				qq[2>&1 ${SUDO} isi_for_array '${SUDO} isi_hwmon -e|grep DIMM'; ]);
    } else {
	$gather = gatherCluster("2>&1 grep -H DIMM */isi_dmilog */hwmon_ecc */isi_hwmon");
    }

    # Analyze information for issues.
    my %dimm_report = ();
	my @nodes = ();
    foreach my $lnn (@LNNS) {
	verboseHeader("$NODE_INFO{$lnn}{'Name'} - checkMemory");
	next unless $gather->{$lnn};

	# Check for DIMM errors
	my %dimm_errors = ();
	foreach my $line (@{$gather->{$lnn}}) {

	    if ($line =~ /^\s*(DIMM\d+-RANK\d+):\s+(\d+)$/) {
		$dimm_report{$lnn}{$1}{$RPS{'Runtime'}}{'Count'} = $2;
		$dimm_report{$lnn}{$1}{$RPS{'Runtime'}}{'Type'} = 'Correctable';
		verbose($line);
		next;
	    }
	    next unless $line =~ /^\s+(\d+)\/(\d+)\/(\d+)\s+(\d+):(\d+):(\d+)\s+\S+\s+(\S.*?):\s+(\d+)\s+times? on\s+(\S+)$/;
	    debug("checkMemory: Node $lnn: $line");
	    my $month = $1;
	    my $day = $2;
	    my $year = $3;
	    my $hour = $4;
	    my $min = $5;
	    my $sec = $6;
	    my $type = $7;
	    my $count = $8;
	    my $dimm = $9;

	    # Sometimes isi_dmilog has reports on 01/00/00 0:00:00..  ignoring these because there is no time stamp
	    next if ($day == 0);

	    # Fix a bug where dmi log rolls over the date by max int size * 2
	    # Also assign epoch time for the error to $time
	    my $time = 0;
	    if ($year > 100) {
		eval {
		    $time = timelocal($sec, $min, $hour, $day, $month - 1, $year + 1832);
		};
		warning($@) if $@;
		next if !$time;
		$time += 1006623105;
		($sec, $min, $hour, $day, $month, $year) = localtime($time);
		$year += 1900;
		$month += 1;
		debug("checkMemory: Time conversion performed, actual date & time: $month/$day/$year $hour:$min:$sec");
	    } else {
		$year += 2000;
		eval {
		    $time = timelocal($sec, $min, $hour, $day, $month - 1, $year);
		};
		warning($@) if $@;
		next if !$time;
	    }
	    verbose("DIMM: $dimm, Count: $count, Type: $type, Time: $month/$day/$year $hour:$min:$sec");

	    # Skip if more than 1 year old
	    next if ($RPS{'Runtime'} - $time >= 30758400);
	    next if ($RPS{'Runtime'} - $time >= 7776000);
	    $dimm_report{$lnn}{$dimm}{$time}{'Type'} = $type;
	    $dimm_report{$lnn}{$dimm}{$time}{'Count'} = $count;
	    my $date_str = sprintf("%04d-%02d-%02d", $year, $month, $day);
	    $dimm_errors{$dimm}{$type}{$date_str} += $count;
	}
	foreach my $dimm (sort keys %dimm_errors) {
	    next unless $dimm_errors{$dimm};
	    foreach my $type (sort keys %{$dimm_errors{$dimm}}) {
		next unless $dimm_errors{$dimm}{$type};
		my $str = "Node $lnn DIMM $dimm is reporting $type with the following dates(count):";
		foreach my $date (sort keys %{$dimm_errors{$dimm}{$type}}) {
		    $str .= " ${date}($dimm_errors{$dimm}{$type}{$date})"
		}
		warning($str);
	    }
	}
    }

    foreach my $lnn (sort {$a <=> $b} keys %dimm_report) {

	# Different generations of hardware have different allowances for DIMM errors in a 24 hour period
	my $allowed = 500;
	if ($NODE_INFO{$lnn}{'Generation'}) {
	    if ($NODE_INFO{$lnn}{'Generation'} eq "Gen4") {
		$allowed = 15;
	    } elsif ($NODE_INFO{$lnn}{'Generation'} eq "Gen5") {
		$allowed = 500;
	    } elsif ($NODE_INFO{$lnn}{'Generation'} eq "Gen6") {
		$allowed = 5000;
	    }
	}

	my @dimms_with_issues = ();
	$dmm_fail = 1;
	foreach my $dimm (keys %{$dimm_report{$lnn}}) {
	    my @under_24_hours = ();
	    my $fatal = 0;
	    my $over_allowed_in_24_hours = 0;
	    foreach my $time (sort keys %{$dimm_report{$lnn}{$dimm}}) {
		# Add new time
		push(@under_24_hours, $time);

		# Shift off old times
		shift(@under_24_hours) while ($time - $under_24_hours[0] > 86400);

		# Fatal errors!?
		$fatal = 1 if not $dimm_report{$lnn}{$dimm}{$time}{'Type'} =~ /correctable|non-fatal/i;

		# Maximum of 15 errors on a DIMM allowed within a 24-hour period
		my $error_count = 0;
		$error_count += $dimm_report{$lnn}{$dimm}{$_}{'Count'} foreach (@under_24_hours);
		$over_allowed_in_24_hours = 1 if $error_count > $allowed;
	    }
	    push(@dimms_with_issues, $dimm) if $fatal or $over_allowed_in_24_hours;
	}
	fail("Node $lnn has DIMMs that met criteria for replacement: ".join(",",@dimms_with_issues)) if @dimms_with_issues;
	$dmm_fail = 1;
    }

	#if ($CLUSTER_INFO{'less than 64GB Nodes'}) {
	#warning("The following nodes has less than the recommended amount of memory installed and may impact workflow: ".compressRange(@{$CLUSTER_INFO{'less than 64GB Nodes'}}));
	#info("< Customer info goes here >");
	#my $no_list = '';
	#details(sprintf($SPF_VERBOSE,
	#	    'Installed Memory',
	#	    'Nodes'));
	#foreach my $k (keys %{ $CLUSTER_INFO{'nodes_memory'} }){
	#	$no_list = compressRange(@{$CLUSTER_INFO{'nodes_memory'}{$k}});
	#	#verbose(sprintf($SPF_VERBOSE, $k, $no_list));
	#	details(sprintf($SPF_VERBOSE,
	#		    $k,
	#		    $no_list));
	#}
	##addCustomerInformation("");
    #}

	info(sprintf($MSG{'KB_1'}, '000035299')) if @dimms_with_issues;


    # Output Check result
    printResults();
} # End checkMemory
###
# checkMirrors: Boot Drives - Cannot perform upgrade with degraded boot drive
$CHECKS{'checkMirrors'} = {
    'Description' => 'Checks the boot mirror health',
    'Exclude' => 1,
};
sub checkMirrors {
    if (wasCalled()) { return }
    if (isVirtual()) { return unsupported('UNSUP_VIRT') }
	
	gatherProductInfo();
	checkPartitions();
	
    printCheck('Mirror Status');

    # Gather information from cluster or from logset
    my $gather = ();
    my $bootdisk_status = ();
    if (isLive()) {
	$gather = gatherCluster("2>&1 ${SUDO} isi_for_array gmirror status");
	$bootdisk_status = gatherCluster(qq[2>&1 ${SUDO} isi_for_array isi_bootdisk_status]);
    } else {
	$gather = gatherCluster("2>&1 grep -H . */gmirror_status");
	$bootdisk_status = gatherCluster(qq[2>&1 grep -H . */isi_bootdisk_status]);
    }

	############
	#Set up for drive list with 'device' for KB 000213248
	############
	#isi devices drive list --no-header --no-footer
	my $CMD_LIVE = q[2>&1 ${SUDO} isi_for_array 'for var in var0 var1; do echo -n " | $var | ";for i in $(gmirror status $var | grep -o "da[^p]*");do isi devices drive list --override -az | grep $i | tr "\\n" "\\t";done;done;echo'];
	#my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array isi devices drive list --no-header --no-footer];
	my $CMD_LOGS = q[2>&1; for d in *-*/ ; do { echo -n "${d}mirror_check:"; for var in var0 var1; do echo -n " | $var | "; for i in $(grep -A1 $var ${d}gmirror_status | grep -o "da[^p]*" | tr "\n" "\t"7);do grep $i ${d}isi_devices_drive_list | tr "\n" "\t"; done; done; echo; }; done;];
   
	my $gather_drives = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
	
	my @mirrors_missing_components = ();
	my @mirrors_degraded = ();
	my @mirrors_same_sled = ();

	my @mirrors_missing_comp;
	my @mirrors_deg;
	my %mirrors_same_fd;
	my %shard_same_sled;

	# Analyze information for issues.
	verboseHeader('Nodes with Shard\'s on same SLED');
    my $spf = '%-5s %-5s %-5s';
    verbose(sprintf($spf, 'LNN', 'Shard', 'SLED' ));
    verbose(sprintf($spf, '-'x5, '-'x5, '-'x5));
    foreach my $lnn (@LNNS) {
		#verboseHeader($NODE_INFO{$lnn}{'Name'});
		next if $NODE_INFO{$lnn}{'Accelerator'};
		next if $NODE_INFO{$lnn}{'Generation'} and $NODE_INFO{$lnn}{'Generation'} eq "IQ";
		
		if (!$gather->{$lnn} or !$bootdisk_status->{$lnn}) {
			missing($lnn);
			next;
		}
			
		# Check isi_bootdisk_status for missing partitions
		my @missing_partitions = ();
		foreach my $line (@{$bootdisk_status->{$lnn}}) {
			my @results = split(/\s+/, $line);
			push(@missing_partitions, $results[0])
			unless $#results;
		}
		fail("Node $lnn is missing the following partitions in isi_bootdisk_status output: ".join(", ", @missing_partitions)) if @missing_partitions;

		my $name ="";
		my $status = "";
		my $components = "";
		my $location = "";
		my $sled = "";
		my %mirrors = ();
		my %drives = ();

		foreach my $line (@{$gather->{$lnn}}) {
			#verbose($line);
			next if ($line =~ /\s*Name\s*Status\s*Components/);
			if ($line =~ /(mirror\/\S+)\s+(\S+)\s+(\S+)\s*/) {
				$name = $1;
				$mirrors{$name}{'status'} = $2;
				push(@{$mirrors{$name}{'components'}}, $3);
			} elsif ($line =~ /^\s+(\S+)/) {
				push(@{$mirrors{$name}{'components'}}, $1) if $mirrors{$name};
			}
		}
		if (%mirrors) {
			$NODE_INFO{$lnn}{'Bootflash'} = 1;
			debug("NODE_INFO->$lnn->Bootflash=1");
		}

		my $comptocheck = "";
		foreach my $name (keys %mirrors) {
			my $components = scalar(@{$mirrors{$name}{'components'}});
			debug("checkMirrors: Node $lnn: Mirror: $name has a status: $mirrors{$name}{'status'} and $components components");
			debug("checkMirrors: Node $lnn: Mirror: $name has a status: $mirrors{$name}{'status'} and components: ".join(",", @{$mirrors{$name}{'components'}}));
			# Degraded mirrors
			if ($mirrors{$name}{'status'} eq "DEGRADED") {
				push(@mirrors_degraded, $name);
			}

			# Only one component
			if($components < 2 and $name !~ /crash|mfg|keystore|dump/) {
				push(@mirrors_missing_components, $name) unless (grep { $lnn eq $_ } @{$CLUSTER_INFO{'Accelerator'}});
			}
		}

		## Updated check for SLED position ##
		# $1 = var0 // Mirror name	 $8 = var1 // Mirror Name
		# $2 = Node Number			 $9 = Node Number
		# $3 = Health State			 $10 = Health State
		# $4 = SLED					 $11 = SLED
		# $5 = Node Number			 $12 = Node Number
		# $6 = Health State			 $13 = Health State
		# $7 = SLED					 $14 = SLED

		foreach my $line (@{$gather_drives->{$lnn}}) {
			if(grep { $lnn eq $_ } @{$CLUSTER_INFO{'Gen6'}} ){ debug("Node $lnn is Gen6");}
			else{debug("Node $lnn is not Gen6 / MLK, skipping.. "); next;};
		#    #verbose($line);
		#	debug("Drive Line = $line");
			if($line =~ /^\s*\|\s*\S+\s*\|\s*\|\s*\S+\s*\|\s*$/){ next; };
		    if ($line =~ /^\s*\|\s*(\S+)\s*\|\s*(\d+)\s*\S+\s*\S+\s*\S+\s*\S+\s*(\S+)\s*\S+\s*(\S+)\s*(\d+)\s*\S+\s*\S+\s*\S+\s*\S+\s*(\S+)\s*\S+\s*(\S+)\s*\|\s*(\S+)\s*\|\s*(\d+)\s*\S+\s*\S+\s*\S+\s*\S+\s*(\S+)\s*\S+\s*(\S+)\s*(\d+)\s*\S+\s*\S+\s*\S+\s*\S+\s*(\S+)\s*\S+\s*(\S+)\s*$/) {
				debug("Checking node $lnn Mirror $1 for SLED $4 and $7 || Mirror $8 for SLED $11 and $14");
				if($4 eq $7){
					push(@{$mirrors_same_fd{$1}{'nodes'}}, $lnn);
					debug("NODE $lnn $1 has SLED assignment $4 and $7 | FAIL Check");
					$shard_same_sled{$lnn}{$1}=$4;
				}
				if($11 eq $14){
					push(@{$mirrors_same_fd{$8}{'nodes'}}, $lnn);
					debug("NODE $lnn $8 has SLED assignment $11 and $14 | FAIL Check");
					$shard_same_sled{$lnn}{$8}=$11;
				}
		    }
		}
	}

	foreach my $l (sort keys %shard_same_sled){
		foreach my $s (sort keys %{ $shard_same_sled{$l} }){
			verbose(sprintf($spf, $l, $s, $shard_same_sled{$l}{$s} ));
		};
	}

	if(@mirrors_degraded || @mirrors_missing_components){
        fail("Node $lnn is reporting degraded for the following mirrors: ".join(",", @mirrors_degraded)) if (@mirrors_degraded);
        fail("Node $lnn is reporting only one component for the following mirrors: ".join(",", @mirrors_missing_components)) if (@mirrors_missing_components);
		#info("To get a list of nodes, their respective shar's affected and their SLED locations, please run perl IOCA -r checkMirrors -e ");
        info(sprintf($MSG{'KB_1'}, '000051090')) if isResults(&WARN);
    }
	
	if( %mirrors_same_fd){
		if($RPS{'CheckList'} ne 'healthcheck'){
			foreach my $fdn (keys %mirrors_same_fd){
				critical("The mirror pair for $fdn appear to be in the same fault domain on nodes: ".compressRange(@{$mirrors_same_fd{$fdn}{'nodes'}}).".") if (%mirrors_same_fd);
			}
			info("To get a list of nodes, their respective shard's affected and their SLED locations, please run perl IOCA -r checkMirrors -e ");
		}
	
		else{
			foreach my $fdn (keys %mirrors_same_fd){
				critical("The mirror pair for $fdn appear to be in the same fault domain on nodes: ".compressRange(@{$mirrors_same_fd{$fdn}{'nodes'}}).".") if (%mirrors_same_fd);
			}
		}
		#if($RPS{'LKG EXPANSION FLAG'}){
		#	critical("The following nodes have /var mirrors of different sizes: ".compressRange(@{$RPS{'LKG Issue Nodes'}{'Nodes'}})) if $RPS{'LKG EXPANSION FLAG'};
		#	info("Please refer to KB 000213248 (https://www.dell.com/support/kbdoc/en-us/000213248) for further information.");
		#}
	}

    # Output Check result
    printResults();
} # End checkMirrors
sub checkNICType {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'DE_NODES'}) { return unsupported('DE_NODES') }
    
    debug("DE NODES = compressRange(@{$CLUSTER_INFO{'DE_NODES'}})");

    my $de_node = compressRange(@{$CLUSTER_INFO{'DE_NODES'}});
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -n $de_node 'ifconfig | grep -i flags | egrep -v "vlan|lagg|lo0" | wc -l'];
    my $CMD_LOGS = q[2>&1; for i in $(grep Prod isi_hw_status/* |grep -E "A200|A2000|H400" |cut -d "/" -f2 |cut -d "-" -f1,2); do echo -n $i":   " ; grep flags $i/ifconfig |grep -v -E "vlan|lagg|lo0" | wc -l; done];
    
    printCheck('Installed NIC');

    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Gather
        my @nodes = ();
        my @nodes_unkown = ();
        foreach my $lnn (@{$CLUSTER_INFO{'DE_NODES'}}) {
            #debug($gather->{$lnn});
            if (!$gather->{$lnn} || $gather->{$lnn} eq '') {
                missing($lnn);
                next;
            }
            else {
                foreach $l (@{$gather->{$lnn}}){
                    debug("Node ".$lnn." Result:\t".$l);
                    my $DE_NIC_TYPE = $1 if $l =~ /^\s*(\d+)\s*$/;
                    my $val = $DE_NIC_TYPE;
                    debug("Node ".$lnn." Regex Result:\t".$DE_NIC_TYPE);
                    if($val eq '5'){
                        #warning
                        push(@nodes, $lnn);
                        debug("Result for node $lnn is $val - PELICAN NIC DETECTED");
                    }
                    elsif($val eq '7'){
                        #pass
                        debug("Result for node $lnn is $val - Pelican NIC not detected");
                    }
                    else{
                        debug("Result for node $lnn - $val - Cannot Determine if there is an issue");
                        push(@nodes_unkown, $lnn);
                    }
                };
            }
        };

warning('Detected Pelican NIC installed on nodes '.compressRange(@nodes)) if @nodes;
critical('Cannot determine if Pelican NIC is installed on nodes:'.compressRange(@nodes_unkown).'.') if @nodes_unkown;

printResults();
} # End checkNICType

$CHECKS{'checkNodeCompatibility'} = {
    'Description' => 'Checks node compatibility for OneFS upgrades by comparing it against known supported versions',
    'Reference' => 'https://dl.dell.com/content/manual63182654',
};
sub checkNodeCompatibility {
    if (wasCalled()) { return }
    if (!isDestinationOneFS()) { return unsupported('UNSUP_TGT') }
    gatherProductInfo();
    if ($CLUSTER_INFO{'IQ'}) { checkPartitions() }

    # Setup
    printCheck('Node Compatibility');
    my $SPF_ISERIES = 'The following i-series nodes are not compatible with'.
	' OneFS 7 or later: %s';
    my $SPF_ROOT = 'The following nodes do not have a sufficient root'.
	' partition to support OneFS %s: %s';
    my $SPF_MAX = 'The following nodes of type %s are compatible up to'.
	' OneFS %s: %s';
	my $SPF_G4_DPN = 'The following nodes are not compatible with'.
	' OneFS 9.7.1.11: %s';
    my $MSG_NOT_COMPAT = 'Due to nodes not being compatible with the'.
	' destination OneFS version, the destination OneFS version was removed'.
	' and no upgrade plan will be generated.';
    my $MSG_DOC = 'For more details review the Isilon Supportability and'.
	' Compatibility Guide [ https://dl.dell.com/content/manual63182654 ]';
    my $SPF = '%5s %-15s %-10s %s';
    my %CompatibilityMatrix = (
	# IQ Node types
	'IQ 1920e' => '5.5',
	'IQ 3000e' => '5.5',
	'IQ 4800e' => '5.5',
	'IQ 6000e' => '5.5',
	'IQ 200' => '5.5',
	'IQ 1920i' => '6.5',
	'IQ 3000i' => '6.5',
	'IQ 6000i' => '6.5',
	'IQ 9000i' => '6.5',
	'IQ 12000i' => '6.5',
	'IQ 1920x' => '7.1',
	'IQ 3000x' => '7.1',
	'IQ 6000x' => '7.1',
	'IQ 9000x' => '7.1',
	'IQ 36NL' => '7.1',
	'IQ 72NL' => '7.1',
	'IQ 12000x' => '7.1',
	'IQ 10000x-ssd' => '8.0.0.6',
	'IQ 32000x-ssd' => '8.0.0.6',
	'IQ 36000x' => '8.0.0.6',
	'IQ 72000x' => '8.0.0.6',
	'Accelerator' => '8.0.0.6',
	'IQ 5000s-ssd' => '8.0.0.6',
	'IQ 5400s' => '8.0.0.6',
	'IQ 108NL' => '8.0',

	# Gen 4
	'X200' => '8.1',
	'S200' => '8.1',
	'X400' => '8.1',
	'NL400' => '8.1',

	# Gen 5
	'A100' => '9.8.',
	'HD400' => '9.8',
	'S210' => '9.8',
	'X210' => 'TBD',
	'X410' => '9.8',
	'NL410' => '9.8',

	# Gen 6
	'F800' => 'TBD',
	'F810' => 'TBD',
	'H600' => 'TBD',
	'H5600' => 'TBD',
	'H500' => 'TBD',
	'H400' => 'TBD',
	'A200' => 'TBD',
	'A2000' => 'TBD',

	# Gen 6 MLK
	'A300' => 'TBD',
	'A3000' => 'TBD',
	'H700' => 'TBD',
	'H7000' => 'TBD',
	'A250' => 'TBD',
	'A2050' => 'TBD',
	'F850' => 'TBD',
	'H550' => 'TBD',
	'H5650' => 'TBD',

	# Gen 6.5
	'F200' => 'TBD',
	'F600' => 'TBD',
	'F900' => 'TBD',
	'P100' => 'TBD',
	'B100' => 'TBD',

	# 16G 
	'F210' => 'TBD',
	'F710' => 'TBD',
	'F910' => 'TBD',
	'PA110' => 'TBD',

	# Virtual
	'IQ VMWare' => '8.1',
	'V200' => 'TBD',
	'SIMULATOR' => 'TBD',
	'AWS' => 'TBD',

	# Isilon SD Edge virtual nodes
	'V100' => '8.1',
       	);

    # Analyze
    verboseHeader('Node Compatibility');
    verbose(sprintf($SPF, 'LNN', 'Node Type', 'Max OneFS', 'Product'));
    verbose(sprintf($SPF, '-'x5, '-'x15, '-'x10, '-'x30));
    my $delete_target = 0;
    my @i_series = ();
    my @root = ();
	my @ic_g4_dpn = ();
    my %max = ();
    foreach my $lnn (@LNNS) {

	# Node Product details must be available
	if (!$NODE_INFO{$lnn}{'Product'}) { next }
	my $product = $NODE_INFO{$lnn}{'Product'};

	# Search for the node type in the compatibility matrix
	my ($type) = grep {$product =~ /^$_-/i} sort keys %CompatibilityMatrix;
	if (!$type) {
	    ($type) = grep {$product =~ /$_/i} sort keys %CompatibilityMatrix;
	}
	my $sed_hw = 1 if $product =~ /SSD-SED/;
	my $type_tmp = $type.' SSD-SED';
	$type_tmp = $type if !$sed_hw;
	$RPS{'Node_Types_Installed'}{$type_tmp} += 1;
	
	if (!$type) {
	    # The node type was not found in the compatibility matrix
	    verbose(sprintf($SPF, $lnn, 'Unknown', 'Unknown', $product));
	    next;
	}

	# The node type was found in the compatibility matrix
	# Store the maximum OneFS version
	my $max = $CompatibilityMatrix{$type};
	verbose(sprintf($SPF, $lnn, $type, $max, $product));

	# Skip to the next node if there is no max
	if ($max eq 'TBD') { next }

	# Perform compatibility checks
	if ($NODE_INFO{$lnn}{'Series'} and
	    $NODE_INFO{$lnn}{'Series'} =~ /i_series/ and
	    isDestinationOneFS('>=7') and
	    compareVersions($max, '7') >= 0) {
	    # i Series nodes are not supported in OneFS 7 or later
	    push(@i_series, $lnn);
	    $delete_target = 1;
	} elsif ($type =~ /^(?:S210|X410|X210|NL410|HD400|A100)/){
		if(isDestinationOneFS('9.7.1.11')){
			# The destination OneFS is not compatible with the Gen4 nodes detected.
			debug("Node is Gen4 and Destination is 9.7.1.11, pushing node $lnn.");
	    	push(@ic_g4_dpn, $lnn);
			$delete_target = 1;
	} elsif (isDestinationOneFS(">$max")) {
	    # The destination OneFS is greater than the maximum supported
	    #  version for that node type
	    push(@{$max{$type}}, $lnn);
	    $delete_target = 1;
	} 
	} elsif ($RPS{'Root less than 500MB'}{$lnn} and
		 (isDestinationOneFS('8.0.0.7') or
		  isDestinationOneFS('>=8.1'))) {
	    # The node has less than 500MB allocated to /
	    push(@root, $lnn);
	    $delete_target = 1;
	}
    }
    if ($delete_target) {
		delete $RPS{'Target'};
		delete $RPS{'UpgradePlan'};
		fail($MSG_NOT_COMPAT);
    }

    # Flag
    if (@i_series) {
		fail(sprintf($SPF_ISERIES, compressRange(@i_series)));
    }
    if (@root) {
		fail(sprintf($SPF_ROOT, $RPS{'Target'}, compressRange(@root)));
    }
	if (@ic_g4_dpn) {
		fail(sprintf($SPF_G4_DPN, compressRange(@ic_g4_dpn)));
    }
    if (%max) {
		foreach my $type (sort keys %max) {
			fail(sprintf($SPF_MAX, $type, $CompatibilityMatrix{$type},
			compressRange(@{$max{$type}})));
		}
    }
    
    if (isResults(&WARN)) { info($MSG_DOC) }

    # Output
    printResults();
}
$CHECKS{'checkNodesInstalled'} = {
    'Description' => 'Checks for nodes installed to display in an agregated list for visibility',
    'Reference' => '',
};
sub checkNodesInstalled {
    if (wasCalled()) { return }
    gatherProductInfo();
        
    printCheck('Installed Nodes');
#$CLUSTER_INFO->{'node_type_list'}

info("$CLUSTER_INFO{'node_type_list'}");

printResults();
} # End  checkNodesInstalled

$CHECKS{'checkPSIReceipt'} = {
    'Description' => 'Checks for presence of PSI reciept and verify if network interfaces match the hardware product information',
};

sub checkPSIReceipt {
    if (wasCalled()) { return }
    gatherProductInfo();
    if(!$CLUSTER_INFO{'Pifano'} and !$CLUSTER_INFO{'16G'} and !$CLUSTER_INFO{'Gen6.5'} and !$CLUSTER_INFO{'Gen6'} and !$CLUSTER_INFO{'Gen6_NMLK'}){ return; }
    
    my @cmbd_node_list = ();
    push(@cmbd_node_list, @{$CLUSTER_INFO{'Pifano'}}, @{$CLUSTER_INFO{'16G'}}, @{$CLUSTER_INFO{'Gen6.5'}}, @{$CLUSTER_INFO{'Gen6'}}, @{$CLUSTER_INFO{'Gen6_NMLK'}});
    my %uniq_nl = map {($_,1)} @cmbd_node_list;
    my @uniq_node_list = keys %uniq_nl;

    my $ntc = compressRange(@uniq_node_list);
    debug("Nodes to check: $ntc");
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -n $ntc "{/usr/bin/isi_hwtools/isi_psi_tool -v | egrep \'NETWORK_\|FE\';isi_hw_status | egrep \'Prod\'}"];
    my $CMD_NW_LIVE = qq[2>&1 ${SUDO} isi_for_array -n $ntc "ifconfig | egrep 'flags|description|status|media'"];
    my $CMD_LOGS = q[2>&1; grep 'NETWORK_\|FE' *-*/isi_psi* |sort -V; grep Product *-*/isi_hw_status ];
    my $CMD_NW_LOGS = q[2>&1; egrep -H 'flags|description|status|media' */ifconfig ];


    printCheck('PSI Receipt');

    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    my $gather_NW = gatherCluster(isLive()?$CMD_NW_LIVE:$CMD_NW_LOGS);
    
    my %node_psi_rcrds;
    my %node_hw_rcrds;
    my %node_ic_rcrds;

    # Gather
    my @nodes = ();
    my @nodes_unkown = ();
    foreach my $lnn (@uniq_node_list) {
        debug($gather->{$lnn});
        if (!$gather->{$lnn} || $gather->{$lnn} eq '') {
            missing($lnn);
            next;
        }
        else {
            foreach $l (@{$gather->{$lnn}}){
                if($l =~ /Product:.*x(\d{2,})gb?e\s+/i){
                    $node_hw_rcrds{$lnn}{$1} = $1;
                }
                if($l =~ /[_-](\d{2,})g(?:b)?[eib]*/i){
                    $node_psi_rcrds{$lnn}{$1} = $1;
                }
            };
        }
    }

my @nodes = ();
    foreach $p (keys %node_hw_rcrds){
        foreach $r (keys %{$node_hw_rcrds{$p}}){

            debug("Checking node $p PSI Record: $r");
            if(exists($node_psi_rcrds{$p}{$r})){
                debug("$r exists in HW records for node $p");
            }
            else{
                debug("INFO - $r does not exist in HW records for node $p");
                push @nodes, $p for grep { !(grep { $_ eq $p } @$_) } (\@{$CLUSTER_INFO{'Pifano'}},\@{$CLUSTER_INFO{'16G'}},\@{$CLUSTER_INFO{'Gen6.5'}},\@{$CLUSTER_INFO{'Gen6'}}, \@{$CLUSTER_INFO{'Gen6_NMLK'}});
            }
        }
    }

if(@nodes){
    fail("PSI receipt for the following nodes do not match HW configuration and may pose an issue for nodes during boot phase of a reboot:  ". compressRange(@nodes));
    info("Please contact Technical Support for further assistance and reference KB 000344843.");
}

printResults();
} # End checkPSIReceipt
$CHECKS{'checkSED'} = {
    'Description' => 'Checks 14G and 16G nodes that contain P5316 drives, if so then node firmware must be updated to 12.4.2 or higher prior to updating drive fimrware.',
    'Reference' => '',
};

sub checkSED {
    if (wasCalled()) { return }
    gatherProductInfo();
    #if(!$CLUSTER_INFO{'14G_NODES'} and !$CLUSTER_INFO{'16G_NODES'} ){ return }
    gatherKB000314255();
    checkDriveFirmware();

    # Setup
    my @nts;
    $Node_list = compressRange(@{$CLUSTER_INFO{'P5316_SED'}});
    debug("14G & 16G with P5316 SED's combined node list: $Node_list");
    my $CMD_LIVE;
    if(isCurrentOneFS('<9.9')){
        $CMD_LIVE = qq[2>&1 ${SUDO} isi devices drive list --node-lnn all --no-header --no-footer | awk '{print \$1" "\$3" "\$4" "\$6}' | grep -v 'EMPTY'];
    }
    else{
        $CMD_LIVE = qq[2>&1 isi devices drive list --node-lnn all --no-header --no-footer | awk '{print \$1" "\$3" "\$4" "\$6}' | grep -v 'EMPTY'];
    } 
    my $CMD_LOGS = q[2>&1; egrep -H -v 'EMPTY|Total|Serial' */isi_devices_drive_list | awk '{print $1" "$3" "$4" "$6}'];
    printCheck('SED Check');

    my @nodes;
    my @nodes_custom_plan;
    my %sed_extra_info;
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    verboseHeader('Readd the P5316 SED Drives');
    #my $spf = '%-5s %-5s %-10s';
    my $spf1 = '%-20s';
    #verbose(sprintf($spf, 'Node', 'Bay', 'State'));
    #verbose(sprintf($spf, '-'x5, '-'x5, '-'x10));
    #verbose(sprintf($spf1, '-'x20));
    my @pdsp = ();
    foreach my $lnn (@{$CLUSTER_INFO{'P5316_SED'}}) {
            debug("checking node $lnn with result: @{$gather->{$lnn}}");
        foreach $l ($gather->{$lnn}){
            debug("Checking Node $lnn with line: $l");
            #check each line for SED_ERROR and compare Bay against $CLUSTER_INFO{'P5316_SED}{$lnn} values to see if the bay matches,
            # if the bay matches then we know the type is SED, on the same node, for the same Model and status is SED_ERROR
            # Trigger a FAIL with additional extra option to list commands to readding drives
            foreach $line (@{$l}){
            if($line =~ /^\s*(\d+)\s*(\S+)\s*(\S+)\s*(\S+)$/){
                debug("AFFECTED DRIVE FOUND: P5316 SED LNN: $lnn, Lnum: $1 BAY: $2 Device: $3 State: $4");
                debug("Drive Model is @{$NODE_INFO{$lnn}{'P5316_SED_types'}{$2}} , Current FW: @{$NODE_INFO{$lnn}{'P5316_SED_fw'}{$2}}");
                if(exists($NODE_INFO{$lnn}{'P5316_SED_fw'}{$2}) and @{$NODE_INFO{$lnn}{'P5316_SED_fw'}{$2}} lt '1.7.0'){
                    #prioritize DSP to apply fix
                    push(@pdsp, $lnn);
                    @{$RPS{'Drive Support Package'}{'Position'}} = 1;
                }
                else{
                    # Ensure callout for custom plan to manually review drives and do nodes rolling to avoid issues
                    push(@nodes_custom_plan, $lnn);
                    push(@{$CLUSTER_INFO{'000350946'}}, $lnn);                    
                }
                # Here we want to FAIL so we don't present any option other than simultaneous or maunal rolling upgrades / updates
                #debug("Bays for node $lnn in SED_ERROR state: ".join(", ",@{$NODE_INFO{$lnn}{'P5316_SED_bays'}}));
                if(exists($NODE_INFO{$lnn}{'P5316_SED_bays'}) and grep { $_ eq $2 } @{$NODE_INFO{$lnn}{'P5316_SED_bays'}} and $4 eq 'SED_ERROR'){
                    debug("FAIL - Device model and type match for node $lnn bay $2 where state is SED_ERROR, further action required");
                    # Here we want to present a FAIL for current state and also add extra output customized based on the bays needing further action
                    $sed_extra_info{$lnn}{$2} = '1';
                    #verbose(sprintf($spf, $lnn, $2, $4));
                    verbose(sprintf($spf1, "isi_for_array -n $lnn \"/usr/bin/isi_hwtools/isi_sed unlock_bands $2\" && isi_for_array -n $lnn \"isi devices drive add $2\";"));
                }
            }
            }
        }
    }

    # Flag
    if(@nodes_custom_plan or @pdsp){
        #$CLUSTER_INFO{'000350946'} = 1;
        if(@pdsp){
            critical("P5316 SED drives are present on this cluster with firmware lower than 1.7.0, additional handling requirements is required for these drives on the following nodes: ".compressRange(@pdsp));
        }
        if(defined($sed_extra_info)){
            fail("Drives are showing in an SED_ERROR state and should these drives should be in a healthy state before proceeding, please ensure you reference the provided document for more information.");
            info("For additional information associated with performing actions required during an upgrade, please rerun IOCA using the additional arguments: -r checkSEDError -e");
        }
        info("Please reference KB 000350946 (https://www.dell.com/support/kbdoc/000350946) for additional information.");
        #addCustomerInformation(critical("Please ensure you update node firmware to 12.4.2 or higher prior to performing any drive updates on the following nodes which have Solidigm P5316 drives: ".compressRange(@nodes)));
    }

    printResults();
} # End checkSED
$CHECKS{'checkSecureBoot'} = {
    'Description' => 'Checks if Secure Boot is enabled on any node, if Secure Boot is enabled, it may pose an issue when the node reboots during an upgrade to 9.5.1.0.',
    'Reference' => '',
};

sub checkSecureBoot {
    if (wasCalled()) { return }
    if (!isOneFSUpgrade() || isCurrentOneFS('>=9.5.1.0') || isDestinationOneFS('<9.5.1.0') || isDestinationOneFS('>=9.5.1.1')) { return } 
    gatherProductInfo();
    
    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array sysctl security.mac.veriexec.state"];
    my $CMD_LOGS = q[2>&1; grep -H 'kern.uefi_secureboot' */varlog.tar/log/messages];
    printCheck('Secure Boot');
    
    my @nodes;
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    foreach my $lnn (@{$CLUSTER_INFO{'Gen6'}}) {
        debug("checking $lnn with result: $gather->{$lnn}");
        foreach $l (@{$gather->{$lnn}}){
            #LOGS: kern.uefi_secureboot=1, sb_enabled=1
            #LIVE: security.mac.veriexec.state: loaded active enforce locked
            if(isLive()){
                if($l =~ /^\s*security\.mac\.veriexec\.state:\s(loaded active enforce locked)\s*$/){
                    push(@nodes, $lnn);
                }
            }
            else{
                if($l =~ /^\s*(kern\.uefi_secureboot\=1,\ssb_enabled\=1)\s*$/){
                    push(@nodes, $lnn);
                }
            }   
        }
    }
    
    # Flag
    if(@nodes){
        addCustomerInformation(critical("Secure Boot is enabled on this cluster. Please upgrade to 9.5.1.1 or higher to avoid known issue documented in https://www.dell.com/support/kbdoc/000231619. The following nodes have SecureBoot enabled: ".compressRange(@nodes)));
        info("Please reference https://www.dell.com/support/kbdoc/000231619 for additional information.");
    }

    printResults();
} # End checkSecureBoot
###
# gatherProductInfo: Gathers basic product info from isi_hw_status -i
sub gatherProductInfo {
    if (wasCalled()){ return }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'isi_hw_status -i'];
    my $CMD_LOGS = q[2>&1 grep -H . */isi_hw_status];
    my $RE_PRODUCT = qr/^\s*Product:\s+(\S.*)$/;
    my $RE_SERIES = qr/^\s*Series:\s+(\S.*)$/;
    my $RE_FAMILY = qr/^\s*FamCode:\s+(\S.*)$/;
    my $RE_ACCEL = qr/^(?:A100|P100|B100|PA110)|Accelerator/;
    my $RE_SD = qr/^V100/;
    my $RE_A100 = qr/^A100/;
	my $RE_F900 = qr/^F900/;
	#my $RE_A_SERIES = qr/^A2000?/;
    my %NODE_GENS = (
	'IQ' => qr/^IQ/,
	'Gen4' => qr/^(?:S200|X400|X200|NL400)/,
	'Gen5' => qr/^(?:S210|X410|X210|NL410|HD400)/,
	'Gen6' => qr/^(?:A2000?|H400|H500|H600|F800|F810|H5600|A3000?|H7000?)/,
	'MLK' => qr/^(?:A3000?|H7000?)/,
	'Gen6_NMLK' => qr/^(?:A2000?|H400|H500|H600|F800|F810|H5600)/,
	'Gen6.5' => qr/^(?:F200|F600|F900)/,
	'16G' => qr/^(?:F210|F710|F910)/,
	'Gen6.5_Large_Mem' => qr/^(?:F900)/,
	'Pifano' => qr/^(?:A3100?|H7100?)/,
	'Virtual' => qr/^AWS|V|VMWare|SIMULATOR/,
       );
	my %MLK_NODES = (
	'MLK' => qr/^(?:A3000?|H7000?)/,
	'Gen6_NMLK' => qr/^(?:A2000?|H400|H500|H600|F800|F810|H5600)/,
	); 
	my %DE_NODES = (
	'de' => qr/^(?:A2000?|H400)/,
	); 
	my %fourteen_G_NODES = (
	'14G' => qr/^(?:F200|F600|F900)/,
	);
	my %sixteen_G_NODES = (
	'16G' => qr/^(?:F210|F710|F910)/,
	);
	my %KB_000293383 = (
	'16G' => qr/^(?:F710)/,
	);
	my %RE_A_SERIES = (
	'A_SERIES' => qr/^(?:A2000?)/,		
	);


    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
	my %node_types = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ $RE_PRODUCT) {
		my $product = $1;
		my $node_type = $1 if $product =~ /^([a-zA-Z0-9]+)-/;
		$node_types{$node_type}{'count'}++;
		push( @{ $node_types{$node_type}{'nodes'} }, $lnn); 
		debug("$node_type is now $node_types{$node_type}{'count'}");
		$NODE_INFO{$lnn}{'Product'} = $product;
		$NODE_INFO{$lnn}{'Node Type'} = $node_type;		
		debug("NODE_INFO->${lnn}->Product=$product");
		debug("NODE_INFO->${lnn}->Node Type=$node_type");
		foreach my $gen (keys %NODE_GENS) {
		    if ($product =~ $NODE_GENS{$gen}) {
			$NODE_INFO{$lnn}{'Generation'} = $gen;
			debug("NODE_INFO->${lnn}->Generation=$gen");
			push(@{$CLUSTER_INFO{$gen}}, $lnn);
			debug("CLUSTER_INFO->${gen}=$lnn");
			#if(($gen eq 'Gen6.5' or $gen eq '16G') and 
			#((isCurrentOneFS('>=9.4') and isCurrentOneFS('<9.4.0.19')) or (isCurrentOneFS('>=9.5') and isCurrentOneFS('<9.5.1.0')) or (isCurrentOneFS('>=9.7') and isCurrentOneFS('<9.7.1.0')) or (isCurrentOneFS('=9.8.0.0')))){ 
			#		debug("CLUSTER_INFO->${n14g}=$lnn | Forcing standalone NFP and OneFS and forcing Node Firmware to be rolling ");
			#		$RPS{'OneFS'}{'Exclude Node Firmware'} = 1;
			#		$RPS{'Node Firmware'}{'Method'} = 'rolling' unless $RPS{'Node Firmware'}{'Method'} eq 'simultaneous'; 
			#	};
		    }
		}
		foreach my $mlk (keys %MLK_NODES) {
		    if ($product =~ $MLK_NODES{$mlk}) {
			$NODE_INFO{$lnn}{'Gen6_NMLK'} = $mlk;
			debug("NODE_INFO->${lnn}->MLK=$mlk");
			push(@{$CLUSTER_INFO{$mlk}}, $lnn);
			debug("CLUSTER_INFO->${nmlk}=$lnn");
		    }
		}
		foreach my $a_series (keys %RE_A_SERIES){
		if ($product =~ $RE_A_SERIES{$a_series}) {
		    $NODE_INFO{$lnn}{'A_Series'} = $a_series;
			push(@{$CLUSTER_INFO{'A_Series'}}, $lnn);
		    debug("CLUSTER_INFO -> A Series=$lnn");
		}
		}
		foreach my $de (keys %DE_NODES) {
		    if ($product =~ $DE_NODES{$de}) {
			$NODE_INFO{$lnn}{'DE_NODES'} = $de;
			debug("NODE_INFO->${lnn}->DE=$lnn");
			push(@{$CLUSTER_INFO{'DE_NODES'}}, $lnn);
			debug("CLUSTER_INFO->${nde}=$lnn");
		    }
		}
		foreach my $ftg (keys %fourteen_G_NODES) {
		    if ($product =~ $fourteen_G_NODES{'14G'}) {
				$NODE_INFO{$lnn}{'14G_NODES'} = $ftg;
				debug("NODE_INFO->${lnn}->14G=$lnn");
				push(@{$CLUSTER_INFO{'14G_NODES'}}, $lnn);
				debug("CLUSTER_INFO->${n14g}=$lnn");
			}
		}
		foreach my $stg (keys %sixteen_G_NODES) {
		    if ($product =~ $sixteen_G_NODES{'16G'}) {
				$NODE_INFO{$lnn}{'16G_NODES'} = $stg;
				debug("NODE_INFO->${lnn}->16G=$lnn");
				push(@{$CLUSTER_INFO{'16G_NODES'}}, $lnn);
				debug("CLUSTER_INFO->${n16g}=$lnn");
				if ($product =~ $KB_000293383{'16G'}){ push(@{$CLUSTER_INFO{'KB_000293383'}}, $lnn); }
			}
		}
		if ($product =~ $RE_ACCEL) {
		    $NODE_INFO{$lnn}{'Accelerator'} = 1;
		    debug("NODE_INFO->${lnn}->Accelerator=1");
		    push(@{$CLUSTER_INFO{'Accelerator'}}, $lnn);
		    debug("CLUSTER_INFO->Accelerator=$lnn");
		}
		if ($product =~ $RE_A100) {
		    push(@{$CLUSTER_INFO{'A100'}}, $lnn);
		    debug("CLUSTER_INFO->A100=$lnn");
		}
		if ($product =~ $RE_F900) {
		    push(@{$CLUSTER_INFO{'F900'}}, $lnn);
		    debug("CLUSTER_INFO->F900=$lnn");
		}
		if ($product =~ $RE_SD) {
		    push(@{$CLUSTER_INFO{'IsilonSD'}}, $lnn);
		    debug("CLUSTER_INFO->IsilonSD=$lnn");
		}
	    } elsif ($line =~ $RE_SERIES) {
		$NODE_INFO{$lnn}{'Series'} = $1;
		debug("NODE_INFO->${lnn}->Series=$1");
	    } elsif ($line =~ $RE_FAMILY) {
		if ($1 eq 'V') {
		    $NODE_INFO{$lnn}{'Generation'} = 'Virtual';
		    debug("NODE_INFO->${lnn}->Generation=Virtual");
		    push(@{$CLUSTER_INFO{'Virtual'}}, $lnn);
		    debug("CLUSTER_INFO->Virtual=$lnn");
		}
	    }
	}
	if (!$NODE_INFO{$lnn}{'Generation'}) {
	    $NODE_INFO{$lnn}{'Generation'} = 'Unknown';
	    debug("NODE_INFO->${lnn}->Generation=Unknown");
	}
    }
	$CLUSTER_INFO{'node_type_list'} = 'The following node types are installed: ';
	foreach my $nt (keys %node_types){
		debug("STORING $nt COUNT");
		$CLUSTER_INFO{'node_type_list'} .= $nt."x".$node_types{$nt}{'count'}." (".compressRange(@{ $node_types{$nt}{'nodes'} })."), ";
		debug("CLUSTER_INFO -> node_type_list -> $nt -> $node_types{$nt}{'count'}");
	}
	$CLUSTER_INFO{'node_type_list'} =~ s/,\s+$//g;

		
} # End gatherProductInfo
###
# getGatherDate: Gather the date of the gather from package_info.xml
sub getGatherDate {
    if (isLive()) { return 0 }
    if (defined $RPS{'Gather Date'}) { return $RPS{'Gather Date'} }

    # Setup
    my $CMD_LOGS = q[2>&1 cat package_info.xml];

    # Gather
    my $gather = gatherLocal($CMD_LOGS);

    # Save
    foreach (@$gather) {
	if (/<gather_date>(.*)<\/gather_date>/) {
	    return $RPS{'Gather Date'} = $1;
	}
    }

    # Bugfix: Due to a bug in some OneFS versions, the contents of
    #  package_info.xml may not be populated.  For those instances use the last
    #  modify timestamp of package_info.xml as the gather date.
    if (!$RPS{'Gather Date'} and -e 'package_info.xml') {
	return $RPS{'Gather Date'} = (stat('package_info.xml'))[9];
    }

} # End getGatherDate
###
# getLicense: Returns a reference to the license hash if licensed, otherwise 0
sub getLicense {
    my ($feature) = @_;

    # Return 0 if there is no license being checked
    if (!$feature) {
	debug('getLicense: Returning 0 (No arguments provided)');
	return 0;
    }

    # Gather license details incase not previously gathered
    gatherLicense();

    # Return 0 if license details were not gathered for whatever reason
    if (!$CLUSTER_INFO{'License'}) {
	debug('getLicense: Returning 0 (No license details available)');
	return 0;
    }

    # Search for the licensed feature
    debug("getLicense: Looking for feature that contains: $feature");
    ($feature) = grep {$_ =~ /$feature/i} keys %{$CLUSTER_INFO{'License'}};

    # Return 0 if unable to locate licensed feature
    if (!$feature or
	!$CLUSTER_INFO{'License'}{$feature} or
	!$CLUSTER_INFO{'License'}{$feature}{'Status'}) {
	debug('getLicense: Returning 0 (No feature found)');
	return 0;
    }

    # Return hash reference to license
    return $CLUSTER_INFO{'License'}{$feature};

} # End getLicense
# getNodesOfProduct: Returns a list of nodes matching the requested product
sub getNodesOfProduct {
    my ($re_nodes) = @_;
    if (!$re_nodes) { return }
    gatherProductInfo();
    my @nodes = ();
    foreach my $lnn (@LNNS) {
	if (!$NODE_INFO{$lnn}{'Product'}) { next }
	if ($NODE_INFO{$lnn}{'Product'} =~ /$re_nodes/) { push(@nodes, $lnn) }
    }
    debug("getNodesOfProduct: Request: $re_nodes, Return: [ @nodes ]");
    return @nodes;
}
###
# hasLicense: Returns 1 if licensed, otherwise 0
sub hasLicense {
    my ($feature) = @_;
    my ($license_href) = getLicense($feature);

    # Return 0 if there is no license found
    if (!$license_href) {
	debug('hasLicense: Returning 0 (No license found)');
	return 0;
    }
    debug("hasLicense: $feature license state is: $license_href->{'Status'}");

    # Return 1 if licensed
    if ($license_href->{'Status'} =~ /^(activated|licensed|evaluation)$/i) {
	debug('hasLicense: Returning 1');
	return 1;
    }

    # Return 0 if not licensed
    debug('hasLicense: Returning 0');
    return 0;

} # End hasLicense
###
# Initiate IOCA
sub initIOCA {
    $RPS{'Runtime'} = time();
    $RPS{'Timestamp'} = $RPS{'Runtime'};

    # Parse command line arguments
    parseArguments(@ARGV);

    ## Flag if this version is over 10 days old but still allow checks to be run
    my $days_since_release =
	int(($RPS{'Runtime'} - $RPS{'Release Timestamp'}) / 86400);
	# and $days_since_release < 30) {
	if ($days_since_release > 30){
	printUrgent("This version is over $days_since_release days old, a new".
		    ' version may be available.');
	printUrgent("It is recommended that you always use the latest version.");
	printUrgent("Please use the following link to download the latest version of IOCA: ");
    printUrgent("https://dellservices.my.salesforce.com/sfc/p/0b000000GaMp/a/6P0000000CMx/MAXiAnA9BPs.RJzdbrk40hK3VSoLT_nnbHrRWYxDMpg");
	}

    # Display current time for live cluster or logset path for a logset
    if (isLive()) {
	printCheck('Live Cluster Analysis', scalar(localtime($RPS{'Runtime'})));
    } else {
	printCheck('Logset Path', getcwd());
	if (getGatherDate()) {
	    printCheck('Gather Date', scalar(localtime(getGatherDate())));
	    $RPS{'Timestamp'} = getGatherDate();

	    # Warn if logset gather date is over 1 month old
	    if ($RPS{'Runtime'} - getGatherDate() > 2592000) {
		printUrgent('Logset is over 1 month old');
	    }
	} else {
	    printCheck('Gather Date', 'Undetermined');
	}
    }

    # Determine if the cluster is compliance mode and assign sudo variable
    $SUDO = (isCompliance())?'sudo ':'';

    # Check that script is being ran by root or compadmin
    if (isLive()) {
	my $expected_user = (isCompliance()?'compadmin':'root');
	chomp(my $current_user = `whoami`);
	if ($current_user ne $expected_user) {
	    printUrgent("IOCA should be ran as $expected_user");
	    printUrgent("Results may not be accurate");
	}
    }

    # Gather and display basic node and cluster information
    gatherArrayXML();
    if (!$CLUSTER_INFO{'Name'}) {
	if (isLive()) {
	    printUrgent('Unable to gather details from /etc/ifs/array.xml');
	} else {
	    printUrgent('Unable to gather details from'.
			' */etc.tar/etc/ifs/array.xml'.
			' */etcifs.tar/ifs/array.xml');
	    printUrgent('Please ensure you are running health checks with a'.
			' full gather');
	}
	exit 1;
    }
    if ($CLUSTER_INFO{'Name'}) {
	printCheck('Cluster Name', $CLUSTER_INFO{'Name'});
    }
    if ($CLUSTER_INFO{'GUID'}) {
	printCheck('Cluster GUID', $CLUSTER_INFO{'GUID'});
    }
    if ($CLUSTER_INFO{'Node Count'}) {
	printCheck('Node Count', $CLUSTER_INFO{'Node Count'});
    }
    if (isCompliance()) {
	printCheck('SmartLock Compliance Mode', 'ENABLED');
    }

    # Gather and display current OneFS version
    gatherOneFSVersions();
    if (isCurrentOneFS()) {
	printCheck('Current OneFS Version', isCurrentOneFS());
    }

    # Check destination OneFS version
    checkDestinationOneFS();

    # Check OneFS Version for issues
    # This script will not work on OneFS 5.5 and earlier
    if (isCurrentOneFS('<6')) {
	printUrgent("IOCA is not compatible with versions prior to OneFS 6\n");
	exit 1;
    }

    # If a specific check is to be ran, run it and exit
    if ($RPS{'Run'}) {
	&{$_}() foreach @{$RPS{'Run'}};
	if (@Logger::RESULTS and $RPS{'JSON'}) {
	    print toJSON(\@Logger::RESULTS)."\n";
	    exit 0;
	}
	if (isResults()) {
	    printCheck('Script clean-up');
	    printResults();
	}
	exit 0;
    }

} # End initIOCA
###
# isCompliance: Returns 1 if the cluster is compliance mode enabled
sub isCompliance {
    if (defined $CLUSTER_INFO{'Compliance'}) {
	return $CLUSTER_INFO{'Compliance'};
    }
    $CLUSTER_INFO{'Compliance'} = 0;
    my $CMD_LIVE = q[2>/dev/null sysctl isi.compliance];
    my $CMD_LOGS = q[2>/dev/null cat local/isi*compliance];
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);
    foreach (@$gather) {
	if (/isi.compliance: (\d)/) { $CLUSTER_INFO{'Compliance'} = $1 }
    }
    debug("CLUSTER_INFO->Compliance=$CLUSTER_INFO{'Compliance'}");
    return $CLUSTER_INFO{'Compliance'};
} # End isCompliance
###
# isCurrentOneFS: Uses either Regexp or scalar to test current OneFS
sub isCurrentOneFS {
    my ($check) = @_;
    gatherOneFSVersions();
    if (!$CLUSTER_INFO{'OneFS'}) { return 0 }
    if (!defined $check) { return $CLUSTER_INFO{'OneFS'} }
    if (ref($check) eq 'Regexp') { return $CLUSTER_INFO{'OneFS'} =~ $check }
    if ($check =~ /^>=(\S+)/) {
	return compareVersions($CLUSTER_INFO{'OneFS'}, $1) >= 0;
    }
    if ($check =~ /^<=(\S+)/) {
	return compareVersions($CLUSTER_INFO{'OneFS'}, $1) <= 0;
    }
    if ($check =~ /^>(\S+)/) {
	return compareVersions($CLUSTER_INFO{'OneFS'}, $1) > 0;
    }
    if ($check =~ /^<(\S+)/) {
	return compareVersions($CLUSTER_INFO{'OneFS'}, $1) < 0;
    }
    return $CLUSTER_INFO{'OneFS'} eq $check;
} # End isCurrentOneFS
###
# isDestinationOneFS: Uses either Regexp or scalar to test destination OneFS
sub isDestinationOneFS {
    my ($check) = @_;
    if (!$RPS{'Target'}) { return 0 }
    if (!defined $check) { return $RPS{'Target'} }
    if (ref($check) eq 'Regexp') { return $RPS{'Target'} =~ $check }
    if ($check =~ /^>=(\S+)/) {
	return compareVersions($RPS{'Target'}, $1) >= 0;
    }
    if ($check =~ /^<=(\S+)/) {
	return compareVersions($RPS{'Target'}, $1) <= 0;
    }
    if ($check =~ /^>(\S+)/) {
	return compareVersions($RPS{'Target'}, $1) > 0;
    }
    if ($check =~ /^<(\S+)/) {
	return compareVersions($RPS{'Target'}, $1) < 0;
    }
    return $RPS{'Target'} eq $check;
} # End isDestinationOneFS
sub isHardened {
    if (defined $CLUSTER_INFO{'Hardened'}) { return $CLUSTER_INFO{'Hardened'} }

    # Default to not hardened
    $CLUSTER_INFO{'Hardened'} = 0;

    # Gather hardening status
    my $CMD_LIVE =
	qq[2>&1 ${SUDO} isi_for_array 'ls /etc/ifs/hardening_info.txt'];
    my $CMD_LOGS =
	q[2>&1 grep -H -m 1 . */etc.tar/etc/ifs/hardening_info.txt];
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # If any nodes are hardened, the cluster should be
    foreach my $lnn (@LNNS) {
	if ($gather->{$lnn}) {
	    $CLUSTER_INFO{'Hardened'} = 1;
	    last;
	}
    }

    # Return whether hardened
    return $CLUSTER_INFO{'Hardened'};
}
###
# isLive: returns 1 when ran live on a cluster or 0 when in a logset
sub isLive {
    # Previously called, return value
    if (defined $RPS{'Live'}) { return $RPS{'Live'} }

    # If /ifs exists, assume it is a live system
    if (-d '/ifs') { return $RPS{'Live'} = 1 }

    # If package_info.xml exists, assume it is a logset
    if (-e 'package_info.xml') { return $RPS{'Live'} = 0 }

    # If unable to determine where we are, exit
    printUrgent('Unable to determine where script is running, please run on a'.
		' live cluster or in the root directory of a logset.');
    exit 1;
} # End isLive
# isOneFSUpgrade: Returns true if the source->destination is a OneFS upgrade
sub isOneFSUpgrade {
    my ($source, $destination) = @_;

    # If both arguments are missing
    if (!$source and !$destination) {
	$destination = isDestinationOneFS();
	$source = isCurrentOneFS();
    }

    # If either argument is missing
    if (!$source or !$destination) { return 0 }

    # If source is greater than or equal to the destination
    if (compareVersions($source, $destination) >= 0) { return 0 }

    # Split the code into major, minor, service pack, and patch
    my ($s_major, $s_minor, $s_service, $s_patch) = split(/\./, $source);
    my ($d_major, $d_minor, $d_service, $d_patch) = split(/\./, $destination);

    # If major / minor / service pack levels differ
    if ($s_major != $d_major or
	$s_minor != $d_minor or
	$s_service != $d_service) {
	return 1;
    }

    # If less than OneFS 9.1
    if ($s_major < 9 and $s_minor < 1) { return 1 }

    if (isCurrentOneFS('>=9.5.1')) { return 1 }

    # Else
    return 0;
} # End isOneFSUpgrade
sub isServiceDisabled {
    gatherServices();
    return 1 if @_ and $_[0] and
	$CLUSTER_INFO{'Services'}{$_[0]} and
	$CLUSTER_INFO{'Services'}{$_[0]} =~ /disabled/i;
    return 0;
}
sub isServiceEnabled {
    gatherServices();
    return 1 if @_ and $_[0] and
	$CLUSTER_INFO{'Services'}{$_[0]} and
	$CLUSTER_INFO{'Services'}{$_[0]} =~ /enabled/i;
    return 0;
}
sub isShortName {
    my ($hostname) = @_;
    return 1 if defined $hostname and $hostname !~ /\./;
    return 0;
}
sub isVirtual { gatherProductInfo(); return $CLUSTER_INFO{'Virtual'}; }
###
# parseArguments: Parses command line arguments and sets global variables.
sub parseArguments {

    ###
    # Parse CLI arguments
    my %args = ('raw' => \@ARGV);
    $RPS{'Arguments'} = \%args;
    for (my $i = 0; $i <= $#ARGV; $i++) {
	$_ = lc($ARGV[$i]);

	# OneFS arguments
	if (/^\d+\.\d+\.\d+(?:\.\d+)?$/) {
	    push(@{$args{'OneFS'}}, $_);
	} elsif (/^-?-o(?:nefs)?(?:=(.*))?$/) {
	    push(@{$args{'OneFS'}}, split(',', $1 // $ARGV[++$i]));
	} elsif (/^-?-d(?:isable-)?p(?:re-)?p(?:patched)?$/) {
	    push(@{$args{'OneFS'}}, 'dpp');
	} 
	elsif (/^-?-u(?:pg_)?f(?:orce_)?r(?:olling)?$/) {
	#    push(@{$args{'OneFS'}}, 'rolling');
		$RPS{'OneFS_IDR'} = 1;
	}

	elsif (/^--check_current_versions$/) {
	    push(@{$args{'CheckList'}}, 'current_versions');
	}

	# Drive Firmware arguments
	elsif (/^-?-d(?:rive-)?s(?:upport-)?p(?:ackage)?(?:=(.*))?$/) {
	    push(@{$args{'Drive Firmware'}}, split(',', $1 // $ARGV[++$i]));
	} elsif (/^-?-d(?:rive-)?f(?:irmware)?(?:=(.*))?$/) {
	    push(@{$args{'Drive Firmware'}}, split(',', $1 // $ARGV[++$i]));
	} elsif (/^-?-p(?:riority-)?d(?:rive-)?f(?:irmware-)?o(?:nly)?$/) {
	    push(@{$args{'Drive Firmware'}}, 'pdfo');
	}

	# Node Firmware arguments
	elsif (/^-?-n(?:ode-)?f(?:irmware-?)?(?:p(?:ackage)?)?(?:=(.*))?$/) {
	    push(@{$args{'Node Firmware'}}, split(',', $1 // $ARGV[++$i]));
	} elsif (/^-?-p(?:riority-)?n(?:ode-)?f(?:irmware-)?o(?:nly)?$/) {
	    push(@{$args{'Node Firmware'}}, 'pnfo');
	}

	# Patch arguments
	elsif (/^-?-p(?:atches)?(?:=(.*))?$/) {
	    push(@{$args{'Patch'}}, split(',', $1 // $ARGV[++$i]));
	}

	# Check specifier arguments
	elsif (/^-?-l(?:ist)(?:=(.*))?$/) {
	    push(@{$args{'CheckList'}}, $1 // $ARGV[++$i]);
	} elsif (/^-?-r(?:un)?(?:=(.*))?$/) {
	    push(@{$args{'Run'}}, split(',', $1 // $ARGV[++$i]));
	}

	# Upgrade method arguments
	elsif (/^--rolling$/) {
#		if (/^-?-u(?:pg_)?f(?:orce_)?r(?:olling)?$/) {
#	    	$args{'OneFS_IDR'} = 1;
#		}
	    $args{'Rolling'} = 1;
	} elsif (/^--parallel$/) {
	    $args{'Parallel'} = 1;
	} elsif (/^-?-s(?:imultaneous)?$/) {
	    $args{'Simultaneous'} = 1;
	}

	# Other arguments
	elsif (/^-?-a(?:utotc)?$/) {
	    $args{'AutoTC'} = 1;
	} elsif (/^-?-d(?:ebug)?$/) {
	    $args{'Debug'} = 1;
	} elsif (/^-?-e(?:xtra)?$/) {
	    $args{'Verbose'} = 1;
	} elsif (/^-?-h(?:elp)?$/) {
	    $args{'Help'} = 1;
	} elsif (/^-?-j(?:son)?$/) {
	    $args{'JSON'} = 1;
	} elsif (/^-?-v(?:ersion)?$/) {
	    $args{'Version'} = 1;
	} elsif (/^--definition$/) {
	    $args{'Definition'} = 1;
	} elsif (/^-?-u(?:pgradeplan|pgradation)?$/) {
	    $args{'UpgradePlan'} = 1;
	} elsif (/^-?-f010517ee$/) {
	    $args{'F010517EE'} = 1;
	} elsif (/^-?-f121917ee$/) {
	    $args{'F121917EE'} = 1;
	} elsif (/^-?-f011018ee$/) {
	    $args{'F011018EE'} = 1;
	} elsif (/^-?-f033018ee$/) {
	    $args{'F033018EE'} = 1;
	} elsif (/^-?-f082318ee$/) {
	    $args{'F082318EE'} = 1;
	} elsif (/^-?-t102518ee$/) {
	    $args{'T102518EE'} = 1;
	} elsif (/^-?-t062519ee$/) {
	    $args{'T062519EE'} = 1;
	} elsif (/^-?-et003823$/) {
	    $args{'ET003823'} = 1;
	} elsif (/^-?-f(?:ile-)?c(?:heck)?(?:[=](\S+))?\s?/) {
	    push(@{$args{'Verify_Files'}}, split(',', $1 // $ARGV[++$i]));
	} elsif (/^-?-v(?:alidate-)?id(?:[=\s](.*))?$/){
		push(@{$args{'Validate_ID'}}, split(',', $1 // $ARGV[++$i]));
		$RPS{'Validate_ID'} = $1;
	} else {
	    local *STDOUT = *STDERR;
	    printCheck('Isilon On-Cluster Analysis', $RPS{'Version'});
	    print "\nUnrecognized command line argument provided: $_\n\n";
	    printUsage();
	    exit 1;
	}
    }

    ###
    # Validate arguments
    ###
    our(%NodeFirmware, %DriveSupport, %Code_Matrix,
	%CheckLists, %Patch_Releases);

    # Set logging level
    if ($args{'Verbose'}) { $Logger::LEVEL = &VERBOSE }
    if ($args{'Debug'}) { $Logger::LEVEL = &DEBUG }

    # Display the check definitions
    if ($args{'Definition'}) {
	printDefinitionJSON();
	exit 0;
    }

    # Enable JSON mode
    if ($args{'JSON'}) {
	*IOCA::printCheck = sub { };
	*IOCA::printUrgent = sub { };
	Logger::enableJSON();
	$RPS{'JSON'} = 1;
	#$RPS{'Patch'}{'Destination'} = 'None';
    }

    # Display version
    printCheck('Isilon On-Cluster Analysis', $RPS{'Version'});

    # Exit after displaying version
    if ($args{'Version'}) { exit 0 }

    # Displays help screen and exit
    if ($args{'Help'}) {
	printUsage();
	exit 0;
    }

    # Upgrade method arguments
	my @method = grep {defined $args{$_}} ('Simultaneous', 'Parallel', 'Rolling');

    if (@method > 1) {
	for (my $i = 0; $i < @method; $i++) {
	    $method[$i] = lc("--$method[$i]");
	}
	print STDERR "\nOnly one of the following arguments is allowed at a time: @method\n\n";
	exit 1;
    }
    if ($args{'Simultaneous'}) {
	$RPS{'Simultaneous'} = 1;
	$RPS{'OneFS'}{'Method'} = 'simultaneous';
	$RPS{'Patch'}{'Method'} = 'simultaneous';
    }
    if ($args{'Parallel'}) {
	$RPS{'OneFS'}{'Method'} = 'parallel';
	$RPS{'Patch'}{'Method'} = 'parallel';
	$RPS{'Node Firmware'}{'Method'} = 'parallel';
    }
    if ($args{'Rolling'}) {
		#if($RPS{'OneFS_IDR'}){
			$RPS{'OneFS'}{'Method'} = 'rolling';
		#}
		#else{ 
		#	$RPS{'OneFS_EDR'} = 1; 
		#	}
		$RPS{'Patch'}{'Method'} = 'rolling';
		$RPS{'Node Firmware'}{'Method'} = 'rolling';
    }


if (defined $args{'Verify_Files'}) {
	@{$args{'Verify_Files'}} = merge(@{$args{'Verify_Files'}});

	# file location overlap check
	my @location = grep { $_ =~ /^(?:[=,](\S+))?\s?$/ } @{$args{'Verify_Files'}};
	if (@location > 1) {
	    print STDERR "\nOnly one location is allowed to be specified at a time\n\n";
	    exit 1;
	}
	foreach (@{$args{'Verify_Files'}}) {
	    if (/^$/) {
		$RPS{'Verify_Files'}{'Location'} = '/ifs/data/';
		$RPS{'CheckList'} = 'file_verification';
	    next;
	    } elsif (/^\/ifs\S+/) {
		$RPS{'Verify_Files'}{'Location'} = $_;
		$RPS{'CheckList'} = 'file_verification';
	    } else {
		print STDERR "\nUnrecognized File Location: $_ (Files must be located in /ifs or a sub directory within /ifs)\n\n";
		exit 1;
	    }
	}
    }

    ###
    # OneFS options
    ###
    if (defined $args{'OneFS'}) {
	@{$args{'OneFS'}} = merge(@{$args{'OneFS'}});

	# Check for upgrade method overlap
	my @method = grep { $_ =~ /^(?:simultaneous|parallel|rolling)$/ } @{$args{'OneFS'}};
	if (@method > 1) {
	    print STDERR "\nOnly one of the following OneFS options is allowed at a time: @method\n\n";
	    exit 1;
	}

	# Check for multiple OneFS destinations
	my @destination = grep {$_ =~ /^[0-9.]+$/} @{$args{'OneFS'}};
	if (@destination > 1) {
	    print STDERR "\nMultiple different OneFS destination arguments detected: @destination\n\n";
	    exit 1;
	}

	foreach (@{$args{'OneFS'}}) {
	    if (/^$/) {
		next;
	    } elsif (/^[0-9.]+$/) {
		$RPS{'OneFS'}{'Destination'} = $_;
	    } elsif (/simultaneous/i) {
		$RPS{'OneFS'}{'Method'} = 'simultaneous';
		$RPS{'Simultaneous'} = 1; # FixMe
	    } elsif (/rolling/i) {
		$RPS{'OneFS'}{'Method'} = 'rolling';
	    } elsif (/parallel/i) {
		$RPS{'OneFS'}{'Method'} = 'parallel';
	    } elsif (/standalone|dpp|disable-pre-patched/i) {
		$RPS{'OneFS'}{'Standalone'} = 1;
		$RPS{'Disable Pre-Patched'} = 1;
	    } elsif (/exclude-nf/) {
		$RPS{'OneFS'}{'Exclude Node Firmware'} = 1;
	    } else {
		print STDERR "\nUnrecognized OneFS option: $_\n\n";
		exit 1;
	    }
	}
    }

    # Check the destination OneFS is recognized
    if (defined $RPS{'OneFS'}{'Destination'}) {
	my @destinations = merge(keys %Code_Matrix, keys %Patch_Releases);
	@destinations = grep {$_ =~ /^\d+\.\d+\.\d+\.\d+$/} @destinations;
	@destinations = sort {compareVersions($b, $a)} @destinations;
	my @exact_match = grep {$_ eq $RPS{'OneFS'}{'Destination'}} @destinations;
	if (scalar(@exact_match) == 1) {
	    $RPS{'OneFS'}{'Destination'} = $exact_match[0];
	} else {
	    my @results = grep {$_ =~ /^$RPS{'OneFS'}{'Destination'}/} @destinations;
	    if (not @results) {
		print STDERR "\nRequested OneFS destination version, $RPS{'OneFS'}{'Destination'},".
		    " was not recognized.";
		print STDERR "\nAvailable OneFS destination versions to check are: @destinations\n\n";
		exit 1;
	    } elsif (scalar(@results) > 1) {
		print STDERR "\nMultiple OneFS versions matched $RPS{'OneFS'}{'Destination'}: @results";
		print STDERR "\nAvailable OneFS destination versions to check are: @destinations\n\n";
		exit 1;
	    } else {
		$RPS{'OneFS'}{'Destination'} = $results[0];
	    }
	}
    }

    ###
    # Patch options
    if (defined $args{'Patch'}) {
	@{$args{'Patch'}} = merge(@{$args{'Patch'}});

	# Patch method overlap check
	my @method = grep { $_ =~ /^(?:simultaneous|parallel|rolling)$/ } @{$args{'Patch'}};
	if (@method > 1) {
	    print STDERR "\nOnly one of the following Patch options is allowed at a time: @method\n\n";
	    exit 1;
	}
	foreach (@{$args{'Patch'}}) {
	    if (/^$/) {
		next;
	    } elsif (/^none$/) {
		$RPS{'Patch'}{'Destination'} = 'None';
	    } elsif (/^simultaneous$/) {
		$RPS{'Patch'}{'Method'} = 'simultaneous';
	    } elsif (/^rolling$/) {
		$RPS{'Patch'}{'Method'} = 'rolling';
	    } elsif (/^parallel$/) {
		$RPS{'Patch'}{'Method'} = 'parallel';
	    } else {
		print STDERR "\nUnrecognized Patch option: $_\n\n";
		exit 1;
	    }
	}
    }

    ###
    # Drive firmware options
    ###
    if (defined $args{'Drive Firmware'}) {
	@{$args{'Drive Firmware'}} = merge(@{$args{'Drive Firmware'}});
	foreach (@{$args{'Drive Firmware'}}) {
	    if (/^$/) {
		next;
	    } elsif (/^none$/i) {
		$RPS{'Drive Firmware'}{'Destination'} = 'None';
	    } elsif (/^([0-9.]+)$/) {
		$RPS{'Drive Firmware'}{'Destination'} = $1;
	    } elsif (/pdfo/) {
		$RPS{'Priority DF Only'} = 1;
	    } else {
		print STDERR "\nUnrecognized Drive Firmware option: $_\n\n";
		exit 1;
	    }
	}
    }
    # Validate destination Drive Support Package is recognized
    if (defined $RPS{'Drive Firmware'}{'Destination'} and not grep {$_ eq $RPS{'Drive Firmware'}{'Destination'}} keys %DriveSupport) {
	print STDERR "\nRequested Drive Support Package version, $RPS{'Drive Firmware'}{'Destination'}, was not recognized.";
	print STDERR "\nAvailable versions to check are: ".join(', ',sort {compareVersions($b, $a)} keys %DriveSupport)."\n\n";
	exit 1;
    }

    ###
    # Node firmware options
    if (defined $args{'Node Firmware'}) {
	@{$args{'Node Firmware'}} = merge(@{$args{'Node Firmware'}});

	# Node Firmware method overlap check
	my @method = grep { $_ =~ /^(?:simultaneous|parallel|rolling|balanced)$/ } @{$args{'Node Firmware'}};
	if (@method > 1) {
	    print STDERR "\nOnly one of the following Node Firmware options is allowed at a time: @method\n\n";
	    exit 1;
	}
	foreach (@{$args{'Node Firmware'}}) {
	    if (/^$/) {
		next;
	    } elsif (/^([0-9.]+)$/) {
		$RPS{'Node Firmware'}{'Destination'} = $1;
	    } elsif (/^none$/i) {
		$RPS{'Node Firmware'}{'Destination'} = 'None';
	    } elsif (/^simultaneous$/i) {
		$RPS{'Node Firmware'}{'Method'} = 'simultaneous';
	    } elsif (/^balanced|parallel$/i) {
		$RPS{'Node Firmware'}{'Method'} = 'parallel';
	    } elsif (/^rolling$/i) {
		$RPS{'Node Firmware'}{'Method'} = 'rolling';
	    } elsif (/^pnfo$/) {
		$RPS{'Priority NF Only'} = 1;
	    } else {
		print STDERR "\nUnrecognized Node Firmware option: $_\n\n";
		exit 1;
	    }
	}
    }
    # Validates target Node Firmware Package is recognized
    if (defined $RPS{'Node Firmware'}{'Destination'} and not grep {$_ eq $RPS{'Node Firmware'}{'Destination'}} keys %NodeFirmware) {
	print STDERR "\nRequested Node Firmware Package version, $RPS{'Node Firmware'}{'Destination'}, was not recognized.";
	print STDERR "\nAvailable versions to check are: ".join(', ',sort {compareVersions($b, $a)} keys %NodeFirmware)."\n\n";
	exit 1;
    }

    # Validates run argument is supported
    if (defined $args{'Run'}) {
	my @unrecognized = ();
	my @checks = ();
	foreach my $check (@{$args{'Run'}}) {
	    my ($match) = grep {$_ =~ /^$check$/i} keys %CHECKS;
	    if ($match || $check eq 'checkNICType') {
		if($check eq 'checkNICType'){$match = $check;};
		push(@checks, $match);
	    } else {
		push(@unrecognized, $check);
	    }
	}
	if (@unrecognized) {
	    print STDERR "\nRequested checks, ".join(', ', @unrecognized).', was not recognized.';
	    print STDERR "\nAvailable checks are: ";
	    foreach my $check (sort keys %CHECKS) {
		next unless $CHECKS{$check}{'Description'};
		printf STDERR "\n\t%-30s%s", $check, $CHECKS{$check}{'Description'};
	    }
	    print STDERR "\n\n";
	    exit 1;
	}
	if (@checks) { $RPS{'Run'} = \@checks }
    }

    # Validates list argument is supported
    if (defined $args{'CheckList'}) {
	if (@{$args{'CheckList'}} > 1) {
	    print STDERR "\nOnly one check list can be ran and multiple check lists requested: @{$args{'CheckList'}}\n\n";
	    exit 1;
	} else {
	    $args{'CheckList'} = $args{'CheckList'}[0];
	}
	my ($checklist) = grep {$_ eq $args{'CheckList'}} keys %CheckLists;
	if ($checklist) {
	    $RPS{'CheckList'} = $checklist;
	} else {
	    print STDERR "\nRequested check list, $args{'CheckList'}, was not recognized.";
	    print STDERR "\nAvailable check lists are: ".join(', ', sort keys %CheckLists);
	    print STDERR "\n\n";
	    exit 1;
	}
    }

    ###
    # FCO Arguments
    # Only allow checking for one FCO/TSE at a time
    if (1 < grep {exists $args{$_}} ('T102518EE',
				     'F010517EE',
				     'F121917EE',
				     'F011018EE',
				     'F033018EE',
				     'F082318EE',
				     'T062519EE',
				     'ET003823')) {
	# Only one FCO should be specified at any given time?
	print STDERR "\nMultiple FCO arguments provided, only one TSE/FCO should be handled at once.\n\n";
	exit 1;
    }

    # F010517EE
    if ($args{'F010517EE'}) {
	if ($RPS{'Node Firmware'}{'Destination'} and compareVersions($RPS{'Node Firmware'}{'Destination'}, '9.3.5') < 0) {
	    print STDERR "\nRequested Node Firmware Package version, $RPS{'Node Firmware'}{'Destination'}, is less than Node Firmware Package 9.3.5 which is required for FCO F010517EE.\n\n";
	    exit 1;
	}
	if ($RPS{'OneFS'}{'Destination'} and
	    (($RPS{'OneFS'}{'Destination'} =~ /^7/ and compareVersions($RPS{'OneFS'}{'Destination'}, '7.2.1.6') < 0) or
	     ($RPS{'OneFS'}{'Destination'} =~ /^8/ and compareVersions($RPS{'OneFS'}{'Destination'}, '8.0.0.4') < 0) or
	     ($RPS{'OneFS'}{'Destination'} =~ /^8\.0\.1\.0/))) {
	    print STDERR "\nDestination OneFS version, $RPS{'OneFS'}{'Destination'}, is less than the minimum required for FCO F010517EE: OneFS 8.0.0.4 or later, OneFS 8.0.1.1 or later, or OneFS 7.2.1.6 or later.\n\n";
	    exit 1;
	}
	$RPS{'F010517EE'} = 1;
	$RPS{'UpgradePlan'} = 1;
    }

    # F121917EE
    if ($args{'F121917EE'}) {
	if ($RPS{'Node Firmware'}{'Destination'} and compareVersions($RPS{'Node Firmware'}{'Destination'}, '10.1.2') < 0) {
	    print STDERR "\nDestination Node Firmware Package version, $RPS{'Node Firmware'}{'Destination'}, is less than Node Firmware Package 10.1.2 which is required for FCO F121917EE.\n\n";
	    exit 1;
	}
	if ($RPS{'OneFS'}{'Destination'}) {
	    print STDERR "\nFCO F121917EE does not include a OneFS update, please re-run without a destination OneFS version.\n\n";
	    exit 1;
	}
	if ($RPS{'Drive Firmware'}{'Destination'}) {
	    print STDERR "\nFCO F121917EE does not include drive firmware updates, please re-run without any drive firmware versions.\n\n";
	    exit 1;
	}
	$RPS{'F121917EE'} = 1;
	$RPS{'UpgradePlan'} = 1;
	$RPS{'Drive Firmware'}{'Destination'} = 'None';
    }

    # F011018EE
    if ($args{'F011018EE'}) {
	if ($RPS{'Node Firmware'}{'Destination'} and compareVersions($RPS{'Node Firmware'}{'Destination'}, '10.1.2') < 0) {
	    print STDERR "\nDestination Node Firmware Package version, $RPS{'Node Firmware'}{'Destination'}, is less than Node Firmware Package 10.1.2 which is required for FCO F011018EE.\n\n";
	    exit 1;
	}
	if ($RPS{'OneFS'}{'Destination'}) {
	    print STDERR "\nFCO F011018EE does not include a OneFS update, please re-run without a destination OneFS version.\n\n";
	    exit 1;
	}
	if ($RPS{'Drive Firmware'}{'Destination'}) {
	    print STDERR "\nFCO F011018EE does not include drive firmware updates, please re-run without any drive firmware versions.\n\n";
	    exit 1;
	}
	$RPS{'F011018EE'} = 1;
	$RPS{'UpgradePlan'} = 1;
	$RPS{'Drive Firmware'}{'Destination'} = 'None';
    }

    # F033018EE
    if ($args{'F033018EE'}) {
	if ($RPS{'OneFS'}{'Destination'} and compareVersions($RPS{'OneFS'}{'Destination'}, '8.1.2.0') < 0) {
	    print STDERR "\nFCO F033018EE includes a OneFS update to OneFS 8.1.2.0 and later, please re-run with a destination OneFS version of OneFS 8.1.2.0 or later.\n\n";
	    exit 1;
	}
	if ($RPS{'Drive Firmware'}{'Destination'} and compareVersions($RPS{'Drive Firmware'}{'Destination'}, '1.24') < 0) {
	    print STDERR "\nFCO F033018EE includes drive firmware updates to Drive Support Package 1.24 and later, please re-run with a Drive Support Package of 1.24 or later.\n\n";
	    exit 1;
	}
	$RPS{'UpgradePlan'} = 1;
	$RPS{'F033018EE'} = 1;
    }

    # F082318EE
    if ($args{'F082318EE'}) {
	if ($RPS{'OneFS'}{'Destination'} and compareVersions($RPS{'OneFS'}{'Destination'}, '8.1.0.4') < 0) {
	    print STDERR "\nFCO F082318EE includes a OneFS update to OneFS 8.1.0.4 and later, please re-run with a destination OneFS version of OneFS 8.1.0.4 or later.\n\n";
	    exit 1;
	}
	if ($RPS{'Node Firmware'}{'Destination'} and compareVersions($RPS{'Node Firmware'}{'Destination'}, '10.1.6') < 0) {
	    print STDERR "\nFCO F082318EE includes node firmware updates to Node Firmware Package 10.1.6 and later, please re-run with a Node Firmware Package of 10.1.6 or later.\n\n";
	    exit 1;
	}
	$RPS{'F082318EE'} = 1;
	$RPS{'UpgradePlan'} = 1;
    }

    # T102518EE
    if ($args{'T102518EE'}) {
	if ($RPS{'OneFS'}{'Destination'} and compareVersions($RPS{'OneFS'}{'Destination'}, '8.1.0.4') < 0) {
	    print STDERR "\nTSE T102518EE includes a OneFS update to OneFS 8.1.0.4 and later, please re-run with a destination OneFS version of OneFS 8.1.0.4 or later.\n\n";
	    exit 1;
	}
	if ($RPS{'Node Firmware'}{'Destination'} and compareVersions($RPS{'Node Firmware'}{'Destination'}, '10.2.1') < 0) {
	    print STDERR "\nTSE T102518EE includes node firmware updates to Node Firmware Package 10.2.1 and later, please re-run with a Node Firmware Package of 10.2.1 or later.\n\n";
	    exit 1;
	}
	if ($RPS{'Drive Firmware'}{'Destination'} and compareVersions($RPS{'Drive Firmware'}{'Destination'}, '1.28') < 0) {
	    print STDERR "\nTSE T102518EE includes drive firmware updates to Drive Support Package 1.28 and later, please re-run with a Drive Support Package of 1.28 or later.\n\n";
	    exit 1;
	}
	$RPS{'T102518EE'} = 1;
	$RPS{'UpgradePlan'} = 1;
    }

    # T062519EE
    if ($args{'T062519EE'}) {
	$RPS{'UpgradePlan'} = 1;
	$RPS{'T062519EE'} = 1;
    }

    # ET003823
    if ($args{'ET003823'}) {
	if ($RPS{'OneFS'}{'Destination'} and compareVersions($RPS{'OneFS'}{'Destination'}, '8.2.1.0') < 0) {
	    print STDERR "\nPFN ET003823 includes a OneFS update to OneFS 8.2.1.0 build 004 from 8.2.1.0 build 003, please re-run with a destination OneFS version of OneFS 8.2.1.0 or later.\n\n";
	    exit 1;
	}
	$RPS{'ET003823'} = 1;
	$RPS{'UpgradePlan'} = 1;
    }

    if ($args{'UpgradePlan'}) { $RPS{'UpgradePlan'} = 1 }

    # If -u or a target OneFS code level is specified, run pre-upgrade checks
    if (!$args{'CheckList'} and ($RPS{'UpgradePlan'} or $RPS{'OneFS'}{'Destination'})) {
	$RPS{'CheckList'} = 'pre-upgrade';
    }

    # Remove colored output for AutoTCs
    if ($args{'AutoTC'}) {
	$RPS{'AutoTC'} = 1;
	disableColors();
	no warnings 'redefine';
	*colored = sub { return $_[0] if $_[0]; }
    }

    # Set legacy arguments
    if ($RPS{'OneFS'}{'Destination'}) {
	$RPS{'Target'} = $RPS{'OneFS'}{'Destination'};
    }
    if ($RPS{'Patch'}{'Destination'}) {
	$RPS{'Patches'} = $RPS{'Patch'}{'Destination'};
	if ($RPS{'Patch'}{'Destination'} eq 'None') {
	    $RPS{'Disable Pre-Patched'} = 1;
	}
    }
    if ($RPS{'Node Firmware'}{'Destination'}) {
	$RPS{'IsiFw'} = $RPS{'Node Firmware'}{'Destination'};
    }
    if ($RPS{'Drive Firmware'}{'Destination'}) {
	$RPS{'DSP'} = $RPS{'Drive Firmware'}{'Destination'};
    }

} # End parseArguments
###
# printDefinitionJSON: Prints the health check framework definition in JSON
sub printDefinitionJSON {

    # Create an array of items which will be the definitions of each check
    my @items = ();

    my %exclude = (
	'checkLogLevel' => 1,
	);

    # Add full IOCA as an item
    my %ioca = (
	'name' => "ioca",
	'reference' => "https://www.dell.com/support/kbdoc/000021811",
	'summary' => "Runs IOCA health checks",
	'description' => "",
	'freshness' => 300,
	'parameters' => [],
	'unsupported' => []);
    push(@items, \%ioca);

	my %target_versions = (
		'NFP Target' => $RPS{'IsiFw'},
		'DSP Target' => $RPS{'DSP'},
		'OneFS Target' => $RPS{'Target'},
	);
	push(@items, \%target_versions);

    # Add each individual check as an item
    foreach my $check (sort keys %CHECKS) {
	next unless $CHECKS{$check}{'Description'};
	next if $CHECKS{$check}{'Exclude'};
	if ($exclude{$check}) { next }
	my %item = ();
	$item{'name'} = "ioca_$check";
	$item{'reference'} = $CHECKS{$check}{'Reference'} || "";
	$item{'summary'} = $CHECKS{$check}{'Description'};
	$item{'description'} = "";
	$item{'freshness'} = 300;
	@{$item{'parameters'}} = ();
	@{$item{'unsupported'}} = ();
	push(@items, \%item);
    }

    # Create the definition hash
    my %definition = ();
    $definition{'version'} = $RPS{'Version'};
    $definition{'node'} = "false";
    $definition{'cost'} = 5;
    @{$definition{'items'}} = @items;

    # Print the definition
    print toJSON(\%definition)."\n";
}
# printUsage: Displays help message
sub printUsage {
print <<HELPMSG;

Usage: IOCA [options] [destination OneFS version]
    -d, --debug       Display debugging information
    -e, --extra       Displays extra details as part of each check
    -j, --json        Displays output in JSON format
    -v, --version     Displays current script version
    -h, --help        Displays this help screen
    -r <checkName>, --run=<checkName>
        Executes only the specified check, can be included multiple times
    -u, --upgradeplan Includes an upgrade plan after health checks
    --parallel        Provide parallel reboot plans [where supported] ** Recommended **
HELPMSG
print <<HELPMSG;  
    --rolling         Provide rolling reboot plans
HELPMSG unless isCurrentOneFS('>=9.11')
    --simultaneous    Provide simultaneous reboot plans [excludes node firmware]
    -o, --onefs
        Supports the following comma separated options [ex. 8.1.2,simultaneous]:
            <version>       Uses the provided destination OneFS version
            simultaneous    Simultaneous OneFS upgrade
            parallel        Parallel OneFS upgrade [requires 8.2.2+] ** Recommended **
            rolling         Rolling OneFS upgrade
	    exclude-nf      Upgrade plans will combine OneFS + node firmware by
	                    default [9.2 feature], this option disables that
    -p, --patches
        Supports the following comma separated options [ex. none,simultaneous]:
            none            Opt out of patch recommendations
            simultaneous    Simultaneous patch installs
            parallel        Parallel patch installs [requires 9.1+] ** Recommended **
            rolling         Rolling patch installs
    -nf, --node-firmware
        Supports the following comma separated options [ex. 13.1,parallel]:
            <version>       Uses the provided version for node firmware checks
            none            Opt out of node firmware recommendations
            simultaneous    Simultaneous node firmware updates [requires 8.2+]
            parallel        Parallel node firmware updates [requires 8.2+] ** Recommended **
            rolling         Rolling node firmware updates
    -df, --drive-firmware
        Supports the following comma separated options [ex. 1.56]:
            <version>       Uses the provided version for drive firmware checks
            none            Opt out of drive firmware recommendations

HELPMSG
} # End printUsage
###
# wasCalled: Returns 1 if the parent subroutine has already called
#  wasCalled, otherwise adds subroutine to list of called subroutines.
sub wasCalled {
    my $subroutine = (caller(1))[3];
    if (scalar(grep {$_ eq $subroutine} @{$RPS{'Called'}})) {
	return 1;
    } else {
	push(@{$RPS{'Called'}}, $subroutine);
	debug("--------------------");
	debug(" Called: $subroutine");
	debug("--------------------");
	return 0;
    }
} # End wasCalled
$CHECKS{'checkBESwitch'} = {
    'Description' => 'Checks for Dell BE Switches running 10.5.2 or higher with 40G cables and mentions that the cable needs to be checked from the switch to ensure it is not a 3M AOC cable.',
    'Exclude' => ,
};
sub checkBESwitch {
	return if wasCalled();

    # Setup
	gatherBESwitch();
    hasDellSwitch();
	
    return if !hasDellSwitch();

    printCheck('BE Switch ID');
	
    # Gather
	my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);
    my @nodes = ();
	my @switches = ();
    my $switch_dnos;
	my $switch_sn;
    if (@$gather){
        my $gather_IC = gatherCluster(isLive()?$CMD_LIVE_IC:$CMD_LOGS_IC);
        foreach my $line (@$gather) {
            debug("Switch details: $line\n");
            next unless ($line =~ /^\s*\"?Dell.*\s*$/);
            if($line =~ /^\s*\"?Dell.*v(\d+\.\d+\.\d+\.\d+).*\(Leaf\)\s+\((\S+)\).*$/){
                $switch_dnos = $1;
                $switch_sn = $2;
                if(compareVersions($switch_dnos,'10.5.2.0') >= 0 and compareVersions($switch_dnos,'10.5.2.13') < 0){
                    debug("Switch is on version 10.5.2 or higher, current version is: $switch_sn | $switch_dnos");
                    push(@switches, $switch_sn);
                }
                else{
                    debug("Switch is not on version 10.5.2 or higher, current version is: $switch_sn | $switch_dnos");
                }
            }
        }

        foreach my $lnn (@LNNS) {
            foreach $l (@{$gather_IC->{$lnn}}){
                debug("Checking Interface: $l");
                if (lc($l) =~ /^.*\>\(40gbase\-sr4\s.*$/){
                    push(@nodes, $lnn);
                }
            }
        }
	}
    # Flag
    if (@nodes and @switches) {
		addCustomerInformation(warning('Dell BE Switches running DNOS 10.5.2 or higher using 40G 3M Active Optical Cable (AOC) may experiance issues when the connected node reboots. Please ensure that the cable in use is not a 3M cable. The following nodes have 40G cables: '.compressRange(@nodes)));
        info("Dell BE Switches should be upgraded to DNOS 10.5.2.13 or higher to avoid this issue.");
        info('Please reference KB 000218103 (https://www.dell.com/support/kbdoc/en-ie/000218103) which provides steps to verify from the switches which cable is in use.');        
    }
     
   	printResults();
   } # End checkBESwitch

$CHECKS{'checkIBInterfaces'} = {
    'Description' => 'Checks for ib0/1 as being active, checks for ETA180317 IB switch firmware versions, and checks for overlapping IB networks',
    'Exclude' => 1,
};
sub checkIBInterfaces {
    return if wasCalled();
    checkProcesses();
    printCheck("IB Interfaces Active");
    gatherNetworkInterfaces();
    gatherArrayXML();
    gatherETA180317();
    my @nodes_with_issues = ();
    my @nodes_with_oactive_issues = ();

    foreach my $lnn (@LNNS) {
		verboseHeader($NODE_INFO{$lnn}{'Name'});
		next unless $NODE_INFO{$lnn}{'Network'} and $CLUSTER_INFO{'Active IB Interfaces'};
		verbose("ib0 Status: $NODE_INFO{$lnn}{'Network'}{'ib0'}{'status'}") if $NODE_INFO{$lnn}{'Network'}{'ib0'}{'status'};
		verbose("ib1 Status: $NODE_INFO{$lnn}{'Network'}{'ib1'}{'status'}") if $NODE_INFO{$lnn}{'Network'}{'ib1'}{'status'};
		verbose("ib0: $NODE_INFO{$lnn}{'Network'}{'ib0'}{'flags'}") if $NODE_INFO{$lnn}{'Network'}{'ib0'}{'flags'};
		verbose("ib1: $NODE_INFO{$lnn}{'Network'}{'ib1'}{'flags'}") if $NODE_INFO{$lnn}{'Network'}{'ib1'}{'flags'};
		
		if ($CLUSTER_INFO{'Active IB Interfaces'} == 1) {
	    	if (($NODE_INFO{$lnn}{'Network'}{'ib0'}{'status'} and $NODE_INFO{$lnn}{'Network'}{'ib0'}{'status'} eq 'active') or
				($NODE_INFO{$lnn}{'Network'}{'ib1'}{'status'} and $NODE_INFO{$lnn}{'Network'}{'ib1'}{'status'} eq 'active')) {
				debug("checkIBInterfaces: Node $lnn passes IB interfaces active check");
	    	} else {
			push(@nodes_with_issues, $lnn);
			debug("checkIBInterfaces: Node $lnn does not have at least one active IB interface");
	    	}
			if (($NODE_INFO{$lnn}{'Network'}{'ib0'}{'flags'} and $NODE_INFO{$lnn}{'Network'}{'ib0'}{'flags'} =~ /OACTIVE/) or
				($NODE_INFO{$lnn}{'Network'}{'ib1'}{'flags'} and $NODE_INFO{$lnn}{'Network'}{'ib1'}{'flags'} =~ /OACTIVE/)) {
					push(@nodes_with_oactive_issues, $lnn);
					debug("checkIBInterfaces: Node $lnn IB interface showing OACTIVE flag present");
			
			}
			else{
				debug("checkIBInterfaces: Node $lnn passes IB interfaces OACTIVE check");
			}
		} elsif ($CLUSTER_INFO{'Active IB Interfaces'} == 2) {
			$CLUSTER_INFO{'IBQP_Check'} = 1;
			debug('ACTIVE IB INTERFACES is 2 - IBQP_Check set to 1');
	    	if (($NODE_INFO{$lnn}{'Network'}{'ib0'}{'status'} and $NODE_INFO{$lnn}{'Network'}{'ib0'}{'status'} eq 'active') and
			($NODE_INFO{$lnn}{'Network'}{'ib1'}{'status'} and $NODE_INFO{$lnn}{'Network'}{'ib1'}{'status'} eq 'active')) {
				debug("checkIBInterfaces: Node $lnn passes IB interfaces active check");
	    	} else {
				push(@nodes_with_issues, $lnn);
				debug("checkIBInterfaces: Node $lnn does not have at least two active IB interface");
	    	}
			if (($NODE_INFO{$lnn}{'Network'}{'ib0'}{'flags'} and $NODE_INFO{$lnn}{'Network'}{'ib0'}{'flags'} =~ /OACTIVE/) or
				($NODE_INFO{$lnn}{'Network'}{'ib1'}{'flags'} and $NODE_INFO{$lnn}{'Network'}{'ib1'}{'flags'} =~ /OACTIVE/)) {
					push(@nodes_with_oactive_issues, $lnn);
					debug("checkIBInterfaces: Node $lnn IB interface showing OACTIVE flag present");
			
			}
			else{
				debug("checkIBInterfaces: Node $lnn passes IB interfaces OACTIVE check");
			}
		}
		$NODE_INFO{$node}{'Network'}{$interface}{'flags'}
	}

    # Check for overlapping IB networks
    if ($CLUSTER_INFO{'Interfaces'}) {
		debug("checkIBInterfaces: Searching for minimum netmask...");
		my $min_netmask = 0b11111111111111111111111111111111;
		foreach my $interface (sort keys %{$CLUSTER_INFO{'Interfaces'}}) {
	    	verboseHeader("Internal Interface $interface");
	    	verbose("Flags: $CLUSTER_INFO{'Interfaces'}{$interface}{'Flags'}") if $CLUSTER_INFO{'Interfaces'}{$interface}{'Flags'};
	    	verbose("Low: $CLUSTER_INFO{'Interfaces'}{$interface}{'Low'}") if $CLUSTER_INFO{'Interfaces'}{$interface}{'Low'};
	    	verbose("High: $CLUSTER_INFO{'Interfaces'}{$interface}{'High'}") if $CLUSTER_INFO{'Interfaces'}{$interface}{'High'};
	    	verbose("Netmask: $CLUSTER_INFO{'Interfaces'}{$interface}{'Netmask'}") if $CLUSTER_INFO{'Interfaces'}{$interface}{'Netmask'};
	    	verbose("MTU: $CLUSTER_INFO{'Interfaces'}{$interface}{'MTU'}") if $CLUSTER_INFO{'Interfaces'}{$interface}{'MTU'};
	    	next unless $CLUSTER_INFO{'Interfaces'}{$interface}{'Netmask'};
	    	next unless $CLUSTER_INFO{'Interfaces'}{$interface}{'Flags'};
	    	next unless $CLUSTER_INFO{'Interfaces'}{$interface}{'Flags'} =~ /enabled/;
	    	my $bin_netmask = convertIPToNumeric($CLUSTER_INFO{'Interfaces'}{$interface}{'Netmask'});
	    	$min_netmask = $bin_netmask if not defined $min_netmask or $bin_netmask < $min_netmask;
		}
		debug("checkIBInterfaces: Minimum mask: ".sprintf("%b", $min_netmask));
		debug("checkIBInterfaces: Evaluating for overlap between IB networks...");
		my @seen_interfaces = ();
		foreach my $interface_a (keys %{$CLUSTER_INFO{'Interfaces'}}) {
	    	next unless $CLUSTER_INFO{'Interfaces'}{$interface_a}{'Netmask'};
	    	next unless $CLUSTER_INFO{'Interfaces'}{$interface_a}{'Flags'};
	    	next unless $CLUSTER_INFO{'Interfaces'}{$interface_a}{'Flags'} =~ /enabled/;
	    	push(@seen_interfaces, $interface_a);
	    	my $a_lowaddr_bin = convertIPToNumeric($CLUSTER_INFO{'Interfaces'}{$interface_a}{'Low'});
	    	foreach my $interface_b (keys %{$CLUSTER_INFO{'Interfaces'}}) {
				next if $interface_a eq $interface_b;
				next unless $CLUSTER_INFO{'Interfaces'}{$interface_b}{'Netmask'};
				next unless $CLUSTER_INFO{'Interfaces'}{$interface_b}{'Flags'};
				next unless $CLUSTER_INFO{'Interfaces'}{$interface_b}{'Flags'} =~ /enabled/;
				my @results = grep {$_ eq $interface_b} @seen_interfaces;
				next if @results;
				my $b_lowaddr_bin = convertIPToNumeric($CLUSTER_INFO{'Interfaces'}{$interface_b}{'Low'});
				if (($a_lowaddr_bin & $min_netmask) == ($b_lowaddr_bin & $min_netmask)) {
		    		debug("checkIBInterfaces: Found overlap between $interface_a and $interface_b");
		    		warning("Overlap found between IB interfaces $interface_a and $interface_b");
				} else {
		    		debug("checkIBInterfaces: No overlap between $interface_a and $interface_b");
				}
	    	}	
		}
    }
	
	if (@nodes_with_oactive_issues){
		fail("The following nodes have IB interfaces showing OACTIVE flag set: ".compressRange(@nodes_with_oactive_issues)) if @nodes_with_oactive_issues;
		info('Please create a technical support SR to engage support for assistance in troubleshooting and resolution.');
    }
	if ($RPS{'ETA'}{'180317'}{'Message'} || @nodes_with_issues){
		warning(@{$RPS{'ETA'}{'180317'}{'Message'}}) if $RPS{'ETA'}{'180317'}{'Message'};
		fail("The following nodes do not have $CLUSTER_INFO{'Active IB Interfaces'} active IB interfaces: ".compressRange(@nodes_with_issues)) if @nodes_with_issues;
		info(sprintf($MSG{'KB_1'}, '000037993')) if isResults(&WARN);
	}
    printResults();
} # End checkIBInterfaces

# checkInternalPingFail: Helper used by checkInternalPing
sub checkInternalPingFail {
    my ($name, $network) = @_;
    if (!$name or !$network) { return }

    # Setup
    my $SPF_FAIL_NETWORK = 'Ping tests for the %s network failed for all'.
	' nodes, the network may be down';
    my $SPF_FAIL_INTERFACE = 'Ping tests for the %s network failed from the'.
	' following nodes to all other nodes, the ports for this network on'.
	' these nodes may be down: %s';
    my $SPF_FAIL_PAIRS = 'Ping tests for the %s network failed between the'.
	' following pairs of nodes: %s';

    # Analyze
    my %fail = ();
    foreach my $lnn (@LNNS) {
	# No results for $lnn
	if (!$network->{$lnn}) { next }

	# No ping issues
	if ($network->{$lnn} =~ /^0+$/) { next }

	# Split each character into a result for a node
	my @results = split(//, $network->{$lnn});

	# Check for any failed pings
	foreach my $i (0..$#LNNS) {
	    # Skip checking pings to and from the same node
	    if ($lnn eq $LNNS[$i]) { next }

	    # results are 1 if ping failed, add it to the list
	    if ($results[$i]) { push(@{$fail{$lnn}}, $LNNS[$i]) }
	}

	# Ping failed to all other nodes from $lnn
	if (scalar(@{$fail{$lnn}}) >= $#LNNS) { $fail{$lnn} = 'all' }
    }

    # The nodes where ping failed to all other nodes
    my @nodes = grep {$fail{$_} eq 'all'} keys %fail;

    # Flag the network is down when ping fails between all nodes
    if (scalar(@nodes) == scalar(@LNNS)) {
	fail(sprintf($SPF_FAIL_NETWORK, $name));
	return;
    }

    # Flag nodes where the interface is down
    if (@nodes) {
	fail(sprintf($SPF_FAIL_INTERFACE, $name, compressRange(@nodes)));
    }

    # Generate pairs of nodes that failed ping tests
    my @pairs = ();
    foreach my $lnn (keys %fail) {
	# Skip nodes flagged with a down interface
	if ($fail{$lnn} eq 'all') { next }

	foreach my $remote_lnn (@{$fail{$lnn}}) {
	    # Skip nodes flagged with a down interface
	    if ($fail{$remote_lnn} eq 'all') { next }

	    # Skip nodes already added to the pairs list in reverse order
	    if (grep { $_ eq "$remote_lnn:$lnn" } @pairs) { next }

	    # Add to the pairs of failed ping tests
	    push(@pairs, "$lnn:$remote_lnn");
	}
    }
    if (@pairs) {
	fail(sprintf($SPF_FAIL_PAIRS, $name, join(', ', @pairs)));
    }
} # End checkInternalPingFail

$CHECKS{'checkInternalPing'} = {
    'Description' => 'Checks internal network by performing network ping operations',
    'Exclude' => 1,
};
sub checkInternalPing {
    if (wasCalled()) { return }
    if (isCompliance()) { return unsupported('UNSUP_COMP') }
    if (!isLive()) { return unsupported('UNSUP_LOGS') }

    # Setup
    my $CMD_NODE =
	q[ret="";].
	q[for i in $(isi_nodes %{internal_a},%{internal_b},%{failover}); do].
	q[ v=0;].
	q[ ping -o -c2 -t4 $(echo $i|cut -d, -f1)||((v+=1));].
	q[ ping -o -c2 -t4 $(echo $i|cut -d, -f2)||((v+=2));].
	q[ ping -o -c2 -t4 $(echo $i|cut -d, -f3)||((v+=4));].
	q[ ret="$ret$v";].
	q[done &>/dev/null;].
	q[echo $ret];
    my $CMD = qq[2>/dev/null isi_for_array '$CMD_NODE'];
    my $SPF_MALFORMED =
	'Results from the following nodes may be incomplete: %s';
    printCheck('Internal Network Ping');

    # Gather
    my $gather = gatherCluster($CMD);
    my $expected_length = $CLUSTER_INFO{'Node Count'};
    my @malformed = ();
    my %results = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { missing($lnn); next }
	my $line = pop(@{$gather->{$lnn}});

	# Trim white space
	$line =~ s/^\s*|\s*$//g;

	# Each line must match the number of nodes in the cluster
	if (length($line) != $expected_length) {
	    push(@malformed, $lnn);
	    next;
	}

	# Extract results for int_[a]/int_[b]/[f]ailover
	$results{'a'}{$lnn} = $line & (1x$expected_length);
	$results{'b'}{$lnn} = $line & (2x$expected_length);
	$results{'f'}{$lnn} = $line & (4x$expected_length);
	$results{'b'}{$lnn} =~ s/2/1/g;
	$results{'f'}{$lnn} =~ s/4/1/g;
    }

    # Analyze & Flag
    checkInternalPingFail('int-a', $results{'a'});
    checkInternalPingFail('int-b', $results{'b'});
    checkInternalPingFail('failover', $results{'f'});

    # Flag
    if (@malformed) {
	warning(sprintf($SPF_MALFORMED, compressRange(@malformed)));
    }

    # Output
    printResults();
} # End checkInternalPing
$CHECKS{'checkKB000197850'} = {
    'Description' => 'Checks for issues with IB queue pairs that would lead to node reboot issues if IB queue pairs are in a degraded state',
    'Exclude' => 1,
};
sub checkKB000197850 {
	return if wasCalled();
	checkIBInterfaces();
	if ($CLUSTER_INFO{'Active IB Interfaces'} < 2) { return unsupported('UNSUP_QP') }
	if ((isCurrentOneFS('<=9.1') || isCurrentOneFS('>=9.2.1.16'))) { return unsupported('UNSUP_CUR') }

	my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array ].
	qq['printf "\tARMED QPs: %s\n" "$(sysctl dev.mlx4_core.0.qp.dump_all | grep -B1 "st (Service type): 0 (RC)" | grep pm_state | grep ARMED | wc -l)"'];
	my $CMD_LOGS = q[2>&1; for i in $(cat nodes_info|awk '{print $3}'|sed '1d' | sort -V); do echo -n $i":"; echo -n "  ARMED QPs: " "$(sed -n '/dev.mlx4_core.0.qp.dump_all/,/dev.mlx4_core.0.qp.dump_single/p' $i/isi_ib_bug_info | grep -B1 "st (Service type): 0 (RC)" | grep pm_state | grep ARMED | wc -l)"; echo -n "  MIGRATED QPs: " "$(sed -n '/dev.mlx4_core.0.qp.dump_all/,/dev.mlx4_core.0.qp.dump_single/p' $i/isi_ib_bug_info | grep -B1 "st (Service type): 0 (RC)" | grep pm_state | grep MIGRATED | wc -l)"; echo ; done ];

	#my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array ].
	qq['printf "\tARMED QPs: %s" "$(sysctl dev.mlx4_core.0.qp.dump_all | grep -B1 "st (Service type): 0 (RC)" | grep pm_state | grep ARMED | wc -l)"; printf "\tMIGRATED QPs: %s\n" "$(sysctl dev.mlx4_core.0.qp.dump_all | grep -B1 "st (Service type): 0 (RC)" | grep pm_state | grep MIGRATED | wc -l)"'];
	#my $CMD_LOGS = q[2>&1; for i in $(cat nodes_info|awk '{print $3}'|sed '1d' | sort -V); do printf "$i:\t"; printf "ARMED QPs: %s | " "$(sed -n '/dev.mlx4_core.0.qp.dump_all/,/dev.mlx4_core.0.qp.dump_single/p' $i/isi_ib_bug_info | grep -B1 "st (Service type): 0 (RC)" | grep pm_state | grep ARMED | wc -l)"; printf "MIGRATED QPs: %s" "$(sed -n '/dev.mlx4_core.0.qp.dump_all/,/dev.mlx4_core.0.qp.dump_single/p' $i/isi_ib_bug_info | grep -B1 "st (Service type): 0 (RC)" | grep pm_state | grep MIGRATED | wc -l)"; echo; done ];
	$gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

	printCheck("IB Queue Pairs");

	my @nodes = ();
	my $clstr_no_cnt = scalar(@LNNS)-1;
	my $pair_count;


	foreach my $lnn (@LNNS) {
        if (!$gather->{$lnn}) {
			debug("MISSING QE PAIR DATA FOR NODE $lnn");
            missing($lnn);
            next;
        }
		foreach my $lpt (@{$gather->{$lnn}}){
			if ($lpt =~ /^\s*\S+\s*\S+\s*(\d+)\s*\S+\s*\S+\s*(\d+)$/) { $pair_count = $1+$2 }
			debug("RESULT FOR NODE $lnn = $lpt | Target: $clstr_no_cnt | PAIR COUNT: $pair_count");

			if($pair_count){
				if($pair_count != $clstr_no_cnt){
					push(@nodes, $lnn);
				}
			}
			else{
				debug("UNABLE TO PARSE Node $lnn QE PAIR COUNT");
			}
		}
	}
	if(@nodes){
    	fail("The following nodes are showing potential issues with IB queue pairs: ".compressRange(@nodes_msgs)) if @nodes;
		info("Please reference KB 000197850 ('https://www.dell.com/support/kbdoc/en-us/000197850') for further information.") if @nodes;
	}
   	printResults();
   } # End checkKB000197850

$CHECKS{'checkKB000218103'} = {
    'Description' => 'Checks for Dell BE Switches running 10.5.2 or higher with 40G cables and mentions that the cable needs to be checked from the switch to ensure it is not a 3M AOC cable.',
    'Exclude' => ,
};
sub checkKB000218103 {
	return if wasCalled();

    # Setup
	my $CMD_LIVE = qq[2>&1 isi_dump_fabric int-a | grep Leaf; isi_dump_fabric int-b | grep Leaf;];
    my $CMD_LOGS = q[2>&1; grep Leaf local/isi_dump_fabric]; 
	my $CMD_LIVE_IC = qq[2>&1 ${SUDO} isi_for_array -X 'for iface in \$(ifconfig |  grep ISIINTERNAL |grep -v lo0 |cut -d: -f1); do ifconfig -vvv \$iface |grep -E "media|plugged"; done'];
    my $CMD_LOGS_IC = q[2>&1; for dir in *-*/ ; do for iface in $(cat $dir\\ifconfig |  grep ISIINTERNAL |grep -v lo0 |cut -d: -f1);do grep -H -A11 $iface $dir\\ifconfig | egrep 'media:|plugged'; done; done];
	
    printCheck('BE Switch AOC');
	
    # Gather
	my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);
    my @nodes = ();
	my @switches = ();
    my $switch_dnos;
	my $switch_sn;
    if (@$gather){
        my $gather_IC = gatherCluster(isLive()?$CMD_LIVE_IC:$CMD_LOGS_IC);
        foreach my $line (@$gather) {
            debug("Switch details: $line\n");
            next unless ($line =~ /^\s*\"?Dell.*\s*$/);
            if($line =~ /^\s*\"?Dell.*v(\d+\.\d+\.\d+\.\d+).*\(Leaf\)\s+\((\S+)\).*$/){
                $switch_dnos = $1;
                $switch_sn = $2;
                if(compareVersions($switch_dnos,'10.5.2.0') >= 0 and compareVersions($switch_dnos,'10.5.2.13') < 0){
                    debug("Switch is on version 10.5.2 or higher, current version is: $switch_sn | $switch_dnos");
                    push(@switches, $switch_sn);
                }
                else{
                    debug("Switch is not on version 10.5.2 or higher, current version is: $switch_sn | $switch_dnos");
                }
            }
        }

        foreach my $lnn (@LNNS) {
            foreach $l (@{$gather_IC->{$lnn}}){
                debug("Checking Interface: $l");
                if (lc($l) =~ /^.*\>\(40gbase\-sr4\s.*$/){
                    push(@nodes, $lnn);
                }
            }
        }
	}
    # Flag
    if (@nodes and @switches) {
		addCustomerInformation(warning('Dell BE Switches running DNOS 10.5.2 or higher using 40G 3M Active Optical Cable (AOC) may experiance issues when the connected node reboots. Please ensure that the cable in use is not a 3M cable. The following nodes have 40G cables: '.compressRange(@nodes)));
        info("Dell BE Switches should be upgraded to DNOS 10.5.2.13 or higher to avoid this issue.");
        info('Please reference KB 000218103 (https://www.dell.com/support/kbdoc/en-ie/000218103) which provides steps to verify from the switches which cable is in use.');        
    }
     
   	printResults();
   } # End checkKB000218103

$CHECKS{'checkSwitchCompatibility'}{'Description'} = 'Checks backend Dell'.
    ' switches to confirm they are at least version 10.5.0.6';
sub checkSwitchCompatibility {
    if (wasCalled()) { return }
    if (!hasDellSwitch()) { return unsupported('UNSUP_BE_DN') }
    printCheck('Back-end Dell Switch OS Compatibility');

    # Setup
    my $RE_DELL = qr/(Dell\s+\S+)\s+-\s+v(\d+\.\d+\.\S+)\s/;
    my $MSG_WARN = q[It was detected that a back-end Dell switch is on a].
	q[ version prior to 10.5. OneFS 9 and later requires Dell Networking].
	q[ OS version 10.5.0.6 or later.];
    my $MSG_UPGRADE = q[It was detected that a back-end Dell switch is running].
	q[ a version prior to 10.5. The switch must be upgraded to 10.5.0.6 or].
	q[ later prior to upgrading OneFS. A case should be raised].
	q[ requesting an upgrade of the back-end Dell switches in preparation].
	q[ for an OneFS upgrade];

    # Analyze
    my $has_old_switch_os = 0;
    if (defined $CLUSTER_INFO{'Switches'}) {
      NETWORK:
	foreach my $switches (values %{$CLUSTER_INFO{'Switches'}}) {
	    foreach my $switch (@$switches) {
		if ($switch !~ $RE_DELL) { next }
		my ($switch_type, $switch_os) = ($1, $2);
		if (compareVersions($switch_os, '10.5') < 0) {
		    $has_old_switch_os = 1;
		    last NETWORK;
		}
	    }
	}
    }

    # Flag
    if ($has_old_switch_os) {
	if (isCurrentOneFS('<9') and isDestinationOneFS('>=9')) {
	    fail($MSG_UPGRADE);
	} else {
	    warning($MSG_WARN);
	}
    }

    # Output
    printResults();
} # End checkSwitchCompatibility
# gatherBESwitch: Gather backend switch details
sub gatherBESwitch {
    if (wasCalled()) { return }
    if (isVirtual()) { return }
    if (isCurrentOneFS('<8.1')) { return }
    if (isLive() and isCompliance()) { return }
    gatherNetworkInterfaces();
    if ($CLUSTER_INFO{'Active IB Interfaces'}) { return }

    # Setup
    my $PIPE_GREP = q(|egrep -n '": \[\]?$|^[\[\{]');
    my $CMD_LIVE;
    if(isCurrentOneFS('<9.9')){
        $CMD_LIVE = qq[2>&1 (${SUDO} isi_dump_fabric int-a;].
        qq[ ${SUDO} isi_dump_fabric int-b)].$PIPE_GREP;
    }
    else{
        $CMD_LIVE = qq[2>&1 (isi_dump_fabric int-a;].
        qq[ isi_dump_fabric int-b)].$PIPE_GREP;
    }
    my $CMD_LOGS = q[2>&1 cat $(ls -S1 */isi_dump_fabric|head -1)].$PIPE_GREP;
    my $RE_NETWORK = qr/^(\d+):[\[\{]$/;
    my $RE_SWITCH = qr/"(.*)": \[$/;

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my $network = '';
    foreach my $line (@$gather) {

	# Use line number to identify which network
	if ($line =~ $RE_NETWORK) {
	    if ($1 == 1) {
		$network = 'int-a';
		debug('Gathering int-a network');
	    } elsif ($1 > 1) {
		$network = 'int-b';
		debug('Gathering int-b network');
	    }
	}
	if (!$network) { next }

	# Extract switch details and append to list of switches
	if ($line !~ $RE_SWITCH) { next }
	my $switch = $1;
	@{$CLUSTER_INFO{'Switches'}{$network}} =
	    merge(@{$CLUSTER_INFO{'Switches'}{$network}}, $switch);
	debug("CLUSTER_INFO->Switches->${network}=$switch");
    }
} # End gatherBESwitch
# hasDellSwitch: Return true if Dell switches identified on backend network
sub hasDellSwitch {
    if (defined $CLUSTER_INFO{'hasDellSwitch'}) {
	return $CLUSTER_INFO{'hasDellSwitch'};
    }
    if (isVirtual()) {
	return $CLUSTER_INFO{'hasDellSwitch'} = 0;
    }
    if (isCurrentOneFS('<8.1')) {
	return $CLUSTER_INFO{'hasDellSwitch'} = 0;
    }
    gatherNetworkInterfaces();
    if ($CLUSTER_INFO{'Active IB Interfaces'}) {
	return $CLUSTER_INFO{'hasDellSwitch'} = 0;
    }

    # Check backend switches
    gatherBESwitch();
    if (defined $CLUSTER_INFO{'Switches'}) {
	foreach my $switches (values %{$CLUSTER_INFO{'Switches'}}) {
	    my ($res) =	grep {$_ =~ /Dell/} @$switches;
	    if ($res) {
		return $CLUSTER_INFO{'hasDellSwitch'} = 1;
	    } else {
		return $CLUSTER_INFO{'hasDellSwitch'} = 0;
	    }
	}
    }

    # If we made it this far there was no switch details gathered
    if (isLive()) {
	# On live clusters, attempt to ping the virtual IPs
	my $CMD_A = qq[2>&1 ${SUDO} isi_for_array -Q].
	    q[ 'ping6 -nc1 fdfe:9042:c53d::5eff:fe00:101 && echo PASS'].
	    q[ | grep PASS];
	my $gather_a = gatherLocal($CMD_A);
	my ($res_a) = grep {$_ =~ /PASS/} @$gather_a;
	if ($res_a) {
	    return $CLUSTER_INFO{'hasDellSwitch'} = 1;
	}
	my $CMD_B = qq[2>&1 ${SUDO} isi_for_array -Q].
	    q[ 'ping6 -nc1 fda7:e6ee:2e09::5eff:fe00:102 && echo PASS'].
	    q[ | grep PASS];
	my $gather_b = gatherLocal($CMD_B);
	my ($res_b) = grep {$_ =~ /PASS/} @$gather_b;
	if ($res_b) {
	    return $CLUSTER_INFO{'hasDellSwitch'} = 1;
	}
    } else {
	# On log gathers check ndp records
	my $RE_VIP =
	    '^(fdfe:9042:c53d::5eff:fe00:101|fda7:e6ee:2e09::5eff:fe00:102) ';
	my $CMD = "egrep -h '$RE_VIP' */ndp";
	my $gather = gatherLocal($CMD);
	my ($res) = grep {$_ =~ /$RE_VIP/} @$gather;
	if ($res) {
	    return $CLUSTER_INFO{'hasDellSwitch'} = 1;
	}
    }
    return $CLUSTER_INFO{'hasDellSwitch'} = 0;
} # End hasDellSwitch
sub checkKB535922 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('8.2.0.0')) { return }
    if (!isDestinationOneFS('8.2.0.0')) { return }
    gatherFlexNet();
    if (!$CLUSTER_INFO{'FlexNet'}{'Subnets'}) { return }

    # Setup
    my $MSG_SSIP = 'Immediately following the commit of the upgrade to OneFS'.
	' 8.2.0.0, the SmartConnect Service IP addresses associated with'.
	' subnets that have VLAN tagging may disappear. To avoid this, upgrade'.
	' to a pre-patched OneFS 8.2 image or OneFS 8.2.1 or later release.'.
	sprintf($MSG{'KB_1'}, '000168627');

    # Analyze and Flag
    foreach my $href_subnet (values %{$CLUSTER_INFO{'FlexNet'}{'Subnets'}}) {
	if ($href_subnet->{'VLAN Enabled'} and $href_subnet->{'VLAN Tag'}) {
	    addCustomerInformation('SmartConnect', critical($MSG_SSIP));
	    return;
	}
    }
}
$CHECKS{'checkNetworkParallelUpgrade'} = {
    'Description' => 'Checks for the risk of inaccessible network pools during a parallel upgrade',
    'Reference' => 'https://www.dell.com/support/kbdoc/000196936',
};
sub checkNetworkParallelUpgrade {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) {	return unsupported('UNSUP_CUR') }
	if(isCurrentOneFS('>=9.11')) {	return unsupported('UNSUP_CUR') }

    # Setup
    my $MSG_MISSING_NETWORK = 'Missing network pool information';
    my $MSG_MISSING_DISK = 'Missing disk pool information';
    my $MSG_SINGLE_NEIGHBORHOOD = 'This cluster has only one diskpool. Consider performing parallel upgrades when available, a parallel upgrade may be quicker than a rolling upgrade and the impacts will be the same';
    my $SPF_RISK = 'A parallel upgrade runs the risk of making one or more external networks temporarily unavailable. Affected pools: %s';
    my $MIN_NODES = 2;
    printCheck('Network Pools - Parallel Upgrade Support');

    # Fail when diskpool information was not gathered
    gatherNeighborhoods();
    if (!$CLUSTER_INFO{'Neighborhoods'}) {
	fail($MSG_MISSING_DISK);
	printResults();
	return;
    }

    # Print neighborhoods as extra
    verboseHeader('Node Neighborhoods');
    my $index = 1;
    foreach my $neighborhood (values %{$CLUSTER_INFO{'Neighborhoods'}}) {
	verbose("$index: [ @$neighborhood ]");
	$index++;
    }

    # Fail when network pool information was not gathered
    gatherFlexNet();
    if (!$CLUSTER_INFO{'FlexNet'}{'Pools'}) {
	fail($MSG_MISSING_NETWORK);
	printResults();
	return;
    }

    # Skip additional checks when there is just one neighborhood
    gatherProductInfo();
    #if (scalar(keys %{$CLUSTER_INFO{'Neighborhoods'}}) <= 1 and
	#!$CLUSTER_INFO{'Accelerator'}) {
	#$RPS{'Single Neighborhood'} = 1;
	#details($MSG_SINGLE_NEIGHBORHOOD);
	#printResults();
	#return;
    #}

    # Analyze
    my %at_risk_pools = ();
    foreach my $id (sort keys %{$CLUSTER_INFO{'FlexNet'}{'Pools'}}) {
	my $href_pool = $CLUSTER_INFO{'FlexNet'}{'Pools'}{$id};
	if (!$href_pool) { next }

	# Skip if pool is for Superna Eyeglass Runbook Robot
	#if (defined $href_pool->{'Name'} and
	#    $href_pool->{'Name'} =~ /^eyeglass-robot/) {
	#    debug("Pool $id: Skipping Superna Eyeglass Runbook Robot pools");
	#    next;
	#}

	# Skip if pool has no members
	if (!defined $href_pool->{'Members'}) {
	    debug("Pool $id: Skipping pools with no members");
	    next;
	}

	# Using the pool members, compile a list of nodes
	my @nodes = merge(map /^(\d+):/, @{$href_pool->{'Members'}});
	debug("Network Pool: $id, Nodes: ".compressRange(@nodes));

	# Skip if pool has less than two nodes
	#if (scalar(@nodes) < 2) {
	#    debug("Pool $id: Skipping pools with less than two nodes");
	#    next;
	#}

	# Check each neighborhood
	my $max_nodes = 0;
	#my $nodes_no_carrier = 0;
	foreach my $neighborhood (values %{$CLUSTER_INFO{'Neighborhoods'}}) {
	    debug("Neighborhood: @$neighborhood");
	    my @pool_neighbors = array_duplicates(@nodes, @$neighborhood);
	    debug("Neighbors in pool $id: @pool_neighbors");
		$node_neighborhoods .= "Neighbors in pool $id: @pool_neighbors\n";
	    my $num_nodes = scalar(@pool_neighbors);
	    if ($num_nodes >= $max_nodes) { $max_nodes = $num_nodes }
		debug("$id = Max: $max_nodes\tMin: $MIN_NODES");
	}
	if ($max_nodes < $MIN_NODES || ($max_nodes == 2 and $CLUSTER_INFO{'pools_at_risk_parallel'}{$id})) { $at_risk_pools{$id} = 1 }
    }

    # Flag
    if (%at_risk_pools and (isCurrentOneFS() ne isDestinationOneFS() || isCurrentOneFS() ne $RPS{'Target'} || !isDestinationOneFS() || !$RPS{'Target'})) {
	$RPS{'Disjoint Networks'} = 1;
	my $msg = sprintf($SPF_RISK, join(', ', sort keys %at_risk_pools));
	debug("OneFS Installation Method: $RPS{'OneFS'}{'Method'} | Patch Installation Method: $RPS{'Patch'}{'Method'} | NFP Installation Method: $RPS{'Node Firmware'}{'Method'}");
	if ((exists $RPS{'OneFS'}{'Method'} and $RPS{'OneFS'}{'Method'} eq 'parallel') 
		or 
		(exists $RPS{'Patch'}{'Method'} and $RPS{'Patch'}{'Method'} eq 'parallel') 
		or
		(exists $RPS{'Node Firmware'}{'Method'} and $RPS{'Node Firmware'}{'Method'} eq 'parallel')) {
	    fail($msg);
	    addCustomerInformation('Upgrade Plan', "$msg ".
				   sprintf($MSG{'KB_1'}, '000196936'));
	} else {
	    warning($msg);
	}
	info(sprintf($MSG{'KB_1'}, '000196936'));
    }

    printResults();
	#print $node_neighborhoods;
} # End checkNetworkParallelUpgrade
$CHECKS{'checkNetworkPoolIFaces'} = {
    'Description' => 'Checks each network pool and their assigned interfaces, if only 1 interface is configured for any pool and IP Ranges are set, this will cause a failure for pre upgrade mandatory checks',
    'Reference' => '',
};
sub checkNetworkPoolIFaces {
    if (wasCalled()) { return }
    #if (isCurrentOneFS('<8')) {	return unsupported('UNSUP_CUR') }

    my $gather = ();
    if (isLive()){
        if(isCurrentOneFS('<9.9')){
	        $gather = gatherLocal(qq[2>&1 ${SUDO} isi network pools list -v | grep 'ID\\|Ifaces\\|IP Ranges']);
        }
        else{
            $gather = gatherLocal(qq[2>&1 isi network pools list -v | grep 'ID\\|Ifaces\\|IP Ranges']);
        }
    } else {
	    $gather = gatherLocal(qq[2>&1 grep 'ID\\|Ifaces\\|IP Ranges' local/isi_network_pools]);
    }

    printCheck("Network Pool Interfaces");

    my $id = "";
    my $line = 1;
    #my $single_iface;
    #my $assigned_ips;
    my @single_iface_pool_issue;
    my @single_iface_pool;
    foreach (@$gather){
        my $single_iface;
        my $assigned_ips;
        debug("checking line $line");
        $line++;
	    if (/^\s*ID:\s*(\S+)$/){
	        # Gather ID
	        $id = $1;
	    } elsif (!$id){
	        # Do nothing without an ID
	    } elsif (/^\s*(Ifaces|IP\sRanges):\s*(\S.*)$/){
	        # Gather list items
            my ($name, $value) = ($1, $2);
            my @values = ($value);
            @values = split(", ", $value) if $value =~ /, /;
            @values = () if $value eq "-";
            #if($id !~ /eyeglass\\-robot/){
            if ($name eq 'Ifaces'){
                if(scalar(@values) < 2){
                    $single_iface = 1;
                    push(@single_iface_pool, $id);
                    debug("SINGLE INTERFACE DETECTED FOR $id");
                }
            }
            if ($name eq 'IP Ranges'){
                if(scalar(@values) > 0){
                    $assigned_ips = 1;
                    debug("IP ASSIGNMENT DETECTED FOR $id");
                    if(grep(/$id/, @single_iface_pool)){
                        debug ("FOUND issue with $id");
                        push(@single_iface_pool_issue, $id);
                    }
                }
            }
            #}
            @{$CLUSTER_INFO{'Network Pools'}{$id}{$name}} = @values;
            debug("CLUSTER_INFO->Network Pools->${id}->${name}=[ @values ]");
	    }
    }

if (@single_iface_pool_issue){
debug("WARN - the following Network Pools are at risk of being impacted during reboots as there is only a single interface configured: ". join ',', @single_iface_pool_issue);
}

    printResults();
} # End checkNetworkPoolIFaces

# checkSCShortNames: Identify if any SC Zone Names or SC DNS Zone Aliases use a shortname, called by checkSmartConnect.
sub checkSCShortNames {
    if (wasCalled()) { return }
    gatherFlexNet();
    if (!$CLUSTER_INFO{'FlexNet'}{'Pools'}) { return }
    my %short_names = ();
    my @pids = sort keys %{$CLUSTER_INFO{'FlexNet'}{'Pools'}};
    foreach my $pid (@pids) {
	my $href_pool = $CLUSTER_INFO{'FlexNet'}{'Pools'}{$pid};
	if (isShortName($href_pool->{'SC Zone Name'})) {
	    push(@{$short_names{$pid}}, $href_pool->{'SC Zone Name'});
	}
	if (!$href_pool->{'SC Zone Aliases'}) { next }
	foreach my $alias (@{$href_pool->{'SC Zone Aliases'}}) {
	    if (isShortName($alias)) {
		push(@{$short_names{$pid}}, $alias);
	    }
	}
    }
    if (!%short_names) { return }
    verboseHeader('SmartConnect Short Names');
    foreach (sort keys %short_names) {
	verbose("Pool: $_, DNS Short Names: ".join(', ', @{$short_names{$_}}));
    }
    if ($RPS{'Target'} and isCurrentOneFS('<8.2') and compareVersions($RPS{'Target'}, '8.2.0') >= 0) {
	addCustomerInformation('SmartConnect', fail("Found SC Zone Name(s) and/or SC DNS Zone Alias(es) that are configured to use a DNS short name as opposed to a fully qualified domain name, this is not supported and will stop working after the upgrade to OneFS $RPS{'Target'}. ". sprintf($MSG{'KB_1'}, '000173410')));
    } else {
	warning("Found SC Zone Name(s) and/or SC DNS Zone Alias(es) that are configured to use a DNS short name as opposed to a fully qualified domain name, this is not supported and will stop working in OneFS 8.2 or later. ".sprintf($MSG{'KB_1'}, '000173410'));
    }
} # End checkSCShortNames
# checkSSIPConnectionss: Checks if clients are connected to a SmartConnect
#  Service IP address (SSIPs).  Called by checkSmartConnect.
sub checkSSIPConnections {
    if (wasCalled()) { return }

    # Create a list of the SSIPs
    gatherFlexNet();
    if (!$CLUSTER_INFO{'FlexNet'}{'Subnets'}) { return }
    my @SSIPs = ();
    foreach my $href_subnet (values %{$CLUSTER_INFO{'FlexNet'}{'Subnets'}}) {
	if (!$href_subnet->{'SSIP'}) { next }
	@SSIPs = merge(@SSIPs, @{$href_subnet->{'SSIP'}});
    }

    # Return if there are no SSIPs
    if (!@SSIPs) { return }

    # Create a grep to capture clients that are connected to the SSIPs
    my $GREP_SSIP = sprintf('grep -E " (%s)."', join('|', @SSIPs));
    $GREP_SSIP =~ s/\./\\./g;

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array].
	qq[ 'netstat -lan | grep ESTABLISHED | $GREP_SSIP'];
    my $CMD_LOGS = qq[grep -H -a ESTABLISHED */netstat | $GREP_SSIP];
    my $SPF = '%5s %-30s %-30s';
    my $RE_PROTOCOLS =
	qr/\.(?:20|21|22|135|137|138|445|80|443|8080|8081|2049|8021|8020)$/;
    my $MSG_FAIL = 'Found client(s) connected to a SmartConnect Service IP'.
	' address (SSIP). This is not supported and will stop working after'.
	' upgrading to OneFS 8.2 or later. '.
	sprintf($MSG{'KB_1'}, '000168292');
    my $MSG_WARN = 'Found client(s) connected to a SmartConnect Service IP'.
	' address (SSIP). This is not supported and will stop working in OneFS'.
	' 8.2 or later. '.
	sprintf($MSG{'KB_1'}, '000168292');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my $found_clients_using_ssip = 0;
    verboseHeader('Clients Connected to SSIPs');
    verbose(sprintf($SPF, 'LNN', 'Local Address', 'Remote Address'));
    verbose(sprintf($SPF, '-'x5, '-'x30, '-'x30));
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line !~ /\s(\S+)\s+(\S+)\s+ESTABLISHED$/) { next }
	    my ($local_addr, $remote_addr) = ($1, $2);
	    if ($local_addr !~ $RE_PROTOCOLS) { next }
	    $local_addr =~ s/\.(\d+)$/:$1/;
	    $remote_addr =~ s/\.(\d+)$/:$1/;
	    $found_clients_using_ssip = 1;
	    verbose(sprintf($SPF, $lnn, $local_addr, $remote_addr));
	}
    }

    # Flag
    if ($found_clients_using_ssip) {
	if (isDestinationOneFS('>=8.2') and isCurrentOneFS('<8.2')) {
	    addCustomerInformation('SmartConnect', fail($MSG_FAIL));
	} else {
	    warning($MSG_WARN);
	}
    }
} # End checkSSIPConnections
$CHECKS{'checkSmartConnect'} = {
    'Description' => 'Check that the SmartConnect Service IPs are all assigned and not being used for client connectivity',
    'Reference' => 'https://www.dell.com/support/kbdoc/000049446',
};
sub checkSmartConnect {
    if (wasCalled()) { return }
    gatherFlexNet();
    my $href_subnets = $CLUSTER_INFO{'FlexNet'}{'Subnets'};
    if (!$href_subnets) { return }
    my $href_pools = $CLUSTER_INFO{'FlexNet'}{'Pools'};
    if (!$href_pools) { return }
    printCheck('SmartConnect Service IP');
    gatherNetworkInterfaces();

    # Run other SmartConnect related checks
    checkSmartConnectNoSystemZone();
    checkSSIPConnections();
    checkKB535922();
    checkSCShortNames();

    # Check that each SSIP is assigned and not assigned more than once
    debug('checkSmartConnect: Looking for each SSIP address...');
    verboseHeader('SmartConnect Service IP Locations');
    my $spf = '%-20s %-5s %-10s';
    verbose(sprintf($spf, 'SSIP', 'LNN', 'Network Interface'));
    verbose(sprintf($spf, '-'x20, '-'x5, '-'x10));
    my %scip_counts = ();
    foreach my $subnet (keys %{$href_subnets}) {
	debug("checkSmartConnect: Investigating Subnet $subnet");
	my $href_subnet = $href_subnets->{$subnet};
	if (!$href_subnet->{'SSIP'}) { next }
	debug("checkSmartConnect: SSIPs: [ @{$href_subnet->{'SSIP'}} ]");
	my ($zero_ssip) = grep {$_ eq '0.0.0.0'} @{$href_subnet->{'SSIP'}};
	if ($zero_ssip) { next }
	my @pools = grep {$_ =~ /^$subnet[\.:]/} keys %{$href_pools};
	if (!@pools) { next }
	debug("checkSmartConnect: Network Pools: [ @pools ]");
	my $members = 0;
	foreach my $pool (@pools) {
	    my $href_pool = $href_pools->{$pool};
	    if (!$href_pool->{'Members'}) { next }
	    $members += scalar(@{$href_pool->{'Members'}});
	}
	debug("checkSmartConnect: Subnet $subnet has $members members");
	# Skip if no members
	if (!$members) { next }
      SCIP:
	foreach my $scip (@{$href_subnet->{'SSIP'}}) {
	    debug("checkSmartConnect: Searching for SCIP: $scip");
	    $scip_counts{$scip} = 0;
	    foreach my $lnn (@LNNS) {
		my $href_network = $NODE_INFO{$lnn}{'Network'};
		if (!$href_network) { next }
		foreach my $interface (keys %{$href_network}) {
		    my $href_interface = $href_network->{$interface};
		    if (!$href_interface) { next }
		    foreach my $address (@{$href_interface->{'addresses'}}) {
			if ($address ne $scip) { next }
			verbose(sprintf($spf, $scip, $lnn, $interface));
			$scip_counts{$scip}++;
		    }
		}
	    }
	}
    }

    # Flag any missing or repeated SSIPs
    my @missing_scips = grep {$scip_counts{$_} == 0} keys %scip_counts;
    if (@missing_scips) { fail("The following SmartConnect Service IP Address(es) could not be located on any node: @missing_scips") }
    my @repeat_scips = grep {$scip_counts{$_} > 1} keys %scip_counts;
    if (@repeat_scips) {
	fail("The following SmartConnect Service IP Address(es) were found on multiple nodes: @repeat_scips. ".sprintf($MSG{'KB_1'}, '000170716'));
    }

    # Print results
    if (isResults(&WARN)) {
	info(sprintf($MSG{'KB_1'}, '000049446'));
    }
    printResults();
} # End checkSmartConnect
sub checkSmartConnectNoSystemZone {
    if (wasCalled()) { return }
    gatherFlexNet();
    if (!$CLUSTER_INFO{'FlexNet'} or !$CLUSTER_INFO{'FlexNet'}{'Pools'}) { return }
    if (isCurrentOneFS('<8')) { return }
    if (isCurrentOneFS('>=8.2')) { return }
    if (!isDestinationOneFS('>=8.2')) { return }
    my $SPF_FAIL = 'Failed to detect a network pool that is configured with the System Access Zone.  Cluster administrative tasks via WebUI or CLI are only available through network pools configured with the System Access Zone.  If a SmartConnect Service IP address is being used to get around this requirement, there will be no access to the cluster via WebUI or CLI after an upgrade to OneFS 8.2 or later releases. Do not proceed with an upgrade to OneFS %s until this has been addressed.';
    my $has_system_zone_pool = 0;
    foreach my $href_pool (values %{$CLUSTER_INFO{'FlexNet'}{'Pools'}}) {
	if ($href_pool->{'Auth Zone'} and
	    $href_pool->{'Auth Zone'} eq 1) {
	    $has_system_zone_pool = 1;
	    last;
	}
    }
    if (!$has_system_zone_pool) {
	fail(sprintf($SPF_FAIL, isDestinationOneFS()));
    }
}
$CHECKS{'checkAggregationMode'} = {
    'Description' => 'Checks that the aggregation mode is not Legacy FEC mode for upgrades to OneFS 8+',
    'Reference' => 'https://www.dell.com/support/kbdoc/000037946',
    'Exclude' => 1,
};
sub checkAggregationMode {
    if (wasCalled()) { return }
    return unsupported('UNSUP_CUR')
	unless isCurrentOneFS('<8.0') and isDestinationOneFS('>=8.0');
    printCheck("Legacy Aggregation Mode");
    gatherFlexNet();
    if (!$CLUSTER_INFO{'FlexNet'}{'Pools'}) {
	fail("Missing network pool information");
	printResults();
	return;
    }
    debug("checkAggregationMode: Checking for network pools configured with Legacy FEC Aggregation Modes");
    my @network_pools_with_legacy_fec = ();
    foreach my $name (keys %{$CLUSTER_INFO{'FlexNet'}{'Pools'}}) {
	# Check for Legacy FEC mode for the network pool
	next unless $CLUSTER_INFO{'FlexNet'}{'Pools'}{$name}{'Aggregation Mode'} and
	    $CLUSTER_INFO{'FlexNet'}{'Pools'}{$name}{'Aggregation Mode'} =~ /legacy/i;

	# Check that the pool has any aggregate network interfaces
	next unless $CLUSTER_INFO{'FlexNet'}{'Pools'}{$name}{'Members'} and
	    grep(/agg/, @{$CLUSTER_INFO{'FlexNet'}{'Pools'}{$name}{'Members'}});

	# Both conditions met, flag as impacted
	debug("checkAggregationMode: !!! Network Pool $name uses Aggregation Mode $CLUSTER_INFO{'FlexNet'}{'Pools'}{$name}{'Aggregation Mode'}");
	push(@network_pools_with_legacy_fec, $name);
    }

    if (@network_pools_with_legacy_fec) {
	warning("Legacy link aggregation in use on the following subnet/pools: ".join(", ", @network_pools_with_legacy_fec));
	addCustomerInformation("Your cluster has at least one pool with \"legacy\" link aggregation in use. This mode is no longer supported starting in OneFS 8. As part of the upgrade the affected pool(s) will be switched to a compatible FEC mode.  Please refer to the OneFS Upgrade Planning and Process Guide at https://www.dell.com/support. Affected subnets / pools: ".join(", ", @network_pools_with_legacy_fec));
    }

    info(sprintf($MSG{'KB_1'}, '000037946')) if isResults(&WARN);
    printResults();
} # End checkAggregationMode
$CHECKS{'checkBESwitch'} = {
    'Description' => 'Checks for Dell BE Switches to diagnose if they are affected by KB 000476036 or 000218103.',
    'Exclude' => ,
};
sub checkBESwitch {
	return if wasCalled();

    # Setup
	gatherBESwitch();
    hasDellSwitch();
	
    return if !hasDellSwitch();

    printCheck('BE Switch');
	
    # Gather
	my @switches = ();
    my @Z9264Leaf = ();
    my $spine_switch;
    if (defined $CLUSTER_INFO{'Switches'}) {
	    foreach my $switches (values %{$CLUSTER_INFO{'Switches'}}) {
	        my ($res) =	grep {$_ =~ /Dell/} @$switches;
	        if ($res =~ /^Dell\s*(\S+)\s*\S*\s*v([\d\.C]*)\s*[\S\[]+\s*\((\S+)\)\s*\(([\S\s]*)\)\s*\((\S+)\)\s*\[(\S+)].*$/) {
		        my $type = $1;
                my $vers = $2;
                my $cfg = $3;
                my $svc_tag = $4;
                my $sn = $5;
                my $status = $6;
                debug("Switch Type: $1, Vers:$2, Config: $3, svc_tag: $4, SN: $5, Status: $6");
                if(lc($cfg) =~ /spine/){ $spine_switch = 1; };
                if($type =~ /Z9264F/ and $vers eq '10.5.6.9' and $cfg =~ /Leaf/){
                    debug("Switches of type Z9264F found with version $vers, simultaneous upgrades should not be performed.");
                    debug("Current installation methods are: OneFS = $RPS{'OneFS'}->{'Method'} and Node Firmware = $RPS{'Node Firmware'}->{'Method'} || Glboal Method Simultaneous: $RPS{'Simultaneous'} ");
                    push(@Z9264Leaf, $svc_tag);
                }
                if((!defined($5) or $5 eq '' or $5 =~ /Unknown SerialNumber/) and $6 eq 'Online'){
                    push(@switches, $4);
                }
	        }
	    }
    }

    # Flag
    if (@Z9264Leaf and ((defined($RPS{'OneFS'}{'Method'}) and $RPS{'OneFS'}{'Method'} eq 'simultaneous') or (defined($RPS{'Node Firmware'}{'Method'}) and $RPS{'Node Firmware'}{'Method'} eq 'simultaneous') or (defined($RPS{'Simultaneous'}) and $RPS{'Simultaneous'} == 1)) and (!$spine_switch or $spine_switch = 0)){
        addCustomerInformation(fail('Checks have identified Leaf only switches of type Z9264F running DNOS 10.5.6.9, Simultaneous upgrade / updates should not be performed.'));
        info('Please use Parallel (recommended) or rolling installation options.');            
        info('Please reference KB000218103 https://www.dell.com/support/kbdoc/000476036 for additional information.');        
    000476036
    }
    if (@switches) {
        addCustomerInformation(fail('Checks have identified a potential issue with Dell BE Switches, the following switch is not reporting device information as expected: '.split(', ',@switches)));
        info('Please reference KB 000218103 (https://www.dell.com/support/kbdoc/000218103) which provides steps to verify from the switches which cable is in use.');        
    }
     
   	printResults();
   } # End checkBESwitch

$CHECKS{'checkGatewayPriority'} = {
    'Description' => 'Checks for subnets with duplicate gateway priorities',
    'Reference' => 'https://www.dell.com/support/kbdoc/000037963',
    'Exclude' => 1,
};
sub checkGatewayPriority {
    return if wasCalled();
    gatherFlexNet();
    return unless $CLUSTER_INFO{'FlexNet'}{'Subnets'};


    printCheck("Duplicate Gateway Priority");
    debug("checkGatewayPriority: Creating hash of array for each subnet priority.");
    my %subnet_by_priority = ();
    verboseHeader("Gateway Priority");
    verbose(sprintf("%-30s %-20s %-10s", "Subnet", "Gateway", "Priority"));
    foreach my $id (keys %{$CLUSTER_INFO{'FlexNet'}{'Subnets'}}) {
	my %subnet = %{$CLUSTER_INFO{'FlexNet'}{'Subnets'}{$id}};
	next unless $subnet{'Gateway Priority'} and $subnet{'Gateway'};
	next if $subnet{'Gateway'} eq "0.0.0.0";
	push(@{$subnet_by_priority{$subnet{'Gateway Priority'}}}, $id);
	verbose(sprintf("%-30s %-20s %-10s", $id, $subnet{'Gateway'}, $subnet{'Gateway Priority'}));
    }

    debug("checkGatewayPriority: Checking for duplicate gateway priorities");
    my $found_duplicate_gateway_priorities = 0;
    foreach my $priority (keys %subnet_by_priority) {
	debug("checkGatewayPriority: Gateway priority of $priority for subnets: ".join(", ", @{$subnet_by_priority{$priority}}));
	next unless (scalar(@{$subnet_by_priority{$priority}} > 1));
	$found_duplicate_gateway_priorities = 1;
	warning("Duplicate gateway priority of $priority found for subnets: ".join(", ", @{$subnet_by_priority{$priority}}));
	debug("checkGatewayPriority: !!! Duplicate gateway priority found for gateway priority $priority !!!");
    }

    addCustomerInformation('It was detected that multiple network subnets have the same gateway priority.  This could result in unpredictable routing issues and lead to temporary data unavailability. '.sprintf($MSG{'KB_1'}, '000104311').' and the Isilon External Network Connectivity Guide (https://dl.dell.com/content/docu89475).') if $found_duplicate_gateway_priorities;
    info(sprintf($MSG{'KB_1'}, '000037963')) if isResults(&WARN);
    printResults();
} # End checkGatewayPriority
$CHECKS{'checkKB000196175'} = {
    'Description' => 'Checks for criteria of KB 000196175',
    'Reference' => 'https://www.dell.com/support/kbdoc/000196175',
};
sub checkKB000196175 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=9.2')) { return unsupported('UNSUP_CUR') }
    if (!isDestinationOneFS(qr/^9\.2\.0\.|^9\.2\.1\.[0-9]$|^9\.3\.0\.[0-4]$/)) {
	return unsupported('UNSUP_TGT_1', '9.2.0.0, 9.2.0.1, 9.2.1.0, 9.2.1.1, 9.2.1.2, 9.2.1.3, 9.2.1.4, 9.2.1.5, 9.2.1.6, 9.2.1.7, 9.2.1.8, 9.2.1.9, 9.3.0.0, 9.3.0.1, 9.3.0.2, 9.3.0.3, or 9.3.0.4');
    }

    # Setup
    my $MSG_MISSING_NETWORK = 'Missing network pool information';
    my $SPF_DYNAMIC = 'If the upgrade to OneFS %s is performed in a rolling manner, after the first node is upgraded all dynamic IP addresses will be moved to the upgraded node';
    my $MSG_WA_HEADER = 'Consider one of the following workarounds for this issue:';
    my $MSG_WA_PARALLEL = '* Perform the OneFS upgrade in a parallel manner (Recommended)';
    my $MSG_WA_SIMULTANEOUS = '* Perform the OneFS upgrade in a simultaneous manner';
    my $MSG_WA_ROLLING = '* Perform the OneFS upgrade in a rolling manner and target one node at a time';

    printCheck('KB 000196175');
    gatherFlexNet();
    if (!$CLUSTER_INFO{'FlexNet'}{'Pools'}) {
	fail($MSG_MISSING_NETWORK);
	printResults();
	return;
    }

    # Analyze
    my $has_dynamic = 0;
    foreach my $href_pool (values %{$CLUSTER_INFO{'FlexNet'}{'Pools'}}) {
	if (!$href_pool->{'Allocation Method'}) { next }
	if ($href_pool->{'Allocation Method'} eq 'dynamic') {
	    $has_dynamic = 1;
	    last;
	}
    }

    # Flag
    if ($has_dynamic) {
	critical(sprintf($SPF_DYNAMIC, isDestinationOneFS()));
	critical($MSG_WA_HEADER);
	if (isCurrentOneFS('>=8.2.2')) {
	    critical($MSG_WA_PARALLEL);
	}
	critical($MSG_WA_ROLLING);
	critical($MSG_WA_SIMULTANEOUS);
	info(sprintf($MSG{'KB_1'}, '000196175'));
    }

    # Output
    printResults();
} # End checkKB000196175
$CHECKS{'checkKB000196762'} = {
    'Description' => 'Checks for criteria of KB 000196762',
    'Reference' => 'https://www.dell.com/support/kbdoc/000196762',
    'Parameters' => { 'name' => 'target_version', 'mandatory' => 'false', 'default' => '' }
};
sub checkKB000196762 {
    if (wasCalled()) { return }

    # Setup
    my $RE_AFFECTED = qr/^9\.1\.0\.1[4567]$|^9\.2\.1\.(?:[789]|10)$/;
    my $SPF_UPGRADE = 'After committing the upgrade to OneFS %s, restart the isi_flexnet_d service by stopping the processes with the following command: isi_for_array killall -9 isi_flexnet_d';
    my $MSG_ISSUE = 'This cluster may be impacted by KB 000196762, restart the isi_flexnet_d service by stopping the processes with the following command: isi_for_array killall -9 isi_flexnet_d';
    my $CMD_LIVE = q[2>&1 ${SUDO} isi_for_array 'pgrep isi_flexnet_d | xargs procstat -f | grep -c isi_flexnet_d'];
    my $CMD_LOGS = q[2>&1 grep -H -c isi_flexnet_d */fstat];
    my $NUM_EXPECTED = 18;

    # Pre-upgrade version check
    if (isCurrentOneFS(qr/^8\.1\./) and isDestinationOneFS($RE_AFFECTED)) {
	printCheck('KB 000196762');
	critical(sprintf($SPF_UPGRADE, isDestinationOneFS()));
	info(sprintf($MSG{'KB_1'}, '000196762'));
	printResults();
	return;
    }

    # Return if not running an affected version
    if (!isCurrentOneFS($RE_AFFECTED)) { return unsupported('UNSUP_CUR') }

    # Return if compliance mode enabled cluster, not able to check process open files
    if (isCompliance()) { return unsupported('UNSUP_COMP') }

    printCheck('KB 000196762');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my $issue = 0;
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { missing($lnn); next }
	my ($results) = map {$_ =~ /^\s*(\d+)\s*$/} @{$gather->{$lnn}};
	if (!$results or $results < $NUM_EXPECTED) { $issue = 1; last }
    }
    if ($issue) {
	fail($MSG_ISSUE);
	info(sprintf($MSG{'KB_1'}, '000196762'));
    }

    # Output
    printResults();
} # End checkKB000196762
###
# checkLcheckKB000391477ACPSFP: Checks for LACP on cxgb interfaces for KB 000174095
$CHECKS{'checkKB000391477'} = {
    'Description' => 'Checks for LACP on cxgb interfaces for KB 000174095',
    'Exclude' => 1,
};
sub checkKB000391477 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=9.12.0.2') or isDestinationOneFS('>=9.12.0.2') or isDestinationOneFS('<9.12')) { return }

    printCheck("Gige Interface Agg Mode ");

	gatherFlexNet();
	if (!$CLUSTER_INFO{'FlexNet'}{'Pools'}) {
		fail("Missing network pool information");
		printResults();
		return;
    }
    debug("checkKB000391477: Checking for network pools configured with LACP aggregation mode");
    my @pools_affected = ();
    foreach my $name (keys %{$CLUSTER_INFO{'FlexNet'}{'Pools'}}) {
		debug("POOL: $name, Agg Mode: $CLUSTER_INFO{'FlexNet'}{'Pools'}{$name}{'Aggregation Mode'}, Members: @{$CLUSTER_INFO{'FlexNet'}{'Pools'}{$name}{'Members'}} ");
		# Check for Legacy FEC mode for the network pool
		next unless $CLUSTER_INFO{'FlexNet'}{'Pools'}{$name}{'Aggregation Mode'};

		# Check that the pool has any aggregate network interfaces, skip if there are no aggregate members in this pool
		next unless $CLUSTER_INFO{'FlexNet'}{'Pools'}{$name}{'Members'} and
			grep(/gige-agg/, @{$CLUSTER_INFO{'FlexNet'}{'Pools'}{$name}{'Members'}});

		if($CLUSTER_INFO{'FlexNet'}{'Pools'}{$name}{'Aggregation Mode'} !~ /lacp/i){
			debug("checkKB000391477: !!! Network pool $name has aggregate members but the pools Aggregation mode is not set to LACP.");
			push(@pools_affected, $name);# unless grep {$_ eq $name} @pools_affected;
		}
    }

    if (@pools_affected) {
	# FAIL message
	fail("The following pools were found to have aggregate member but the pools is not set LACP aggregation mode: ".join(', ', @pools_affected));
	fail("Aggregation mode for the pools identified above should be set to LACP to avoid an issue identified in OneFS 9.12 outlined in KB 000391477.");
	info("Please reference KB 000391477 (https://www.dell.com/support/kbdoc/000391477) for additional information.");
    }

    printResults();
} # End checkKB000391477
$CHECKS{'checkKB519890'} = {
    'Description' => 'Checks for a known issue with LAGG interfaces in LACP mode when running OneFS 8.0.0.6, 8.0.1.2, 8.1.0.2, and 8.1.1.1',
    'Reference' => 'https://www.dell.com/support/kbdoc/000167905',
    'Exclude' => 1,
};
sub checkKB519890 {
    if (wasCalled()) { return }

    # Skip unless upgrading to or running an impacted version
    return unless isDestinationOneFS(qr/^(8\.0\.0\.6|8\.0\.1\.2|8\.1\.0\.2|8\.1\.1\.1)$/) or
	(!isDestinationOneFS() and isCurrentOneFS(qr/^(8\.0\.0\.6|8\.0\.1\.2|8\.1\.0\.2|8\.1\.1\.1)$/));

    printCheck("KB 519890");
    gatherNetworkInterfaces();

    # Analyze information for issues.
    my $has_lacp = 0;
    my @nodes_with_incomplete_lagg = ();
    foreach my $lnn (@LNNS) {
	next unless $NODE_INFO{$lnn}{'Network'};
	my $has_incomplete_lagg = 0;
	my @lagg_interfaces = grep {$_ =~ /^lagg/} keys %{$NODE_INFO{$lnn}{'Network'}};
	foreach my $lagg_interface (@lagg_interfaces) {
	    next unless $NODE_INFO{$lnn}{'Network'}{$lagg_interface}{'laggport'};
	    next unless $NODE_INFO{$lnn}{'Network'}{$lagg_interface}{'laggproto'};
	    next unless $NODE_INFO{$lnn}{'Network'}{$lagg_interface}{'laggproto'} =~ /lacp/;
	    $has_lacp = 1;
	    my $total_ports = 0;
	    my $inactive_ports = 0;
	    foreach my $laggport (@{$NODE_INFO{$lnn}{'Network'}{$lagg_interface}{'laggport'}}) {
		my $interface = "";
		$interface = $1 if $laggport =~ /^(\S+)/;
		next unless $interface and $NODE_INFO{$lnn}{'Network'}{$interface}{'status'};
		$total_ports++;
		next if $NODE_INFO{$lnn}{'Network'}{$interface}{'status'} eq "active";
		$inactive_ports++;
	    }
	    $has_incomplete_lagg = 1 if $inactive_ports and $total_ports ne $inactive_ports;
	}
	push(@nodes_with_incomplete_lagg, $lnn) if $has_incomplete_lagg;
    }

    # Fail during pre-checks for upgrades targetting an affected version
    if ($has_lacp) {
	$RPS{'KB519890'} = 1 if isCurrentOneFS(qr/^(8\.0\.0\.6|8\.0\.1\.2|8\.1\.0\.2|8\.1\.1\.1)$/) and !hasPatch('patch-(225079|226984)');
	if (isDestinationOneFS(qr/^(8\.0\.0\.6|8\.0\.1\.2|8\.1\.0\.2|8\.1\.1\.1)$/)) {
	    addCustomerInformation(fail("In OneFS 8.0.0.6, 8.0.1.2, 8.1.0.2, and 8.1.1.1, when some of the ports of a lagg interface in LACP mode go down with other ports still active, OneFS treats the lagg interface as down with No Carrier status.  If this lagg is the only interface for front end network, connection to the node from outside the cluster will be lost.")) if $has_lacp;
	}

	# If we didn't fail above, warn if the current OneFS version is impacted
	elsif (isCurrentOneFS(qr/^(8\.0\.0\.6|8\.0\.1\.2|8\.1\.0\.2|8\.1\.1\.1)$/)) {
	    addCustomerInformation(warning("In OneFS 8.0.0.6, 8.0.1.2, 8.1.0.2, and 8.1.1.1, when some of the ports of a lagg interface in LACP mode go down with other ports still active, OneFS treats the lagg interface as down with No Carrier status.  If this lagg is the only interface for front end network, connection to the node from outside the cluster will be lost.")) if $has_lacp;
	}
    }

    if (@nodes_with_incomplete_lagg) {
	if (isDestinationOneFS()) {
	    fail("Not all ports of a lagg interface are active on the following nodes which would cause the lagg interface to be down following the OneFS upgrade to $RPS{'Target'}: ".compressRange(@nodes_with_incomplete_lagg));
	} else {
	    fail("Not all ports of a lagg interface are active on the following nodes which may cause the lagg interface to be down: ".compressRange(@nodes_with_incomplete_lagg));
	}
    }

    printResults();
}
$CHECKS{'checkKB521890'} = {
    'Description' => 'Checks for criteria outlined in KB 000167681',
    'Exclude' => 1,
};
sub checkKB521890 {
    if (wasCalled()) { return }

    # Skip unless upgrading to or running an impacted version (8.0.0.[56], 8.1.0.[123], 8.1.1.[01])
    return unless
	isDestinationOneFS(qr/^(8\.0\.0\.[56]|8.1.0.[123]|8\.1\.1\.[01])$/) or
	(!isDestinationOneFS() and
	 isCurrentOneFS(qr/^(8\.0\.0\.[56]|8\.1\.0\.[123]|8\.1\.1\.[01])$/));

    printCheck("KB 521890");
    gatherNetworkInterfaces();

    # Analyze information for issues.
    my $has_lagg = 0;
    my %nodes_with_ix_lagg = ();
    my %nodes_with_em_lagg = ();
    my %nodes_with_igb_lagg = ();
    foreach my $lnn (@LNNS) {
	next unless $NODE_INFO{$lnn}{'Network'};

	# Issue exists in lagg interfaces
	my @lagg_interfaces = grep {$_ =~ /^lagg/} keys %{$NODE_INFO{$lnn}{'Network'}};
	next unless @lagg_interfaces;

	# Flag for if the cluster has any lagg ports
	$has_lagg = 1;

	# For each lagg interface, check each lagg port for impacted interface types
	foreach my $lagg_interface (@lagg_interfaces) {
	    next unless $NODE_INFO{$lnn}{'Network'}{$lagg_interface}{'laggport'};
	    my @laggports = @{$NODE_INFO{$lnn}{'Network'}{$lagg_interface}{'laggport'}};

	    my @ix_interfaces = grep {$_ =~ /^ix\d/} @laggports;
	    $nodes_with_ix_lagg{$lnn} = 1 if @ix_interfaces;

	    my @em_interfaces = grep {$_ =~ /^em\d/} @laggports;
	    $nodes_with_em_lagg{$lnn} = 1 if @em_interfaces;

	    my @igb_interfaces = grep {$_ =~ /^igb\d/} @laggports;
	    $nodes_with_igb_lagg{$lnn} = 1 if @igb_interfaces;
	}
    }

    if ($has_lagg)
    { # There are laggs

	# Flag issues in destination OneFS versions
	if (isDestinationOneFS())
	{ # Upgrading OneFS

	    if (isDestinationOneFS(qr/^(8\.0\.0\.[56]|8.1.0.[123]|8\.1\.1\.[01])$/))
	    { # Upgrading to an impacted version

		if (%nodes_with_ix_lagg)
		{ # Has Gen 6 impacted interfaces
		    fail("In the destination OneFS $RPS{'Target'}, the following nodes have a high risk of being impacted by KB 000167681: ".compressRange(keys %nodes_with_ix_lagg));
		}

		if (%nodes_with_em_lagg or %nodes_with_igb_lagg)
		{ # Has other Gen impacted interfaces
		    warning("In the destination OneFS $RPS{'Target'}, the following nodes have a very low risk of being impacted by KB 000167681: ".compressRange(merge(keys %nodes_with_em_lagg, keys %nodes_with_igb_lagg)));
		}
	    }

	    if (isDestinationOneFS(qr/^(8.1.0.[123]|8\.1\.1\.[01])$/) and isCurrentOneFS('<8.1'))
	    { # Upgrading to an impacted OneFS 8.1 code level, perhaps to add Gen6 nodes?
		addCustomerInformation(fail("After upgrading to OneFS $RPS{'Target'}, avoid configuring 10 GbE Intel network interfaces (ix interfaces) in link aggregation.  These interfaces are most commonly seen in Generation 6 nodes.  Please review the KB 000167681 for details on an issue which impacts 10 GbE Intel network interfaces when used in link aggregation."));
	    }
	}

	# Flag issues in current OneFS versions
	if (isCurrentOneFS(qr/^(8\.0\.0\.[56]|8\.1\.0\.[123]|8\.1\.1\.[01])$/))
	{ # Running an impacted version

	    if (isDestinationOneFS() and !isDestinationOneFS(qr/^(8\.0\.0\.[56]|8.1.0.[123]|8\.1\.1\.[01])$/) and %nodes_with_ix_lagg)  {
		critical("In the current OneFS $CLUSTER_INFO{'OneFS'}, the following nodes have a high risk of being impacted by KB 000167681: ".compressRange(keys %nodes_with_ix_lagg));

		# Toggle a flag that will be used to recommend against node firmware or reboot activities
		$RPS{'KB521890'} = 1;
	    } else {
		if (%nodes_with_ix_lagg)
		{ # Has Gen 6 impacted interfaces
		    fail("In the current OneFS $CLUSTER_INFO{'OneFS'}, the following nodes have a high risk of being impacted by KB 000167681: ".compressRange(keys %nodes_with_ix_lagg));

		    # Toggle a flag that will be used to recommend against node firmware or reboot activities
		    $RPS{'KB521890'} = 1;
		}

		if (%nodes_with_em_lagg or %nodes_with_igb_lagg)
		{ # Has other Gen impacted interfaces
		    warning("In the current OneFS $CLUSTER_INFO{'OneFS'}, the following nodes have a very low risk of being impacted by KB 000167681: ".compressRange(merge(keys %nodes_with_em_lagg, keys %nodes_with_igb_lagg)));
		}

		if (!%nodes_with_ix_lagg and isCurrentOneFS(qr/^(8\.1\.0\.[123]|8\.1\.1\.[01])$/))
		{ # Has no impacted interfaces, but might be adding Generation 6 nodes
		    addCustomerInformation(warning("If planning to add Generation 6 nodes, please first review the KB 000167681 for details on an issue which impacts 10 GbE (ix) network interfaces on Generation 6 nodes when used in link aggregation."));
		}
	    }
	}
    }

    printResults();
}
###
# checkLACPSFP: Checks for LACP on cxgb interfaces for KB 000174095
$CHECKS{'checkLACPSFP'} = {
    'Description' => 'Checks for LACP on cxgb interfaces for KB 000174095',
    'Exclude' => 1,
};
sub checkLACPSFP {
    if (wasCalled()) { return }
    if (!isDestinationOneFS(qr/^8\.0\.0\.[012345]$|^8\.0\.1\.[01]$|^8\.1\.0\.0$/)) { return }
    gatherNetworkInterfaces();

    printCheck("LACP over CXGB interfaces with TwinAx SFP");
    debug("checkLACPSFP: Checking for network pools configured with LACP aggregation mode");
    my @nodes_affected = ();
    foreach my $lnn (sort {$a <=> $b} keys %NODE_INFO) {
	next unless $NODE_INFO{$lnn}{'Network'};
	foreach my $interface (keys %{$NODE_INFO{$lnn}{'Network'}}) {
	    next unless $interface;
	    if ($interface =~ /cxgb/ and
		$NODE_INFO{$lnn}{'Network'}{$interface}{'media'} and
		$NODE_INFO{$lnn}{'Network'}{$interface}{'laggdev'} and
		$NODE_INFO{$lnn}{'Network'}{$interface}{'media'} =~ /twinax/i and
		$NODE_INFO{$lnn}{'Network'}{$NODE_INFO{$lnn}{'Network'}{$interface}{'laggdev'}}{'laggproto'} and
		$NODE_INFO{$lnn}{'Network'}{$NODE_INFO{$lnn}{'Network'}{$interface}{'laggdev'}}{'laggproto'} =~ /lacp/i) {
		debug("checkLACPSFP: !!! Network interface $interface on node $lnn may be impacted");
		push(@nodes_affected, $lnn) unless grep {$_ eq $lnn} @nodes_affected;
	    }
	    if ($NODE_INFO{$lnn}{'Network'}{$interface}{'laggport'} and
		$NODE_INFO{$lnn}{'Network'}{$interface}{'laggproto'} and
		$NODE_INFO{$lnn}{'Network'}{$interface}{'laggproto'} =~ /lacp/i) {
		foreach my $cxgb_interface (grep {$_ =~ /^cxgb/} @{$NODE_INFO{$lnn}{'Network'}{$interface}{'laggport'}}) {
		    next unless $cxgb_interface;
		    if($NODE_INFO{$lnn}{'Network'}{$cxgb_interface}{'media'} and
		       $NODE_INFO{$lnn}{'Network'}{$cxgb_interface}{'media'} =~ /twinax/i) {
			debug("checkLACPSFP: !!! Network interface $interface on node $lnn may be impacted");
			push(@nodes_affected, $lnn) unless grep {$_ eq $lnn} @nodes_affected;
		    }
		}
	    }
	}
    }
    if (@nodes_affected) {
	# FAIL message
	fail("The following nodes were found as having Chelsio NICs using TwinAx SFP transceivers that are part of an aggregate network configured as LACP: ".compressRange(@nodes_affected));
	fail("An issue exists in OneFS 8.x releases where communication over the associated aggregate network interfaces may fail to work after an upgrade to impacted OneFS 8.x versions");
	fail("A fix for this issue is targeted for OneFS 8.0.0.6, OneFS 8.0.1.2, and OneFS 8.1.0.1.");
	fail("A directed availability patch is available upon request and approval of Isilon support SMEs, however, if the only connectivity to the cluster is via impacted interfaces, this may leave the cluster in an unreachable state until the patch has been applied.");
	fail("Isilon Certified SFP transceivers are also known to work, review the 'SFP transceivers for Generation 4 and Generation 5 nodes' section of the 'Isilon Supportability and Compatibility Guide'.");

	# Information appended to customer information

	addCustomerInformation
	    ("TwinAx SFP, LAGG, & LACP",
	     "The following nodes were found as having Chelsio NICs using TwinAx SFP transceivers that are part of an aggregate network configured as LACP: ".compressRange(@nodes_affected),
	     "An issue exists in OneFS 8.x releases where communication over the associated aggregate network interfaces may fail to work after an upgrade to impacted OneFS 8.x versions",
	     "A fix for this issue is targeted for OneFS 8.0.0.6, OneFS 8.0.1.2, and OneFS 8.1.0.1.",
	     "A directed availability patch is available upon request and approval of Isilon support SMEs, however, if the only connectivity to the cluster is via impacted interfaces, this may leave the cluster in an unreachable state until the patch has been applied.",
	     "Isilon Certified SFP transceivers are also known to work, review the 'SFP transceivers for Generation 4 and Generation 5 nodes' section of the 'Isilon Supportability and Compatibility Guide'.");
    }

    printResults();
} # End checkLACPSFP
$CHECKS{'checkNetstat'} = {
    'Description' => 'Checks connections counts for specific protocols via netstat',
    'Exclude' => 1,
};
sub checkNetstat {
    return if wasCalled();
    printCheck("Netstat Connections");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array 'awk "].
				q[\\$4~/\.(20|21)$/{FTP++}].
				q[\\$4~/\.(22)$/{SSH++}].
				q[\\$4~/\.(135|137|138|139|445)$/{SMB++}].
				q[\\$4~/\.(80)$/{HTTP++}].
				q[\\$4~/\.(443)$/{HTTPS++}].
				q[\\$4~/\.(8080|8081)$/{WEBUI++}].
				q[\\$4~/\.(2049)$/{NFS++}].
				q[\\$4~/\.(9999|3260|3205|860)$/{ISCSI++}].
				q[\\$4~/\.(8021|8020)$/{HDFS++}].
				q[END{].
				q[print \\"FTP:\\" FTP;].
				q[print \\"SSH:\\" SSH;].
				q[print \\"SMB:\\" SMB;].
				q[print \\"HTTP:\\" HTTP;].
				q[print \\"HTTPS:\\" HTTPS;].
				q[print \\"WEBUI:\\" WEBUI;].
				q[print \\"NFS:\\" NFS;].
				q[print \\"ISCSI:\\" ISCSI;].
				q[print \\"HDFS:\\" HDFS;].
				q[}" <(netstat -lan|grep ESTABLISHED)']);
    } else {
	$gather = gatherCluster(qq[for i in */netstat;do
				echo -n \$i; awk '{if(\$6~/ESTABLISHED/ && \$4~/\\.(20|21)\$/) C++}END{print ":FTP:" C}' \$i;
				echo -n \$i; awk '{if(\$6~/ESTABLISHED/ && \$4~/\\.(22)\$/) C++}END{print ":SSH:" C}' \$i;
				echo -n \$i; awk '{if(\$6~/ESTABLISHED/ && \$4~/\\.(135|137|138|139|445)\$/) C++}END{print ":SMB:" C}' \$i;
				echo -n \$i; awk '{if(\$6~/ESTABLISHED/ && \$4~/\\.(80)\$/) C++}END{print ":HTTP:" C}' \$i;
				echo -n \$i; awk '{if(\$6~/ESTABLISHED/ && \$4~/\\.(443)\$/) C++}END{print ":HTTPS:" C}' \$i;
				echo -n \$i; awk '{if(\$6~/ESTABLISHED/ && \$4~/\\.(8080|8081)\$/) C++}END{print ":WEBUI:" C}' \$i;
				echo -n \$i; awk '{if(\$6~/ESTABLISHED/ && \$4~/\\.(2049)\$/) C++}END{print ":NFS:" C}' \$i;
				echo -n \$i; awk '{if(\$6~/ESTABLISHED/ && \$4~/\\.(9999|3260|3205|860)\$/) C++}END{print ":ISCSI:" C}' \$i;
				echo -n \$i; awk '{if(\$6~/ESTABLISHED/ && \$4~/\\.(8021|8020)\$/) C++}END{print ":HDFS:" C}' \$i;
				done]);
    }

    # Analyze information for issues.
    my %high_connection_counts = ();
    foreach my $lnn (@LNNS) {
	verboseHeader($NODE_INFO{$lnn}{'Name'});
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	my %connection_counts = ();
	foreach my $line (@{$gather->{$lnn}}) {
	    next unless ($line =~ /\s*(\S+):(\d+)/);
	    verbose("$1 Client Connections: $2");
	    $connection_counts{$1} = $2;
	}
	push(@{$high_connection_counts{'FTP'}}, $lnn) if ($connection_counts{'FTP'} and $connection_counts{'FTP'} >= 25);
	push(@{$high_connection_counts{'HDFS'}}, $lnn) if ($connection_counts{'HDFS'} and $connection_counts{'HDFS'} >= 25);
	push(@{$high_connection_counts{'SMB'}}, $lnn) if ($connection_counts{'SMB'} and $connection_counts{'SMB'} >= 750);
	push(@{$high_connection_counts{'NFS'}}, $lnn) if ($connection_counts{'NFS'} and $connection_counts{'NFS'} >= 750);
	push(@{$high_connection_counts{'SSH'}}, $lnn) if ($connection_counts{'SSH'} and $connection_counts{'SSH'} >= 5);
	push(@{$high_connection_counts{'WEBUI'}}, $lnn) if ($connection_counts{'WEBUI'} and $connection_counts{'WEBUI'} >= 5);
	push(@{$high_connection_counts{'HTTP'}}, $lnn) if ($connection_counts{'HTTP'} and $connection_counts{'HTTP'} >= 250);
	push(@{$high_connection_counts{'ISCSI'}}, $lnn) if ($connection_counts{'ISCSI'} and $connection_counts{'ISCSI'} >= 2);
	push(@{$high_connection_counts{'HTTPS'}}, $lnn) if ($connection_counts{'HTTPS'} and $connection_counts{'HTTPS'} >= 10);
    }
    foreach my $protocol (keys %high_connection_counts) {
	warning("High connection count for $protocol on nodes: ".compressRange(@{$high_connection_counts{$protocol}}));
    }

    # Output Check result
    printResults();
} # End checkNetstat
$CHECKS{'checkRoutingTables'} = {
    'Description' => 'Displays routing table for each node',
    'Exclude' => 1,
};
sub checkRoutingTables {
    return if wasCalled();
    printCheck("Routing Tables");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array netstat -rn]);
    } else {
	$gather = gatherCluster(q[for i in */netstat; do grep -H . $i | awk '/Routing tables/, /Active/'; done]);
    }

    # Analyze information for issues.
    foreach my $lnn (sort {$a <=> $b} keys %NODE_INFO) {
	verboseHeader($NODE_INFO{$lnn}{'Name'});
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	foreach my $line (@{$gather->{$lnn}}) {
	    last if $line =~ /Active Internet connections/;
	    verbose("$line");
	}
    }

    # Output Check result
    printResults();
} # End checkRoutingTables
$CHECKS{'checkStaticRouteConflict'} = {
    'Description' => 'Checks for conflicting static routes',
    'Exclude' => 1,
};
sub checkStaticRouteConflict {
    if (wasCalled()) { return }

    # Setup
    my $MSG_MISSING_NETWORK = 'Missing network pool information';
    my $SPF_ROUTES = 'At least one static route is conflicting with a subnet route.  Offending static routes: %s';

    # Start
    printCheck('Static Route Conflict');
    gatherFlexNet();
    if (!$CLUSTER_INFO{'FlexNet'}{'Subnets'} or
	!$CLUSTER_INFO{'FlexNet'}{'Pools'}) {
	fail($MSG_MISSING_NETWORK);
	printResults();
	return;
    }

    # Analyze
    my @default_routes = ();
    foreach my $subnet (values %{$CLUSTER_INFO{'FlexNet'}{'Subnets'}}) {
	if (!$subnet->{'Gateway'} or $subnet->{'Gateway'} eq '0.0.0.0') { next }
	if (!$subnet->{'Base Addr'} or !$subnet->{'Netmask'}) { next }
	push(@default_routes, "$subnet->{'Base Addr'}/$subnet->{'Netmask'}");
    }
    my @conflicting_routes = ();
    foreach my $pool (values %{$CLUSTER_INFO{'FlexNet'}{'Pools'}}) {
	if (!$pool->{'Static Routes'}) { next }
	foreach my $route (@{$pool->{'Static Routes'}}) {
	    my ($match) = grep {$route eq $_} @default_routes;
	    my ($exists) = grep {$route eq $_} @conflicting_routes;
	    if ($match and !$exists) { push(@conflicting_routes, $route) }
	}
    }

    # Flag
    if (@conflicting_routes) {
	fail(sprintf($SPF_ROUTES, join(', ', @conflicting_routes)));
	info(sprintf($MSG{'KB_1'}, '000196212'));
    }

    # Output
    printResults();
} # End checkStaticRouteConflict
sub gatherFlexNet {
    if (wasCalled()) { return }

    # The awk command in awk_string will remove xml groups we have no interest in
    my $awk_string = q[awk 'BEGIN{FS="[^0-9]*"}/device-id/{D=$2;next}/class-index/{I=$2;next}/<\/member>/{print "member:"D":"I;next}/<(ipranges|members|interfaces|provisioning-rules)>/{flag=1;next}/<\/(ipranges|members|interfaces|provisioning-rules)>/{flag=0;next}!flag{print}/<interface-id>/{print}'];

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	my ($file) = gatherLocal(q[2>/dev/null ls -t /etc/ifs/flx_config.xml /etc/ifs/flexnet/flx_config.xml /ifs/.ifsvar/modules/flexnet/flx_config.xml|head -1]);
	$gather = gatherLocal(qq[2>&1 $awk_string '$file']) if $file;
    } else {
	my ($file) = gatherLocal(q[2>/dev/null ls -t local/ifsvar_modules.tar/modules/flexnet/flx_config.xml */etcifs.tar/ifs/flx_config.xml */etc.tar/etc/ifs/flx_config.xml */etc.tar/etc/ifs/flexnet/flx_config.xml|head -1]);
	$gather = gatherLocal(qq[2>&1 $awk_string '$file']) if $file;
    }

    # Analyze information for issues.
    my @level = ();
    my @names = ();
    my %level_conv = (
	'groupnet' => 'Groupnets',
	'subnet' => 'Subnets',
	'network-pool' => 'Pools',
	'static-routes' => 'Static Routes');
    my %tmp = ();
    my $gather_ssips = 0;
    my $gather_static = 0;
    my $baseaddr = "";
    my $netmask = "";
    my $tag = "";
	gatherNetworkInterfaces();
    foreach (@$gather) {
	$tag = $level[$#level] if !$tag and @level;
	last if /<\/flexnet/;
	if (/<(groupnet|subnet|network-pool)>/) {
	    $tag = $1;
	    push(@level, $tag);
	} elsif (/<\/(groupnet|subnet|network-pool)>/) {
	    if ($tag ne $1) { return; }
	    $CLUSTER_INFO{'FlexNet'}{$level_conv{$tag}}{join(".",@names)} = \%{$tmp{$tag}};
	    delete $tmp{$tag}; %{$tmp{$tag}} = (); pop(@level); pop(@names); $tag = "";
	} elsif (/<default-groupnet>(\d+)</) {
	    $CLUSTER_INFO{'FlexNet'}{'Default Groupnet'} = $1;
	} elsif (/<sbr-enabled>(\d)</) {
	    $CLUSTER_INFO{'FlexNet'}{'SBR'} = $1;
	} elsif (!$tag) {
	    next;
	# Handle gathering of SSIP list
	} elsif (/<sc-service-address>([^<]+)</) {
	    next if $1 =~ /^0\.0\.0\.0$/;
	    @{$tmp{$tag}{'SSIP'}} = ($1);
	} elsif (/<sc-service-addresses>/) {
	    $gather_ssips = 1;
	} elsif (/<\/sc-service-addresses>/) {
	    $gather_ssips = 0;
	} elsif (/<static-routes>/) {
	    $gather_static = 1;
	} elsif (/<\/static-routes>/) {
	    $gather_static = 0;
	} elsif ($gather_ssips) {
	    if (/low>([^<]+)</) {
		$gather_ssips = $1;
	    } elsif (/high>([^<]+)</) {
		next if $1 =~ /^0\.0\.0\.0$/ or $gather_ssips =~ /^0\.0\.0\.0$/;
		my $first = $gather_ssips;
		my $last = $1;
		my @addrs = ($gather_ssips);
		if ($first ne $last) {
		    my @first = split(/\.|:/, $first);
		    my @last = split (/\.|:/, $last);
		    $first =~ s/$first[$#first]$//;
		    if ($first =~ /\./) { #IPV4
			@addrs = map {sprintf("$first%d", $_)} $first[$#first] .. $last[$#last];
		    } elsif ($first =~ /:/) { #IPV6
			@addrs = map {sprintf("$first%x", $_)} hex($first[$#first]) .. hex($last[$#last]);
		    }
		}
		push(@{$tmp{$tag}{'SSIP'}}, @addrs);
	    }
	} elsif ($gather_static) {
	    if (/<base-addr>([^<]+)/) {
		$baseaddr = $1;
	    } elsif (/<netmask>([^<]+)</) {
		$netmask = $1;
	    }
	    if ($baseaddr and $netmask) {
		push(@{$tmp{$tag}{'Static Routes'}}, "$baseaddr/$netmask");
		$baseaddr = "";
		$netmask = "";

	    }
	} elsif (/<name>([^<]+)</) {
	    $tmp{$tag}{'Name'} = $1;
	    push(@names, $1);
	} elsif (/<id>([^<]+)</) {
	    $tmp{$tag}{'ID'} = $1;
	} elsif (/<pool-id>([^<]+)</) {
	    $tmp{$tag}{'ID'} = $1;
	} elsif (/<gateway-priority>([^<]+)</) {
	    $tmp{$tag}{'Gateway Priority'} = $1;
	} elsif (/<gateway>([^<]+)</) {
	    $tmp{$tag}{'Gateway'} = $1;
	} elsif (/<sc-zone-name>([^<]+)</) {
	    $tmp{$tag}{'SC Zone Name'} = $1;
	} elsif (/<auth-zone>([^<]+)</) {
	    $tmp{$tag}{'Auth Zone'} = $1;
	} elsif (/<alloc-method>([^<]+)</) {
	    $tmp{$tag}{'Allocation Method'} = $1;
	} elsif (/<aggregation-mode>([^<]+)</) {
	    $tmp{$tag}{'Aggregation Mode'} = $1;
	} elsif (/<sc-zone-alias>([^<]+)</) {
	    push(@{$tmp{$tag}{'SC Zone Aliases'}}, $1);
	} elsif (/^member:(\d+):(\d+)$/) {
	    my $type = ((sprintf("%b",$2) =~ tr/1//) == 1)?"ext":"agg";
	    if ($LNN_FROM_DEVID{$1}) {
		push(@{$tmp{$tag}{'Members'}}, "$LNN_FROM_DEVID{$1}:$type");
	    }
	} elsif (/<interface-id>(\d+):([^<]+)</) {
	    if ($LNN_FROM_DEVID{$1}) {
		push(@{$tmp{$tag}{'Members'}}, "$LNN_FROM_DEVID{$1}:$2");
		}
	} elsif (/<interface-id>(\d+):([^<]+)</) {
	    if ($LNN_FROM_DEVID{$1}) {
		push(@{$tmp{$tag}{'Suspended Nodes'}}, "$LNN_FROM_DEVID{$1}:$2");
		}
	} elsif (/<vlan-enabled>([^<]+)</) {
	    $tmp{$tag}{'VLAN Enabled'} = $1;
	} elsif (/<vlan-tag>([^<]+)</) {
	    $tmp{$tag}{'VLAN Tag'} = $1;
	} elsif (/<base-addr>([^<]+)/) {
	    $tmp{$tag}{'Base Addr'} = $1;
	} elsif (/<netmask>([^<]+)</) {
	    $tmp{$tag}{'Netmask'} = $1;
	}
    }
    debug("CLUSTER_INFO->FlexNet=".toJSON($CLUSTER_INFO{'FlexNet'}));

    # Generate a list of nodes not on any external network
    if ($CLUSTER_INFO{'FlexNet'}{'Pools'}) {
	my %is_nanon = map {$_ => 1} @LNNS;
	$iface = '';
	$node_num = '';
	foreach my $href_pool (keys %{$CLUSTER_INFO{'FlexNet'}{'Pools'}}) {
		debug("POOL = $href_pool");
	    if (!$CLUSTER_INFO{'FlexNet'}{'Pools'}{$href_pool}->{'Members'}) { next }
	    foreach my $member (@{$CLUSTER_INFO{'FlexNet'}{'Pools'}{$href_pool}->{'Members'}}) {
			if($member =~ /^(\d+):(\S+)/){
				$node_num = $1;
				$iface = $2;
				debug("$node_num = $iface");
				debug("$NODE_INFO{$node_num}{'Network'}{$iface}{'status'}");
				if($NODE_INFO{$node_num}{'Network'}{$iface}{'status'} =~ '^\s*active\s*'){ 
					delete $is_nanon{$node_num} 
				}
				else{ 
					$CLUSTER_INFO{'pools_at_risk_parallel'}{$href_pool}{'$node_num'} = $node_num; 
				}
			}
		#if ($member =~ /^(\d+):/) { 
		#	if($NODE_INFO{$node_num}{'Network'}{$iface}{'status'} !~ '^\s*no carrier\s*'){ delete $is_nanon{$1} }
		#}
		}
	}
	foreach my $lnn (keys %is_nanon) {
	    $NODE_INFO{$lnn}{'No External Network'} = 1;
	    debug("NODE_INFO->${lnn}->No External Network=1");
	}
    }
}
###
# gatherNetworkInterfaces: Gathers network interface information.
sub gatherNetworkInterfaces {
    return if wasCalled();

    # Gather IP Addresses from ifconfig
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster("2>&1 ${SUDO} isi_for_array ifconfig");
    } else {
	$gather = gatherCluster("2>&1 grep -H . */ifconfig");
    }

    # Store IP Addresses under NODE_INFO{$node}{'Network'}{$interface}{'addresses'} and
    # the status of each interface in NODE_INFO{$node}{'Network'}{$interface}{'status'}
    foreach my $node (@LNNS) {
	if (!$gather->{$node}) {
	    missing($node);
	    next;
	}
	my $interface = "";
	my $description = "";
	foreach my $line (@{$gather->{$node}}) {
	    $interface = $1 if ($line =~ /^\s*(\S+): flags/);
	    next if (!$interface);
	    # if ($line =~ /mtu (\d+)/) {
	    # 	$NODE_INFO{$node}{'Network'}{$interface}{'mtu'} = $1;
	    # 	debug("NODE_INFO->${node}->Network->${interface}->mtu=$1");
	    # }
	    # if ($line =~ /options=(.*)/) {
	    # 	$NODE_INFO{$node}{'Network'}{$interface}{'options'} = $1;
	    # 	debug("NODE_INFO->${node}->Network->${interface}->options=$1");
	    # }
	    if ($line =~ /lagg:\s+laggdev\s+(\S+)/) {
		$NODE_INFO{$node}{'Network'}{$interface}{'laggdev'} = $1;
		debug("NODE_INFO->${node}->Network->${interface}->laggdev=$1");
	    }
	    if ($line =~ /media: (.*)/) {
		$NODE_INFO{$node}{'Network'}{$interface}{'media'} = $1;
		debug("NODE_INFO->${node}->Network->${interface}->media=$1");
	    }
		if ($line =~ /description: (.*)/) {
		$description = $1;
		$NODE_INFO{$node}{'Network'}{$interface}{'description'} = $description;
		$NODE_INFO{$node}{'Network'}{$description}{'description'} = $description;
		debug("NODE_INFO->${node}->Network->$1->description=$1");
	    }
	    if ($line =~ /status: (.*)/) {
		$NODE_INFO{$node}{'Network'}{$interface}{'status'} = $1;
		$NODE_INFO{$node}{'Network'}{$description}{'status'} = $1;	
		debug("NODE_INFO->${node}->Network->${interface}->status=$1");
	    debug("NODE_INFO->${node}->Network->$description->status=$1");
		}
		if ($line =~ /.*: flags=(.*)/ ){
			$NODE_INFO{$node}{'Network'}{$interface}{'flags'} =  $1;
			debug("NODE_INFO->${node}->Network->${interface}->flags=$1");
		}
	    if ($line =~ /inet (\S+)/) {
		push(@{$NODE_INFO{$node}{'Network'}{$interface}{'addresses'}}, $1);
		debug("NODE_INFO->${node}->Network->${interface}->addresses=$1");
	    }
	    if ($line =~ /inet6 (\S+)/) {
		push(@{$NODE_INFO{$node}{'Network'}{$interface}{'addresses'}}, $1);
		debug("NODE_INFO->${node}->Network->${interface}->addresses=$1");
	    }
	    if ($line =~ /^\s*laggproto\s+(\S+.*)$/) {
		$NODE_INFO{$node}{'Network'}{$interface}{'laggproto'} = $1;
		debug("NODE_INFO->${node}->Network->${interface}->laggproto=$1");
	    }
	    if ($line =~ /^\s*laggport:\s+(\S.*)$/) {
		push(@{$NODE_INFO{$node}{'Network'}{$interface}{'laggport'}}, $1);
		debug("NODE_INFO->${node}->Network->${interface}->laggport=$1");
	    }
	    # if ($line =~ /lladdr\s+(\S+)$/) {
	    # 	$NODE_INFO{$node}{'Network'}{$interface}{'lladdr'} = $1;
	    # 	debug("NODE_INFO->${node}->Network->${interface}->lladdr=$1");
	    # }
	}

	# Track nodes with BXE interfaces for BXE related patches
	push(@{$CLUSTER_INFO{'BXE Nodes'}}, $node) if ($NODE_INFO{$node}{'Network'}{'bxe0'} or $NODE_INFO{$node}{'Network'}{'bxe1'});

	# Track nodes with CXGB interfaces
	push(@{$CLUSTER_INFO{'CXGB Nodes'}}, $node) if ($NODE_INFO{$node}{'Network'}{'cxgb0'} or $NODE_INFO{$node}{'Network'}{'cxgb1'});

	# Tally how many active InfiniBand interfaces there is on each node, store this in CLUSTER_INFO{'Active IB Interfaces'}
	my $ibinterfaces = 0;
	$ibinterfaces++ if ($NODE_INFO{$node}{'Network'}{'ib0'} and $NODE_INFO{$node}{'Network'}{'ib0'}{'status'} eq 'active');
	$ibinterfaces++ if ($NODE_INFO{$node}{'Network'}{'ib1'} and $NODE_INFO{$node}{'Network'}{'ib1'}{'status'} eq 'active');
	if ((!$CLUSTER_INFO{'Active IB Interfaces'} or $ibinterfaces > $CLUSTER_INFO{'Active IB Interfaces'}) and (!isServiceDisabled('opensm_ib0') and !isServiceDisabled(opensm_ib1))) {
	    $CLUSTER_INFO{'Active IB Interfaces'} = $ibinterfaces;
	    debug("CLUSTER_INFO->Active IB Interfaces=$ibinterfaces");
	}
    }
} # End gatherNetworkInterfaces
$CHECKS{'checkKB000215473'} = {
    'Description' => 'Checks if any node meets criteria for KB 000215473',
    'Reference' => 'https://www.dell.com/support/kbdoc/000215473',
};
$MSG{'checkKB000215473_FAIL_1'} = 'The following nodes show that there may be a BIOS job stuck in iDRAC which may pose an issue for other firmware updates on the node: %s';
sub checkKB000215473 {
    if (wasCalled()) { return }
    gatherProductInfo();
    gatherNodeFirmware();
    checkNodeFirmware();
    if (!$CLUSTER_INFO{'Gen6.5'}) { return unsupported('UNSUP_HW_1') }
    if(!$RPS{'Node Firmware'}{'Devices'}){ return }
    # Start
    printCheck('KB 000215473');
    
    my $gen65_node = compressRange(@{$CLUSTER_INFO{'Gen6.5'}});

    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -n $gen65_node '/opt/dell/DellPTAgent/tools/pta_call POST /api/PT/v1/host/idrac/jobs'];

    # Gather
    my $gather = isLive()?gatherCluster($CMD_LIVE):();
    my @nodes = ();
    foreach $lnn (@{$CLUSTER_INFO{'Gen6.5'}}){
        debug("Checking node $lnn");
        foreach my $line (@{$gather->{$lnn}}) {
            debug("Node ".$lnn." Result:\t".$line);
            my $job_state = $1 if $line =~ /^\s*\"JobStatus\":\s*\"(\S+)\",\s*$/;
            if ($job_state =~ /.*Downloaded.*/){
                debug("BIOS JOB STATE FAIL:\t".$job_state);
                push(@nodes, $lnn);
            }
        }
    }

    # Flag
    if (@nodes) {
	fail(sprintf($MSG{'checkKB000215473_FAIL_1'}, compressRange(@nodes)));
	info("Please reference KB 000215473 (https://www.dell.com/support/kbdoc/en-ie/000215473) for additional information and remediation steps.");
    }

    # Output
    printResults();
}
# End checkKB000215473
$CHECKS{'checkKB201488'} = {
    'Description' => 'Checks if any node meets criteria for KB 000201488',
    'Reference' => 'https://www.dell.com/support/kbdoc/000201488',
};
$MSG{'checkKB201488_FAIL_1'} = 'The DEeth_infinity device is reporting incorrectly on the following nodes: %s';
sub checkKB201488 {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen6'}) { return unsupported('UNSUP_HW_1', 'A200, A2000, or H400') }

    # Start
    printCheck('KB 201488');
    gatherNodeFirmware();

    # Flag
    if ($RPS{'KB000201488'}) {
	fail(sprintf($MSG{'checkKB201488_FAIL_1'}, compressRange(@{$RPS{'KB000201488'}})));
	info(sprintf($MSG{'KB_INT_1'}, '000201488'));
    }

    # Output
    printResults();
}
$CHECKS{'checkKB489473'} = {
    'Description' => 'Checks if any node meets criteria for KB 000061983',
    'Reference' => 'https://www.dell.com/support/kbdoc/000061983',
};
$MSG{'checkKB489473_WARN_1'} = 'The IsilonFPV1 device is reporting None on the following nodes: %s';
sub checkKB489473 {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen5'} and !$CLUSTER_INFO{'Gen4'}) {
	return unsupported('UNSUP_GEN45');
    }

    # Start
    printCheck('KB 489473');
    gatherNodeFirmware();

    # Analyze
    my @affected_nodes = ();
    foreach my $lnn (@{$CLUSTER_INFO{'Gen5'}}, @{$CLUSTER_INFO{'Gen4'}}) {
	if (exists $NODE_INFO{$lnn} and
	    exists $NODE_INFO{$lnn}{'Firmware'} and
	    (!exists $NODE_INFO{$lnn}{'Firmware'}{'IsilonFPV1'} or
	     $NODE_INFO{$lnn}{'Firmware'}{'IsilonFPV1'} eq 'None')) {
	    push(@affected_nodes, $lnn);
	}
    }

    # Flag
    if (@affected_nodes) {
	warning(sprintf($MSG{'checkKB489473_WARN_1'},
			compressRange(@affected_nodes)));
    }

    # Output
    if (isResults()) { info(sprintf($MSG{'KB_1'}, '000061983')) }
    printResults();
}
$CHECKS{'checkKB516613'} = {
    'Description' => 'Checks if any node meets criteria for KB 000057267',
    'Reference' => 'https://www.dell.com/support/kbdoc/000057267',
};
sub checkKB516613 {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen5'}) { return unsupported('UNSUP_GEN5') }

    # Setup
    my $NODELIST_GEN5 = compressRange(@{$CLUSTER_INFO{'Gen5'}});
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -n $NODELIST_GEN5 '/sbin/nvmecontrol logpage -p 3 lnv0 | grep Slot'];
    my $SPF_OLD_LOX = 'LOx NVRAM card running older rpc180[abc] firmware version on the following nodes: %s';
    my $SPF_CRITERIA = 'KB 000057267 criteria met on the following nodes: %s';
    my $MSG_LOGS = 'KB 000057267 criteria cannot be verified in logs, it will be assumed criteria is met';
    my $MSG_ONSITE = 'Nodes that meet criteria for KB 000057267 may fail to boot and require on-site remediation following KB 000079815';
    my $MSG_REBOOTS = 'Try to avoid any reboots before applying node firmware updates';

    printCheck('KB 516613');
    gatherNodeFirmware();

    # Gather
    my $gather = isLive()?gatherCluster($CMD_LIVE):();

    # Analyze
    my %nodes_with_slot_3_active = ();
    my %nodes_with_old_lox = ();
    foreach my $lnn (@{$CLUSTER_INFO{'Gen5'}}) {

	# Check LOx NVRAM card firmware version being impacted
	if ($NODE_INFO{$lnn}{'Firmware'}) {
	    my ($device) = grep {$_ =~ /LOx/} keys %{$NODE_INFO{$lnn}{'Firmware'}};
	    if ($device and $NODE_INFO{$lnn}{'Firmware'}{$device} =~ /rp180[abc]/) {
		$nodes_with_old_lox{$lnn} = 1;
	    }
	}

	# Gathered details required from here
	if (!$gather->{$lnn}) { next }

	# Check if Slot 3 is active with an impacted firmware version
        verboseHeader($NODE_INFO{$lnn}{'Name'});
        foreach my $line (@{$gather->{$lnn}}) {
            verbose($line);
            if ($line !~ /Slot 3:.*Active.*?rp180[abc]/) { next }
            $nodes_with_slot_3_active{$lnn} = 1;
        }
    }

    # Flag
    if (%nodes_with_old_lox) {
        warning(sprintf($SPF_OLD_LOX, compressRange(keys %nodes_with_old_lox)));
    }
    if (%nodes_with_slot_3_active) {
	$RPS{'KB516613'} = 1;
        critical(sprintf($SPF_CRITERIA, compressRange(keys %nodes_with_slot_3_active)));
    }
    if (!isLive() and %nodes_with_old_lox) {
	$RPS{'KB516613'} = 1;
	critical($MSG_LOGS);
    }
    if ($RPS{'KB516613'}) {
	critical($MSG_ONSITE);
	warning($MSG_REBOOTS);
    }
    if (isResults()) { info(sprintf($MSG{'KB_1'}, '000057267')) }

    # Output
    printResults();

}
$CHECKS{'checkNodeFirmware'} = {
    'Description' => 'Checks node firmware for updates',
    'Reference' => 'https://dl.dell.com/content/manual44174649',
	'Parameters' => { 'target_version' => $RPS{'IsiFw'}, },
};
sub checkNodeFirmware {
    if (wasCalled()) { return }

    # Skip firmware checks if no firmware version to check against
    if (!$RPS{'IsiFw'} or $RPS{'IsiFw'} eq 'None') {
	return unsupported('UNSUP_NO_FW_1', 'Node Firmware Package');
    }

    # Skip firmware checks if the cluster is virtual
    if (isVirtual()) { return unsupported('UNSUP_VIRT') }

    checkPatches();
	checkHardwareStatus();
    checkPartitions();
    checkKB521890();
    checkKB516613();
    checkKB489473();
    loadRestrictedNFP();
	checkKB000314255();

    our %NodeFirmware;
    our @NFP;

    my $MSG_BMC_PKG = 'Node firmware updates may not be accurate when a BMC firmware package is installed. Uninstall the BMC firmware package and re-run health checks.';
    my $MSG_9_GEN4 = "Node Firmware Package $RPS{'IsiFw9'} selected as it contains the latest firmware versions for this cluster";
    my $MSG_9_ONEFS = "Node Firmware Package $RPS{'IsiFw9'} selected as the current and/or destination OneFS versions are less than OneFS 7.1";
    my $MSG_10_ONEFS = "Node Firmware Package $RPS{'IsiFw10'} selected as the current and/or destination OneFS versions are less than OneFS 9";
    my $MSG_11_GEN6 = 'Node Firmware Package 11.5 and newer releases require OneFS 9 and newer releases and may contain additional node firmware updates';
    my $MSG_NVRAM1 = 'There is a known issue that NVRam LOx updates fail when running OneFS 7.1.1.0 to 7.1.1.8. For the NVRam to be updated, you must upgrade the OneFS version of the cluster to 7.1.1.9 or above.';
    my $MSG_NVRAM2 = 'There is a known issue that NVRam LOx will not display or update its secondary component when running OneFS 7.1.1.0 to 7.1.1.5 or 7.2.0.0 to 7.2.0.3. For the NVRam secondary component to be displayed and updated, you must first upgrade the OneFS version of the cluster to a version not affected.';
    my $MSG_QLOGIC = "If performing updates to QLogic firmware on OneFS $CLUSTER_INFO{'OneFS'}, a BXE patch must first be installed and all nodes with BXE network interfaces rebooted.";
    my $MSG_BBU_DEG = "If performing Node Firmware updates to versions 10.3.9 or 11.6 or newer, HealthCheckFramework patch version 34.0.0 or newer must first be installed and follow steps outlined in DTA 207476 (https://www.dell.com/support/kbdoc/en-ie/000207476).";
	my $SPF_NEWER = 'The following nodes are on firmware versions that are newer than what is available in Isilon Node Firmware Package %s: %s';
    my $SPF_VAR10 = 'To install the Node Firmware Package there must be at least 500MB of available space in the /var partition and the following nodes do not meet this requirement: %s';
    my $SPF_VAR11 = 'To perform node firmware updates there must be at least 200MB of available space in the /var partition and the following nodes do not meet this requirement: %s';
    my $SPF_CMCSDR = 'The following nodes were identified as having a CMCSDR device reporting None: %s. Applying node firmware updates may be necessary to resolve CMCSDR devices reporting None.';
    my $MSG_SIGNED = 'OneFS 9.4 and later releases require signed Node Firmware Packages and the requested Node Firmware Package is not a signed release. Consider updating to the latest available Node Firmware Package';
    my $MSG_NFP13 = 'OneFS 9.11 and later releases require Node Firmware 13.0 or higher. Consider updating to the latest available Node Firmware Package';
	my $MSG_13_ONEFS = "Node Firmware Package $RPS{'IsiFw13'} selected as the current and/or destination OneFS versions require version 13.0 or higher.";
	my $MSG_CXNIC1 = 'Mellanox CX NIC\'s detected as being out of date on nodes: %s, the Node Firmware will need to take place POST OneFS to allow the NIC\'s to be updated after mlx5 driver is installed during the upgrade to '.isDestinationOneFS().'. For more information, please review the Node Firmware Release Notes (https://dl.dell.com/content/manual42271337) and https://www.dell.com/support/kbdoc/en-ie/000207982.';
	my $MSG_CXNIC2 = 'Mellanox CX NIC\'s detected as being out of date on nodes: %s cannot be updated on current ('.isCurrentOneFS().') or chosen destination ('.isDestinationOneFS().') OneFS version, please consider upgrading to 9.2.1.15, 9.3.0.8, 9.4.0.5, 9.5.0.0 or newer for mlx5 Driver Compatibility. For more information, please review the Node Firmware Release Notes (https://dl.dell.com/content/manual42271337) and https://www.dell.com/support/kbdoc/en-ie/000207982.';
	my $MSG_CXNIC3 = 'Mellanox CX NIC\'s detected as being out of date on nodes: %s cannot be updated on current ('.isCurrentOneFS().') OneFS version, please consider upgrading to 9.2.1.15, 9.3.0.8, 9.4.0.5, 9.5.0.0 or newer for mlx5 Driver Compatibility. For more information, please review the Node Firmware Release Notes (https://dl.dell.com/content/manual42271337) and https://www.dell.com/support/kbdoc/en-ie/000207982.';
	my $MSG_KB000226297 = 'Mellanox CX6 NIC\'s detected that should be updated using node firmware package 12.2.1 or higher. For more information, please see https://www.dell.com/support/kbdoc/000226297';
	$CX_trig_1;
	$CX_trig_2;
	$CX_trig_3;
	my @cx_nic_found = ();
	my @cx6_nic_with_nfp12 = ();
	###
    # Selecting another node firmware package must occur before starting the
    # check so that the destination version is shown in the check output

    # Select 9.x if running a OneFS < 7.1 and not upgrading to a OneFS >= 7.1
    if (compareVersions($RPS{'IsiFw'}, $RPS{'IsiFw9'}) > 0 and
	isCurrentOneFS('<7.1') and
	!isDestinationOneFS('>=7.1')) {
	details($MSG_9_ONEFS);
	$RPS{'IsiFw'} = $RPS{'IsiFw9'};
    }
    # Select 9.x if Gen4 nodes and no Gen5/6 or A100 nodes.
    if (compareVersions($RPS{'IsiFw'}, $RPS{'IsiFw9'}) > 0 and
	($CLUSTER_INFO{'Gen4'} or $CLUSTER_INFO{'IQ'}) and
	!($CLUSTER_INFO{'Gen6'} or
	  $CLUSTER_INFO{'Gen5'} or
	  $CLUSTER_INFO{'A100'})) {
    	$RPS{'IsiFw'} = $RPS{'IsiFw9'};
	details($MSG_9_GEN4);
    }
    # Select 10.x if running OneFS < 9 and not upgrading to OneFS >= 9
    if (compareVersions($RPS{'IsiFw'}, '11.0') >= 0 and
	isCurrentOneFS('<9') and
	!isDestinationOneFS('>=9')) {
	$RPS{'IsiFw'} = $RPS{'IsiFw10'};
	details($MSG_10_ONEFS);
	if ($CLUSTER_INFO{'Gen6'}) { warning($MSG_11_GEN6) }
    }
    # Flag that OneFS 9.4 requires 11.3 or newer
    if (compareVersions($RPS{'IsiFw'}, '11.3') < 0 and
	isCurrentOneFS('>=9.4')) {
	fail($MSG_SIGNED);
    }
	# Flag that OneFS 9.11 requires 13.0 or newer
    if (compareVersions($RPS{'IsiFw'}, '13.0') < 0 and
	(isCurrentOneFS('>=9.11') or
	isDestinationOneFS('>=9.11'))) {
	details($MSG_13_ONEFS);
	$RPS{'IsiFw'} = $RPS{'IsiFw13'};
    }

    printCheck("Node Firmware (Target: $RPS{'IsiFw'})");

    # Results may not be accurate if a BMC firmware package is installed
    if (hasPatch('IsiFw.*BMC')) { fail($MSG_BMC_PKG) }

    gatherNodeFirmware();
    gatherNetworkInterfaces();
    gatherPSRedundancy();
	gatherm2();

    # Analyze information for issues
    my $nvram_lox_issue = 0;
    my $nvram_lox_issue2 = 0;
    my @nvram_lox_issue3 = ();
	my @gen6_deth_infinity_issue = ();
    my @redundancy_lost = ();
    my $requires_bxe_patch = 0;
	my $requires_hcf34_patch = 0;
	my @cx6_defect_fw = ();
    foreach my $lnn (@LNNS) {
	if (!$NODE_INFO{$lnn}{'Firmware'}) {
	    missing($lnn);
	    next;
	}

	if(grep{$_ == $lnn} @{$RPS{'KB000226297'}} and $RPS{'IsiFw'} =~ /^12\.[012]$/ and $firmware !~ /^22\.36\.1010\+DEL0000000027$/){
		debug("DO NOT UPDATE FW on node: $lnn");
		next;
	}

	my $firmware_out_of_date = 0;
	my $firmware_is_newer = 0;
	$RPS{'max_device_length'} = 40;
	$RPS{'max_fw_length'} = 30;
	$RPS{'max_desiredfw_length'} = 30;
      DEVICE:
	foreach my $device (keys %{$NODE_INFO{$lnn}{'Firmware'}}) {

	    my $firmware = $NODE_INFO{$lnn}{'Firmware'}{$device};

		if (!$device or !$NODE_INFO{$lnn}{'Firmware'}{$device}) { next }
		if ($device =~ /^([DE][EP])(bcc_infinity)$/ and compareVersions($RPS{'IsiFw'}, '12.4') >=0){
				debug("Device name change from $device to $1"."bem_mongoose to support updated name.");
				#$NODE_INFO{$lnn}{'Firmware'}{$device} = $NODE_INFO{$lnn}{'Firmware'}{$1.'bem_mongoose'};
				$device = $1.'bem_mongoose';
		}

		if (($device =~ /^([DE][EP])(bcc_infinity)$/ or $device =~ /^.*bem_mongoose$/) and compareVersions($firmware, '02.40') < 0){
			push(@{$RPS{'BBU_DTA'}{'Nodes'}}, $lnn);
			debug("checkNodeFirmware: !!! Node $lnn Device $device Firmware $firmware affected by PFN DTA 207476 (BBU Firmware needs updating)");
		}

		if ($device =~ /^M.2-NVMe/){
				if(exists($NODE_INFO{$lnn}{'m.2'}) and $NODE_INFO{$lnn}->{'m.2'} ne $device){
					debug("Device name change for node $lnn from $device to $NODE_INFO{$lnn}->{'m.2'}.");
					$device = $NODE_INFO{$lnn}->{'m.2'};
				}
		}

		#$RPS{'Node FW Info'}{$device}{'Current'}{'Firmware'} = $firmware;
		#$RPS{'Node FW Info'}{$device}{'Current'}{'Nodes'} .= $lnn.', ';
		$RPS{'Node FW Info'}{$firmware}{$device}{'Current'}{'Firmware'} = $firmware;
		$RPS{'Node FW Info'}{$firmware}{$device}{'Current'}{'Nodes'} .= $lnn.', ';
		$RPS{'Current_FW_revisions'}{$lnn}{$device} = $firmware;
		
		#if(compareVersions($RPS{'IsiFw'}, '12.4') >=0){
		#	if ($device =~ /^DEbcc_infinity$/){
		#		debug("Device was: $device");
		#		$device = 'DEbem_mongoose';
		#		debug("Device is no: $device");
		#	}
		#	if ($device =~ /^EPbcc_infinity$/){
		#		debug("Device was: $device");
		#		$device = 'EPbem_mongoose';
		#		debug("Device is no: $device");
		#	}
		#	}
		
	    debug("checkNodeFirmware: Node $lnn Device: $device Firmware: $firmware");

	    # Check for an issue where the LOx reports unknown information
	    if ($device =~ /unknown:LOx/i
		and ! grep(/^lox/i, keys %{$NODE_INFO{$lnn}{'Firmware'}})
		and isCurrentOneFS('<7.2.1')) {
		push(@nvram_lox_issue3, $lnn);
		debug("checkNodeFirmware: !!! Node $lnn is reporting unknown:LOx information");
	    }

		if ($device =~ /DEeth_infinity/ and (!$firmware or $firmware eq '' or $firmware =~ /^\s*.*\\u0000.*\s*$/)) {
		push(@gen6_deth_infinity_issue, $lnn);
		debug("checkNodeFirmware: !!! Node $lnn is reporting $firmware for $device information");
	    }

	    # Address when CFFPS1/2 is the device type but gathering the CFFPS assembly identifies differently.
	    if ($NODE_INFO{$lnn}{$device}) {
		$device = $NODE_INFO{$lnn}{$device};
	    }

	    # Check for firmware updates
	  PACKAGE:
	    foreach my $fwpkg (@NFP) {
			debug("NFP Package is $fwpkg");
			my $dvc_mdl;
			my $ps_slt = PS.$1 if $device =~ /^(?:DE|EP)ps([01])$/;
			my $ps_mdl = $RPS{$lnn}->{$ps_slt};
			if($device =~ /^(?:DE|EP)ps[01]$/){
				my $dvc_mdl;
				my $ps_slt = PS.$1 if $device =~ /^(?:DE|EP)ps([01])$/;
				my $ps_mdl = $RPS{$lnn}->{$ps_slt};
			};
			if($device =~ /ps[01]/){
				foreach my $k (keys %{$NodeFirmware{'12.4.1'}}){
					debug("Checking $device against $k with FW $NodeFirmware{'12.4.1'}{$k}");
					if ($k =~ /^$device/ and grep {$firmware eq $_} @{$NodeFirmware{'12.4.1'}{$k}}) {
						$device = $k;

						debug("Match found: Device $k was found for $device and FW $NodeFirmware{$fwpkg}{$k} was found in package $fwpkg\n");
					}
				}
			}

		if (!$NodeFirmware{$fwpkg} and $NodeFirmware{$fwpkg}{$device} !~ /^$device/) { next }

		# Skip packages that are less than what was selected
		if (compareVersions($fwpkg, $RPS{'IsiFw'}) < 0) { next PACKAGE }

		# Device match

		if ($NodeFirmware{$fwpkg}{$device} || $NodeFirmware{$fwpkg}{$NODE_INFO{$lnn}{'Node Type'}}{$device}) {
			debug("Found $device within NFP $fwpkg - proceeding with further checks to find $firmware within target package");

		    # Firmware match
		    if ((grep {$firmware eq $_} @{$NodeFirmware{$fwpkg}{$device}}) || (grep {$firmware eq $_} @{$NodeFirmware{$fwpkg}{$NODE_INFO{$lnn}{'Node Type'}}{$device}})) {
				debug("FIRMWARE FOR $device CURRENT: $firmware matches version from selected package $fwpkg");

			# Does not match the requested package
			if ($fwpkg ne $RPS{'IsiFw'}) {

			    # Check if the firmware is also available in
			    # the requested package or if it is newer
			    # than available in the requested package
			    my ($match) = grep {$firmware eq $_} @{$NodeFirmware{$RPS{'IsiFw'}}{$device}};	
				my ($match_nt) = grep {$firmware eq $_} @{$NodeFirmware{$RPS{'IsiFw'}}{$NODE_INFO{$lnn}{'Node Type'}}{$device}};
				debug("Match = $match \t MatchNT = $match_nt for node $lnn and device $device");
				if (!$match && !$match_nt) {
					#if(){
						$firmware_is_newer = 1; 
					#} 
				}
			}

			# Move to the next device
			next DEVICE;
		    }

                    # Skip if bcc_infinity is running 02.20 and the versions being checked for are less than 02.20
                    #if ($device =~ /bcc_infinity/ and
                    #    compareVersions($firmware, '02.20') >= 0) {
                    #    my ($match) = grep {compareVersions($_, '02.20') > 0} @{$NodeFirmware{$fwpkg}{$device}};
                    #    if (!$match) { next DEVICE }
                    #}
                }

		# The requested package has been reached, stop checking packages
		if ($fwpkg eq $RPS{'IsiFw'}) { last PACKAGE }
	    }

	    # Devices affected by KB 489473
	    if ($device eq 'IsilonFPV1'	and $firmware eq 'None') { next DEVICE }

	    # Check for KB000207184/PFN ET004812
		my @non_mlx5_skip = ();
	    if ($device =~ '^Mellanox5-EN0'){
			if ($NODE_INFO{$lnn}{'Product'} and $NODE_INFO{$lnn}{'Product'} =~ /^(F810)/){
				if ((compareVersions($RPS{'IsiFw'}, '11.0') > 0 && compareVersions($RPS{'IsiFw'}, '11.1.3') >= 0) || (compareVersions($RPS{'IsiFw'}, '11.0') < 0 && compareVersions($RPS{'IsiFw'}, '10.3.6') < 0)){
					if (compareVersions($firmware, '16.28.1002+EMC0000000017') < 0){
						my $Mellanox5_EN0_issue = 1;
						push(@{$RPS{'PFN'}{'ET004812'}{'Nodes'}}, $lnn);
						#warning("Node $lnn Device Firmware $firmware affected by PFN ET004812");
						debug("checkNodeFirmware: !!! Node $lnn Device $device Firmware $firmware affected by PFN ET004812");
					}
				}
				else {
					push(@non_mlx5_skip, $lnn);
					next DEVICE;
				}
			}
			else{
				push(@non_mlx5_skip, $lnn);
				next DEVICE;
			}
		}
		else{
			push(@non_mlx5_skip, $lnn);
		}

		if(grep{$_ == $lnn} @{$RPS{'KB000226297'}} and $device =~ '.*CX\-?6.*' and compareVersions($firmware, '22.36.1010+DEL0000000027') == 0){
			debug("CX6 defect detected on node $lnn with firmware version $firmware on $device");
			push (@cx6_defect_fw, $lnn);
		}

				# check for new CX4, CX5, and CX6 driver firmware (when OneFS is 9.2.1.15, 9.3.0.8, 9.4.0.5, and OneFS version 9.5.0.0 or higher)
		# if version is below above supported versions but upgrading to supported versions where drivers were introduced, firmware will need
		# to be performed AFTER OneFS (or if using combined method, then all other components are updated during OneFS+FW update, then an 
		# additional set of updates reboots are needed for the new NIC driver firmware once the upgrade to supported version is comitted)
				
		if ($device =~ '^Mellanox-CX[456]'){
			debug("Mellanox CX NIC's detected");
			push(@{$RPS{'Node Firmware Additional'}{'Devices'}{'CX NIC'}}, 1);
			if(compareVersions($RPS{'IsiFw'}, '11.0') > 0 && compareVersions($RPS{'IsiFw'}, '11.6.1') >= 0){
					# we are due to update FW to a version with automated updates for CX4/5/6 drivers
				if(($device =~ 'CX\-?4' && compareVersions($firmware, '14.32.2004+DEL2420110034') < 0) 
				|| ($device =~ 'CX\-?5' && compareVersions($firmware, '16.32.2004+DEL0000000004') < 0) 
				|| ($device =~ 'CX\-?6' && compareVersions($firmware, '22.32.2004+DEL0000000027') < 0)
				) {
					push(@{$RPS{'Node Firmware Additional'}{'Devices'}{'CX NIC OOD'}}, 1);
					push(@cx_nic_found, $lnn);
					debug("CX NIC's ($device) detected and are out of date on node $lnn");
					if((isCurrentOneFS('<9.2.1.15') 
					|| (isCurrentOneFS('>=9.3') && isCurrentOneFS('<9.3.0.8')) 
					|| (isCurrentOneFS('>=9.4') && isCurrentOneFS('<9.4.0.5')))	){
						debug("Current version does not support CX NIC's updates, further checks needed");
							# unsupported current version for CX 4/5/6 updates
							if(isOneFSUpgrade()){
								# OneFS upgrade requested
								debug("This plan includes OneFS upgrade");
								if((isDestinationOneFS('>=9.2') && isDestinationOneFS('>=9.2.1.15') && isDestinationOneFS('<9.3')) 
								|| (isDestinationOneFS('>=9.3') && isDestinationOneFS('>=9.3.0.8') && isDestinationOneFS('<9.4')) 
								|| (isDestinationOneFS('>=9.4') && isDestinationOneFS('>=9.4.0.5') && isDestinationOneFS('<9.5'))
								|| isDestinationOneFS('>=9.5')){
									# Is OneFS and is upgrading to supported version for CX updates
									debug("Is OneFS and is upgrading to supported version for CX updates");
									push(@{$RPS{'Node Firmware Additional'}{'Devices'}{'CX NIC POST OE'}}, 1);
									$CX_trig_1 = 1;
									#info("CX NIC's detected as being out of date, the Node Firmware will need to take place POST OneFS to allow the NIC's to be updated after mlx5 driver is installed during the upgrade to.");
								}
								else{
									# Is OneFS but not upgrading to supported version for CX updates
									debug("Is OneFS and destination version DOES NOT support CX updates");
									$CX_trig_2 = 1;
									#info("CX NIC's detected as being out of date cannot be updated on current or chosen OneFS version, please consider upgrading to 9.2.1.15, 9.3.0.8, 9.4.0.5, 9.5.0.0 or newer for mlx5 Driver Compatibility.");
								}
							}
							else{
								# no OneFS upgrade included - provide INFO on CX NIC's 
								debug("Not a OneFS and current version does not support CX updates");
								$CX_trig_3 = 1;
								#info("CX NIC's detected as being out of date cannot be updated on current OneFS version, please consider upgrading to 9.2.1.15, 9.3.0.8, 9.4.0.5, 9.5.0.0 or newer for mlx5 Driver Compatibility.");
							}
						}
						else{
							# supported current version for CX 4/5/6 updates
							debug("CX NIC's supported updates for current version");
						}
						#if ($device =~ '^Mellanox-CX[6]' and $RPS{'IsiFw'} =~ /12\.[012]/){
						#	push(@cx6_nic_with_nfp12, $lnn);
						#	debug("DO NOT UPDATE FW on node: $lnn");
						#}
					}
				}
			}
	    # Updates for this device not available in the selected firmware package
	    if (!$NodeFirmware{$RPS{'IsiFw'}}{$device} && !@{$NodeFirmware{$RPS{'IsiFw'}}{$NODE_INFO{$lnn}{'Node Type'}}{$device}}) { next DEVICE }

		# Firmware is out of date
	    $firmware_out_of_date = 1;

my $device_rcd = $device;
                my $desiredfw_length;
                my $fw_length = length($firmware);
                if ($fw_length > $RPS{'max_fw_length'}) { $RPS{'max_fw_length'} = $fw_length }
				if($device =~ /MLKcec.*/){
					foreach my $dfw (@{$NodeFirmware{$RPS{'IsiFw'}}{$device}}) { 
						debug("checking that $firmware is less than $dfw");
						if($firmware < $dfw){
							debug("!!! Firmware $firmware is less than $dfw");
						}
						else{
								debug("!!! Firmware $firmware is greater than $dfw");
								next DEVICE;
						}

					}
				}
                debug("checkNodeFirmware: !!! Node $lnn Device $device Firmware $firmware out of date, valid recent firmware revisions: ".join(",", @{$NodeFirmware{$RPS{'IsiFw'}}{$device}}));
                #if($device =~ /\S+ps[01]_/){ $device = $1 };
                $device_rcd = $1 if $device =~ /^(\S+ps[01])/;
                push(@{$RPS{'Node Firmware'}{'Devices'}{$device_rcd}{$firmware}}, $lnn);
                debug("FINDINGS: @{$NodeFirmware{$RPS{'IsiFw'}}{$NODE_INFO{$lnn}{'Node Type'}}{$device}}");
                if(@{$NodeFirmware{$RPS{'IsiFw'}}{$NODE_INFO{$lnn}{'Node Type'}}{$device}} eq '' || !@{$NodeFirmware{$RPS{'IsiFw'}}{$NODE_INFO{$lnn}{'Node Type'}}{$device}}){
                        #       $device = $1 if $device =~ /^(\S+ps[01]/;
                        debug("PUSHING ".join(",", @{$NodeFirmware{$RPS{'IsiFw'}}{$device}})." TO RPS{Node FW Info}");
                        #if($device =~ /\S+ps[01]_/){ $device_rcd = $1 };
                        $RPS{'Node FW Info'}{$firmware}{$device_rcd}{'Desired'}{'Firmware'} = join(",", @{$NodeFirmware{$RPS{'IsiFw'}}{$device}});
                        $desiredfw_length = length(join(",", @{$NodeFirmware{$RPS{'IsiFw'}}{$device}}));
                }
                else{
                        debug("PUSHING ".join(",", @{$NodeFirmware{$RPS{'IsiFw'}}{$NODE_INFO{$lnn}{'Node Type'}}{$device}})." TO RPS{Node FW Info}");
                        #if($device =~ /\S+ps[01]_/){ $device_rcd = $1 };
                        $RPS{'Node FW Info'}{$firmware}{$device_rcd}{'Desired'}{'Firmware'} = join(",", @{$NodeFirmware{$RPS{'IsiFw'}}{$NODE_INFO{$lnn}{'Node Type'}}{$device}});
                        $desiredfw_length = length(join(",", @{$NodeFirmware{$RPS{'IsiFw'}}{$NODE_INFO{$lnn}{'Node Type'}}{$device}}));
                }
                $RPS{'Node FW Info'}{$firmware}{$device_rcd}{'Desired'}{'Nodes'}.= $lnn.', ';

                #my $desiredfw_length = length(join(",", @{$NodeFirmware{$RPS{'IsiFw'}}{$device}}));
                if ($desiredfw_length > $RPS{'max_desiredfw_length'}) { $RPS{'max_desiredfw_length'} = $desiredfw_length }

                my $device_length = length($device);
                if ($device_length > $RPS{'max_device_length'}) { $RPS{'max_device_length'} = $device_length }

                debug(">> RPS->{'Node Firmware'}->{'Devices'}->{$device}}\t Old FW= \t New FW = ");

	    # Check for ETA 184813
	    if ($device eq 'Lsi'
		and $firmware =~ /^[\d\.]+$/
		and compareVersions($firmware, '10.00.06.00') <= 0) {
		push(@{$RPS{'ETA'}{'184813'}{'Nodes'}}, $lnn);
		debug("checkNodeFirmware: !!! Node $lnn Device $device Firmware $firmware affected by ETA 184813");
	    }

	    # Check for ETA 184406
	    if ($device eq 'Mellanox'
		and $NODE_INFO{$lnn}{'Product'} and $NODE_INFO{$lnn}{'Product'} =~ /^A100/
		and $firmware =~ /^([\d\.]+)/
		and compareVersions($1, '2.30.8020') < 0) {
		push(@{$RPS{'ETA'}{'184406'}{'Nodes'}}, $lnn);
		debug("checkNodeFirmware: !!! Node $lnn Device $device Firmware $firmware affected by ETA 184406");
	    }
	    # Check for KB466373/FCO F010517EE
	    if ($device =~ '^CMC_'
		and $NODE_INFO{$lnn}{'Product'} and $NODE_INFO{$lnn}{'Product'} =~ /^(S210|X210|X410|NL410|HD400)/
		and compareVersions($firmware, '02.05') < 0) {
		push(@{$RPS{'FCO'}{'F010517EE'}{'Nodes'}}, $lnn);
		debug("checkNodeFirmware: !!! Node $lnn Device $device Firmware $firmware affected by FCO F010517EE");
	    }

		#if ($device =~ '^Mellanox-CX[6]' and $RPS{'IsiFw'} =~ /^12\.[012]$/){
		#	push(@cx6_nic_with_nfp12, $lnn);
		#	debug("DO NOT UPDATE FW on node: $lnn");
		#}

	    # Check for KB 000036783/FCO F121917EE
	    if ($device eq 'EPpost_warthog'
		and $NODE_INFO{$lnn}{'Product'} and $NODE_INFO{$lnn}{'Product'} =~ /^(H500|H600|F800)/
		and compareVersions($firmware, '20.81') < 0) {
		push(@{$RPS{'FCO'}{'F121917EE'}{'Nodes'}}, $lnn);
		debug("checkNodeFirmware: !!! Node $lnn Device $device Firmware $firmware affected by FCO F121917EE");
	    }

	    # Check for KB 000036252/FCO F011018EE
	    if ($device eq 'EPsas_infinity'
		and $NODE_INFO{$lnn}{'Product'} and $NODE_INFO{$lnn}{'Product'} =~ /^F800/
		and compareVersions($firmware, '0001.0004.0000.11181') < 0) {
		push(@{$RPS{'FCO'}{'F011018EE'}{'Nodes'}}, $lnn);
		debug("checkNodeFirmware: !!! Node $lnn Device $device Firmware $firmware affected by FCO F011018EE");
	    }

	    if ($device =~ /LOx/
		and isCurrentOneFS('<7.1.1.9')) {
		$nvram_lox_issue = 1;
		debug("checkNodeFirmware: !!! Known issue with LOx and OneFS 7.1.1.[012345678] branch: KB 000015166");
	    }
	    if ($device =~ /LOx/
		and !($firmware =~ /\+/)) {
		$nvram_lox_issue2 = 1;
		debug("checkNodeFirmware: !!! Known issue with LOx and OneFS 7.2.0.[0123] and OneFS 7.1.1.[012345] where secondary firmware does not show or get updated in these versions");
	    }

	    # A flag if QLogic firmware updates are needed
	    if ($device =~ /QLogic-NX2/
		and compareVersions($firmware, '7.13.0') < 0) {
		$RPS{'Node Firmware'}{'QLogic'} = 1;
	    }

	    # Flag CMCSDR reporting None and recommend firmware updates to fix it
	    if ($device =~ /^CMCSDR/ and $firmware eq 'None') {
		debug("checkNodeFirmware: !!! Known issue with CMCSDR reporting None");
		push(@{$RPS{'Node Firmware'}{'CMCSDR'}}, $lnn);
	    }
	}
	if ($firmware_out_of_date) {
	    push(@{$RPS{'Node Firmware'}{'Nodes'}}, $lnn);
	} elsif ($firmware_is_newer) {
	    push(@{$RPS{'Node Firmware'}{'Newer'}}, $lnn);
	}

	# Check for Gen6 nodes having fully redundant power supplies
	if ($NODE_INFO{$lnn}{'Power Supply Redundancy'} and $NODE_INFO{$lnn}{'Power Supply Redundancy'} ne 'Fully Redundant') {
	    push(@redundancy_lost, $lnn)
	}
    }

    # Flag issue where Gen6 nodes do not have fully redundant power supplies which will cause node firmware updates to error.
    if (@redundancy_lost) {
	fail('The following nodes are not reporting "Fully Redundant" power supplies: '.compressRange(@redundancy_lost));
    }

	if (@gen6_deth_infinity_issue) {
		fail('The following nodes have compute modules showing a blank firmware version that will cause a node firmware update to fail: '.compressRange(@gen6_deth_infinity_issue));
		info('Please contact Isilon Support for assistance in resolving this issue.');
	}

    # NVRam LOx known issue should be displayed regardless of updates being available
    if (@nvram_lox_issue3) {
	warning('Unable to determine if NVRam LOx firmware updates are needed due to it reporting <unknown>.');
	warning('If NVRam LOx updates are needed, you must first upgrade the OneFS version of the cluster to 7.1.1.9 or above.') if isCurrentOneFS('<=7.1.1.8');
    }

    # Node Firmware updates available
    if ($RPS{'Node Firmware'}{'Nodes'}) {

	# Show nodes requiring updates
	warning(scalar(@{$RPS{'Node Firmware'}{'Nodes'}}).' nodes have out of date node firmware: '.compressRange(@{$RPS{'Node Firmware'}{'Nodes'}}));
	debug('checkNodeFirmware: !!! Devices with out of date firmware: '.join(', ', keys %{$RPS{'Node Firmware'}{'Devices'}{$firmware}}));

	# Display ETAs and known issues
	if ($RPS{'ETA'}{'184406'}{'Nodes'}) {
	    $RPS{'ETA'}{'184406'}{'Severity'} = 'INFO';
	    $RPS{'ETA'}{'184406'}{'Message'} = scalar(@{$RPS{'ETA'}{'184406'}{'Nodes'}}).' nodes are running firmware versions affected by ETA 184406: '.compressRange(@{$RPS{'ETA'}{'184406'}{'Nodes'}});
	    warning($RPS{'ETA'}{'184406'}{'Message'});
	    push(@{$RPS{'Node Firmware'}{'Priority'}}, "Node firmware updates are strongly recommended because $RPS{'ETA'}{'184406'}{'Message'}");
	}
	if ($RPS{'ETA'}{'184813'}{'Nodes'}) {
	    $RPS{'ETA'}{'184813'}{'Severity'} = 'INFO';
	    $RPS{'ETA'}{'184813'}{'Message'} = scalar(@{$RPS{'ETA'}{'184813'}{'Nodes'}}).' nodes are running firmware versions affected by ETA 184813: '.compressRange(@{$RPS{'ETA'}{'184813'}{'Nodes'}});
	    warning($RPS{'ETA'}{'184813'}{'Message'});
	    push(@{$RPS{'Node Firmware'}{'Priority'}}, "Node firmware updates are strongly recommended because $RPS{'ETA'}{'184813'}{'Message'}");
	}
	if($CX_trig_1){
info(sprintf($MSG_CXNIC1, compressRange(@cx_nic_found)));
addCustomerInformation(sprintf($MSG_CXNIC1, compressRange(@cx_nic_found)));
	}
	if($CX_trig_2){
info(sprintf($MSG_CXNIC2, compressRange(@cx_nic_found)));
addCustomerInformation(sprintf($MSG_CXNIC2, compressRange(@cx_nic_found)));
	}
	if($CX_trig_3){
info(sprintf($MSG_CXNIC3, compressRange(@cx_nic_found)));
addCustomerInformation(sprintf($MSG_CXNIC3, compressRange(@cx_nic_found)));
	}
	if ($RPS{'FCO'}{'F010517EE'}{'Nodes'}) {
	    $RPS{'FCO'}{'F010517EE'}{'Severity'} = 'INFO';
	    $RPS{'FCO'}{'F010517EE'}{'Message'} = scalar(@{$RPS{'FCO'}{'F010517EE'}{'Nodes'}}).' nodes are running firmware versions affected by FCO F010517EE: '.compressRange(@{$RPS{'FCO'}{'F010517EE'}{'Nodes'}});
	    warning($RPS{'FCO'}{'F010517EE'}{'Message'});
	    push(@{$RPS{'Node Firmware'}{'Priority'}}, "Node firmware updates are strongly recommended because $RPS{'FCO'}{'F010517EE'}{'Message'}");
	}
	if ($RPS{'FCO'}{'F121917EE'}{'Nodes'}) {
	    $RPS{'FCO'}{'F121917EE'}{'Severity'} = 'INFO';
	    $RPS{'FCO'}{'F121917EE'}{'Message'} = scalar(@{$RPS{'FCO'}{'F121917EE'}{'Nodes'}}).' nodes are running firmware versions affected by FCO F121917EE: '.compressRange(@{$RPS{'FCO'}{'F121917EE'}{'Nodes'}});
	    warning($RPS{'FCO'}{'F121917EE'}{'Message'});
	    push(@{$RPS{'Node Firmware'}{'Priority'}}, "Node firmware updates are strongly recommended because $RPS{'FCO'}{'F121917EE'}{'Message'}");
	    my $msg = 'Certain voltage threshold monitoring values were incorrectly programmed to be too stringent for H500, H600, and F800 nodes. This could result in unnecessary preventative action by the system, such as powering down. This issue is resolved in Node Firmware Package 10.1.1 and later, but before you perform a node firmware update on F800, H600, or H500 nodes, you must either install patch-211361 or run the update_dpol_setpoint.py script based on the OneFS version of the cluster.';
	    push(@{$RPS{'Node Firmware'}{'Priority'}}, $msg);
	    critical($msg);
	    if (not hasPatch('patch-211361') and isCurrentOneFS(qr/^8\.1\.0\.[01]$/)) {
		$RPS{'Node Firmware'}{'Patch'}{'patch-211361'}{'Impacts'} = '';
	    }
	}
	if ($RPS{'FCO'}{'F011018EE'}{'Nodes'}) {
	    $RPS{'FCO'}{'F011018EE'}{'Severity'} = 'INFO';
	    $RPS{'FCO'}{'F011018EE'}{'Message'} = scalar(@{$RPS{'FCO'}{'F011018EE'}{'Nodes'}}).' nodes are running firmware versions affected by FCO F011018EE: '.compressRange(@{$RPS{'FCO'}{'F011018EE'}{'Nodes'}});
	    warning($RPS{'FCO'}{'F011018EE'}{'Message'});
	    push(@{$RPS{'Node Firmware'}{'Priority'}}, "Node firmware updates are strongly recommended because $RPS{'FCO'}{'F011018EE'}{'Message'}");
	}
	if ($RPS{'FCO'}{'F082318EE'}{'Nodes'}) {
	    $RPS{'FCO'}{'F082318EE'}{'Severity'} = 'INFO';
	    $RPS{'FCO'}{'F082318EE'}{'Message'} = scalar(@{$RPS{'FCO'}{'F082318EE'}{'Nodes'}}).' nodes are running node firmware versions affected by FCO F082318EE: '.compressRange(@{$RPS{'FCO'}{'F082318EE'}{'Nodes'}});
	    warning($RPS{'FCO'}{'F082318EE'}{'Message'});
	    push(@{$RPS{'Node Firmware'}{'Priority'}}, "Node firmware updates are strongly recommended because $RPS{'FCO'}{'F082318EE'}{'Message'}");
	}
	if ($nvram_lox_issue) { warning($MSG_NVRAM1) }
	if ($nvram_lox_issue2) { warning($MSG_NVRAM2) }
	if ($RPS{'Node Firmware'}{'QLogic'}
	    and isCurrentOneFS(qr/^7\.1\.1\.([0123456789]|10|11)$|^7\.2\.0\.[012345]$|^7\.2\.1\.[012345]$|^8\.0\.0\.[01234]/)
	    and not hasPatch('patch-(188239|189133|175451)')) {
	    $requires_bxe_patch = 1;
	    critical($MSG_QLOGIC);
	}
	if ((isCurrentOneFS('^9.5') || isDestinationOneFS('^9.5')) && $CLUSTER_INFO{'Gen6'} && $RPS{'IsiFw'} && $RPS{'IsiFw'} ne 'none'  && (compareVersions($RPS{'IsiFw'}, '11.6') >= 0 || (compareVersions($RPS{'IsiFw'}, '11.0') < 0 && compareVersions($RPS{'IsiFw'}, '10.3.9') >= 0))) {
	    $requires_hcf34_patch = 1;
		$RPS{'BBU_SAFE_CHK'} = 1;

		$patchinfratmp = '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186' if isCurrentOneFS(qr/^9\.4\.0\./);
		$patchinfratmp = '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925' if isCurrentOneFS(qr/^9\.2\.1\./);
		$patchinfratmp = '9.1.0_UGA-PATCH-INFRA_2022-07_PSP-2682' if isCurrentOneFS(qr/^9\.1\.0\./);

		$patchtmp = 'HealthCheck_9.4.0_34.0.0' if isCurrentOneFS(qr/^9\.4\.0\./);
		$patchtmp = 'HealthCheck_9.3.0_34.0.0' if isCurrentOneFS(qr/^9\.3\.0\./);
		$patchtmp = 'HealthCheck_9.2.1_34.0.0' if isCurrentOneFS(qr/^9\.2\.1\./);
		$patchtmp = 'HealthCheck_9.2.0_34.0.0' if isCurrentOneFS(qr/^9\.2\.0\./);
		$patchtmp = 'HealthCheck_9.1.0_34.0.0' if isCurrentOneFS(qr/^9\.1\.0\./);
		$patchtmp = 'HealthCheck_9.0.0_34.0.0' if isCurrentOneFS(qr/^9\.0\.0\./);

		if(!hasPatch('HealthCheck_9.[0-9].[0-9]_34.0.0') || !isDestinationOneFS('9.4.0.12')){

		if(!hasPatch('9.[0-9].[0-9]_UGA-PATCH-INFRA_*')){
		
		$RPS{'Pre NFW Patches'}{$patchinfratmp} = ();
		$RPS{'Pre NFW Patches'}{$patchinfratmp}{'Impacts'} = $impacts if $impacts;
		$RPS{'Pre NFW Patches'}{$patchinfratmp}{'Important'} = $important if $important;
		$RPS{'Pre NFW Patches'}{$patchinfratmp}{'Service Restart'} = $service_restart if $service_restart;
		$RPS{'Pre NFW Patches'}{$patchinfratmp}{'Priority'} = $priority if $priority;
		$RPS{'Pre NFW Patches'}{$patchinfratmp}{'Target'} = $target if $target;
		@{$RPS{'Pre NFW Patches'}{$patchinfratmp}{'Requires'}} = @requires if @requires;
		@{$RPS{'Pre NFW Patches'}{$patchinfratmp}{'Reboot'}} = @reboot if @reboot;

		}

		$RPS{'Pre NFW Patches'}{$patchtmp} = ();
		$RPS{'Pre NFW Patches'}{$patchtmp}{'Impacts'} = $impacts if $impacts;
		$RPS{'Pre NFW Patches'}{$patchtmp}{'Important'} = $important if $important;
		$RPS{'Pre NFW Patches'}{$patchtmp}{'Service Restart'} = $service_restart if $service_restart;
		$RPS{'Pre NFW Patches'}{$patchtmp}{'Priority'} = $priority if $priority;
		$RPS{'Pre NFW Patches'}{$patchtmp}{'Target'} = $target if $target;
		@{$RPS{'Pre NFW Patches'}{$patchtmp}{'Requires'}} = @requires if @requires;
		@{$RPS{'Pre NFW Patches'}{$patchtmp}{'Reboot'}} = @reboot if @reboot;
		warning("$patchtmp is required to be installed prior to Node Firmware updates to $RPS{'IsiFw'}.");
		warning($MSG_BBU_DEG);
		}
		else{
 			#warning("$patchtmp is required to be installed prior to Node Firmware updates to $RPS{'IsiFw'}.");
			warning($MSG_BBU_DEG);
		}

	}
	
	if(@cx6_defect_fw){
		critical("CX6 NIC's detected with firmware level affected by KB000226297, please update the affected NIC's to NFP version 12.2.1 or higher. Please reference https://www.dell.com/support/kbdoc/000226297 for more details.");
	}

	if ($RPS{'RQR_NF8'} and
	    !hasPatch('^IsiFw') and
	    compareVersions($RPS{'IsiFw'}, '10.0') >= 0 and
	    compareVersions($RPS{'IsiFw'}, '11.0') < 0 and
	    isCurrentOneFS('<9.0')) {
	    fail(sprintf($SPF_VAR10, compressRange(@{$RPS{'RQR_NF8'}})));
	}
	if ($RPS{'RQR_NF9'} and
	    !hasPatch('^IsiFw') and
	    compareVersions($RPS{'IsiFw'}, '11.0') >= 0) {
	    fail(sprintf($SPF_VAR11, compressRange(@{$RPS{'RQR_NF9'}})));
	}
	if ($RPS{'Node Firmware'}{'CMCSDR'}) {
	    critical(sprintf($SPF_CMCSDR, compressRange(@{$RPS{'Node Firmware'}{'CMCSDR'}})));
	}

	if ($RPS{'Node Firmware'}{'Newer'}) {
	    warning(sprintf($SPF_NEWER, $RPS{'IsiFw'},
			    compressRange(@{$RPS{'Node Firmware'}{'Newer'}})));
	}
	# Add the package to required files
        push(@{$RPS{'Node Firmware'}{'Files'}}, "Isilon Node Firmware Package $RPS{'IsiFw'}");

	# Add the target version for the node firmware updates
	$RPS{'Node Firmware'}{'Target'} = "Node Firmware $RPS{'IsiFw'}";

	# Adjust order based on current OneFS version and known issues
	$RPS{'Node Firmware'}{'Position'} = 1;
	$RPS{'Node Firmware'}{'Position'} = 2 if isCurrentOneFS('<6.5.2.85');
	$RPS{'Node Firmware'}{'Position'} = 2 if isCurrentOneFS('<7.1') and
	    compareVersions($RPS{'IsiFw'}, '10.0.1') >= 0;
	$RPS{'Node Firmware'}{'Position'} = 2 if ($nvram_lox_issue and isDestinationOneFS('>=7.1.1.9'));
	$RPS{'Node Firmware'}{'Position'} = 2 if ($nvram_lox_issue2 and isDestinationOneFS() and !isDestinationOneFS(qr/^7\.1\.1\.[012345]|^7\.2\.0\.[0123]/));
	$RPS{'Node Firmware'}{'Position'} = 2 if (@nvram_lox_issue3 and isDestinationOneFS('>=7.1.1.9'));
	$RPS{'Node Firmware'}{'Position'} = 2 if $requires_hcf34_patch;
	$RPS{'Node Firmware'}{'Position'} = 2 if $requires_bxe_patch;
	$RPS{'Node Firmware'}{'Position'} = 2 if !isDestinationOneFS();
	$RPS{'Node Firmware'}{'Position'} = 2 if $RPS{'KB521890'};
	$RPS{'Node Firmware'}{'Position'} = 2 if $RPS{'KB519890'};
	$RPS{'Node Firmware'}{'Position'} = 2 if !$RPS{'KB516613'} and
	    !$RPS{'Node Firmware'}{'CMCSDR'} and
	    isDestinationOneFS('>=8.1.0.4') and
	    isCurrentOneFS('<8.1.0.4');
	$RPS{'Node Firmware'}{'Position'} = 2 if compareVersions($RPS{'IsiFw'}, '11.0') >= 0 and
	    isCurrentOneFS('<9.0') and
	    isDestinationOneFS('>=9.0');
	$RPS{'Node Firmware'}{'Position'} = 2 if compareVersions($RPS{'IsiFw'}, '11.0') >= 0 and
	    isCurrentOneFS('<9.2') and
	    isDestinationOneFS('>=9.2');
	$RPS{'Node Firmware'}{'Position'} = 2 if compareVersions($RPS{'IsiFw'}, '13.0') >= 0 and
	    isCurrentOneFS('>=9.0') and
	    isCurrentOneFS('<=9.2');
	$RPS{'Node Firmware'}{'Position'} = 2 if $RPS{'Node Firmware Additional'}{'Devices'}{'CX NIC POST OE'};
	if(($CLUSTER_INFO{'14G_NODES'} or $CLUSTER_INFO{'16G_NODES'}) and ((isCurrentOneFS('>=9.4') and isCurrentOneFS('<=9.4.0.18')) or (isCurrentOneFS('=9.4.0.13')) or (isCurrentOneFS('>=9.5') and isCurrentOneFS('<9.5.1.0')) or (isCurrentOneFS('>=9.7') and isCurrentOneFS('<9.7.1.0')) or (isCurrentOneFS('=9.8.0.0')))){ 
		debug("Current is on an affected code");
		if(!isDestinationOneFS() or ((isDestinationOneFS('>=9.4') and isDestinationOneFS('<=9.4.0.18')) or (isDestinationOneFS('>=9.5') and isDestinationOneFS('<9.5.1.0')) or (isDestinationOneFS('>=9.7') and isDestinationOneFS('<9.7.1.0')) or (isDestinationOneFS('=9.8.0.0')))){
				debug("Destination is on an affected code");
					debug("CLUSTER_INFO->${n14g}=$lnn | Forcing standalone NFP and OneFS and forcing Node Firmware to be rolling ");
					$RPS{'OneFS'}{'Exclude Node Firmware'} = 1;
					$RPS{'Node Firmware'}{'Method'} = 'rolling' unless $RPS{'Node Firmware'}{'Method'} eq 'simultaneous'; 
	}
	else{
				debug("Current / destination is not on an affected code.");
		$RPS{'Node Firmware'}{'Position'} = 2;
		$RPS{'OneFS'}{'Exclude Node Firmware'} = 1;
	}
};

	# Parallel method requested and available in destination OneFS version
	if (exists $RPS{'Node Firmware'}{'Method'} and
	    $RPS{'Node Firmware'}{'Method'} eq 'parallel' and
	    isCurrentOneFS('<8.2') and isDestinationOneFS('>=8.2')) {
	    $RPS{'Node Firmware'}{'Position'} = 2;
	}

	# Remove patch-211361 install if node firmware is after OneFS
	if ($RPS{'Node Firmware'}{'Position'} == 2 and
	    $RPS{'Node Firmware'}{'Patch'} and
	    $RPS{'Node Firmware'}{'Patch'}{'patch-211361'} and
	    isDestinationOneFS('>=8.1.0.2')) {
	    delete $RPS{'Node Firmware'}{'Patch'};
	}
	if ($RPS{'F011018EE'}) {
	    push(@{$RPS{'Node Firmware'}{'Additional Comments'}},
		 'Start IntegrityScan job after node firmware updates');
	}

	# Remove node firmware updates from upgrade plan if requested only
	# priority updates and there are no priority updates needed.
	if ($RPS{'Priority NF Only'} and
	    !$RPS{'Node Firmware'}{'Priority'}) {
	    delete $RPS{'Node Firmware'};
	}

    }

    # Add Node Firmware Package to required files for use post OneFS 9.1
    if (!$RPS{'Node Firmware'}{'Target'} and
	isOneFSUpgrade() and
	isDestinationOneFS('>=9.1')) {
	addRequiredFiles("Isilon Node Firmware Package $RPS{'IsiFw'}");
    }

	if($RPS{'BBU_DTA'}){
		critical("The following nodes have Battery Backup Units affected by DTA 207476 and require an update to latest firmware versions: ".compressRange(@{$RPS{'BBU_DTA'}{'Nodes'}}));
	}


	if(@{$RPS{'KB000226297'}} and $RPS{'IsiFw'} =~ /^12\.[012]$/){
		addCustomerInformation(warning("Nodes on this cluster contain CX6 NIC's and the firmware should not be updated at this time, a new Node Firmware Package is being developed to enable firmware updates where CX6 NIC's are present."));
		addCustomerInformation(info("Please use the following when updating Node Firmware so nodes with CX6 NIC's are excluded: isi upgrade cluster firmware start --fw-pkg=\"full-path-filename\" --nodes-to-upgrade=".compressRange(@{$RPS{'Node Firmware'}{'Nodes'}}))) if @{$RPS{'Node Firmware'}{'Nodes'}};
		push(@{$RPS{'Node Firmware'}{'Additional Comments'}},
		 "Please use the following when updating Node Firmware so nodes with CX6 NIC's are excluded: isi upgrade cluster firmware start --fw-pkg=\"full-path-filename\" --nodes-to-upgrade=".compressRange(@{$RPS{'Node Firmware'}{'Nodes'}})) if @{$RPS{'Node Firmware'}{'Nodes'}};
	}

    # Flag if parallel was requested but not available
    my $MSG_NFP_PARALLEL = 'Parallel method for node firmware updates was'.
	' requested, but is not available prior to OneFS 8.2 release, rolling'.
	' method will be used';
    if (exists $RPS{'Node Firmware'}{'Position'} and
	exists $RPS{'Node Firmware'}{'Method'} and
	$RPS{'Node Firmware'}{'Method'} eq 'parallel' and
	(($RPS{'Node Firmware'}{'Position'} == 1 and
	  isCurrentOneFS('<8.2')) or
	 ($RPS{'Node Firmware'}{'Position'} == 2 and
	  (isDestinationOneFS('<8.2') or
	   (!isDestinationOneFS() and
	    isCurrentOneFS('<8.2')))))) {
	warning($MSG_NFP_PARALLEL);
	$RPS{'Node Firmware'}{'Method'} = 'rolling';
    }

	
    # Output Check result
    if (isResults(&WARN)) {

                 # Print Node Firmware information
    my $SPF_SUMMARY = "%-${RPS{'max_device_length'}}s %-${RPS{'max_fw_length'}}s %-${RPS{'max_desiredfw_length'}}s %-5s %s";
    details(sprintf($SPF_SUMMARY,
                    'Component',
                    'Current',
                    'Desired',
                    'Nodes')) if $RPS{'Node FW Info'};
        #foreach my $device (keys %{$RPS{'Node FW Info'}}) {
        #       my $fw = $RPS{'Node FW Info'}{$device}{'Current'}{'Firmware'};
        #       #my $nodes = $RPS{'Node FW Info'}{$device}{'Desired'}{'Nodes'};
        #       my $nodes = compressRange(@{$RPS{'Node Firmware'}{'Devices'}{$device}});
        #       my $desired = $RPS{'Node FW Info'}{$device}{'Desired'}{'Firmware'};
        #       next unless ($desired and $desired ne ''); #$fw->{'DSP'}{'Nodes'} and
        #       #        $fw->{'DSP'}{'Count'});
    #        details(sprintf($SPF_SUMMARY,
        #                   $device,
        #                   $fw,
        #                   ($desired or '-'),
        #                   $nodes));
        #}
my %fw_Sorted;
        foreach my $flvl (keys %{ $RPS{'Node FW Info'} }) {
                debug("FLVL = $flvl");
                foreach my $fdevice (sort keys %{ $RPS{'Node FW Info'}{$flvl} } ){
                        #if($fdevice =~ /^(\S+ps[01])_/){ $fdevice eq $1};
                        debug("$flvl -> $fdevice");
                        #       if($fdevice =~ /^(\S+ps[01])_/){ $fdevice eq $1};
                        my $fw = $RPS{'Node FW Info'}{$flvl}{$fdevice}{'Current'}{'Firmware'};
                        #my $nodes = $RPS{'Node FW Info'}{$flvl}{$fdevice}{'Desired'}{'Nodes'};
                        my $nodes = compressRange(@{$RPS{'Node Firmware'}{'Devices'}{$fdevice}{$fw}});
                        #        $fw->{'DSP'}{'Count'});
            #if ($fdevice =~ /^([DE][EP])(bem_mongoose)$/ and compareVersions($RPS{'fwpkg'}, '12.4') >=0){
                        #       $fdevice = $1.'bcc_infinity';
                        #}
                        my $desired = $RPS{'Node FW Info'}{$flvl}{$fdevice}{'Desired'}{'Firmware'};
                        next unless ($desired and $desired ne ''); #$fw->{'DSP'}{'Nodes'} and
                        $fw_dev = $fdevice.$fw;
                        $fw_sorted{$fw_dev}{'Device'} = $fdevice;
                        $fw_sorted{$fw_dev}{'Current'} = $fw;
                        $fw_sorted{$fw_dev}{'Desired'} = $desired;
                        $fw_sorted{$fw_dev}{'Nodes'} = $nodes;
                        #       details(sprintf($SPF_SUMMARY,
                        #    $fdevice,
                        #    $fw,
                        #    ($desired or '-'),
                        #    $nodes));
                }
        }
        foreach $fwopt (sort keys %fw_sorted){
                #       details("PRINT $fwopt");
        details(sprintf($SPF_SUMMARY,
                            $fw_sorted{$fwopt}->{'Device'},
                            $fw_sorted{$fwopt}->{'Current'},
                            ($fw_sorted{$fwopt}->{'Desired'} or '-'),
                            $fw_sorted{$fwopt}->{'Nodes'}));
        }
                info(sprintf($MSG{'KB_1'}, '000121515'));
        }

	printResults();
} # End checkNodeFirmware
###
# gatherLOxFirmware: Gathers ( or attempts to gather ) LOx NVRAM firmware revision information.
# Currently only works on a live cluster, only designed to address an issue in the 7.1.1 branch.
sub gatherLOxFirmware {
    if (wasCalled()) { return }
    if (!isLive()) { return }
    if (!isCurrentOneFS(qr/^7\.1\.1\./)) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen5'}) { return }

    # Setup
    my $NODELIST_GEN5 = compressRange(@{$CLUSTER_INFO{'Gen5'}});
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -n $NODELIST_GEN5 '/sbin/nvmecontrol identify lnv0; /sbin/nvmecontrol devinfo lnv0'];
    my $RE_PRIMARY = qr/Firmware Version:\s+(\S+)/;
    my $RE_SECONDARY = qr/Secondary Boot Loader Rev:\s+(\S+)/;
    my $RE_MODEL = qr/Model Number:\s+(\S+)/;
    my $RE_LOX2 = qr/303-409-000A/;
    my $RE_LOX2_MLC = qr/303-409-001[AB]/;

    # Gather
    my $gather = gatherCluster($CMD_LIVE);

    # Look for firmware versions and assign based on found information
    foreach my $lnn (@{$CLUSTER_INFO{'Gen5'}}) {
	if (!$gather->{$lnn}) { next }

	# If LOx was already gathered, skip to the next node.
	if ($NODE_INFO{$lnn}{'Firmware'}{'LOx'} or
	    $NODE_INFO{$lnn}{'Firmware'}{'LOx2'} or
	    $NODE_INFO{$lnn}{'Firmware'}{'LOx2-MLC-YD'}) {
	    next;
	}

	# Reset variables
	my ($primary, $secondary, $model, $device);

	# Gather information for the node
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ $RE_PRIMARY) {
		$primary = $1;
		debug("gatherLOxFirmware: Primary Firmware Version found: $primary");
	    } elsif ($line =~ $RE_SECONDARY) {
		$secondary = $1;
		debug("gatherLOxFirmware: Secondary Firmware Version found: $secondary");
	    } elsif ($line =~ $RE_MODEL) {
		$model = $1;
		debug("gatherLOxFirmware: Model found: $model");
	    }
	}

	# A minimum of the model and the primary firmware version is required
	if (!$model or !$primary) { next }

	# Assign device by the model
	if ($model =~ $RE_LOX2) { $device = 'LOx2' }
	elsif ($model =~ $RE_LOX2_MLC) { $device = 'LOx2-MLC-YD' }
	else { $device = 'LOx' }
	debug("gatherLOxFirmware: Model identified as device: $device");

	# Assign firmware information
	$NODE_INFO{$lnn}{'Firmware'}{$device} = $primary.($secondary?("+".$secondary):'');
	debug("NODE_INFO->${lnn}->Firmware->$device=$NODE_INFO{$lnn}{'Firmware'}{$device}");

	# Delete any previously found unknown LOx devices
	my @unknown_devices = grep(/unknown:LOx/, keys %{$NODE_INFO{$lnn}{'Firmware'}});
	foreach my $unknown_device (@unknown_devices) {
	    delete $NODE_INFO{$lnn}{'Firmware'}{$unknown_device};
	}
    }
} # End gatherLOxFirmware
###
# gatherNodeFirmware: Gathers node firmware
sub gatherNodeFirmware {
    if (wasCalled()) { return }

    # Gather node firmware status from JSON file first for accuracy
    gatherNodeFirmwareStatusJSON();

    # Gather from other locations
    gatherNodeFirmwareStatus();
    gatherNodeFirmwareVersions();
    gatherQLogicFirmware();
    gatherLOxFirmware();

    # Normalize a few firmware versions to their device names
    foreach my $lnn (@LNNS) {
	my ($CMC) = grep {$_ =~ /^CMC_/} keys %{$NODE_INFO{$lnn}{'Firmware'}};
	if ($CMC) {
	    $NODE_INFO{$lnn}{'Firmware'}{'CMC'} = $NODE_INFO{$lnn}{'Firmware'}{$CMC};
	    debug("NODE_INFO->${lnn}->Firmware->CMC=$NODE_INFO{$lnn}{'Firmware'}{$CMC}");
	}
	my ($BMC) = grep {$_ =~ /^BMC_/} keys %{$NODE_INFO{$lnn}{'Firmware'}};
	if ($BMC) {
	    $NODE_INFO{$lnn}{'Firmware'}{'BMC'} = $NODE_INFO{$lnn}{'Firmware'}{$BMC};
	    debug("NODE_INFO->${lnn}->Firmware->BMC=$NODE_INFO{$lnn}{'Firmware'}{$BMC}");
	}
	my ($CFFPS1) = grep {$_ =~ /^CFFPS1_/} keys %{$NODE_INFO{$lnn}{'Firmware'}};
	if ($CFFPS1) {
	    $NODE_INFO{$lnn}{'Firmware'}{'CFFPS1'} = $NODE_INFO{$lnn}{'Firmware'}{$CFFPS1};
	    debug("NODE_INFO->${lnn}->Firmware->CFFPS1=$NODE_INFO{$lnn}{'Firmware'}{$CFFPS1}");
	    delete $NODE_INFO{$lnn}{'Firmware'}{$CFFPS1};
	    debug("Removed NODE_INFO->${lnn}->Firmware->$CFFPS1");
	}
	my ($CFFPS2) = grep {$_ =~ /^CFFPS2_/} keys %{$NODE_INFO{$lnn}{'Firmware'}};
	if ($CFFPS2) {
	    $NODE_INFO{$lnn}{'Firmware'}{'CFFPS2'} = $NODE_INFO{$lnn}{'Firmware'}{$CFFPS2};
	    debug("NODE_INFO->${lnn}->Firmware->CFFPS2=$NODE_INFO{$lnn}{'Firmware'}{$CFFPS2}");
	    delete $NODE_INFO{$lnn}{'Firmware'}{$CFFPS2};
	    debug("Removed NODE_INFO->${lnn}->Firmware->$CFFPS2");
	}
	# KB 000201488
	if ($NODE_INFO{$lnn}{'Firmware'}{'DEeth_infinity'} and
	    $NODE_INFO{$lnn}{'Firmware'}{'DEeth_infinity'} eq '\u00000.25') {
	    $NODE_INFO{$lnn}{'Firmware'}{'DEeth_infinity'} = '02.52';
	    push(@{$RPS{'KB000201488'}}, $lnn);
	    debug("RPS->KB000201488=$lnn");
	}
	my ($NVMe) = grep {$_ =~ /^NVMe_/} keys %{$NODE_INFO{$lnn}{'Firmware'}};
	if($NVMe){
		
		
	}

	#my ($CX6) = grep {$_ =~ /^Mellanox-CX6/} keys %{$NODE_INFO{$lnn}{'Firmware'}};
	my ($CX6) = grep {$_ =~ /^Mellanox-CX6-40GbE\S+\s+(40GigE)/} keys %{$NODE_INFO{$lnn}{'Firmware'}};
	if($CX6){
		push(@{$RPS{'KB000226297'}}, $lnn) unless grep{$_ == $lnn} @{$RPS{'KB000226297'}};
		debug("ADDING NODE $lnn to CX6 list");
	}
    }
} # gatherNodeFirmware
# gatherNodeFirmwareStatus: Gathers node firmware
sub gatherNodeFirmwareStatus {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LOGS = q[2>&1 cat firmware local/firmware];
    my $CMD_LIVE; 
	if(isCurrentOneFS('<9.9')){
		$CMD_LIVE = qq[2>&1 ${SUDO} isi firmware status];
	}
	else{
		$CMD_LIVE = qq[2>&1 ${SUDO} isi firmware status];
	}

    my $RE_INFO = qr/^\s*(Device|NOTE|WARNING|-)/;

    # Gather
    my $gather = ();
    if (isLive()) {
	if (isCurrentOneFS('<8')) {
	    $gather = gatherLocal($CMD_LIVE);
	}
    } else {
	$gather = gatherLocal($CMD_LOGS);
    }
    foreach my $line (@$gather) {
	# Ignore informational lines
	if ($line =~ $RE_INFO) { next }

	# Split by whitespace, removing groups of just whitespace
	my @columns = grep { /\S/ } split(/\s+/, $line);
	if (!@columns) { next }

	# Remove the column if it is a * indicating out of date firmware
	if ($columns[0] eq '*') { shift @columns }

	# Must be at least four columns
	if (!@columns or scalar @columns < 4) { next }

	# The nodes column must only consist of nodes
	if ($columns[-1] !~ /^[0-9,-]+$/) { next }

	# Assign out firmware information
	my $device = $columns[0];
	my $type = $columns[1];
	my $firmware = $columns[2];
	my @nodes = expandRange($columns[-1]);

	foreach my $lnn (@nodes) {
	    # Make sure lnn is a valid node
	    if (!$NODE_INFO{$lnn}) { next }

	    # Skip if firmware already gathered from firmware_status.json
	    if ($RPS{'gatherNodeFirmwareStatusJSON'}{$lnn}) { next }

	    $NODE_INFO{$lnn}{'Firmware'}{$device} = $firmware;
	    debug("NODE_INFO->${lnn}->Firmware->${device}=$firmware");
	}
    }
} # gatherNodeFirmwareStatus
# gatherNodeFirmwareStatusJSON: Gather from /var/ifs/upgrade/firmware_status.json
sub gatherNodeFirmwareStatusJSON {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) { return }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'grep -h . /var/ifs/upgrade/firmware_status.json'];
    my $CMD_LOGS = q[2>&1 grep -H . */upgrade_local.tar/var/ifs/upgrade/firmware_status.json];
    my $RE_SEPARATOR = qr/\}\s*,\s*\{/;
    my $RE_DEVICE = qr/"device"[^"]+"([^"]+)"/;
    my $RE_FIRMWARE = qr/"version"[^"]+"([^"]+)"/;
    my $RE_TYPE = qr/"device"[^"]+"[^"]+","(\S+)"/;
	my $RE_ASSEM_NUM = qr/"assembly_num"[^"]+"([^"]+)"/;

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Store
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	foreach my $line (@{$gather->{$lnn}}) {
	    my @devices = split($RE_SEPARATOR, $line);
	    foreach my $device_info (@devices) {
		my ($device, $firmware, $type);
		if ($device_info =~ $RE_DEVICE) { $device = $1 }
		if ($device_info =~ $RE_FIRMWARE) { $firmware = $1 }
        if ($device_info =~ $RE_TYPE) {$type = $1}
		#if($NODE_INFO{$lnn}{'Node Type'} = 'A310'){
		#	$device = $1 if $device_info =~ $RE_ASSEM_NUM and $device =~ /NVMe/;
		#}
		if (!$device or !$firmware) { next }
        if($device =~ /(^Mellanox-CX6)(\S+)$/){
			my $devst = $1;
			my $devnd = $2;
			my $tpe = $1 if $type =~ /^(\d+)\S+$/;
			$device = $devst.'-'.$tpe.'GbE'.$devnd;
		}
		$NODE_INFO{$lnn}{'Firmware'}{$device} = $firmware;
		debug("NODE_INFO->${lnn}->Firmware->${device}=${firmware}");
		$RPS{'gatherNodeFirmwareStatusJSON'}{$lnn} = 1;
	    }
	}
    }
} # gatherNodeFirmwareStatusJSON
###
# gatherNodeFirmwareVersions: Gathers node firmware
sub gatherNodeFirmwareVersions {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'cat /etc/ifs/firmware_versions'];
    my $CMD_LOGS = q[2>&1 grep -H . */etcifs.tar/ifs/firmware_versions];

    # Gathers from /etc/ifs/firmware_versions, this is information that is
    # updated during node reboots and /may/ be inaccurate!
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }

	# Skip if firmware already gathered from firmware_status.json
	if ($RPS{'gatherNodeFirmwareStatusJSON'}{$lnn}) { next }

	foreach my $line (@{$gather->{$lnn}}) {
	    # Ignore informational lines
	    if ($line =~ /^\s*(Device|NOTE|WARNING|-|Writing out firmware status JSON)/) { next }

	    # Split by whitespace, removing groups of just whitespace
	    my @columns = grep { /\S/ } split(/\s+/, $line);
	    if (!@columns) { next }

	    # Remove the column if it is a * indicating out of date firmware
	    if ($columns[0] eq '*') { shift @columns }

	    # Must be at least four columns
	    if (!@columns or scalar @columns < 4) { next }

	    # Assign out firmware information
	    my $device = $columns[0];
	    my $type = $columns[1];
	    my $firmware = $columns[2];
		if($device =~ /(^Mellanox-CX6)(\S+)$/){
			my $devst = $1;
			my $devnd = $2;
			my $tpe = $1 if $type =~ /^(\d+)\S+$/;
			$device = $devst.'-'.$tpe.'GbE'.$devnd;
		}
	    $NODE_INFO{$lnn}{'Firmware'}{$device} = $firmware;
	    debug("NODE_INFO->${lnn}->Firmware->${device}=$firmware");
	}
    }
} # gatherNodeFirmwareVersions
sub gatherPSRedundancy {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen6'}) { return }

    # Setup
    my $NODELIST_GEN6 = compressRange(@{$CLUSTER_INFO{'Gen6'}});
    my $CMD_LIVE8 = qq[2>&1 ${SUDO} isi_for_array -n $NODELIST_GEN6 '${SUDO} isi_hwmon -r' ];
    my $CMD_LIVE9 = qq[2>&1 ${SUDO} isi_for_array -n $NODELIST_GEN6 '${SUDO} isi_hwmon -b PSRedundancy'];
    my $CMD_LOGS = q[2>&1 grep -H 'Power Supply Redundancy' */isi_hwmon */hwmon_psu];
    my $SPF = '%5s %s';

    # Gather
    my $gather = ();
    if (isCurrentOneFS('>=9')) {
	$gather = gatherCluster(isLive()?$CMD_LIVE9:$CMD_LOGS);
    } else {
	$gather = gatherCluster(isLive()?$CMD_LIVE8:$CMD_LOGS);
    }

    # Save
    verboseHeader(sprintf($SPF, 'LNN', 'Power Supply Redundancy'));
    foreach my $lnn (@{$CLUSTER_INFO{'Gen6'}}) {
	if (!$gather->{$lnn}) { missing($lnn); next; }
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line !~ /Power Supply Redundancy:\s+(\S.*)$/) { next }
	    verbose(sprintf($SPF, $lnn, $1));
	    $NODE_INFO{$lnn}{'Power Supply Redundancy'} = $1;
	    debug("NODE_INFO=>${lnn}=>Power Supply Redundancy=$1");
	}
    }
} # End gatherPSRedundancy
# gatherQLogicFirmware: Gathers ( or attempts to gather ) QLogic BXE firmware revision information.
#  This is will work 100% of the time live on a cluster but only works on logsets when the log gather
#  was performed with an FRU/CTO package of 201506 or newer..
# This is a fallback method used when failing to gather QLogic-NX2 on older OneFS versions
sub gatherQLogicFirmware {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>8')) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'Gen5'}) { return }

    # Setup
    my $gen5_nodes = compressRange(@{$CLUSTER_INFO{'Gen5'}});
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -n $gen5_nodes 'sysctl dev.bxe.0.bc_version'];
    my $CMD_LOGS = q[2>&1 grep -H BCM57810 */asbuilt.xml];
    my $RE_SYSCTL = qr/dev\.bxe\.0\.bc_version:\s+(\S+)$/;
    my $RE_BCM57810 = qr/BCM57810/;
    my $RE_FIRMWARE = qr/firmware=\"([^"]+)\"/;

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Look for firmware versions and assign based on found information
    foreach my $lnn (@{$CLUSTER_INFO{'Gen5'}}) {

	# Skip if gather has nothing
	if (!$gather->{$lnn}) { next }

	# Skip if already gathered
	if ($NODE_INFO{$lnn}{'Firmware'}{'QLogic-NX2'}) { next }

	# Parse and store
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ $RE_SYSCTL or
		($line =~ $RE_BCM57810 and $line =~ $RE_FIRMWARE)) {
		$NODE_INFO{$lnn}{'Firmware'}{'QLogic-NX2'} = $1;
		debug("NODE_INFO->${lnn}->Firmware->QLogic-NX2=$1");
	    }
	}
    }
} # End gatherQLogicFirmware
# Temporary sub-routine to load into the NodeFirmware and NFP globals restricted Node Firmware Packages that are newer than currently available packages.
sub loadRestrictedNFP {
    our %NodeFirmware;
    our @NFP;

    $NodeFirmware{'11.3'} = {
        'BIOS' => ['2.8.2'],
        'BMCSDR_A100' => ['01.07'],
        'Backplane 1' => ['2.46', '4.35'],
        'Backplane 2' => ['4.35'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.20'],
        'DEbios_banshee' => ['37.41'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.51'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.41'],
        'DEfw_map_table' => ['12.17'],
        'DEpost_banshee' => ['28.15'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11192'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.02'],
        'DEssp_infinity' => ['02.83'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'Dell HBA330 Mini' => ['16.17.01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.20'],
        'EPbios_warthog' => ['32.51'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.51'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.51'],
        'EPfw_map_table' => ['12.17'],
        'EPpost_warthog' => ['22.14'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11192'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['08.00'],
        'EPssp_infinity' => ['02.83'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'Integrated Remote Access Controller' => ['4.40.10.00'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'MLKadpt_skylanders' => ['02.80'],
        'MLKbem_mongoose' => ['02.20'],
        'MLKbios_gryphon' => ['10.57'],
        'MLKccdes' => ['1.10.4+21.00'],
        'MLKcdes' => ['2.41.1+0.18.0+21.00'],
        'MLKcec_region0' => ['02.00'],
        'MLKcec_region1' => ['32.02'],
        'MLKcec_region3' => ['00.03'],
        'MLKcmd_100dc12pin' => ['02.02.72.03'],
        'MLKcmd_64pin' => ['02.05.102.02'],
        'MLKcmd_rev_table' => ['20.66'],
        'MLKeth_infinitymlk' => ['02.64'],
        'MLKfp_fpinffw' => ['02.0c'],
        'MLKfp_fpioc' => ['00.0b'],
        'MLKfullbios_gryphon_post' => ['10.57'],
        'MLKfw_map_table' => ['40.49'],
        'MLKgem_skylanders' => ['45.60'],
        'MLKpost_gryphon' => ['10.32'],
        'MLKps0_gen2_artesyn_optimus' => ['02.14'],
        'MLKps0_gen2_artesyn_ramjet' => ['02.14'],
        'MLKps1_gen2_artesyn_optimus' => ['02.14'],
        'MLKps1_gen2_artesyn_ramjet' => ['02.14'],
        'MLKsas_catamaran' => ['0015.0004.0000.0000'],
        'MLKsas_infinitymlk' => ['0001.0004.0000.11192'],
        'MLKserdes_infinitymlk' => ['00.40'],
        'MLKspime_gryphon' => ['04.00'],
        'MLKssp_infinitymlk' => ['01.40'],
        'MLKuefi_fw_volume' => ['40.86'],
        'MLKvrd0_infinitymlk_infineon' => ['01.01'],
        'MLKvrd1_infinitymlk_infineon' => ['01.01'],
        'MLKvrd2_infinitymlk_infineon' => ['01.01'],
        'MLKvrd3_infinitymlk_infineon' => ['01.02'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-CX4' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4-BE' => ['14.27.6008+DEL2420110034'],
        'Mellanox-CX4R' => ['14.27.6008+DEL2810000034'],
        'Mellanox-CX5-BE' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX5-FE0' => ['16.27.6106+DEL0000000004'],
        'Mellanox-CX6-BE' => ['22.28.4512+DEL0000000027'],
        'Mellanox-CX6-FE0' => ['22.28.4512+DEL0000000027'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x100a+0x100a'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'Port 0: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.04.24'],
        'Port 1: Emulex LightPulse LPe31002-M6-D 2-Port 16Gb Fibre  - FC' => ['03.04.24'],
        'QLogic-NX2' => ['7.13.0'],
    };

    $NodeFirmware{'10.3.7'} = {
        'BMCSDR_A100' => ['01.07'],
        'CAR_Colossus' => ['02.07'],
        'CAR_Ermine' => ['02.07'],
        'CFFPS1' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS1_Blastoff' => ['03.09'],
        'CFFPS1_Octane' => ['09.0b'],
        'CFFPS1_Octane_Acbel' => ['0a.01'],
        'CFFPS1_Optimus' => ['04.14', '02.02'],
        'CFFPS1_Optimus_Acbel' => ['02.02'],
        'CFFPS1_Optimus_Flex' => ['04.14'],
        'CFFPS1_Ramjet' => ['06.06'],
        'CFFPS1_Ramjet_Acbel' => ['01.03'],
        'CFFPS2' => ['0a.01', '01.03', '04.14', '03.09', '06.06', '09.0b', '02.02'],
        'CFFPS2_Blastoff' => ['03.09'],
        'CFFPS2_Octane' => ['09.0b'],
        'CFFPS2_Octane_Acbel' => ['0a.01'],
        'CFFPS2_Optimus' => ['04.14', '02.02'],
        'CFFPS2_Optimus_Acbel' => ['02.02'],
        'CFFPS2_Optimus_Flex' => ['04.14'],
        'CFFPS2_Ramjet' => ['06.06'],
        'CFFPS2_Ramjet_Acbel' => ['01.03'],
        'CMCSDR_Colossus' => ['00.08'],
        'CMCSDR_Ermine' => ['00.03'],
        'CMCSDR_Honeybadger' => ['00.0B'],
        'CMCSDR_Hotfoot' => ['00.0B'],
        'CMCSDR_Yeti' => ['00.06'],
        'CMC_Colossus' => ['02.07'],
        'CMC_Ermine' => ['02.07'],
        'CMC_HFHB' => ['02.07'],
        'CMC_Yeti' => ['02.07'],
        'CMD_Colossus' => ['02.0b+49+01'],
        'CMD_Ermine' => ['02.0d+4c+01'],
        'DEadpt_moons' => ['01.80'],
        'DEbcc_infinity' => ['02.20'],
        'DEbios_banshee' => ['37.41'],
        'DEbmc_bblock_moons' => ['01.40'],
        'DEbmc_main' => ['24.51'],
        'DEccdes' => ['1.10.4+21.00'],
        'DEcdes' => ['2.38.5+0.18.0+21.00'],
        'DEcmd_100d815pin' => ['03.08.103.01'],
        'DEcmd_64pin' => ['02.05.102.02'],
        'DEcmd_cat_64pin' => ['02.05.102.02'],
        'DEcmd_mam_64pin' => ['02.05.102.02'],
        'DEcmd_rev_table' => ['20.45'],
        'DEeth_infinity' => ['02.52'],
        'DEeth_sageville_infinity' => ['0128.0000.0005.0195'],
        'DEfp_fpinffw' => ['02.0c'],
        'DEfp_fpioc' => ['00.0b'],
        'DEfullbios_banshee_post' => ['37.41'],
        'DEfw_map_table' => ['12.17'],
        'DEpost_banshee' => ['28.15'],
        'DEps0_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps0_gen2_artesyn' => ['02.14'],
        'DEps1_gen2_acbel_hv_zeus' => ['04.12'],
        'DEps1_gen2_artesyn' => ['02.14'],
        'DEsas_catamaran' => ['0015.0004.0000.0000'],
        'DEsas_infinity' => ['0001.0004.0000.11192'],
        'DEserdes_banshee' => ['08.00'],
        'DEspime_banshee' => ['02.02'],
        'DEssp_infinity' => ['02.83'],
        'DEuefi_fw_volume_infinity' => ['03.43'],
        'DEvrd0_banshee_stmicro' => ['02.01'],
        'DEvrd1_banshee_stmicro' => ['01.00'],
        'EPadpt_moons' => ['01.80'],
        'EPbcc_infinity' => ['02.20'],
        'EPbios_warthog' => ['32.51'],
        'EPbmc_bblock_moons' => ['01.40'],
        'EPbmc_main' => ['24.51'],
        'EPccdes' => ['1.10.4+21.00'],
        'EPcdes' => ['2.38.5+0.18.0+21.00'],
        'EPcmd_100d815pin' => ['03.08.103.01'],
        'EPcmd_64pin' => ['02.05.102.02'],
        'EPcmd_cat_64pin' => ['02.05.102.02'],
        'EPcmd_mam_64pin' => ['02.05.102.02'],
        'EPcmd_rev_table' => ['20.45'],
        'EPeth_infinity' => ['02.52'],
        'EPfp_fpinffw' => ['02.0c'],
        'EPfp_fpioc' => ['00.0b'],
        'EPfullbios_warthog_post' => ['32.51'],
        'EPfw_map_table' => ['12.17'],
        'EPpost_warthog' => ['22.14'],
        'EPps0_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps0_gen2_artesyn' => ['02.14'],
        'EPps1_gen2_acbel_hv_zeus' => ['04.12'],
        'EPps1_gen2_artesyn' => ['02.14'],
        'EPsas_catamaran' => ['0015.0004.0000.0000'],
        'EPsas_infinity' => ['0001.0004.0000.11192'],
        'EPserdes_warthog' => ['08.00'],
        'EPspime_warthog' => ['08.00'],
        'EPssp_infinity' => ['02.83'],
        'EPuefi_fw_volume_infinity' => ['03.43'],
        'EPvrd0_warthog_stmicro' => ['01.01'],
        'EPvrd1_warthog_stmicro' => ['01.01'],
        'EPvrd2_warthog_stmicro' => ['01.01'],
        'Emulex LightPulse FC Adapter' => ['11.4.204.21'],
        'IsilonFPV1' => ['UI.01.36'],
        'IsilonIB' => ['4.8.930+205-0002-05_A'],
        'LOx' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2' => ['rp180d01', 'rp180d01+rp180d01'],
        'LOx2-MLC-YD' => ['rp180d01', 'rp180d01+rp180d01'],
        'Lsi' => ['13.00.66.00', '20.00.04.00'],
        'Lsi-3008 SAS Controller' => ['15.00.00.00+17.00.03.00'],
        'Lsi-35x36 SAS Expander' => ['1.10.0+21.00'],
        'LsiExp0' => ['0910+0210'],
        'LsiExp1' => ['0910+0210'],
        'Mellanox' => ['4.8.930+MT_02F0140002', '2.40.5030+MT_1090110018', '2.40.5030+ISL1090110018'],
        'Mellanox-EN' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023'],
        'Mellanox-EN0' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-EN1' => ['2.40.5030+MT_1090111023', '2.40.5030+EMC1090111023', '2.40.5030+EMC0000000002'],
        'Mellanox-FPGA' => ['0x1002+0x1002', '0x100a+0x100a'],
        'Mellanox0' => ['2.40.5030+EMC0000000004'],
        'Mellanox1' => ['2.40.5030+EMC0000000004'],
        'Mellanox5-EN0' => ['14.25.1020+DEL2420110034', '16.23.4010+MT_0000000158', '16.28.1002+EMC0000000017'],
        'PMC8056' => ['2.38.5+0.18.0+21.00'],
        'QLogic-NX2' => ['7.13.0'],
        'XL710' => ['5.04+80002505'],
    };
}
sub checkAccessZoneOverlap {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<7') or
	isCurrentOneFS('>=7.1.1') or
	!isDestinationOneFS() or
	isDestinationOneFS('<7.1.1') or
	isDestinationOneFS('>=8')) { return }

    # Setup
    my $CMD_SMB_LIVE = qq[2>&1 ${SUDO} isi_classic smb dump];
    my $CMD_SMB_LOGS = q[2>&1 cat local/isi_smb];
    my $CMD_ZONE_LIVE = q[2>&1 cat /ifs/.ifsvar/main_config.gc];
    my $CMD_ZONE_LOGS = q[2>&1 cat local/gconfig_ifsvar.tar/ifs/_ifsvar/main_config.gc];
    my $SPF_LOWEST = 'Lowest common SMB share paths for each Access Zone are as follows: %s';
    my $MSG_NESTED = 'Detected one or more SMB shares associated with multiple access zones where the share paths are equal or nested.  This configuration is no longer supported.  Upgrade will succeed, but you will be unable to create new access zones until the path overlap is fixed.';

    # Gather
    my $smb_gather = gatherLocal(isLive()?$CMD_SMB_LIVE:$CMD_SMB_LOGS);

    # Populate a hash of share name -> share path
    my %SMB_Shares = ();
    my ($share_path, $share_name);
    foreach (@$smb_gather) {
	if (/SMB Share Permissions:/) { last }
	if (/^Name:\s*(\S.*)/ or /^\s*Share Name:\s*(\S.*)/) {
	    $share_name = $1;
	}
	if (/^Path:\s*(\S.*)/ or /^\s*Path:\s*(\S.*)/) {
	    $share_path = $1;
	}
	if (!$share_name or !$share_path) { next }
	$SMB_Shares{$share_name} = $share_path;
	($share_name, $share_path) = '';
    }

    # Gather
    my $zone_gather = gatherLocal(isLive()?$CMD_ZONE_LIVE:$CMD_ZONE_LOGS);

    # Populate an array of SMB shares associated with each zone
    my $zone_name;
    foreach (@$zone_gather) {
	if (!/Zones/) { next }
	if (/\.Zones\._key\.[^\.]*\._name.*\"(.*)\"/) { $zone_name = $1 }
	if (!$zone_name) { next }
	if (/ShareList\.value .*?\"\[ (.*) \]\"/ and
	    !$CLUSTER_INFO{'Zones'}{$zone_name}{'All SMB Shares'}) {
	    my $sharelist = $1;
	    $sharelist =~ s/\\\"//g;
	    my @sharelist = split(', ', $sharelist);
	    foreach (@sharelist) {
		my $share_name;
		if (/^(.*?):/) { $share_name = $1 }
		if (!$share_name or !$SMB_Shares{$share_name}) { next }
		push(@{$CLUSTER_INFO{'Zones'}{$zone_name}{'Share List'}}, $share_name);
		debug("CLUSTER_INFO->Zones->${zone_name}->Share List=$share_name");
	    }
	}
	if (/AllSMBShares\.value .* \"1\"/) {
	    $CLUSTER_INFO{'Zones'}{$zone_name}{'All SMB Shares'} = 1;
	    debug("CLUSTER_INFO->Zones->${zone_name}->All SMB Shares=1");
	    @{$CLUSTER_INFO{'Zones'}{$zone_name}{'Share List'}} = keys %SMB_Shares;
	}
    }

    debug('For each access zone, find the lowest common path for the SMB shares associated with it');
    my @lowest_common_paths = ();
    foreach my $zone_name (keys %{$CLUSTER_INFO{'Zones'}}) {
	if ($zone_name eq 'System') { next }
	debug("findLowestCommonPath: $zone_name");
	if ($CLUSTER_INFO{'Zones'}{$zone_name}{'Share List'}) {
	    $CLUSTER_INFO{'Zones'}{$zone_name}{'Lowest Common Path'} =
		findLowestCommonPath(@SMB_Shares{@{$CLUSTER_INFO{'Zones'}{$zone_name}{'Share List'}}});
	}
	if ($CLUSTER_INFO{'Zones'}{$zone_name}{'Lowest Common Path'}) {
	    push(@lowest_common_paths, "$zone_name -> ".$CLUSTER_INFO{'Zones'}{$zone_name}{'Lowest Common Path'});
	}
    }
    if (@lowest_common_paths) {
	info(sprintf($SPF_LOWEST, @lowest_common_paths));
    }

    debug('Check each zones shares against every other zones shares for nested shares');
    my $nested_share = 0;
    my $shared_share = 0;
    foreach my $zone_a (keys %{$CLUSTER_INFO{'Zones'}}) {
	if ($zone_a eq 'System') { next }
	foreach my $zone_b (keys %{$CLUSTER_INFO{'Zones'}}) {
	    if ($zone_b eq 'System' or $zone_a eq $zone_b) { next }
	    foreach my $share_a (@{$CLUSTER_INFO{'Zones'}{$zone_a}{'Share List'}}) {
		foreach my $share_b (@{$CLUSTER_INFO{'Zones'}{$zone_b}{'Share List'}}) {
		    if ($SMB_Shares{$share_a} eq $SMB_Shares{$share_b}) {
			$shared_share = 1;
			debug("!!! $share_a associated with $zone_a and $zone_b");
		    } elsif ($SMB_Shares{$share_a} =~ /^$SMB_Shares{$share_b}/) {
			$nested_share = 1;
			debug("!!! ${share_a}:$SMB_Shares{$share_a} in $zone_a nested within ${share_b}:$SMB_Shares{$share_b} of $zone_b");
		    }
		}
	    }
	}
    }
    if ($nested_share or $shared_share) { warning($MSG_NESTED) }

} # End checkOverlap
$CHECKS{'checkAccessZones'} = {
    'Description' => 'Checks if multiple access zones are configured for upgrades across 7.1.1. Checks for nested or overlapping SMB shares. Adds warnings when any non-System access zone pools are found, only applies for upgrades across OneFS 7.1.1',
    'Exclude' => 1,
};
sub checkAccessZones {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<7.0')) { return unsupported('UNSUP_CUR') }
    if (isCurrentOneFS('>=7.1.1')) { return unsupported('UNSUP_CUR') }
    if (!isDestinationOneFS('>=7.1.1')) {
	return unsupported('UNSUP_TGT_1', '7.1.1 and later');
    }

    # Setup
    my $MSG_UNABLE = 'Unable to verify the number of Access Zones, manually check if there is more than just the System Access Zone configured';
    my $MSG_UPPG = 'We strongly recommend that you read the OneFS Upgrade Planning and Process Guide and contact EMC Isilon Technical Support before upgrading the cluster to OneFS 7.1.1 or later. This will help to ensure that changes to access zones will not interrupt your workflow.';

    # Start
    printCheck('Access Zones');
    gatherAccessZoneCount();

    # Flag
    if (!$CLUSTER_INFO{'Access Zones'}) {
	warning($MSG_UNABLE);
    } elsif ($CLUSTER_INFO{'Access Zones'} > 1) {
	checkAccessZoneOverlap();
	checkNonSystemPools();
	warning($MSG_UPPG);
	addCustomerInformation('Access Zones', @Logger::WARN, @Logger::FAIL);
    }
    if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000037962')) }

    # Output
    if ($CLUSTER_INFO{'Access Zones'}) {
	printResults($CLUSTER_INFO{'Access Zones'});
    } else {
	printResults();
    }
} # End checkAccessZones
$CHECKS{'checkDestinationOneFS'} = {
    'Description' => 'Checks destination OneFS version',
    'Reference' => 'https://dl.dell.com/content/manual44495381',
};
$MSG{'checkDestinationOneFS_FCO_2'} = 'No Destination OneFS version provided for %s, setting destination to recommended, OneFS %s';
$MSG{'checkDestinationOneFS_T102518EE_1'} = 'Destination OneFS version %s meets TSE requirements for TSE T102518EE, but OneFS 8.1.2.0 and later is recommended.';
$MSG{'checkDestinationOneFS_REMOVE_1'} = 'Destination OneFS version %s is less than or equal to the current OneFS version, removing destination OneFS version';
$MSG{'checkDestinationOneFS_BUG_2'} = 'Due to critical bugs seen in OneFS %s the destination OneFS version has been automatically adjusted to OneFS %s';
$MSG{'checkDestinationOneFS_95'} = 'Due to critical fixes in 9.5.0.8, it is highly recommended you upgrade to this version or higher, current latest recommended for %s is %s.';
$MSG{'checkDestinationOneFS_PR_2'} = 'There is a newer patch release available for OneFS %s: %s';
$MSG{'checkDestinationOneFS_PR_3'} = 'There is a newer release available for OneFS %s: %s';
$MSG{'checkDestinationOneFS_not_latest'} = 'It is always recommended to upgrade to latest in family, the base OneFS %s will remain the same but latest version %s will contain additional fixes and would be the recommended destination.';
$MSG{'checkDestinationOneFS_current_not_latest'} = 'Current code is not the latest available for your current code family %s. It is always recommended to upgrade to latest in family, the base OneFS %s will remain the same but latest version %s will contain additional fixes and would be the recommended destination.';
sub checkDestinationOneFS {
    if (wasCalled()) { return }

	checkServices();
	debug("CHECKING SERVICE STATE: $CLUSTER_INFO{'Services'}{'smb'}");
	if((isDestinationOneFS() eq '9.4.0.14' or isDestinationOneFS() eq '9.4.0.15' or isDestinationOneFS() eq '9.5.0.4' or isDestinationOneFS() eq '9.5.0.5') and $CLUSTER_INFO{'Services'}{'smb'} eq 'Enabled' ){
		critical("SMB is enabled on this cluster, upgrading to $RPS{'Target'} may impact SMB clients. This issue is currently resolved in 9.5.0.6 and 9.4.0.16 or higher. It is strongly recommended to upgrade to 9.5.0.8 or higher");
		info('Please reference https://www.dell.com/support/kbdoc/en-ie/000216978 for additional information.');
	}

#	my @lts_destinations = ();
#    foreach my $version (@{$Code_Matrix{$key}}, $source) {
#	foreach (generateTargetVersions($version)) {
#	    if (compareVersions($_, $source) > 0 and
#		compareVersions($_, $target) <= 0) {
#		push(@lts_destinations, $_);
#	    }
#	}
#    }
#    info("LTS Destinations: ".join(", ", @lts_destinations));
    # FCO F010517EE and not already at an FCO required OneFS version
    if ($RPS{'F010517EE'} and !isDestinationOneFS() and
	((isCurrentOneFS(qr/^7/) and isCurrentOneFS('<7.2.1.6')) or
	 (isCurrentOneFS(qr/^8/) and isCurrentOneFS('<8.0.0.4')) or
	 isCurrentOneFS('8.0.1.0'))) {

	# No destination OneFS version provided
	if (isCurrentOneFS(qr/8\.0\.1\.0/)) {
	    $RPS{'Target'} = '8.1.2.0';
	} else {
	    $RPS{'Target'} = '8.0.0.7';
	}
	warning(sprintf($MSG{'checkDestinationOneFS_FCO_2'},
			'FCO F010517EE',
			isDestinationOneFS()));
    }

    # FCO F033018EE but no selected target and running OneFS 8.1.2.0 or lower
    if ($RPS{'F033018EE'} and !isDestinationOneFS() and
	isCurrentOneFS('<8.1.2')) {
	$RPS{'Target'} = '8.1.2.0';
	warning(sprintf($MSG{'checkDestinationOneFS_FCO_2'},
			'FCO F033018EE',
			isDestinationOneFS()));
    }

    # FCO F082318EE but no selected target and running OneFS 8.1.0.4 or lower
    if ($RPS{'F082318EE'} and !isDestinationOneFS() and
	isCurrentOneFS('<=8.1.0.4')) {
	$RPS{'Target'} = '8.1.2.0';
	warning(sprintf($MSG{'checkDestinationOneFS_FCO_2'},
			'FCO F082318EE',
			isDestinationOneFS()));
    }

    # TSE T102518EE but no selected target and running OneFS 8.1.0.4 or lower
    if ($RPS{'T102518EE'} and !isDestinationOneFS() and
	isCurrentOneFS('<=8.1.0.4')) {
	$RPS{'Target'} = '8.1.2.0';
	warning(sprintf($MSG{'checkDestinationOneFS_FCO_2'},
			'TSE T102518EE',
			isDestinationOneFS()));
    }

    # TSE T102518EE but OneFS 8.1.0.4 selected as destination OneFS
    if ($RPS{'T102518EE'} and isDestinationOneFS() and
	isDestinationOneFS('<=8.1.0.4')) {
	warning(sprintf($MSG{'checkDestinationOneFS_T102518EE_1'},
			isDestinationOneFS()));
    }

    # TSE T062519EE but no destination OneFS
    if ($RPS{'T062519EE'} and !isDestinationOneFS() and
	isCurrentOneFS('8.2.0.0') and $CLUSTER_INFO{'Build'} < 11) {
	$RPS{'Target'} = '8.2.0.0';
	warning(sprintf($MSG{'checkDestinationOneFS_FCO_2'},
			'TSE T062519EE',
			isDestinationOneFS()));
    }

    # PFN ET003823, but no destination OneFS
    if ($RPS{'ET003823'} and !isDestinationOneFS() and
	isCurrentOneFS('8.2.1.0') and $CLUSTER_INFO{'Build'} < 4) {
	$RPS{'Target'} = '8.2.1.0';
	warning(sprintf($MSG{'checkDestinationOneFS_FCO_2'},
			'PFN ET003823',
			isDestinationOneFS()));
    }

    # Removes the destination OneFS version if it is less than the current
    if (isDestinationOneFS()) {
	if (isCurrentOneFS('8.2.0.0') and $CLUSTER_INFO{'Build'} < 11 and
	    isDestinationOneFS('8.2.0.0')) {
	    # Allow upgrade to 8.2.0 build 11 from 8.2.0 build 9
	} elsif (isCurrentOneFS('8.2.1.0') and $CLUSTER_INFO{'Build'} < 4 and
		 isDestinationOneFS('8.2.1.0')) {
	    # Allow upgrade to 8.2.1 build 3 from 8.2.1 build 4
	} elsif (isCurrentOneFS('>='.isDestinationOneFS())) {
	    # Remove the destination OneFS version if it is less than or equal
	    # to the current OneFS version
	    warning(sprintf($MSG{'checkDestinationOneFS_REMOVE_1'},
			    isDestinationOneFS()));
	    delete $RPS{'Target'};
	}
    }

    # Recommend the latest patch releases for OneFS 9.1+
    if (isDestinationOneFS('>=9.1') or isCurrentOneFS('>=9.1')) {
	our %Patch_Releases;
	my $rel = isDestinationOneFS()?isDestinationOneFS():isCurrentOneFS();
	my ($major, $minor, $service) = split(/\./, $rel);
	my $re_branch;
	if(compareVersions($rel , '9.5') >= 0){
		$re_branch = "^$major\.$minor\.";
	}
	else{
		$re_branch = "^$major\.$minor\.$service\.";
	}
	my @destinations = grep { /$re_branch/ } keys %Patch_Releases;
	debug("destinations = [@destinations], re_branch = $re_branch");
	my ($latest) = sort {compareVersions($b, $a)} @destinations;
	debug("latest = $latest");
	if ($latest) {

	    # Avoid 9.2.0.0/9.2.1.0/9.3.0.0 unpatched releases at all costs
	    if (isDestinationOneFS('9.2.1.0') or
		isDestinationOneFS('9.2.0.0') or
		isDestinationOneFS('9.3.0.0')) {
		addCustomerInformation(
		    'Upgrade Plan',
		    critical(sprintf($MSG{'checkDestinationOneFS_BUG_2'}, isDestinationOneFS(), $latest)));
		$RPS{'Target'} = $latest;
	    }

		# Avoid any 9.5 code prior to 9.5.0.8
	    if ((isCurrentOneFS('>=9.5') and isCurrentOneFS('<=9.5.0.7')) or (isDestinationOneFS('>=9.5') and isDestinationOneFS('<=9.5.0.7'))) {
			if(isDestinationOneFS()){
				if(hasCloudPools() and isCurrentOneFS('<9.5')){
					addCustomerInformation('Upgrade Plan', critical(sprintf($MSG{'checkDestinationOneFS_95'}, "$major.$minor.$service", $latest))) if hasCloudPools();
				}
				else{
					addCustomerInformation('Upgrade Plan', warning(sprintf($MSG{'checkDestinationOneFS_not_latest'}, "$major.$minor.$service", $latest)));	
				}
			}
			else{
				addCustomerInformation('Upgrade Plan', warning(sprintf($MSG{'checkDestinationOneFS_current_not_latest'}, "$major.$minor.$service", "$major.$minor.$service", $latest)));	
			}
	    }


		if (isDestinationOneFS('>=9.1') and
		isDestinationOneFS("<$latest")) {
		if(isDestinationOneFS('>=9.5')){
		warning(sprintf($MSG{'checkDestinationOneFS_PR_3'},
				"$major.$minor", $latest));			
		}
		else{
		warning(sprintf($MSG{'checkDestinationOneFS_PR_2'},
				"$major.$minor.$service", $latest));			
		}
		} elsif (!isDestinationOneFS() and
		     isCurrentOneFS('>=9.1') and
		     isCurrentOneFS("<$latest")) {
				if(isCurrentOneFS('>=9.5')){
					warning(sprintf($MSG{'checkDestinationOneFS_PR_3'},
				"$major.$minor", $latest));
				}
				else{
					warning(sprintf($MSG{'checkDestinationOneFS_PR_2'},
					"$major.$minor.$service", $latest));
				}    
		}
	}
    }

    # Identify F600/F900 nodes running/upgrading to a version less than 9.1.0.5
    gatherProductInfo();
    my $SPF_NVME = 'OneFS 9.1.0.5 and later releases contain critical updates'.
	' for %s nodes';
    my %nvme_types = ();
    foreach my $lnn (@LNNS) {
	# Node Product details must be available
	if (!$NODE_INFO{$lnn}{'Product'}) { next }

	# Identify F600/F900 NVMe nodes
	if ($NODE_INFO{$lnn}{'Product'} =~ /^(F[69]00)/) { $nvme_types{$1} = 1 }
    }
    if (%nvme_types) {
	my $msg = sprintf($SPF_NVME, join(' and ', keys %nvme_types));
	if (!isDestinationOneFS() and
	    isCurrentOneFS('<9.1.0.5') and
	    !hasPatch('9.1.0_KDA-CUSTOM_2021-02_PSP-945') or
	    isDestinationOneFS('<9.1.0.5')) {
	    addCustomerInformation(critical($msg));
	}
    }

	if(
			isCurrentOneFS('<9.5') and 
			(
				isDestinationOneFS('=9.7.1.1') 
				or (isDestinationOneFS('>=9.8') and isDestinationOneFS('<9.8.0.2')) 
				or isDestinationOneFS('=9.9.0.0')
			)
		){
		addCustomerInformation(critical("During the commit phase of the OneFS upgrade from ".isCurrentOneFS()." to ".isDestinationOneFS()." there will be a brief period of disruption to clients as lsass restarts, you will need to ensure that you are prepared for this client disconnect as the commit is performed."));
		info("As always, it is highly recommended that the upgrade takes place during a maintenance window and at a time of low usage for this cluster.");
		info("Please reference https://www.dell.com/support/kbdoc/000227821 for additional information.");
	}

    # Display destination OneFS version and any messages
    if (isDestinationOneFS()) {
	printCheck('Destination OneFS Version', isDestinationOneFS());
    }
    if (isResults(&INFO)) {
	printCheck('Destination OneFS Version');
	printResults();
    }
} # End checkDestinationOneFS
$CHECKS{'checkDiskpools'} = {
    'Description' => 'Checks diskpools and class equivalence for OneFS upgrades going across 7.0',
    'Exclude' => 1,
};
sub checkDiskpools {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=7')) { return unsupported('UNSUP_CUR') }
    if (!isDestinationOneFS('>=7')) {
	return unsupported('UNSUP_TGT_1','7 and later');
    }

    # Setup
    my $SPF_SAME = 'Nodes %s and %s are of equivalent type, and need to be in the same diskpool';
    my $SPF_DIFF = 'Nodes %s and %s are not of equivalent type, and need to be in different diskpools';

    # Start
    printCheck('Diskpools');

    # Gather
    gatherIsiStatPools();
    gatherProductInfo();

    # Populate an equivalence class stored in NODE_INFO{$node}{'Class'}
    # based on node product information in NODE_INFO{$node}{'Product'}
    # Class 0 means there is no equivalence class.
    foreach my $node (sort {$a <=> $b} keys %NODE_INFO) {
	next unless $NODE_INFO{$node}{'Product'};
	$_ = $NODE_INFO{$node}{'Product'};
	if (/IQ 6000x/i or /^X200.*6144MB.*6144GB$/i) {
	    $NODE_INFO{$node}{'Class'} = 1;
	    debug("NODE_INFO->${node}->Class=$NODE_INFO{$node}{'Class'}");
	} elsif (/IQ 12000x/i or /^X200.*6144MB.*12TB$/i) {
	    $NODE_INFO{$node}{'Class'} = 2;
	    debug("NODE_INFO->${node}->Class=$NODE_INFO{$node}{'Class'}");
	} elsif (/IQ 36000x/i or /^X400.*24GB.*36TB$/i) {
	    $NODE_INFO{$node}{'Class'} = 3;
	    debug("NODE_INFO->${node}->Class=$NODE_INFO{$node}{'Class'}");
	} elsif (/IQ 72000x/i or /^X400.*24GB.*72TB$/i) {
	    $NODE_INFO{$node}{'Class'} = 4;
	    debug("NODE_INFO->${node}->Class=$NODE_INFO{$node}{'Class'}");
	} elsif (/IQ 32000X-SSD/i or /^X400.*24GB.*-32TB.*SSD$/i) {
	    $NODE_INFO{$node}{'Class'} = 5;
	    debug("NODE_INFO->${node}->Class=$NODE_INFO{$node}{'Class'}");
	} elsif (/IQ 36NL/i or /^NL400.*12GB.*-36TB$/i) {
	    $NODE_INFO{$node}{'Class'} = 6;
	    debug("NODE_INFO->${node}->Class=$NODE_INFO{$node}{'Class'}");
	} elsif (/IQ 72NL/i or /^NL400.*12GB.*-72TB$/i) {
	    $NODE_INFO{$node}{'Class'} = 7;
	    debug("NODE_INFO->${node}->Class=$NODE_INFO{$node}{'Class'}");
	} elsif (/IQ 108NL/i or /^NL400.*12GB.*-108TB$/i) {
	    $NODE_INFO{$node}{'Class'} = 8;
	    debug("NODE_INFO->${node}->Class=$NODE_INFO{$node}{'Class'}");
	} else {
	    $NODE_INFO{$node}{'Class'} = 0;
	    debug("NODE_INFO->${node}->Class=$NODE_INFO{$node}{'Class'}");
	}
    }

    # Iterate through and compare equivalence classes for each node
    foreach my $lnn_a (@LNNS) {
	if (!$NODE_INFO{$lnn_a}) { next }
	my $node_a = $NODE_INFO{$lnn_a};
	if (!$node_a->{'Class'} or !$node_a->{'Pool'}) { next }
	foreach my $lnn_b (@LNNS) {
	    if (!$NODE_INFO{$lnn_b}) { next }
	    my $node_b = $NODE_INFO{$lnn_b};
	    if (!$node_b->{'Class'} or !$node_b->{'Pool'}) { next }
	    # Move to the next node if we've already done the comparison.
	    # I.E.: Node A compares against every node greater than it on it's
	    # first path.  So if the node b is less than the node a, it's already
	    # been analyzed.
	    if (compareVersions($lnn_a, $lnn_b) >= 0) { next }

	    # Node a and node b are in equivalent classes, but they are not in the same diskpool.
	    if ($node_a->{'Class'} == $node_b->{'Class'} and
		$node_a->{'Pool'} ne $node_b->{'Pool'}) {
		fail(sprintf($SPF_SAME, $lnn_a, $lnn_b));
	    }

	    # Node a and node b are in the same diskpool, and either
	    # they are not in the same equivalence class or they are different products.
	    if ($node_a->{'Pool'} eq $node_b->{'Pool'} and
		$node_a->{'Class'} != $node_b->{'Class'}) {
		fail(sprintf($SPF_DIFF, $lnn_a, $lnn_b));
	    }
	}
    }

    # Output
    printResults();
} # End checkDiskPools
###
# checkFilepoolPolicies: Checks filepools for final match being set and
#  names starting with a number.
$CHECKS{'checkFilepoolPolicies'} = {
    'Description' => 'Checks GNA requirements and checks filepools for final match being set and names starting with a number',
    'Exclude' => 1,
};
sub checkFilepoolPolicies {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=7.1.1')) {
	debug("checkFilepoolPolicies: Skipping. Current version greater than 7.1.1");
	return;
    } elsif (!$RPS{'Target'}) {
	debug("checkFilepoolPolicies: Skipping. No target version");
	return;
    }
    printCheck("Filepool Policies");
    gatherForceSpill();

    # Gather information
    debug("checkFilepoolPolicies: Gathering filepool policy information");
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal(qq[2>&1 ${SUDO} isi_gconfig smartpools.filepools]);
    } else {
	$gather = gatherLocal(qq[2>&1 grep -h 'smartpools.filepools' local/isi_gconfig local/gconfig_ifsvar.tar/ifs/_ifsvar/main_config.gc]);
    }

    # Analyze for issues
    my $policy_name = "";
    my $gna_enabled = 0;
    my @policies_with_final_match = ();
    my @policies_start_with_number = ();
    debug("checkFilepoolPolicies: Searching for filepool policy related issues");
    foreach (@$gather) {
	if (/smartpools\.filepools\.policies\.(\d+) /i) {
	    $policy_name = "";
	} elsif (/smartpools\.filepools\.policies\.\d\.name .*\"(.*)\"/i) {
	    $policy_name = $1;
	    if ($policy_name =~ /^\d/) {
		unless (grep {$_ eq $policy_name} @policies_start_with_number) {
		    push(@policies_start_with_number, $policy_name);
		    debug("checkFilepoolPolicies: !!! Policy name begins with a number: $policy_name");
		}
	    }
	} elsif (/smartpools\.filepools.*final_match.*false/i) {
	    unless (grep {$_ eq $policy_name} @policies_with_final_match) {
		push(@policies_with_final_match, $policy_name);
		debug("checkFilepoolPolicies: !!! Policy with final match set to false: $policy_name");
	    }
	} elsif (/smartpools\.filepools\.gna_enabled.*true/i) {
	    debug("checkFilepoolPolicies: Found GNA is enabled");
	    $gna_enabled = 1;
	}
    }

    # Policies start with a number
    if (isCurrentOneFS('<7.1.1') and compareVersions($RPS{'Target'}, "7.1.1") >= 0 and @policies_start_with_number) {
	fail("Upgrade would invalidate all file pool policies with names that start with a number. As of OneFS 7.1.1, filepool policy names can no longer have a numeric first character. The following policies must be renamed before upgrade: ".join(",",@policies_start_with_number));
	fail("Upgrade would invalidate a file pool policy that doesn't stop matching.  As of OneFS 7.1.1, filepool policy matching terminates after a first match is found.  The following polices must be rewritten before upgrade: ".join(",",@policies_with_final_match));
    }

    # GNA enabled
    if ($gna_enabled) {

	# Determine the number of nodes with SSD storage not used as L3 cache
	my @nodes_without_ssd = ();
	foreach my $lnn (sort {$a <=> $b} keys %NODE_INFO) {
	    next unless $NODE_INFO{$lnn}{'SSD Storage'};
	    next unless $NODE_INFO{$lnn}{'SSD Storage'} =~ /No SSDs|No Storage SSDs|Diskless|L3/;
	    push(@nodes_without_ssd, $lnn);
	}

	# Calculate the percent of nodes which are being used for SSD storage
	my $percent_ssd_nodes = 0;
	if(@nodes_without_ssd) {
	    $percent_ssd_nodes = 100 * ($CLUSTER_INFO{'Node Count'} - scalar(@nodes_without_ssd))/$CLUSTER_INFO{'Node Count'};
	    debug("checkFilepoolPolicies: Percent SSD Nodes: $percent_ssd_nodes");
	}
	# A minimum of 20% must be SSD storage nodes not used for L3 cache
	if ($percent_ssd_nodes < 20) {
	    warning("Cluster is enabled for GNA but only ${percent_ssd_nodes}% of all nodes have SSD storage, the minimum requirement is 20%");
	}

	# Calculate the percent of storage which is SSD storage
	my $percent_ssd_storage = 0;
	if ($CLUSTER_INFO{'SSD Size'} and $CLUSTER_INFO{'HDD Size'}) {
	    $percent_ssd_storage = sprintf("%.1f", 100 * $CLUSTER_INFO{'SSD Size'}/$CLUSTER_INFO{'HDD Size'});
	    debug("checkFilepoolPolicies: Percent SSD Storage: $percent_ssd_storage");
	}
	# A minimum of 1.5% of storage must be SSD storage
	if ($percent_ssd_storage < 1.5) {
	    warning("Cluster is enabled for GNA but only ${percent_ssd_storage}% of all storage is SSD storage, the minimum requirement is 1.5%");
	}

	# Flag an issue for upgrades from prior to 7 to 7 or later where GNA would be deactivated as part of the upgrade
	if (isCurrentOneFS('<7')
	    and compareVersions($RPS{'Target'}, "7") >= 0
	    and ($percent_ssd_nodes < 20 or $percent_ssd_storage < 1.5)) {
	    fail("Cluster does not meet minimum requirements for Global Namespace Acceleration and an upgrade would deactivate Global Namespace Acceleration.  Either disable GNA, or call Isilon support.  Important: do NOT bypass this check if you are also failing the CheckDiskpoolsMembership check or any other upgrade check.") if $percent_ssd_storage < 1.5;
	}

	# Warn if force spill over is enabled which could be bad
	warning("Disk Pools are configured to spill over and write data to SSD drives.");
    }

    addCustomerInformation($_) foreach (@Logger::FAIL);
    printResults();
} # End checkFilepoolPolicies
$CHECKS{'checkKerberos8000'} = {
    'Description' => 'Checks for an issue with the Kerberos configuration file when upgrading to OneFS 8.0.0.0',
    'Exclude' => 1,
};
$MSG{'checkKerberos8000_FAIL'} = 'Kerberos configuration file identified as being at risk. Consider upgrading to OneFS 8.0.0.1 or later.';
sub checkKerberos8000 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8')) { return unsupported('UNSUP_CUR') }
    if (!isDestinationOneFS('8.0.0.0')) { return unsupported('UNSUP_TGT_1', '8.0.0.0') }

    # Setup
    my $CMD = qq[2>&1 grep permitted_enctypes /etc/likewise-krb5-ad.conf local/ifsvar_etc.tar/etc/likewise-krb5-ad.conf];

    # Start
    printCheck('Kerberos & OneFS 8.0.0.0');

    # Gather
    my $gather = gatherLocal($CMD);

    # Analyze
    my ($found_permitted_enctypes) = grep /permitted_enctypes/, @$gather;

    # Flag
    if (!$found_permitted_enctypes) { fail($MSG{'checkKerberos8000_FAIL'}) }

    # Output
    printResults();
} # End checkKerberos8000
$CHECKS{'checkLastZoneID8000'} = {
    'Description' => 'Checks for gaps in access zones that may cause major issues when upgrading to OneFS 8.0.0.0',
    'Exclude' => 1,
};
$MSG{'checkLastZoneID8000_FAIL'} = 'Cluster identified as being at risk. Consider upgrading to OneFS 8.0.0.1 or later.';
sub checkLastZoneID8000 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8')) { return unsupported('UNSUP_CUR') }
    if (!isDestinationOneFS('8.0.0.0')) { return unsupported('UNSUP_TGT_1', '8.0.0.0') }

    # Setup
    my $CMD = qq[2>&1 grep lastzoneid /ifs/.ifsvar/main_config.gc local/gconfig_ifsvar.tar/ifs/_ifsvar/main_config.gc];

    # Start
    printCheck('LastZoneID & OneFS 8.0.0.0');

    # Gather
    gatherAccessZoneCount();
    my $gather = gatherLocal($CMD);

    # Analyze
    my ($lastzoneid) = map {$_ =~ /.*lastzoneid.*\"(.*)\"/i} @$gather;

    # Default lastzoneid to 1
    if (!$lastzoneid) { $lastzoneid = 1 }

    # Flag
    if ($CLUSTER_INFO{'Access Zones'} and $CLUSTER_INFO{'Access Zones'} ne $lastzoneid) {
	fail($MSG{'checkLastZoneID8000_FAIL'});
	info(sprintf($MSG{'KB_1'}, '000052591'));
    }

    # Output
    printResults();
} # End checkLastZoneID8000
$CHECKS{'checkLegacyLDAP'} = {
    'Description' => 'Checks for Legacy LDAP being enabled in OneFS 6 to OneFS 7 upgrades',
    'Exclude' => 1,
};
sub checkLegacyLDAP {
    if (wasCalled()) { return }
    if (!isCurrentOneFS('<7')) { return unsupported('UNSUP_CUR') }
    if (!isDestinationOneFS('>=7')) {
	return unsupported('UNSUP_TGT_1', '7 and later');
    }

    # Setup
    my $CMD_LIVE = q[2>&1 cat /etc/mcp/override/ldap.xml];
    my $CMD_LOGS = q[2>&1 cat local/ifsvar_etc.tar/etc/mcp/override/ldap.xml];
    my $RE_LDAP = qr/modify-attribute.*id="ldap".*attribute="enable".*value="1"/;
    my $MSG_FAIL = q[Legacy LDAP cannot be upgraded. Migrate all settings under 'Legacy LDAP' in the WebUI to the 'LDAP' tab and validate correct LDAP functionality before upgrading];

    # Start
    printCheck('Legacy LDAP');

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my ($has_legacy_ldap) = grep {$_ =~ $RE_LDAP} @$gather;

    # Flag
    if ($has_legacy_ldap) {
	addCustomerInformation('OneFS Upgrade', fail($MSG_FAIL));
    }

    # Output
    printResults();
} # End checkLegacyLDAP
sub checkNFS8004 {
    if (wasCalled()) { return }
    if ($RPS{'Target'}) {
	return unless $RPS{'Target'} eq "8.0.0.4";
	checkUpgradePath();
	my $current_code = $CLUSTER_INFO{'OneFS'};
	my $affected = 0;
	foreach my $target_code (sort {compareVersions($a, $b)} @{$RPS{'Upgrade Path'}}) {
	    if (compareVersions($current_code, "7.2") == 0 and
		compareVersions($target_code, "8.0.0.4") == 0) {
		$affected = 1;
	    }
	    $current_code = $target_code;
	}
	return unless $affected;
    } else {
	return unless isCurrentOneFS('8.0.0.4');
    }

    gatherNFSExport();
    return unless $CLUSTER_INFO{'NFS Export'};

    # Post upgrade issue, NFS Export has - as the Security Type
    my @blank_export_ids = ();
    my @unix_export_ids = ();
    foreach my $id (keys %{$CLUSTER_INFO{'NFS Export'}}) {
	next unless $CLUSTER_INFO{'NFS Export'}{$id}{'Security Type'};
	if ($CLUSTER_INFO{'NFS Export'}{$id}{'Security Type'} =~ /^\s*-\s*$/) {
	    push(@blank_export_ids, $id);
	    debug("checkNFS8004: Export ID $id impacted!");
	}
	if ($CLUSTER_INFO{'NFS Export'}{$id}{'Security Type'} =~ /^\s*unix\s*$/) {
	    push(@unix_export_ids, $id);
	    debug("checkNFS8004: Export ID $id is unix.");
	}
    }

    if (@blank_export_ids and isCurrentOneFS('8.0.0.4')) {
	fail("The following NFS Export IDs are impacted by KB 000050858: ".join(",",@blank_export_ids));
	fail("Review the KB article and complete resolution steps for Post upgrade on 8.0.0.4");
    }
    if (@unix_export_ids and isCurrentOneFS('<8')) {
	warning("The following NFS Export IDs have unix Security Type, may be impacted by KB 000050858: ".join(",",@unix_export_ids));
	warning("Prior to an upgrade to 8.0.0.4, review the KB article and complete pre-upgrade resolution steps");
	addRequiredFiles("NFS Export Security Repair Script");
    }

} # End checkNFS8004


$CHECKS{'checkNFSUpgComplete'} = {
    'Description' => 'Checks for the existance of riptide-nfs-exports-upgrde-complete which should be present and if not, checks will flag to ensure the user is notified not to upgrade to a newer code.',
    'Reference' => '',
};
sub checkNFSUpgComplete {
    if (wasCalled()) { return }
    if(isCurrentOneFS('<8.0')){return}

    my $CMD_LIVE = q[2>&1 ${SUDO} find /ifs/.ifsvar/modules/nfs -type f -name riptide-nfs-exports-upgrade-complete | grep \/ || echo '!! FILE NOT FOUND !!'];
    my $CMD_LOGS = q[2>&1 find local/ifsvar_modules.tar/modules/nfs -type f -name riptide-nfs-exports-upgrade-complete | grep \/ || echo '!! FILE NOT FOUND !!'];
   
    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);
    my $nfs_rt_issue;
    
    printCheck('NFS Riptide Complete');
    
    foreach my $line (@$gather) {
        debug("checking: $line");
        if(!$line or $line eq ''){
            missing();
        }
        else{
            if($line !~ /^\s*.*\/([a-z\-]+)$/){
                debug("Found File $1 present which does not match the expected file.");
                $nfs_rt_issue = 1;
            }
        }
    }

    if ($nfs_rt_issue) {
	fail("riptide-nfs-exports-upgrade-complete cannot be found on this cluster, do not proceed with an upgrade.");
	info("Please engage technical support for further review and assistance.");
    }

printResults();

} # End checkNFSUpgComplete
sub checkNonSystemPools {
    if (wasCalled()) { return }
    return unless
	$RPS{'Target'} and
	compareVersions($RPS{'Target'}, "7.1.1") >= 0 and
	isCurrentOneFS('<7.1.1');
    gatherFlexNet();
    my $non_system_zone_pools = 0;
    foreach my $id (keys %{$CLUSTER_INFO{'FlexNet'}{'Pools'}}) {
	next unless $CLUSTER_INFO{'FlexNet'}{'Pools'}{$id}{'Auth Zone'};
	if ($CLUSTER_INFO{'FlexNet'}{'Pools'}{$id}{'Auth Zone'} ne 1) {
	    # Flag issue
	    $non_system_zone_pools = 1;
	    debug("checkNonSystemPools: !!! Network pool $id configured with access zone $CLUSTER_INFO{'FlexNet'}{'Pools'}{$id}{'Auth Zone'}");
	}
    }
    if ($non_system_zone_pools) {
	warning("NFS clients accessing the cluster via any non-System access zone network pools will experience disruption during the upgrade.");
	warning("NFS exports will default to the System access zone after upgrading to OneFS 7.2 or later.");
	warning("NFS access is only available via System access zone network pools in OneFS 7.1.1.");
	warning("CLI and WebUI administration is only available via System access zone network pools starting in OneFS 7.1.1.");
	warning("HTTP and FTP access is only available via System access zone network pools starting in OneFS 7.1.1.");
    }
} # End checkNonSystemPools
$CHECKS{'checkOneFSVersions'} = {
    'Description' => 'Checks running version and target version for any issues. Failure: Any version mismatch across all nodes',
    'Reference' => 'https://www.dell.com/support/kbdoc/000015452',
};
sub checkOneFSVersions {
    if (wasCalled()) { return }
    printCheck("OneFS Version");

    verboseHeader("OneFS Versions");
    my $spf = "%-5s %-12s";
    verbose(sprintf($spf, "LNN", "OneFS"));
    verbose(sprintf($spf, "-"x5, "-"x12));
    my %onefs_versions = ();
    my %OneFSs = ();
    my %current_onefs_versions = ();
    my $commit_mismatch = 0;
    foreach my $lnn (@LNNS) {
	verbose(sprintf($spf, $lnn, $NODE_INFO{$lnn}{'OneFS'})) if $lnn and $NODE_INFO{$lnn}{'OneFS'};

	# Compile a list of OneFS versions as keys in a hash
	if ($NODE_INFO{$lnn}{'onefs_version'}) {
	    $onefs_versions{$NODE_INFO{$lnn}{'onefs_version'}} = 1;
	    if ($NODE_INFO{$lnn}{'committed_version'} and
		$NODE_INFO{$lnn}{'committed_version'} ne $NODE_INFO{$lnn}{'onefs_version'}) {
		$commit_mismatch = 1;
	    }
	}

	$current_onefs_versions{$NODE_INFO{$lnn}{'current_onefs_version'}} = 1 if $NODE_INFO{$lnn}{'current_onefs_version'};
	$OneFSs{$NODE_INFO{$lnn}{'OneFS'}} = 1 if $NODE_INFO{$lnn}{'OneFS'};
    }

    # Fail if multiple OneFS versions found
    if (scalar(keys %OneFSs) > 1) {
	fail("Multiple OneFS versions detected: ".join(", ", keys %OneFSs));
    } elsif (scalar(keys %current_onefs_versions) > 1) {
	fail("Multiple OneFS versions detected: ".join(", ", keys %current_onefs_versions));
    } elsif (scalar(keys %onefs_versions) > 1) {
	fail("Multiple OneFS versions detected: ".join(", ", keys %onefs_versions));
    }

    # Fail if commit version differs from node versions
    if ($commit_mismatch) {
	fail("Committed OneFS version does not match node OneFS versions");
    }

    # Beta versions
    if ($CLUSTER_INFO{'Beta'}) {
	if ($RPS{'Target'}) {
	    fail("BETA version detected, OneFS upgrades are not supported from BETA versions, destination OneFS version removed");
	    delete $RPS{'Target'};
	} else {
	    warning("BETA version detected, OneFS upgrades are not supported from BETA versions");
	}
    }

    if ($RPS{'F010517EE'}) {
	if ((isCurrentOneFS(qr/^7/) and isCurrentOneFS('<7.2.1.6')) or
	    (isCurrentOneFS(qr/^8/) and isCurrentOneFS('<8.0.0.4')) or
	    (isCurrentOneFS(qr/^8\.0\.1\.0/))) {
	    $RPS{'FCO'}{'F010517EEA'}{'Severity'} = "INFO";
	    @{$RPS{'FCO'}{'F010517EEA'}{'Message'}} = ("Current OneFS $CLUSTER_INFO{'OneFS'} does not meet requirements for FCO F010517EE");
	}
    }


    if($CLUSTER_INFO{'Gen5'}){
        debug("TARGET: $RPS{'Target'}");
        if(
            (
                (isCurrentOneFS(qr/^9.1/) and isCurrentOneFS('<9.1.0.14')) or 
                (isCurrentOneFS(qr/^9.2/) and isCurrentOneFS('<9.2.1.8')) or 
                (isCurrentOneFS(qr/^9.3/) and isCurrentOneFS('<9.3.0.3')) 
            )
            and 
            (
                (isDestinationOneFS(qr/^9.1/) and isDestinationOneFS('<9.1.0.14')) or 
                (isDestinationOneFS(qr/^9.2/) and isDestinationOneFS('<9.2.1.8')) or 
                (isDestinationOneFS(qr/^9.3/) and isDestinationOneFS('<9.3.0.3')) or 
                !$RPS{'Target'} or
                $RPS{'Target'} eq ''
            )
        ){
            critical("It is highly recommended that you upgrade to latest RUP release to avoid issues being encountered if you need to replace boot drives on your Gen5 nodes.");
            info("Please raise a support SR referencing the above message along with KB 191603 (https://www.dell.com/support/kbdoc/000191603).");
        }
    }

    if (isCurrentOneFS('8.2.0.0') and $CLUSTER_INFO{'Build'} < 11 and !$RPS{'Target'}) {
	# Encourage an upgrade to 8.2.0 build 011 from 8.2.0 build 009
	critical("OneFS 8.2.0 build $CLUSTER_INFO{'Build'} was detected, an upgrade to OneFS 8.2.0 build 011 or later is recommended as monthly RUP patches will not be developed for build $CLUSTER_INFO{'Build'}.");
    }

    if (isCurrentOneFS("8.2.1.0") and $CLUSTER_INFO{'Build'} < 4 and !$RPS{'Target'}) {
	# Encourage an upgrade to 8.2.1 build 004 from 8.2.1 build 003
	critical("OneFS 8.2.1 build $CLUSTER_INFO{'Build'} was detected, an upgrade to OneFS 8.2.1 build 004 or later is recommended as monthly RUP patches will not be developed for build $CLUSTER_INFO{'Build'}.");
    }

    # Refer to KB for details
    info(sprintf($MSG{'KB_1'}, '000015452')) if isResults(&WARN);

    # Output results
    printResults();
} # End checkOneFSVersion
$CHECKS{'checkRealACL'} = {
    'Description' => 'Checks if Real ACLs are configured / set on /ifs/.ifsvar or ifs/.ifsvar/patch. These should not be set and if they are, it would cause an issue for upgrades / installs',
    #'Exclude' => 1,
};
sub checkRealACL {
    if (wasCalled()) { return }
    if (!isLive()) { return }

    # Setup
    my $CMD_LIVE = q[2>&1 ls -led /ifs/.ifsvar /ifs/.ifsvar/patch /ifs/.ifsvar/patch/*];
    
    # Start
    printCheck('Real ACLs');

    # Gather
    my $gather = gatherLocal($CMD_LIVE);
    my @acl_issues = ();
    my @ownership_issues = ();
    if(!$gather || $gather eq ''){
        # missing info
    }
    else{
        my $acl;
        my $owner;
        my $group;
        my $item;
        foreach my $line (@$gather) {
            debug('checking '.$line);
	        if ($line !~ /^\s*([ldrwx-]+)\s*(\+?|\s)\s+(\d+)\s+(\S+)\s+(\S+).*(\/ifs.*)$/) { next }
	        if ($line =~ /^\s*([ldrwx-]+)\s*(\+?|\s)\s+(\d+)\s+(\S+)\s+(\S+).*(\/ifs.*)$/) { 
                debug('checking '.$line);
                my $acl = $2;
                my $owner = $4;
                my $group = $5;
                my $item = $6;
                debug("Item: $item\t");
                if($item =~ /^\s*\/ifs\/\.ifsvar\s*$/){
                    debug('checking item '.$item);
                    if($acl eq '+'){
                        #ACL found - FAIL
                        push(@acl_issues, $item);
                    }
                    if($owner ne 'root' || ($group ne 'wheel' and $group ne 'root' )){
                        # User / Group not as expected - WARN
                        push(@ownership_issues, $item);
                    }
                }
                if($item =~ /^\s*\/ifs\/\.ifsvar\/patch\/?.*$/){
                    debug('checking item '.$item);
                    if($acl eq '+'){
                        #ACL found - FAIL
                        push(@acl_issues, $item);
                    }
                    if($owner ne 'root' || ($group ne 'wheel' and $group ne 'root' )){
                        # User / Group not as expected - WARN
                        push(@ownership_issues, $item);
                    }
                }
            }
        }
    }
    
    if(@acl_issues || @ownership_issues){

        if(@acl_issues){
            fail('Real ACL found which could cause upgrades / installs to encounter failures, directories / Files detected: '. join(", ", @acl_issues));
        }
        if(@ownership_issues){
            warning('Ownership of the following directories / files are not as expected: '. join(", ", @ownership_issues));
        }
        info('Please open a support case and paste the above information into the case description');
    }

    # Output
    printResults();
} # End checkRealACL
$CHECKS{'checkSBR8000'} = {
    'Description' => 'Checks if SBR is enabled pre-upgrade for OneFS upgrades targeting 8.0.0.0/1',
    'Exclude' => 1,
};
sub checkSBR8000 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8.0.0.2') or isCurrentOneFS('<7.2')) {
	return unsupported('UNSUP_CUR');
    } elsif (!isDestinationOneFS(qr/^8.0.0.[01]$/)) {
	return unsupported('UNSUP_TGT_1', '8.0.0.0 or 8.0.0.1');
    }

    # Setup
    my $MSG_FAIL = 'Source Based Routing is enabled. An issue exists in 8.0.0.0 and 8.0.0.1 where when source based routing is enabled, nodes will not reply outside of their subnet. This could potentially leave the cluster in an unreachable state. It is not recommended to upgrade to either of these versions with SBR enabled.  This issue is due to be fixed in the release of 8.0.0.2.  Alternatively, if upgraded to 8.0.0.0 or 8.0.0.1, a fix is available in patch-169853.';

    # Start
    printCheck('SBR & OneFS 8.0.0.0/1');

    # Gather
    gatherFlexNet();

    # Flag
    if ($CLUSTER_INFO{'FlexNet'}{'SBR'}) { fail($MSG_FAIL) }

    # Output
    printResults();
} # End checkSBR8000
$CHECKS{'checkSupportability'} = {
    'Description' => 'Checks cluster hardware and software supportability',
    'Reference' => 'https://dl.dell.com/content/manual45268461',
};
sub checkSupportability {
    if (wasCalled()) { return }
    printCheck('Supportability');
    my $MSG_DOC = 'For more information on Isilon product support life refer to https://dl.dell.com/content/manual45268461';
    checkSupportabilityOneFS();
    checkSupportabilityNodeType();
    checkSupportabilityIsilonSD();
    if (isResults(&INFO)) { info($MSG_DOC) }
    printResults();
} # End checkSupportability
sub checkSupportabilityIsilonSD {
    if (wasCalled()) { return }
    gatherProductInfo();
    if (!$CLUSTER_INFO{'IsilonSD'}) { return }
    my $CMD_LOGS = q[grep ^model local/ifsvar_modules.tar/modules/vonefs/gconfig/vonefs_config.gc];
    
    my $CMD_LIVE;
    if(isCurrentOneFS('>=9.12') and isCompliance()){
        $CMD_LIVE = qq[${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig -t vonefs_config | grep ^model];
    }
    else{
        $CMD_LIVE = qq[${SUDO} isi_gconfig -t vonefs_config | grep ^model];
    }
    my $MSG_SUPPORT = 'With a FREE license, you are not entitled to any support from Isilon for product-related questions.';
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal(qq[2>&1 $CMD_LIVE ]);
    } else {
	$gather = gatherLocal(qq[2>&1 $CMD_LOGS ]);
    }
    my $free_edition = 0;
    foreach my $line (@$gather) {
	my ($key, $value) = parseGconfigItem($line);
	if ($key != 'model') { next }
	if ($value =~ /free/i) { $free_edition = 1 }
    }
    if ($free_edition) {
	if ($RPS{'UpgradePlan'}) {
	    fail($MSG_SUPPORT);
	} else {
	    warning($MSG_SUPPORT);
	}
    }
}
sub checkSupportabilityNodeType {
    if (wasCalled()) { return }
    gatherProductInfo();
    my $SPF_EOSS = 'The following nodes are EOSS: %s';
    my $MSG_SIMULATOR = 'Isilon OneFS simulators are not entitled to any support from Isilon for product-related questions.';
    my @eoss = ();
    my $simulator = 0;
    foreach my $lnn (@LNNS) {
	if (!$NODE_INFO{$lnn}{'Product'}) { next }
	if ($simulator) { last }
	if ($NODE_INFO{$lnn}{'Product'} =~ /SIMULATOR/i) { $simulator = 1 }
	if ($NODE_INFO{$lnn}{'Product'} =~ /^IQ/i) { push(@eoss, $lnn) }
	if ($NODE_INFO{$lnn}{'Product'} =~ /^(?:S2[01]0|NL4[01]0|X2[01]0|X4[01]0|HD400|A100)/i) {
	    push(@eoss, $lnn);
	}
    }
    if (@eoss) { warning(sprintf($SPF_EOSS, compressRange(@eoss))) }
    if ($simulator) {
	if ($RPS{'UpgradePlan'}) {
	    fail($MSG_SIMULATOR);
	} else {
	    warning($MSG_SIMULATOR);
	}
    }
}
sub checkSupportabilityOneFS {
    if (isCurrentOneFS('<=9.4')) {
	warning('Current OneFS version, '.isCurrentOneFS().', is EOSS');
    }
    if (isDestinationOneFS('<=9.4')) {
	warning('Destination OneFS version, '.isDestinationOneFS().', is EOSS');
    }
}
$CHECKS{'checkUpgradePath'} = {
    'Description' => 'Checks where multiple jumps are required and provides specifics required',
    'Reference' => 'https://dl.dell.com/content/manual81478978',
    'Parameters' => { 'name' => 'target_version', 'mandatory' => 'true', 'default' => '' },
};
$MSG{'checkUpgradePath_ISILONSD'} = 'Prior to performing an OneFS upgrade on an IsilonSD Edge cluster, make sure to review the IsilonSD Edge compatibility details available at: https://dl.dell.com/content/manual63182654';
$MSG{'checkUpgradePath_SSIP'} = 'The SmartConnect Service IP (SSIP) was never intended for client connectivity or WebUI/CLI administration.  In OneFS 8.2 and later, this is strictly enforced and the SSIP is not usable for client or administrative CLI/WebUI access to the cluster. '.sprintf($MSG{'KB_1'}, '000168292');
$MSG{'checkUpgradePath_HDFS'} = 'Check and ensure that all local user accounts on the Isilon cluster that are used for HDFS operations are enabled.  Changes to behavior in OneFS 8.2 and later will cause HDFS operations to fail when using local user accounts that are disabled. '.sprintf($MSG{'KB_1'}, '000157732');
$MSG{'checkUpgradePath_DRIVE_2'} = 'During an upgrade from OneFS %s to %s the isi_drive_repurpose_d service should be disabled to prevent an issue where drives are prematurely failed';
$MSG{'checkUpgradePath_801'} = 'Upgrades to OneFS 8.0.1 releases are not recommended, consider performing an OneFS upgrade to a newer release';
$MSG{'checkUpgradePath_NONE_2'} = 'No found upgrade path from %s to %s';
$MSG{'checkUpgradePath_MULTI_1'} = 'The requested destination version will require multiple upgrade jumps with the following recommended steps: %s';
$MSG{'checkUpgradePath_SIMULTANEOUS_2'} = 'The upgrade from %s to %s must be performed as a simultaneous upgrade';
$MSG{'checkUpgradePath_RESTRIPE_2'} = 'Following an OneFS upgrade from %s to %s, an "Upgrade" job will run. While the "Upgrade" job is running, some impacts to performance may be seen. '.sprintf($MSG{'KB_1'}, '000194551');
$MSG{'checkUpgradePath_LICENSE_2'} = 'After the upgrade from OneFS %s to OneFS %s, a one-time license conversion to a software license must be performed, and, following the license update, if using an SRS gateway for remote connectivity the SRS configuration on the cluster should be updated.  '.sprintf($MSG{'KB_1'}, '000021757');
$MSG{'checkUpgradePath_PARALLEL'} = 'Parallel reboots for the OneFS upgrade was requested, but is not available prior to OneFS 8.2.2 release, rolling method will be used';
$MSG{'checkUpgradePath_ROLLING'} = 'Rolling OneFS installation method is deprecated as of the release of 9.11, any upgrade from 9.11 to a higher version should be performed using Parallel or Simultaneous method. Defaulting to Parallel install method as no installation preference was defined.';
$MSG{'checkUpgradePath_ROLLING_WARN'} = 'Rolling reboots have been selected for the OneFS upgrade, Please note that as of the release of 9.11, the rolling installation method is deprecated and it is highly recommended you choose parallel installation method instead.';
sub checkUpgradePath {
    if (wasCalled()) { return }

    # If there is no OneFS upgrade, skip
    if (!isOneFSUpgrade()) { return }

    checkPatches();
    checkKB000213188(); # Required for generateUpgradePaths (SED config check for 9.5 upg)
    checkKB546604(); # Required for generateUpgradePaths

    printCheck('Upgrade Path');

    # Generate an upgrade path
    @{$RPS{'Upgrade Path'}} = generateUpgradePaths(isCurrentOneFS(), isDestinationOneFS());
    debug("UPGRADE PATH FOUND: ".join(', ', @{$RPS{'Upgrade Path'}}));

    # Fail if we couldn't find any path
    if (scalar(@{$RPS{'Upgrade Path'}}) <= 1) {
	fail(sprintf($MSG{'checkUpgradePath_NONE_2'}, isCurrentOneFS(), isDestinationOneFS()));
	delete $RPS{'UpgradePlan'};
    }

    # Warn if target code requires multiple jumps
    if (scalar(@{$RPS{'Upgrade Path'}}) >= 3) {
	addCustomerInformation('Upgrade Plan', warning(sprintf($MSG{'checkUpgradePath_MULTI_1'}, join(' -> ', @{$RPS{'Upgrade Path'}}))));
    }

    my ($source, $target);
    my $parallel_onefs_support = 0;
    my $rolling_onefs_deprecated = 0;
    my $rolling_deprecation_conflict = 0;
    foreach (@{$RPS{'Upgrade Path'}}) {
	$target = $_;
	if ($target and $source) {
	    $RPS{'OneFS Upgrade'}{$source}{'Target'} = $target;
	    if (isUpgradeSimultaneous($source, $target)) {
		addCustomerInformation('Upgrade Plan', warning(sprintf($MSG{'checkUpgradePath_SIMULTANEOUS_2'}, $source, $target)));
	    }
	    if (isUpgradeRestripe($source, $target)) {
		addCustomerInformation(warning(sprintf($MSG{'checkUpgradePath_RESTRIPE_2'}, $source, $target)));
	    }
	    # For all upgrades from prior to OneFS 8.1 to OneFS 8.1 and later
	    if (compareVersions($source, '8.1') < 0 and compareVersions($target, '8.1') >= 0) {
		addCustomerInformation(warning(sprintf($MSG{'checkUpgradePath_LICENSE_2'}, $source, $target)));
	    }
	    # Check if parallel reboots are supported
	    if (compareVersions($source, '8.2.2') >= 0) {
		$parallel_onefs_support = 1;
	    }
        if (compareVersions($source, '9.11') >= 0 and $RPS{'OneFS'}{'Method'} eq 'rolling' ) {
		    $rolling_onefs_deprecated = 1;
	    }
        elsif(compareVersions($source, '9.11') >= 0 and !$RPS{'OneFS'}{'Method'}){ 
            $rolling_deprecation_conflict = 1; 
        }  
	}
	$source = $target;
    }

    # Flag if requested parallel method is not supported
    if (exists $RPS{'OneFS'}{'Method'} and
	$RPS{'OneFS'}{'Method'} eq 'parallel' and
	!$parallel_onefs_support) {
	warning($MSG{'checkUpgradePath_PARALLEL'});
	$RPS{'OneFS'}{'Method'} = 'rolling';
    }

    # Flag if requested rolling method is deprecated in favour of parallel
    if($rolling_deprecation_conflict) {
	    info($MSG{'checkUpgradePath_ROLLING'});
        addCustomerInformation("Please note that rolling OneFS installation method is deprecated with the release of 9.11, all OneFS upgrades should be performed using either Parallel or Simultaneous install method.");
        $RPS{'OneFS'}{'Method'} = 'parallel';
    }
    if($rolling_onefs_deprecated){
        addCustomerInformation(warning($MSG{'checkUpgradePath_ROLLING_WARN'}));
        $RPS{'OneFS'}{'Method'} = 'rolling'
    }

    # Inform customers/warn about OneFS 8.0.1 being withdrawn and recommend other branches
    if (isDestinationOneFS(qr/^8\.0\.1\./)) {
	addCustomerInformation(warning($MSG{'checkUpgradePath_801'}));
    }

    # For all upgrades from OneFS 8.0 to OneFS 8.1, disable isi_drive_repurpose_d service during the upgrade
    if (isCurrentOneFS(qr/^8\.0\./) and isDestinationOneFS(qr/^(8\.1\.0\.[012]|8\.1\.1\.0)$/)) {
	critical(sprintf($MSG{'checkUpgradePath_DRIVE_2'}, isCurrentOneFS(), isDestinationOneFS()));
    }

    # For all upgrades from prior to OneFS 8.2 to OneFS 8.2 and later
    if (isCurrentOneFS('<8.2') and isDestinationOneFS('>=8.2')) {
	addCustomerInformation(warning($MSG{'checkUpgradePath_SSIP'}));
	if (hasLicense('HDFS')) {
	    addCustomerInformation(warning($MSG{'checkUpgradePath_HDFS'}));
	}
    }

    if ($CLUSTER_INFO{'IsilonSD'}) {
	addCustomerInformation(info($MSG{'checkUpgradePath_ISILONSD'}));
    }

    printResults();
} # End checkUpgradePath
sub gatherAccessZoneCount {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<7.0')) { return }

    # Setup
    my $CMD_LIVE;
    if(isCurrentOneFS('<9.9')){
        $CMD_LIVE = qq[2>&1 ${SUDO} isi auth local list];
    }
    else{
        $CMD_LIVE = qq[2>&1 isi auth local list];
    }
    my $CMD_LOGS = q[2>&1 cat local/isi_auth_local_list local/isi_auth];

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my $parse_total = isLive();
    foreach my $line (@$gather) {
	if ($line =~ /auth local list$/) { $parse_total = 1 }
	if ($parse_total and $line =~ /^\s*Total:\s*(\d*)$/) {
	    $CLUSTER_INFO{'Access Zones'} = $1;
	    debug("CLUSTER_INFO->Access Zones=$CLUSTER_INFO{'Access Zones'}");
	    last;
	}
    }
}
###
# gatherForceSpill: Checks if force spill is enabled
sub gatherForceSpill {
    return if wasCalled();

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal("2>&1 ${SUDO} grep -h spill /etc/mcp/override/sysctl.conf");
    } else {
	$gather = gatherLocal("2>&1 grep -h spill local/ifsvar_etc.tar/etc/mcp/override/sysctl.conf");
    }

    # Analyze information for issues.
    my $force_spill;
    foreach my $line (@$gather) {
	if ($line =~ /disk_pool_global_force_spill.*1$/ or $line =~ /disk_pool_global_spill.*1$/) {
	    $CLUSTER_INFO{'Force Spill'} = 1;
	    debug("CLUSTER_INFO->Force Spill=1");
	    last;
	}
    }
} # End gatherForceSpill
###
# gatherOneFS: Gathers OneFS version from node_status.gc
sub gatherOneFS {
    return if wasCalled();

    # Gather information
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster
	    #(qq[2>&1 ${SUDO} isi_for_array 'grep current_onefs_version /etc/ifs/node_status.gc']);
		(qq[2>&1 ${SUDO} isi_for_array 'uname -a']);
    } else {
	$gather = gatherCluster
	    #(qq[2>&1 grep -H OneFS */uanme]);

    }

    # Assign variables
    foreach my $lnn (@LNNS) {
	next unless $gather->{$lnn};
	foreach (@{$gather->{$lnn}}) {
		my $val = $1 if $_ =~ /^.*,\s+([0-9x]+),.*$/;
	    #my ($key, $value) = parseGconfigItem($_);
	    #next unless $key eq "version_info.current_onefs_version";
	    #$value =~ s/L$//;
	    #$NODE_INFO{$lnn}{'current_onefs_version'} = $value;
	    #debug("NODE_INFO->${lnn}->current_onefs_version=$value");
		$NODE_INFO{$lnn}{'current_onefs_version'} = $val;
	    debug("NODE_INFO->${lnn}->current_onefs_version=$val");
	}
    }
} # End gatherOneFS
###
# gatherOneFSVersions: Gathers OneFSVersion from all nodes.
sub gatherOneFSVersions {
    return if wasCalled();
    gatherUname();
    gatherOneFS();
    gatherArrayXML();

    # Assign variables
    foreach my $lnn (@LNNS) {
	# Parse uname for a OneFS version
	my $version_uname = "";
	if ($NODE_INFO{$lnn}{'uname'} and
	    $NODE_INFO{$lnn}{'uname'} =~ /[ v](\d+\.\d+\.\d+\.\d+) /) {
	    $version_uname = $1;
	    $NODE_INFO{$lnn}{'OneFS'} = $1;
	}
	# In order of reliability for gathering the OneFS version:
	#   array.xml, node_status.gc, and uname output
	my $version = "";
	if ($NODE_INFO{$lnn}{'onefs_version'}) {
	    $version = lc $NODE_INFO{$lnn}{'onefs_version'};
	} elsif ($NODE_INFO{$lnn}{'current_onefs_version'}) {
	    $version = lc $NODE_INFO{$lnn}{'current_onefs_version'};
	} elsif ($NODE_INFO{$lnn}{'uname'} and
		 $NODE_INFO{$lnn}{'uname'} =~ /(0x[0-9a-f]+)/i) {
	    $version = lc $1;
	}
	if ($version) {
	    my %version = %{parseOneFSVersion($version)};
	    if (!$NODE_INFO{$lnn}{'OneFS'}) {
		$NODE_INFO{$lnn}{'OneFS'} =
		    sprintf("%s.%s.%s.%s", $version{'major'}, $version{'minor'},
			    $version{'sub'}, $version{'dot'});
	    }
	    if (!$CLUSTER_INFO{'onefs_version'} or
		$CLUSTER_INFO{'onefs_version'} lt $version) {
		$CLUSTER_INFO{'onefs_version'} = $version;
		$CLUSTER_INFO{'Build'} = $version{'build'};
		$CLUSTER_INFO{'Style'} = $version{'style'};
	    }
	}
	if (!$NODE_INFO{$lnn}{'OneFS'}) { next }
	debug("NODE_INFO->${lnn}->OneFS=$NODE_INFO{$lnn}{'OneFS'}");
	if (!isCurrentOneFS(">$NODE_INFO{$lnn}{'OneFS'}")) {
	    $CLUSTER_INFO{'OneFS'} = $NODE_INFO{$lnn}{'OneFS'};
	}

    }
    debug("CLUSTER_INFO->onefs_version=$CLUSTER_INFO{'onefs_version'}")
	if $CLUSTER_INFO{'onefs_version'};
    debug("CLUSTER_INFO->OneFS=$CLUSTER_INFO{'OneFS'}")
	if $CLUSTER_INFO{'OneFS'};
    debug("CLUSTER_INFO->Build=$CLUSTER_INFO{'Build'}")
	if $CLUSTER_INFO{'Build'};
    debug("CLUSTER_INFO->Style=$CLUSTER_INFO{'Style'}")
	if $CLUSTER_INFO{'Style'};
    if ($CLUSTER_INFO{'Style'} and $CLUSTER_INFO{'Style'} eq 0x3) {
	$CLUSTER_INFO{'Beta'} = 1;
	debug("CLUSTER_INFO->Beta=1");
    }

    # Abort if unable to determine what OneFS version is currently in use
    if (!$CLUSTER_INFO{'OneFS'}) {
	fail("Unable to determine a OneFS version, checks will not proceed.");
	printCheck("Gathering OneFS Versions");
	printResults();
	exit 1;
    }
} # End gatherOneFSVersions
###
# gatherUname: Gathers the uname output from each node
sub gatherUname {
    return if wasCalled();

    # Gather information
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array uname -a]);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -H . */uname]);
    }

    # Assign variables
    foreach my $lnn (@LNNS) {
	next unless $gather->{$lnn};
	foreach my $line (@{$gather->{$lnn}}) {
	    $NODE_INFO{$lnn}{'uname'} = $line;
	    debug("NODE_INFO->${lnn}->uname=$line");
	}
    }
} # End gatherUname
###
# generateTargetVersions: Generates an array of OneFS versions within
# a specific family that are >= the provided version (inclusive).
sub generateTargetVersions {
    my ($version) = @_;
    return () unless $version;

    ensure_family_index();      # lazy build

    if (exists $GEN_TGT_MEMO{$version}) {
        my $ref = $GEN_TGT_MEMO{$version};
        debug("generateTargetVersions (memo): $version -> " . scalar(@$ref) . " candidates");
        return @$ref;
    }

    debug("generateTargetVersions: Argument: $version");

    # Derive family from the first three segments of the input version
    my ($major, $minor, $service, $patch) = split(/\./, $version, 4);
    unless (defined $service) {
        debug("generateTargetVersions: '$version' does not have 4 parts; returning empty");
        $GEN_TGT_MEMO{$version} = [];
        return ();
    }
    my $family = "$major.$minor.$service";

    # Pull pre-sorted (desc) list for this family
    my $arr_ref = $FAMILY_INDEX{$family} // [];

    # Early-break: list is desc; stop when entries drop below $version
    my @out;
    for my $v (@$arr_ref) {
        my $cmp = compareVersions($v, $version);
        last if $cmp < 0;     # remaining entries will also be < $version
        push @out, $v;        # include == and >
    }

    debug("generateTargetVersions: $version -> " . scalar(@out) . " candidates");
    $GEN_TGT_MEMO{$version} = \@out;
    return @out;
} # End generateTargetVersions
###
# generateUpgradePaths: Generates an array for the upgrade path

# =============================================================================
# LTS helpers (build once, O(1) checks)
# =============================================================================
our %IS_LTS_FULL;         # full code -> 1
our $LTS_LOOKUP_BUILT = 0;

# Prefer LTS items in a set if any exist; otherwise return the original set.
sub prefer_lts_full_codes {
    my (@items) = @_;
    my @lts = grep { is_lts_full($_) } @items;
    return @lts ? @lts : @items;
}

# =============================================================================
# Family index for fast target generation (build once)
# If generateTargetVersions lives in another file, move these there.
# =============================================================================
our %FAMILY_INDEX;         # 'major.minor.service' => [ versions (desc) ]
our $FAMILY_INDEX_BUILT = 0;

sub build_family_index {
    %FAMILY_INDEX = ();

    # Collect all version-like keys once
    my @all = (keys %Code_Matrix, keys %Patch_Releases);

    for my $v (@all) {
        next unless defined $v && $v =~ /^\d+\.\d+\.\d+\.\d+$/;
        my ($a,$b,$c,$d) = split /\./, $v, 4;
        push @{ $FAMILY_INDEX{"$a.$b.$c"} }, $v;
    }

    # De-dup and sort each family descending once
    for my $fam (keys %FAMILY_INDEX) {
        my %seen;
        my @uniq = grep { !$seen{$_}++ } @{ $FAMILY_INDEX{$fam} };
        @uniq = sort { compareVersions($b, $a) } @uniq;  # desc
        $FAMILY_INDEX{$fam} = \@uniq;
    }

    $FAMILY_INDEX_BUILT = 1;
    debug("Family index built: " . scalar(keys %FAMILY_INDEX) . " families");
}

sub ensure_family_index {
    build_family_index() unless $FAMILY_INDEX_BUILT;
}

# =============================================================================
# Memoization caches and safety caps
# =============================================================================
our %PATH_MEMO;        # (source|target) -> arrayref path; [] means no path
our %GEN_TGT_MEMO;     # version -> arrayref of generated targets
our $MAX_DEPTH = undef;   # safety net; set to undef to disable

# ----- LTS lookup (build once) -----
our %IS_LTS_FULL;         # full version -> 1 if LTS
our $LTS_LOOKUP_BUILT = 0;

sub build_is_lts_lookup_from_families {
    my ($lts_href) = @_;
    %IS_LTS_FULL = ();
    for my $family (keys %{$lts_href}) {
        my $aref = $lts_href->{$family} // [];
        next unless ref($aref) eq 'ARRAY';
        @IS_LTS_FULL{@$aref} = (1) x @$aref;
    }
    $LTS_LOOKUP_BUILT = 1;
}

sub ensure_lts_lookup {
    build_is_lts_lookup_from_families(\%LTS_Codes) unless $LTS_LOOKUP_BUILT;
}

sub is_lts_full {
    my ($code) = @_;
    return $IS_LTS_FULL{$code} // 0;
}


# ----- Sort helper: LTS first, then version descending -----
sub sort_lts_desc {
    my (@items) = @_;
    return sort {
        # Put LTS first
        my $al = is_lts_full($a);
        my $bl = is_lts_full($b);
        ($bl <=> $al)               # LTS (1) before non-LTS (0)
        || compareVersions($b, $a)  # within same LTS family, newer first
    } @items;
}


###
# generateUpgradePaths: Generates an array for the upgrade path
sub generateUpgradePaths {
    my ($source, $target) = @_;
    if (!$source or !$target) {
        debug('Insufficient arguments provided');
        return ();
    }
    debug("Source: $source, Destination: $target");
    our %Code_Matrix;

    ensure_lts_lookup();   # <-- ensure the LTS map is built

    # The first call to generateUpgradePaths
    if (isDestinationOneFS($target) and isCurrentOneFS($source)) {

        # (all your special cases unchanged) ...
        if (isDestinationOneFS('>=8.1.1') and isCurrentOneFS('<7.1.1.11')) {
            my @path_to_interim =
                generateUpgradePaths(isCurrentOneFS(), '7.1.1.11');
            my @path_from_interim =
                generateUpgradePaths('7.1.1.11', isDestinationOneFS());

            if (!@path_to_interim or !@path_from_interim) { return () }

            pop(@path_to_interim);
            return @path_to_interim, @path_from_interim;
        }

        if ($RPS{'KB546604'} and isDestinationOneFS('>=9') and isCurrentOneFS('<=8.1')) {
            my @path_to_interim =
                generateUpgradePaths(isCurrentOneFS(), '8.2.2.0');
            my @path_from_interim =
                generateUpgradePaths('8.2.2.0', isDestinationOneFS());

            if (!@path_to_interim or !@path_from_interim) { return () }

            pop(@path_to_interim);
            return @path_to_interim, @path_from_interim;
        }

        if ($RPS{'000213188'} and isDestinationOneFS('>=9.5') and isCurrentOneFS('<9.2')) {
            my @path_to_interim =
                generateUpgradePaths(isCurrentOneFS(), '9.4.0.14');
            my @path_from_interim =
                generateUpgradePaths('9.4.0.14', isDestinationOneFS());

            if (!@path_to_interim or !@path_from_interim) { return () }

            pop(@path_to_interim);
            debug("\nCOMBINED PATH: TO: @path_to_interim\t|\tFROM: @path_from_interim\n");
            return @path_to_interim, @path_from_interim;
        }

        if (isCurrentOneFS('8.2.0.0') and isDestinationOneFS('8.2.0.0') and $CLUSTER_INFO{'Build'} < 11) {
            return ('8.2.0.0', '8.2.0.0');
        }

        if (isCurrentOneFS('8.2.1.0') and isDestinationOneFS('8.2.1.0') and $CLUSTER_INFO{'Build'} < 4) {
            return ('8.2.1.0', '8.2.1.0');
        }
    }

    # Recursion call where multiple jumps are needed (unchanged guards)
    if (!isCurrentOneFS($source)) {
        if ($source =~ /^9\.2\./) { return () }
        if ($target eq '7.2.1.3' and  $source =~ /^7\.1\.1\.[89]$/) { return () }
        if ($source eq '8.0.1.0') { return () }
        if ($source eq '8.1.3.0') { return () }
        if (compareVersions($target, '8.1') >= 0 and isCurrentOneFS('<=7.1.1') and compareVersions($source, '8.0') == 0) {
            return ();
        }
        if (compareVersions($target, '7.2.1') >= 0 and isCurrentOneFS('<=7.1.1') and compareVersions($source, '7.2.0') == 0) {
            return ();
        }
    }

    # Locate the key in the code matrix to use for out-of-family upgrades
    my ($key) = grep { compareVersions($source, $_) == 0 } keys %Code_Matrix;
    debug("Source key: $key");

    # Seed candidates: matrix entries + source
    my @seed_candidates = (@{ $Code_Matrix{$key} // [] }, $source);

    # (Optional) de-dup seeds
    {
        my %seen;
        @seed_candidates = grep { !$seen{$_}++ } @seed_candidates;
    }

    # ---- CHANGED: LTS-first ordering for seeds ----
    my @seeds = sort_lts_desc(@seed_candidates);

    # Generate destination versions
    my @destinations;
    foreach my $version (@seeds) {
        my ($major, $minor, $service) = split(/\./, $version);
        my $extract_code_fmly = "$major.$minor.$service";
        debug("Preparing to generate target versions using $extract_code_fmly");
        debug("Seed is LTS: $version") if is_lts_full($version);

        # Generate next-hop candidates
        my @generated = generateTargetVersions($version);

        # Bound to (source, target]
        @generated = grep {
            compareVersions($_, $source) > 0 &&
            compareVersions($_, $target) <= 0
        } @generated;

        # ---- CHANGED: LTS-first ordering for generated candidates ----
        my @generated_choice = sort_lts_desc(@generated);

        for my $cand (@generated_choice) {
            debug("Pushing $cand into destinations");
            push @destinations, $cand;
        }
    }

    # (Optional) de-dup
    {
        my %seen;
        @destinations = grep { !$seen{$_}++ } @destinations;
    }

    # ---- CHANGED: final ordering LTS-first, then newest ----
    @destinations = sort_lts_desc(@destinations);
    debug("Destination version array: @destinations");

    # Search for the destination version and return if found
    my ($results) = grep { compareVersions($_, $target) == 0 } @destinations;
    if ($results) { return ($source, $target) }

    # If the destination was not found, it may be a multi-jump upgrade
    debug('No match found, searching for multiple jumps');
    foreach (@destinations) {
        my @upgrade_path = generateUpgradePaths($_, $target);
        if (@upgrade_path) { return ($source, @upgrade_path) }
    }

    return ();
} # End generateUpgradePaths

sub checkKB520799 {
    if (wasCalled()) { return }
    if (!isLive()) { return }
    if (!hasPatch('patch-208294')) { return }

    # Setup
    my $CMD = qq[2>&1 ${SUDO} isi_for_array 'ls /lib/libcrypto.so*'];
    my $RE_PATCHED = qr/\/lib\/libcrypto\.so\.8\.patched/;
    my $SPF_MISSING = 'patch-208294 is installed but'.
	' /lib/libcrypto.so.8.patched is missing on the following nodes: %s.'.
	' Follow resolution steps in KB 000036454'.
	' [https://www.dell.com/support/kbdoc/000036454 - Patch 208294 is'.
	' missing /lib/libcrypto.so.8.patched]';

    # Gather
    my $gather = gatherCluster($CMD);

    # Analyze
    my @nodes_missing_patched = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	my ($res) = grep {/$RE_PATCHED/} @{$gather->{$lnn}};
	if (!$res) { push(@nodes_missing_patched, $lnn) }
    }

    # Flag
    if (@nodes_missing_patched) {
	fail(sprintf($SPF_MISSING, compressRange(@nodes_missing_patched)));
    }

}
# Uses local variables to validate patches
sub checkPatch {
    if (!$patch or !$re_versions) { return }
	if (isOneFSUpgrade()) {
	if (!isDestinationOneFS($re_versions)) { return }
    } else {
	if (!isCurrentOneFS($re_versions) or hasPatch($patch)) { return }
    }

    $RPS{'Post Upgrade Patches'}{$patch} = ();
    $RPS{'Post Upgrade Patches'}{$patch}{'Impacts'} = $impacts if $impacts;
    $RPS{'Post Upgrade Patches'}{$patch}{'Important'} = $important if $important;
    $RPS{'Post Upgrade Patches'}{$patch}{'Service Restart'} = $service_restart if $service_restart;
    $RPS{'Post Upgrade Patches'}{$patch}{'Priority'} = $priority if $priority;
    $RPS{'Post Upgrade Patches'}{$patch}{'Target'} = $target if $target;
    @{$RPS{'Post Upgrade Patches'}{$patch}{'Requires'}} = @requires if @requires;
    @{$RPS{'Post Upgrade Patches'}{$patch}{'Reboot'}} = @reboot if @reboot;
    if (isDestinationOneFS()) {
	warning("$patch is recommended for the destination OneFS $RPS{'Target'}.");
    } else {
	warning("$patch is recommended for the current OneFS $CLUSTER_INFO{'OneFS'}.");
	if (@conflicting_patches) {
	    warning("$patch is recommended but is known to conflict with patches: ".join(", ", @conflicting_patches));
	    warning("If the customer would like to have $patch performed they should review the conflicting patches and determine whether they want to keep them or perform $patch");
	}
	if (@deprecated_patches) {
	    warning("Prior to installing $patch, the following deprecated patches must be uninstalled: ".join(", ", @deprecated_patches));
	}
	if (@replaceable_patches) {
	    warning("During the installation of $patch, the following deprecated patches will be replaced: ".join(", ", @replaceable_patches));
	}
	foreach my $other_patch (@deprecated_patches, @conflicting_patches) {
	    if ($patches_with_svc_scripts and $other_patch =~ $patches_with_svc_scripts) {
		$RPS{'Post Upgrade Patches'}{"uninstall $other_patch"}{'Service Restart'} = 1 if $service_restart;
	    } else {
		$RPS{'Post Upgrade Patches'}{"uninstall $other_patch"}{'Service Restart'} = $service_restart if $service_restart;
	    }
	    $RPS{'Post Upgrade Patches'}{"uninstall $other_patch"}{'Impacts'} = "Please review the README for $other_patch for details on impacts during removal";
	    @{$RPS{'Post Upgrade Patches'}{"uninstall $other_patch"}{'Reboot'}} = @reboot if @reboot;
	    push(@{$RPS{'Post Upgrade Patches'}{$patch}{'Requires'}}, "uninstall $other_patch");
	}
    }
}
sub checkPatch146974 {
    local $patch = "patch-146974";
    local $re_versions = qr/^(7\.1\.1\.2)$/;
    local $impacts = "During installation or removal of $patch, the SMB and authentication services are unavailable and SMB client connections to the cluster are disconnected. After the patch is installed, SMB clients must reestablish a connection to the cluster.";
    checkPatch();
}
sub checkPatch147686 {
    local $patch = "patch-147686";
    local $re_versions = qr/^(7\.1\.0\.(4|5|6))$/;
    local $impacts = "During installation or removal of $patch, the SMB and authentication services are unavailable and SMB client connections to the cluster are disconnected. After the patch is installed, SMB clients must reestablish a connection to the cluster.";
    checkPatch();
}
sub checkPatch161166 {
    local $patch = "patch-161166";
    local $re_versions = qr/^(7\.2\.0\.2)$/;
    local $impacts = "During installation or removal of $patch, the SMB and authentication services are unavailable and SMB client connections to the cluster are disconnected. After the patch is installed, SMB clients must reestablish a connection to the cluster.";
    checkPatch();
}
sub checkPatch164666 {
    local $patch = "patch-164666";
    local $re_versions = qr/^(7\.1\.1\.(0|1|2|3|4|5|6|7|8)|7\.2\.1\.(0|1|2))$/;
    checkPatch();
}
sub checkPatch175451 {
    gatherNetworkInterfaces();
    local $patch = "patch-175451";
    local $re_versions = qr/^(7\.2\.0\.(0|1|2|3|4|5))$/;
    local $impacts = "During installation or removal of patch 175451, clients and applications that are connected to the cluster through BXE 10 GigE network interfaces may be disconnected. These connections must be reestablished after the patch installation is complete." if $CLUSTER_INFO{'BXE Nodes'};
    local @reboot = @{$CLUSTER_INFO{'BXE Nodes'}} if $CLUSTER_INFO{'BXE Nodes'};
    checkPatch();
}
sub checkPatch178793 {
    local $patch = "patch-178793";
    local $re_versions = qr/^(8\.0\.0\.(0|1))$/;
    local $impacts = "During installation or removal of $patch, the isi_papi_d process and SmartConnect service are stopped. As a result, CLI commands beginning with the 'isi' prefix cannot be run and both the OneFS web administration interface and the SmartConnect service are unavailable. After installation or removal of this patch, the isi_papi_d process and SmartConnect service restart automatically.";
    checkPatch();
}
sub checkPatch179385 {
    local $patch = "patch-179385";
    local $re_versions = qr/^(8\.0\.0\.2)$/;
    local $impacts = "During installation or removal of $patch, the isi_papi_d process and SmartConnect service are stopped. As a result, CLI commands beginning with the 'isi' prefix cannot be run and both the OneFS web administration interface and the SmartConnect service are unavailable. After installation or removal of this patch, the isi_papi_d process and SmartConnect service restart automatically.";
    checkPatch();
}
sub checkPatch181490 {
    local $patch = "patch-181490";
    local $re_versions = qr/^(8\.0\.1\.0)$/;
    local $impacts = "During installation or removal of $patch, the isi_papi_d process and SmartConnect service are stopped. As a result, CLI commands beginning with the 'isi' prefix cannot be run and both the OneFS web administration interface and the SmartConnect service are unavailable. After installation or removal of $patch, the isi_papi_d process and SmartConnect service restart automatically.";
    checkPatch();
}
sub checkPatch188239 {
    gatherNetworkInterfaces();
    local $patch = "patch-188239";
    local $re_versions = qr/^(8\.0\.0\.[01234])$/;
    local $impacts = "During installation or removal of patch 188239, clients and applications that are connected to the cluster through BXE 10 GigE network interfaces may be disconnected. These connections must be reestablished after the patch installation is complete." if $CLUSTER_INFO{'BXE Nodes'};
    local @reboot = @{$CLUSTER_INFO{'BXE Nodes'}} if $CLUSTER_INFO{'BXE Nodes'};
    checkPatch();
}
sub checkPatch188660 {
    local $patch = "patch-188660";
    local $re_versions = qr/^(8\.0\.1\.0)$/;
    local @deprecated_patches = hasPatch("patch-(187743|186696|185821|184615)");
    local $impacts = "During installation or removal of $patch, the service that manages SMB, NFS, HDFS, and Swift client connections as well as authentication to the cluster is restarted. As a result, during installation or removal of $patch, client connections to the cluster over these protocols might be disrupted and new client connections cannot be authenticated. FTP and HTTP connections might be affected in some environments, and client connections might have to be reestablished manually after the installation or removal process is complete.";
    checkPatch();
}
sub checkPatch189132 {
    gatherNetworkInterfaces();
    local $patch = "patch-189132";
    local $re_versions = qr/^(7\.1\.1\.(0|1|2|3|4|5|6|7|8|9|10|11))$/;
    local $impacts = "During installation or removal of patch 189132, clients and applications that are connected to the cluster through BXE 10 GigE network interfaces might be disconnected. These connections must be reestablished after the patch installation is complete." if $CLUSTER_INFO{'BXE Nodes'};
    local @reboot = @{$CLUSTER_INFO{'BXE Nodes'}} if $CLUSTER_INFO{'BXE Nodes'};
    checkPatch();
}
sub checkPatch189133 {
    gatherNetworkInterfaces();
    local $patch = "patch-189133";
    local $re_versions = qr/^(7\.2\.1\.[012345])$/;
    local @deprecated_patches = hasPatch("patch-(175451)");
    local $impacts = "During installation or removal of patch 189133, clients and applications that are connected to the cluster through BXE 10 GigE network interfaces may be disconnected. These connections must be reestablished after the patch installation is complete." if $CLUSTER_INFO{'BXE Nodes'};
    local @reboot = @{$CLUSTER_INFO{'BXE Nodes'}} if $CLUSTER_INFO{'BXE Nodes'};
    checkPatch();
}
sub checkPatch206322 {
    local $patch = "patch-206322";
    local $re_versions = qr/^(8\.0\.0\.4)$/;
    local @deprecated_patches = hasPatch("patch-(187902|188699|189708|189853|191603|194238|194268|195171|196308|196448|196845|196906|197503|197648|198834|199265)");
    local @conflicting_patches = hasPatch("patch-(191606|194795|196968|197390|197710|197902|199123|200631|200950|200951|203704|203895)");
    local $impacts = "During installation or removal of $patch, the following services will be stopped and restarted automatically: SMB, Likewise, NFS, HDFS, Isi Migrate, CloudPools, Flexnet, SmartConnect, NDMP, PAPI, Tardis";

    # Patch 206322 is deprecated by patch 212411
    if (!isDestinationOneFS() and
	isCurrentOneFS($re_versions) and
	hasPatch('patch-212411')) {
	return;
    }
    checkPatch();
}
sub checkPatch213286 {
    local $patch = "patch-213286";
    local $re_versions = qr/^(7\.2\.1\.5)$/;
    local @deprecated_patches = hasPatch("patch-(211400)");
    local @conflicting_patches = hasPatch("patch-(200764|205288)");
    local $impacts = "During installation or removal of $patch, the following services will be stopped and restarted automatically: webui, apache2, sshd";
    local $important = "IMPORTANT: If you update Isilon OneFS with $patch, and you are using Insight IQ, you must upgrade to Insight IQ 4.1.2 prior to installing the patch.\nIMPORTANT: For complete protection against these vulnerabilities, you must disable basic authentication. Additionally, if you are installing $patch due to cross-site request forgery (CSRF) concerns, AND you have custom OneFS API-based automation, you must also update your automation to incorporate anti-CSRF tokens provided by your Isilon cluster. For more information about these two procedures, see https://www.dell.com/support/kbdoc/000156530 on the Customer support website.";
    checkPatch();
}
sub checkPatch213287 {
    local $patch = "patch-213287";
    local $re_versions = qr/^(7\.2\.1\.6)$/;
    local @conflicting_patches = hasPatch("patch-(200764|205288)");
    local $impacts = "During installation or removal of $patch, the following services will be stopped and restarted automatically: webui, apache2, sshd";
    local $important = "IMPORTANT: If you update Isilon OneFS with $patch, and you are using Insight IQ, you must upgrade to Insight IQ 4.1.2 prior to installing the patch.\nIMPORTANT: For complete protection against these vulnerabilities, you must disable basic authentication. Additionally, if you are installing $patch due to cross-site request forgery (CSRF) concerns, AND you have custom OneFS API-based automation, you must also update your automation to incorporate anti-CSRF tokens provided by your Isilon cluster. For more information about these two procedures, see https://www.dell.com/support/kbdoc/000156530 on the Customer support website.";
    checkPatch();
}
sub checkPatch219279 {
    local $patch = "patch-219279";
    local $re_versions = qr/^(8\.0\.1\.2)$/;
    local @deprecated_patches = hasPatch("patch-(216929|216763|215881)");
    local @conflicting_patches = hasPatch("patch-(215020|213630)");
    local $impacts = "During installation or removal of  $patch, the following services will be stopped and restarted automatically: SMB, Likewise, NFS, HDFS, SNMP, isi_avscan_d, Migrate, pcnfsd, NDMP, PAPI, Tardis";
    local $service_restart = 1;
    local $priority = 5;
    checkPatch();
}
sub checkPatch220098 {
    local $patch = "patch-220098";
    local $re_versions = qr/^(8\.1\.0\.2)$/;
    local @deprecated_patches = hasPatch("patch-(219689|224219|227398)");
    local @conflicting_patches = hasPatch("patch-(222005)");
    local $impacts = "During installation or removal of $patch, the following services are restarted: auth, celog, hdfs, isi_hwmon, isi_smartconnect, isi_stats_d, isi_stats_hist_d, lwdm, lwswift, nfs, smb";
    local $service_restart = 1;
    checkPatch();
}
sub checkPatch220176 {
    local $patch = "patch-220176";
    local $re_versions = qr/^(8\.0\.1\.2)$/;
    local @deprecated_patches = hasPatch("patch-(213280)");
    local $impacts = "During installation or removal of $patch, the following services are restarted: isi_mcp, apache2, isi_webui";
    local $important = "IMPORTANT: If you update Isilon OneFS with $patch, and you are using Insight IQ, you must upgrade to Insight IQ 4.1.2 prior to installing $patch.\nIMPORTANT: For complete protection against these vulnerabilities, you must disable basic authentication. Additionally, if you are installing $patch due to cross-site request forgery (CSRF) concerns, AND you have custom OneFS API-based automation, you must also update your automation to incorporate anti-CSRF tokens provided by your Isilon cluster. For more information about these two procedures, see https://www.dell.com/support/kbdoc/000156530 on the Customer support website.";
    local $service_restart = 1;
    checkPatch();
}
sub checkPatch221602 {
    local $patch = "patch-221602";
    local $re_versions = qr/^(8\.0\.0\.4)$/;
    local @conflicting_patches = hasPatch("patch-(208294|205289)");
    local $important = "IMPORTANT: If you update Isilon OneFS with patch 221602, and you are using Insight IQ, you must upgrade to Insight IQ 4.1.2 prior to installing the patch.\nIMPORTANT: For complete protection against these vulnerabilities, you must disable basic authentication. Additionally, if you are installing patch 221602 due to cross-site request forgery (CSRF) concerns, AND you have custom OneFS API-based automation, you must also update your automation to incorporate anti-CSRF tokens provided by your Isilon cluster. For more information about these two procedures, see https://www.dell.com/support/kbdoc/000156530 on the Customer support website.";
    local $service_restart = 1;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch221603 {
    local $patch = "patch-221603";
    local $re_versions = qr/^(8\.0\.0\.5)$/;
    local @deprecated_patches = hasPatch("patch-(217637|211167|209258|208294|207787|205289)");
    local $impacts = "During installation or removal of $patch, the following services are restarted: mcp, webui, http, isi_papi_d, isi_object_d";
    local $important = "IMPORTANT: If you update Isilon OneFS with $patch, and you are using Insight IQ, you must upgrade to Insight IQ 4.1.2 prior to installing $patch.\n IMPORTANT: For complete protection against these vulnerabilities, you must disable basic authentication. Additionally, if you are installing $patch due to cross-site request forgery (CSRF) concerns, AND you have custom OneFS API-based automation, you must also update your automation to incorporate anti-CSRF tokens provided by your Isilon cluster. For more information about these two procedures, see https://www.dell.com/support/kbdoc/000156530 on the Customer support website.";
    local $service_restart = 1;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch221947 {
    local $patch = "patch-221947";
    local $re_versions = qr/^(8\.1\.0\.1)$/;
    local @deprecated_patches = hasPatch("patch-(213282|217638)");
    local @conflicting_patches = hasPatch("patch-(211962|220053|194276)");
    local $important = "IMPORTANT: If you update Isilon OneFS with $patch, and you are using Insight IQ, you must upgrade to Insight IQ 4.1.2 prior to installing the patch.\nIMPORTANT: For complete protection against these vulnerabilities, you must disable basic authentication. Additionally, if you are installing $patch due to cross-site request forgery (CSRF) concerns, AND you have custom OneFS API-based automation, you must also update your automation to incorporate anti-CSRF tokens provided by your Isilon cluster. For more information about these two procedures, see https://www.dell.com/support/kbdoc/000156530 on the Customer support website.";
    local $service_restart = 1;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch221949 {
    local $patch = "patch-221949";
    local $re_versions = qr/^(8\.1\.0\.0)$/;
    local @deprecated_patches = hasPatch("patch-(213281)");
    local @conflicting_patches = hasPatch("patch-(211967|211961|205291|200762)");
    local $important = "IMPORTANT: If you update Isilon OneFS with $patch, and you are using Insight IQ, you must upgrade to Insight IQ 4.1.2 prior to installing $patch.\nIMPORTANT: For complete protection against these vulnerabilities, you must disable basic authentication. Additionally, if you are installing $patch due to cross-site request forgery (CSRF) concerns, AND you have custom OneFS API-based automation, you must also update your automation to incorporate anti-CSRF tokens provided by your Isilon cluster. For more information about these two procedures, see https://www.dell.com/support/kbdoc/000156530 on the Customer support website.";
    local $service_restart = 1;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch222487 {
    local $patch = "patch-222487";
    local $re_versions = qr/^(8\.0\.1\.1)$/;
    local @deprecated_patches = hasPatch("patch-(213279|211960)");
    local @conflicting_patches = hasPatch("patch-(215497|211403|210691|207276|205290)");
    local $important = "IMPORTANT: If you update Isilon OneFS with $patch, and you are using Insight IQ, you must upgrade to Insight IQ 4.1.2 prior to installing $patch.\nIMPORTANT: For complete protection against these vulnerabilities, you must disable basic authentication. Additionally, if you are installing $patch due to cross-site request forgery (CSRF) concerns, AND you have custom OneFS API-based automation, you must also update your automation to incorporate anti-CSRF tokens provided by your Isilon cluster. For more information about these two procedures, see https://www.dell.com/support/kbdoc/000156530 on the Customer support website.";
    local $service_restart = 1;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch223311 {
    local $patch = "patch-223311";
    local $re_versions = qr/^(8\.0\.0\.5)$/;
    local @deprecated_patches = hasPatch("patch-(211169|217838)");
    local @conflicting_patches = hasPatch("patch-(203466|204429|204812|204900|206113|210031|210400|211170|211795|213989|215592|215685|215703|216731)");
    local $impacts = "During installation or removal of $patch, the following services are restarted: NFS, PC NFS Server, SMB, LIKEWISE, HDFS, CloudPools, Tardis, PAPI, SmartConnect, NDMP, Authentication, LWSM, Swift, SNMP, FlexNet, JobEngine, Statistics & Monitoring, Statistics History";
    local $service_restart = 1;
    local $priority = 5;
    checkPatch();
}
sub checkPatch223338 {
    local $patch = "patch-223338";
    local $re_versions = qr/^(8\.0\.0\.6)$/;
    local @deprecated_patches = hasPatch("patch-(208154|214178|215884|219375|219904|221633|224127|224804|227485|231811)");
    local @conflicting_patches = hasPatch("patch-(222935|218751)");
    local $impacts = "During installation or removal of $patch, the following services are restarted: auth, celog, hdfs, isi_avscan_d, isi_cpool_d, isi_cpool_io_d, isi_flexnet_d, isi_hangdump, isi_job_d, isi_km_d, isi_mcp, isi_papi_d, isi_smartconnect, isi_stats_d, isi_stats_hist_d, lwsm, lwswift, nfs, smb, snmp";
    local $service_restart = 1;
    local $priority = 5;
    checkPatch();
}
sub checkPatch224116 {
    local $patch = "patch-224116";
    local $re_versions = qr/^(8\.1\.1\.1)$/;
    local @deprecated_patches = hasPatch("patch-(222493)");
    local $important = "IMPORTANT: If you update Isilon OneFS with $patch, and you are using Insight IQ, you must upgrade to Insight IQ 4.1.2 prior to installing the patch.\nIMPORTANT: For complete protection against these vulnerabilities, you must disable basic authentication. Additionally, if you are installing $patch due to cross-site request forgery (CSRF) concerns, AND you have custom OneFS API-based automation, you must also update your automation to incorporate anti-CSRF tokens provided by your Isilon cluster. For more information about these two procedures, see https://www.dell.com/support/kbdoc/000156530 on the Customer support website.";
    local $service_restart = 1;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch226323 {
    local $patch = "patch-226323";
    local $re_versions = qr/^(8\.1\.0\.2)$/;
    local @deprecated_patches = hasPatch("patch-(220928|222464|223838)");
    local @conflicting_patches = hasPatch("patch-(223989)");
    local $service_restart = 1;
    local $priority = 10;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch226984 {
    local $patch = "patch-226984";
    local $re_versions = qr/^(8\.0\.0\.6)$/;
    local @deprecated_patches = hasPatch("patch-(225079)");
    local $service_restart = 1;
    local $priority = 10;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch228458 {
    local $patch = "patch-228458";
    local $re_versions = qr/^(8\.1\.0\.2)$/;
    local @deprecated_patches = hasPatch("patch-(213283|219709|221946|223678)");
    local $important = "IMPORTANT: If you update Isilon OneFS with $patch, and you are using Insight IQ, you must upgrade to Insight IQ 4.1.2 prior to installing the patch.\nIMPORTANT: For complete protection against these vulnerabilities, you must disable basic authentication. Additionally, if you are installing $patch due to cross-site request forgery (CSRF) concerns, AND you have custom OneFS API-based automation, you must also update your automation to incorporate anti-CSRF tokens provided by your Isilon cluster. For more information about these two procedures, see https://www.dell.com/support/kbdoc/000156530 on the Customer support website.";
    local $service_restart = 1;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch229156 {
    local $patch = "patch-229156";
    local $re_versions = qr/^(8\.0\.0\.6)$/;
    local @deprecated_patches = hasPatch("patch-(215690|221638)");
    local $impacts = "During installation or removal of $patch, the following services are restarted: SyncIQ, NDMP, PAPI, MCP";
    local $service_restart = 1;
    checkPatch();
}
sub checkPatch229158 {
    local $patch = "patch-229158";
    local $re_versions = qr/^(8\.0\.0\.5)$/;
    local @deprecated_patches = hasPatch("patch-(203734|211168|203743|213179|217463|224399)");
    local @conflicting_patches = hasPatch("patch-(206113|210400|211169|217838)");
    local $impacts = "During installation or removal of $patch, the following services are restarted: SyncIQ, PAPI";
    local $service_restart = 1;
    checkPatch();
}
sub checkPatch229160 {
    local $patch = "patch-229160";
    local $re_versions = qr/^(8\.1\.0\.2)$/;
    local $impacts = "During installation or removal of $patch, the following services are restarted: SyncIQ, PAPI, NDMP";
    local $service_restart = 1;
    local @deprecated_patches = hasPatch("patch-(222718)");
    checkPatch();
}
sub checkPatch231386 {
    local $patch = "patch-231386";
    local $re_versions = qr/^(8\.0\.0\.6)$/;
    local @deprecated_patches = hasPatch("patch-(213278|221617|223243)");
    local @conflicting_patches = hasPatch("patch-(228563)");
    local $important = "IMPORTANT: If you update Isilon OneFS with $patch, and you are using Insight IQ, you must upgrade to Insight IQ 4.1.2 prior to installing the patch.\nIMPORTANT: For complete protection against these vulnerabilities, you must disable basic authentication. Additionally, if you are installing $patch due to cross-site request forgery (CSRF) concerns, AND you have custom OneFS API-based automation, you must also update your automation to incorporate anti-CSRF tokens provided by your Isilon cluster. For more information about these two procedures, see https://www.dell.com/support/kbdoc/000156530 on the Customer support website.";
    local $service_restart = 1;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch233663 {
    local $patch = "patch-233663";
    local $re_versions = qr/^(8\.0\.0\.7)$/;
    local @deprecated_patches = hasPatch("patch-(232890|234237)");
    local $impacts = "During installation or removal of $patch, the following services will be stopped and restarted automatically: celog, isi_cpool_d, isi_cpool_io_d, isi_drive_repurpose_d, isi_job_d, isi_migrate, isi_ph_rpcd, isi_smartpools_d, ndmpd, isi_papi_d, auth, hdfs, isi_mcp, isi_tardis_gcfg_d, isi_tardis_d, lwsm, lwswift, nfs, smb";
    local $important = "The OneFS Protocols/Userspace Rollup patch, $patch, should be considered mandatory for every upgrade to OneFS 8.0.0.7.";
    local $service_restart = 1;
    local $priority = 10;
    checkPatch();
}
sub checkPatch8007HC {
    local $patch = 'HealthCheck_8.0.0.7_2020-02';
    local $re_versions = qr/^8\.0\.0\.7$/;
    local @deprecated_patches = hasPatch('^HealthCheck_8.0.0.7_(?:v|2019|2020-01)');
    local $impacts = "During installation or removal of $patch, the platform API service (isi_papi_d) will be restarted along with the healthcheck service.";
    local $important = "$patch requires that patch-240796 is first installed";
    local @requires = ('patch-240796');
    checkPatch();
}
sub checkPatch8007Kernel {
    local $patch = "patch-240795";
    local $re_versions = qr/^(8\.0\.0\.7)$/;
    local @conflicting_patches = hasPatch("patch-(233621)");
    local $service_restart = 1;
    local @reboot = @LNNS;
    local $priority = 5;
    checkPatch();
}
sub checkPatch8007Userspace {
    local $patch = "patch-240796";
    local $re_versions = qr/^(8\.0\.0\.7)$/;
    local @deprecated_patches = hasPatch("patch-(232890|233663|234237|236720|237180|240436|240514|241945)");
    local @conflicting_patches = hasPatch("HealthCheck_8.0.0.7_(v1.0|v2.0)|patch-(227974|242012|243995)");
    local $impacts = "During installation or removal of $patch, the following services will be stopped and restarted automatically: apache2, auth, celog, hdfs, isi_audit_cee, isi_audit_d, isi_audit_syslog, isi_cpool_d, isi_cpool_io_d, isi_drive_d, isi_drive_repurpose_d, isi_drivemon, isi_hangdump, isi_healthcheck, isi_job_d, isi_km_d, isi_mcp, isi_migrate, isi_papi_d, isi_ph_rpcd, isi_object_d, isi_smartpools_d, isi_smartquotas, isi_tardis_d, isi_tardis_gcfg_d, isi_webui_d, lwsm, lwswift, ndmpd, nfs, rpcbind, smb";
    local $service_restart = 1;
    local $priority = 10;
    checkPatch();
}
sub checkPatch8104Kernel {
    local $patch = "8.1.0.4_KGA-RUP_2019-09_256245";
    local $re_versions = qr/^(8\.1\.0\.4)$/;
    local @conflicting_patches = hasPatch("patch-(235853|241067)");
    local @deprecated_patches = hasPatch('^(8\.1\.0\.4|patch).*(228202|236488|236994|240815|242570|243892|244713|245399|246716|248573|249719|250863|252460|254526)$');
    local @reboot = @LNNS;
    local $priority = 5;
    local $important = "
Patch $patch includes a number of changes that may affect existing
configurations:

* ssh(1)/sshd(8): Drop compatibility support for some very old SSH
  implementations, including ssh.com <=2. and OpenSSH <= 3.. These
  versions were all released in or before 2001 and predate the final
  SSH RFCs. The support in question isn't necessary for RFC-compliant
  SSH implementations.

* ssh(1): Delete SSH protocol version 1 support, associated
  configuration options and documentation.

* ssh(1)/sshd(8): Remove support for the hmac-ripemd160 MAC.

* ssh(1)/sshd(8): Remove support for the arcfour, blowfish and CAST
  ciphers.

* Refuse RSA keys <1024 bits in length and improve reporting for keys
  that do not meet this requirement.

* ssh(1): Do not offer CBC ciphers by default.

* This patch deprecates the sshd_config UsePrivilegeSeparation
  option, thereby making privilege separation mandatory. Privilege
  separation has been on by default for almost 15 years and
  sandboxing has been on by default for almost the last five.
";
    checkPatch();
}
sub checkPatch8104Userspace {
    local $patches_with_svc_scripts = qr/patch-(222280|228133|231017|231712|231786|232742|233356|234345|234538|235862|236576|237861|239173|239649|240437|240815|241584|241946|242136|243890|243891|245397|245398|246957|247138|247848|249239)/;

    # GA RUP details
    local $patch = "8.1.0.4_UGA-RUP_2019-09_256243";
    local $re_versions = qr/^(8\.1\.0\.4)$/;
    local @conflicting_patches = hasPatch('^(8\.1\.0\.4|patch).*(228133|231712|232711|234538|235853|241067|242136|243891|245398|246667|246715|246957|247138|248572|248825|249239|249289|249718|250862|251180|252458|252459|254525|256244)$');
    local @deprecated_patches = hasPatch('^(8\.1\.0\.4|patch).*(222280|231017|233356|234345|239173|239649|241096|241584|241946|243890|245397|246714|247848|248571|249717|250861|254524)$');
    local $impacts = "During installation or removal of $patch, the following services will be stopped and restarted automatically: apache2, auth, celog, connectemc, dmilog, hdfs, ifsd, isi_audit_cee, isi_audit_d, isi_audit_syslog, isi_avscan_d, isi_boot_d, isi_cbind_d, isi_cpool_d, isi_cpool_io_d, isi_drive_d, isi_drive_repurpose_d, isi_drivemon, isi_esrs_d, isi_flexnet_d, isi_group_change_d, isi_hangdump, isi_hwmon, isi_job_d, isi_join_d, isi_km_d, isi_mcp, isi_migrate, isi_object_d, isi_papi_d, isi_ph_rpcd, isi_protohealth_d, isi_replicate, isi_rpc_d, isi_rsapi_d, isi_sasphymon, isi_smartconnect, isi_smartpools_d, isi_smartquotas, isi_snapshot_d, isi_stats_d, isi_stats_hist_d, isi_tardis_d, isi_tardis_gcfg_d, isi_upgrade_d, isi_vasa_d, isi_webui, lwsm, lwswift, ndmpd, nfs, pcnfsd, rpcbind, scheduler, smb, snmp, syslogd";
    local $service_restart = 2;
    local $priority = 10;

    # DA RUP details
    if (hasPatch('^(8\.1\.0\.4|patch).*(228133|231712|231786|232742|234538|235862|236576|237861|242136|243891|245398|246715|246957|247138|248572|249239|249718|250862|252458|252459|254525|256244)$')) {
	$patch = "8.1.0.4_UDA-RUP_2019-09_256244";
	@conflicting_patches = hasPatch('^(8\.1\.0\.4|patch).*(232711|235853|240437|241067|246667|248825|251180)$');
	@deprecated_patches = hasPatch('^(8\.1\.0\.4|patch).*(222280|228133|231017|231712|231786|232742|233356|234345|234538|235862|236576|237861|239173|239649|241096|241584|241946|242136|243890|243891|245397|245398|246714|246715|246957|247138|247848|248571|248572|249239|249717|249718|250861|250862|252458|252459|254524|254525)$');
	$impacts = "During the installation of removal of $patch, the following services are restarted: apache2, auth, celog, connectemc, cron, dmilog, gmond, hdfs, ifsd, isi_audit_cee, isi_audit_d, isi_audit_syslog, isi_avscan_d, isi_boot_d, isi_cbind_d, isi_cpool_d, isi_cpool_io_d, isi_drive_d, isi_drive_repurpose_d, isi_drivemon, isi_esrs_d, isi_flexnet_d, isi_group_change_d, isi_hangdump, isi_hwmon, isi_ipmi_d, isi_job_d, isi_join_d, isi_km_d, isi_mcp, isi_migrate, isi_object_d, isi_papi_d, isi_ph_rpcd, isi_protohealth_d, isi_replicate, isi_rpc_d, isi_rsapi_d, isi_sasphymon, isi_smartconnect, isi_smartpools_d, isi_smartquotas, isi_snapshot_d, isi_stats_d, isi_stats_hist_d, isi_tardis_d, isi_tardis_gcfg_d, isi_upgrade_d, isi_vasa_d, isi_webui, lwsm, lwswift, ndmpd, nfs, ntpd, pcnfsd, rpcbind, scheduler, smb, snmp, syslogd, xinetd";
    }
    checkPatch();
}
sub checkPatch810HC {
    local $patch = 'HealthCheck_8.1.0_2021-05';
    local $re_versions = qr/^8\.1\.0\.4$/;
    local @deprecated_patches = hasPatch('^HealthCheck_8.1.0_(?:v|2019|2020|2021-0[123])');
    checkPatch();
}
sub checkPatch8120DA {
    if (!hasPatch('^(?:patch|8\.1\.2).*[-_](?:43|103|221|367|405|482|525|644|727|825|864|998|1102|1195|239260|240798|243624|243896|245049|245401|245889|245974|246059|246712|248569|248876|249618|249621|249928|250807|251712|252456|254384|255591|256177|257611|257847|258683|259126|261109|262896|264011|264785|266228|269474|269974|270946|272855)$')) { return }
    local $patch = '8.1.2.0_DA-RUP_2021-06_PSP-1195';
    local $re_versions = qr/^8\.1\.2\.0$/;
    local @reboot = @LNNS;
    local $priority = 5;
    local @requires = ('8.1.2.0_UGA-PATCH-INFRA_2019-09_255624');
    # @deprecated left for use with patch infrastructure patch conflicts
    local @deprecated_patches = hasPatch('^(?:patch|8\.1\.2).*[-_](?:240794|243897|245402|246713|248570|249622|250808|252457|254360|256016)$');
    local @replaceable_patches = hasPatch('^(?:patch|8\.1\.2).*[-_](?:42|43|102|103|193|220|221|265|366|367|405|479|482|525|643|644|726|727|825|826|863|864|997|998|1101|1102|1194|236288|237113|237483|239260|239343|240163|240798|241940|241947|242854|243624|243894|243896|243912|245049|245400|245401|245889|245974|246059|246545|246711|246712|248568|248569|248876|249618|249620|249621|249770|249928|250806|250807|250901|251188|251712|251937|252455|252456|252507|254383|254384|254385|255591|256176|256177|256178|257610|257611|257612|257847|258052|258683|259125|259126|259127|260832|261108|261109|261110|262172|262895|262896|262897|264011|264566|264784|264785|264786|265016|265068|266227|266228|266229|266931|269352|269353|269473|269474|269475|269847|269974|270944|270946|272854|272855)$');
    local @conflicting_patches = hasPatch('^(?:patch|8\.1\.2).*[-_](?:222|241673|246754|249151|250131|251091|252360|262667|263651|268122|268621|269607|271537)$');
    checkPatch();
}
sub checkPatch8120GA {
    if (hasPatch('^(?:patch|8\.1\.2).*[-_](?:43|103|221|367|405|482|525|644|727|825|864|998|1102|1195|239260|240798|243624|243896|245049|245401|245889|245974|246059|246712|248569|248876|249618|249621|249928|250807|251712|252456|254384|255591|256177|257611|257847|258683|259126|261109|262896|264011|264785|266228|269474|269974|270946|272855)$')) { return }
    local $patch = '8.1.2.0_GA-RUP_2021-06_PSP-1194';
    local $re_versions = qr/^8\.1\.2\.0$/;
    local @reboot = @LNNS;
    local $priority = 5;
    local @requires = ('8.1.2.0_UGA-PATCH-INFRA_2019-09_255624');
    # @deprecated left for use with patch infrastructure patch conflicts
    local @deprecated_patches = hasPatch('^(?:patch|8\.1\.2).*[-_](?:240794|243897|245402|246713|248570|249622|250808|252457|254360|256016)$');
    local @replaceable_patches = hasPatch('^(?:patch|8\.1\.2).*[-_](?:42|102|193|220|265|366|479|643|726|826|863|997|1101|236288|237113|237483|239343|240163|241940|241947|242854|243894|243912|245400|246545|246711|248568|249620|249770|250806|250901|251188|251937|252455|252507|254383|254385|256176|256178|257610|257612|258052|259125|259127|260832|261108|261110|262172|262895|262897|264566|264784|264786|265016|265068|266227|266229|266931|269352|269353|269473|269475|269847|270944|272854)$');
    local @conflicting_patches = hasPatch('^(?:patch|8\.1\.2).*[-_](?:43|103|221|222|367|405|482|525|644|727|825|864|998|1102|1195|239260|240798|241673|243624|243896|245049|245401|245889|246059|246712|246754|248569|248876|249151|249618|249621|249928|250131|250807|251091|251712|252360|252456|254384|255591|256177|257611|257847|258683|259126|261109|262667|262896|263651|264011|264785|266228|268122|268621|269474|269607|269974|270946|271537|272855)$');
    checkPatch();
}
sub checkPatch8120Patch {
    local $patch = '8.1.2.0_UGA-PATCH-INFRA_2019-09_255624';
    local $re_versions = qr/^(8\.1\.2\.0)/;
    local $impacts = "During installation or removal of $patch, the following services will be stopped and restarted automatically: isi_papi_d";
    local $priority = 15;
    local @requires = ();
    my @conflicts = hasPatch('^(?:patch|8\.1\.2\.0).*(?:240794|243897|245402|246713|248570|248876|249622|250808|252457|252639|254360|256016)$');
    foreach my $p (@conflicts) {
	push(@requires, "uninstall $p");
    }
    if (!hasPatch('^8.1.2.0_UGA-PATCH-INFRA_')) { checkPatch() }

    $patch = '8.1.2.0_UGA-PATCH-INFRA_2020-08_PSP-268';
    $impacts = "During installation or removal of $patch, the following services will be stopped and restarted automatically: isi_papi_d";
    @requires = ('8.1.2.0_UGA-PATCH-INFRA_2019-09_255624');
    checkPatch();
}
sub checkPatch812HC {
    local $patch = 'HealthCheck_8.1.2_2021-05';
    local $re_versions = qr/^8\.1\.2\.0$/;
    local @requires = ('8.1.2.0_UGA-PATCH-INFRA_2019-09_255624');
    local @replaceable_patches = hasPatch('^HealthCheck_8.1.2_(?:v|2019|2020|2021-0[123])');
    checkPatch();
}
sub checkPatch813HC {
    local $patch = 'HealthCheck_8.1.3_2021-05';
    local $re_versions = qr/^8\.1\.3\.0$/;
    local @deprecated_patches = hasPatch('^HealthCheck_8.1.3_(?:2019|2020|2021-0[123])');
    checkPatch();
}
sub checkPatch813Kernel {
    local $patch = "patch-244941";
    local $re_versions = qr/^(8\.1\.3\.0)$/;
    local @reboot = @LNNS;
    local $service_restart = 1;
    local $priority = 5;
    checkPatch();
}
sub checkPatch813Userspace {
    local $patch = "patch-244942";
    local $re_versions = qr/^(8\.1\.3\.0)$/;
    local $impacts = "During installation or removal of $patch, the following services are restarted: apache2, auth, celog, hdfs, httpd, isi_audit_cee, isi_audit_d, isi_audit_syslog, isi_cpool_d, isi_cpool_io_d, isi_drive_repurpose_d, isi_drivemon, isi_group_change_d, isi_hangdump, isi_hwmon, isi_km_d, isi_mcp, isi_migrate, isi_object_d, isi_papi_d, isi_ph_rpcd, isi_rsapi_d, isi_smartpools_d, isi_tardis_d, isi_webui, lwsm, lwswift, ndmpd, nfs, smb";
    local @reboot = @LNNS;
    local $service_restart = 1;
    local $priority = 10;
    checkPatch();
}
sub checkPatch820GA {
    return if not isDestinationOneFS('8.2.0.0')
	and isCurrentOneFS('8.2.0.0')
	and $CLUSTER_INFO{'Build'} < 11;
    if (hasPatch('^8\.2\.0.*(?:250838|254106|254793|254798|255568|257480|257594|259123|261104|262893|264782)')) { return }
    local $patch = '8.2.0_GA-RUP_2020-06_273332';
    local $re_versions = qr/^8\.2\.0\.0$/;
    local @requires = ('8.2.0_UGA-PATCH-INFRA_2019-08_251917');
    local @replaceable_patches = hasPatch('^8\.2\.0.*(?:250837|250839|254105|254107|254527|254792|254807|254894|255567|255569|256081|256808|256855|256885|257593|257595|257853|258529|258860|259122|259124|259916|260818|261103|261105|262892|262894|264781|264783|267633|268444|269057|269435|269642)$');
    local @conflicting_patches = hasPatch('^8\.2\.0.*(?:250838|252451|252624|254106|254695|254793|254798|255568|257480|257594|259123|260301|260849|261104|262893|264647|264782|264803|266617|266990|270126|271477|271507)$');
    local $priority = 5;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch820HC {
    local $patch = 'HealthCheck_8.2.0_32.0.6';
    local $re_versions = qr/^8\.2\.0\.0$/;
    local @requires = ('8.2.0_UGA-PATCH-INFRA_2019-08_251917');
    local @replaceable_patches = hasPatch('^HealthCheck_8.2.0_(?:2019|202[01])');
    checkPatch();
}
sub checkPatch820Kernel {
    return if not isDestinationOneFS('8.2.0.0')
	and isCurrentOneFS('8.2.0.0')
	and $CLUSTER_INFO{'Build'} < 11;
    if (!hasPatch('^8\.2\.0.*(?:250838|254106|254793|254798|255568|257480|257594|259123|261104|262893|264782)')) { return }
    local $patch = '8.2.0_KGA-RUP_2020-03_270126';
    local $re_versions = qr/^8\.2\.0\.0$/;
    local @requires = ('8.2.0_UGA-PATCH-INFRA_2019-08_251917');
    local @replaceable_patches = hasPatch('^8\.2\.0.*(?:250839|254107|254527|255569|256808|257595|258529|258860|259124|260818|261105|262894|264783)');
    local @conflicting_patches = hasPatch('^8\.2\.0.*(?:252451|252624|254695|264803|267633|268444|269057)');
    local $priority = 5;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatch820Patch {
    if (hasPatch('8.2.0_UGA-PATCH-INFRA_2019-11_263084')) { return }
    local $patch = '8.2.0_UGA-PATCH-INFRA_2019-08_251917';
    local $re_versions = qr/^(8\.2\.0)/;
    local $impacts = "During installation or removal of $patch, the following services will be stopped and restarted automatically: isi_papi_d";
    local $priority = 15;
    return if not isDestinationOneFS('8.2.0.0')
	and isCurrentOneFS('8.2.0.0')
	and $CLUSTER_INFO{'Build'} < 11;
    checkPatch();
}
sub checkPatch820Userspace {
    return if not isDestinationOneFS('8.2.0.0')
	and isCurrentOneFS('8.2.0.0')
	and $CLUSTER_INFO{'Build'} < 11;
    if (!hasPatch('^8\.2\.0.*(?:250838|254106|254793|254798|255568|257480|257594|259123|261104|262893|264782)')) { return }
    local $patch = '8.2.0_UDA-RUP_2020-02_264782';
    local $re_versions = qr/^8\.2\.0\.0$/;
    local @requires = ('8.2.0_UGA-PATCH-INFRA_2019-08_251917');
    local @replaceable_patches = hasPatch('^8\.2\.0_.*(?:250837|250838|254105|254106|254792|254793|254798|254807|254894|255567|255568|256081|256855|256885|257480|257593|257594|257853|259122|259123|259916|261103|261104|262892|262893|264781)$');
    local @conflicting_patches = hasPatch('^8\.2\.0_.*(?:252624|254695|260301|264647|266617|266990)$');
    local $impacts = "During installation of removal of $patch, the following services will be stopped and restarted automatically: apache2, auth, celog, dmilog, hdfs, ifsd, isi_audit_cee, isi_audit_d, isi_audit_syslog, isi_avscan_d, isi_boot_d, isi_cbind_d, isi_comp_d, isi_cpool_d, isi_cpool_io_d, isi_drive_d, isi_drive_repurpose_d, isi_drivemon, isi_esrs_d, isi_flexnet_d, isi_group_change_d, isi_hardening_d, isi_healthcheck, isi_hwmon, isi_ipmi_d, isi_job_d, isi_join_d, isi_km_d, isi_lbfo_d, isi_mcp, isi_migrate, isi_object_d, isi_papi_d, isi_ph_rpcd, isi_protohealth_d, isi_rpc_d, isi_rsapi_d, isi_sasphymon, isi_smartconnect, isi_smartpools_d, isi_smartquotas, isi_snapshot_d, isi_stats_d, isi_stats_hist_d, isi_tardis_d, isi_tardis_gcfg_d, isi_upgrade_agent_d, isi_upgrade_d, isi_vasa_d, isi_vc_d, isi_webui, lwsm, lwswift, ndmpd, nfs, pcnfsd, scheduler, smb, snmp, syslogd, telnetd, xinetd";
    local $service_restart = 2;
    local $priority = 10;
    checkPatch();
}
sub checkPatch821DA {
    if (not isDestinationOneFS('8.2.1.0')
	and isCurrentOneFS('8.2.1.0')
	and $CLUSTER_INFO{'Build'} < 4) { return }
    if (!hasPatch('^8\.2\.1.*(?:50|62|72|260|261|274|275|310|590|424|259120|261101|261351|262890|264779|266225|266915|268537|270424|272583)$')) { return }
    local $patch = '8.2.1_DA-RUP_2021-01_PSP-590';
    local $re_versions = qr/^8\.2\.1\.0/;
    local @reboot = @LNNS;
    local $priority = 5;
    local @conflicting_patches = hasPatch('^8\.2\.1.*[-_](?:52|69|166|260302|266771|271509|273131|273703|273852)$');
    local @replaceable_patches = hasPatch('^8\.2\.1.*[-_](?:50|62|72|260|261|274|275|310|397|424|257607|259119|259120|259121|261100|261101|261102|261351|262889|262890|262891|264778|264779|264780|264877|265633|266224|266225|266226|266915|268536|268537|268538|269272|270423|270424|271249|271478|272580|272583)$');
    checkPatch();
}
sub checkPatch821GA {
    if (not isDestinationOneFS('8.2.1.0')
	and isCurrentOneFS('8.2.1.0')
	and $CLUSTER_INFO{'Build'} < 4) { return }
    if (hasPatch('^8\.2\.1.*(?:50|62|72|260|261|274|275|310|590|424|259120|261101|261351|262890|264779|266225|266915|268537|270424|272583)$')) { return }
    local $patch = '8.2.1_GA-RUP_2021-01_PSP-397';
    local $re_versions = qr/^8\.2\.1\.0/;
    local @reboot = @LNNS;
    local $priority = 5;
    local @conflicting_patches = hasPatch('^8\.2\.1.*[-_](?:50|52|62|69|72|166|260|261|274|275|310|424|259120|260302|261101|261351|262890|264779|266225|266771|266915|268537|270424|271509|272583|273131|273703|273852)$');
    local @replaceable_patches = hasPatch('^8\.2\.1.*[-_](?:257607|259119|259121|261100|261102|262889|262891|264778|264780|264877|265633|266224|266226|268536|268538|269272|270423|271249|271478|272580)$');
    checkPatch();
}
sub checkPatch821HC {
    local $patch = 'HealthCheck_8.2.1_32.0.6';
    local $re_versions = qr/^8\.2\.1\.0$/;
    local @replaceable_patches = hasPatch('^HealthCheck_8.2.1_(?:2019|202[01])');
    checkPatch();
}
sub checkPatch822DA {
    if (!hasPatch('8\.2\.2.*[-_](?:41|94|201|327|365|438|586|684|702|793|861|953|1032|1091|1150|1208|1243|1251|1308|1397|1493|1499|1687|258480|266222|267832|270135|271965)$')) { return }
    local $patch = '8.2.2_DA-RUP_2021-09_PSP-1687';
    local $re_versions = qr/^8\.2\.2\.0/;
    local $priority = 10;
    local @reboot = @LNNS;
    local @replaceable_patches = hasPatch('^8\.2\.2.*[-_](?:40|41|93|94|99|101|200|201|326|327|365|386|437|438|507|514|585|586|683|684|702|716|793|794|839|860|861|903|952|953|1032|1088|1091|1092|1149|1150|1208|1243|1250|1251|1308|1396|1397|1490|1493|1498|1499|258480|258514|266221|266222|266223|267822|267830|267832|270134|270135|271964|271965)$');
    local @conflicting_patches = hasPatch('^8\.2\.2.*[-_](?:382|735|821|922|1207|1491|270717|274246)$');
    checkPatch();
}
sub checkPatch822GA {
    if (hasPatch('8\.2\.2.*[-_](?:41|94|201|327|365|438|586|684|702|793|861|953|1032|1091|1150|1208|1243|1251|1397|1493|1499|258480|266222|267832|270135|271965)$')) { return }
    local $patch = '8.2.2_GA-RUP_2022-06_PSP-2240';
    local $re_versions = qr/^8\.2\.2\.0/;
    local $priority = 10;
    local @reboot = @LNNS;
    local @replaceable_patches = hasPatch('^8\.2\.2.*[-_](?:40|93|99|101|200|326|386|437|507|514|585|683|716|794|839|860|903|952|1088|1092|1149|1250|1396|1490|1498|1571|1572|2019|258514|266221|266223|267822|267830|270134|271964)$');
    local @conflicting_patches = hasPatch('^8\.2\.2.*[-_](?:41|94|201|327|365|382|438|586|684|735|793|821|861|953|1032|1091|1150|1207|1208|1243|1251|1308|1397|1491|1493|1499|1611|1612|1642|1681|1686|1687|1717|1775|1822|2285|258480|266222|267832|270135|271965)$');
    checkPatch();
}
sub checkPatch822HC {
    local $patch = 'HealthCheck_8.2.2_32.0.6';
    local $re_versions = qr/^8\.2\.2\.0$/;
    local @replaceable_patches = hasPatch('^HealthCheck_8.2.2_202[01]');
    checkPatch();
}
sub checkPatch900DA {
    if (!hasPatch('9\.0\.0.*[-_](?:68|171|278|407)$')) { return }
    local $patch = '9.0.0_DA-RUP_2020-11_PSP-407';
    local $re_versions = qr/^9\.0\.0\.0/;
    local $priority = 10;
    local @reboot = @LNNS;
    local @replaceable_patches = hasPatch('^9\.0\.0.*[-_](?:38|67|68|170|171|277|278|406)$');
    checkPatch();
}
sub checkPatch900GA {
    if (hasPatch('9\.0\.0.*[-_](?:68|171|278|407)$')) { return }
    local $patch = '9.0.0_GA-RUP_2020-11_PSP-406';
    local $re_versions = qr/^9\.0\.0\.0/;
    local $priority = 10;
    local @reboot = @LNNS;
    local @conflicting_patches = hasPatch('^9\.0\.0.*[-_](?:68|171|278|407)$');
    local @replaceable_patches = hasPatch('^9\.0\.0.*[-_](?:38|67|170|277)$');
    checkPatch();
}
sub checkPatch900HC {
    local $patch = 'HealthCheck_9.0.0_35.0.0';
    local $re_versions = qr/^9\.0\.0\.0$/;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.0\.0_(?:202[01]-[01][0123569]|3[234]\.0\.[056])');
    checkPatch();
}
sub checkPatch9100HC {
    local $patch = 'HealthCheck_9.10_43.2.0';
    local $re_versions = qr/^9\.10\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.10.0_[3][56789]\.[01234]\.[012]');
    #push(@replaceable_patches, ('HealthCheck_9.10.0_39.4.1','HealthCheck_9.10_40.0.0','HealthCheck_9.10_40.1.0','HealthCheck_9.10_40.2.0','HealthCheck_9.10_40.3.1','HealthCheck_9.10_41.1.0','HealthCheck_9.10_42.0.0','HealthCheck_9.10_42.1.0','HealthCheck_9.10_43.0.0','HealthCheck_9.10_43.1.1'));
    checkPatch();
}
sub checkPatch910HC {
    local $patch = 'HealthCheck_9.1.0_36.0.7';
    local $re_versions = qr/^9\.1\.0\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.1\.0_(?:202[01]-[01][0123569]|3[2345]\.0\.[056])');
    checkPatch();
}
sub checkPatch910Patch {
    local $patch = '9.1.0_UGA-PATCH-INFRA_2022-07_PSP-2682';
    local $re_versions = qr/^(9\.1\.0)/;
    local $priority = 15;
    local @replaceable_patches = hasPatch('_PSP-902$');
    checkPatch();
}
sub checkPatch9110HC {
    local $patch = 'HealthCheck_9.11_43.2.0';
    local $re_versions = qr/^9\.11\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.11_[4][01]\.[0123]\.[01]');
    push(@replaceable_patches, ('HealthCheck_9.11_42.0.0','HealthCheck_9.11_42.1.0','HealthCheck_9.11_43.0.0','HealthCheck_9.11_43.1.1'));
    checkPatch();
}
sub checkPatch9120HC {
    local $patch = 'HealthCheck_9.12_43.2.0';
    local $re_versions = qr/^9\.12\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.12_[4][1]\.[1]\.[0]');
    push(@replaceable_patches, ('HealthCheck_9.12_42.0.0','HealthCheck_9.12_42.1.0','HealthCheck_9.12_43.0.0','HealthCheck_9.12_43.1.1'));
    checkPatch();
}
sub checkPatch9130HC {
    local $patch = 'HealthCheck_9.13_43.2.0';
    local $re_versions = qr/^9\.13\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.13_[4][23]\.[01]\.[01]');
    checkPatch();
}
sub checkPatch9140HC {
    local $patch = 'HealthCheck_9.14_43.2.0';
    local $re_versions = qr/^9\.14\./;
    #local @replaceable_patches = hasPatch('^HealthCheck_9\.13_[4][23]\.[01]\.[01]');
    checkPatch();
}
sub checkPatch920HC {
    local $patch = 'HealthCheck_9.2.0_37.1.4';
    local $re_versions = qr/^9\.2\.0\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.2\.0_(?:2021-0[23569]|[3][234567]\.[0]\.[01567])');
    checkPatch();
}
sub checkPatch921HC {
    local $patch = 'HealthCheck_9.2.1_37.1.4';
    local $re_versions = qr/^9\.2\.1\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.2\.1_(?:2021-0[569]|[3][234567]\.0\.[01567])');
    checkPatch();
}
sub checkPatch921Patch {
    if (hasPatch('9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925')) { return }
    local $patch = '9.2.1_UGA-PATCH-INFRA_2022-06_PSP-1925';
    local $re_versions = qr/^(9\.2\.1\.)/;
    local $priority = 15;
    checkPatch();
}
sub checkPatch930HC {
    local $patch = 'HealthCheck_9.3.0_38.1.2';
    local $re_versions = qr/^9\.3\.0\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.3\.0_[3][2345678]\.[01]\.[014567]');
    checkPatch();
}
sub checkPatch940HC {
    local $patch = 'HealthCheck_9.4.0_40.0.0';
    local $re_versions = qr/^9\.4\.0\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.4\.0_[3][345678]\.[012]\.[012457]');
    push(@replaceable_patches, ('HealthCheck_9.4.0_39.0.0','HealthCheck_9.4.0_39.1.2','HealthCheck_9.4.0_39.2.1','HealthCheck_9.4.0_39.3.2','HealthCheck_9.4.0_39.4.1'));
    checkPatch();
}
sub checkPatch940Patch {
    local $patch = '9.4.0_UGA-PATCH-INFRA_2023-02_PSP-3186';
    local $re_versions = qr/^(9\.4\.0\.)/;
    local $priority = 15;
    local @replaceable_patches = hasPatch('_PSP-(?:2427|2787|2901|3037)$');
    checkPatch();
}
sub checkPatch940Tools {
    local $patch = '9.4.0_GA-TOOLS_2023-03_PSP-3323';
    local $re_versions = qr/^(?:9\.4\.0\.)/;
    local $priority = 1;
    checkPatch();
}
sub checkPatch950HC {
    local $patch = 'HealthCheck_9.5_43.2.0';
    local $re_versions = qr/^9\.5\.[01]\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.5\.?[0]?_[34][045678]\.[0123]\.[01247]');
     push(@replaceable_patches, ('HealthCheck_9.5_39.0.0','HealthCheck_9.5_39.1.2','HealthCheck_9.5_39.2.1','HealthCheck_9.5_39.3.2','HealthCheck_9.5_39.4.1','HealthCheck_9.5_40.0.0','HealthCheck_9.5_40.1.0','HealthCheck_9.5_40.2.0','HealthCheck_9.5_40.3.1','HealthCheck_9.5_41.1.0','HealthCheck_9.5_42.0.0','HealthCheck_9.5_42.1.0','HealthCheck_9.5_43.0.0','HealthCheck_9.5_43.1.1'));
    checkPatch();
}
sub checkPatch950Patch {
    local $patch = '9.5.0_UGA-PATCH-INFRA_2023-03_PSP-3206';
    local $re_versions = qr/^(9\.5\.0\.)/;
    local $priority = 15;
    checkPatch();
}sub checkPatch950Tools {
    local $patch = '9.5.0_UGA-TOOL_2023-04_PSP-2835';
    local $re_versions = qr/^(?:9\.5\.0\.)/;
    local $priority = 1;
    checkPatch();
}
sub checkPatch961HC {
    local $patch = 'HealthCheck_9.6.1_40.3.1';
    local $re_versions = qr/^9\.6\.1\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.6\.1_[3][567]\.[01]\.[0147]');
    push(@replaceable_patches, ('HealthCheck_9.6.1_39.0.0','HealthCheck_9.6_39.1.2','HealthCheck_9.6_39.2.1','HealthCheck_9.6_39.3.2','HealthCheck_9.6_39.4.1', 'HealthCheck_9.6.1_40.0.0', 'HealthCheck_9.6.1_40.1.0','HealthCheck_9.6.1_40.2.0'));
    checkPatch();
}
sub checkPatch970HC {
    local $patch = 'HealthCheck_9.7_43.2.0';
    local $re_versions = qr/^9\.7\.[01]\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.7_[3][5678]\.[012]\.[01247]');
    push(@replaceable_patches, ('HealthCheck_9.7_39.0.0','HealthCheck_9.7_39.1.2','HealthCheck_9.7_39.2.1','HealthCheck_9.7_39.3.2','HealthCheck_9.7_39.4.1','HealthCheck_9.7_40.0.0','HealthCheck_9.7_40.1.0','HealthCheck_9.7_40.2.0','HealthCheck_9.7_40.3.1','HealthCheck_9.7_41.1.0','HealthCheck_9.7_42.0.0','HealthCheck_9.7_42.1.0','HealthCheck_9.7_43.0.0','HealthCheck_9.7_43.1.1'));
    checkPatch();
}
sub checkPatch980HC {
    local $patch = 'HealthCheck_9.8_43.2.0';
    local $re_versions = qr/^9\.8\.[0]\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.8_[3][5678]\.[012]\.[01247]');
     push(@replaceable_patches, ('HealthCheck_9.8_39.0.0','HealthCheck_9.8_39.1.2','HealthCheck_9.8_39.2.1','HealthCheck_9.8_39.3.2','HealthCheck_9.8_39.4.1','HealthCheck_9.8_40.0.0','HealthCheck_9.8_40.1.0','HealthCheck_9.8_40.2.0','HealthCheck_9.8_40.3.1','HealthCheck_9.8_41.1.0','HealthCheck_9.8_42.0.0','HealthCheck_9.8_42.1.0','HealthCheck_9.8_43.0.0','HealthCheck_9.8_43.1.1'));
    checkPatch();
}
sub checkPatch990HC {
    local $patch = 'HealthCheck_9.9_43.2.0';
    local $re_versions = qr/^9\.9\.[0]\./;
    local @replaceable_patches = hasPatch('^HealthCheck_9\.9_[3][5678]\.[012]\.[01247]');
    push(@replaceable_patches, ('HealthCheck_9.9_39.0.0','HealthCheck_9.9_39.1.2','HealthCheck_9.9_39.2.1','HealthCheck_9.9_39.3.2','HealthCheck_9.9_39.4.1','HealthCheck_9.9_40.0.0','HealthCheck_9.9_40.1.0','HealthCheck_9.9_40.2.0','HealthCheck_9.9_40.3.1','HealthCheck_9.9_41.1.0','HealthCheck_9.9_42.0.0','HealthCheck_9.9_42.1.0','HealthCheck_9.9_43.0.0','HealthCheck_9.9_43.1.1'));
    checkPatch();
}
sub checkPatchCertRefresh {
    local $patch = '9.X_GA-RUP_2025-10_PSP-4737';
    local $re_versions = qr/^(9\.(5|7|8|9|10|11|12))/;
    local $priority = 0;
    #checkPatch() unless hasPatch('PSP-4737');
    if(
        ((isCurrentOneFS('>=9.5') and isCurrentOneFS('<9.5.1.5')) or 
        (isCurrentOneFS('>=9.7') and isCurrentOneFS('<9.7.1.11')) or 
        (isCurrentOneFS('=9.8') and isCurrentOneFS('<9.7.1.11')) or 
        (isCurrentOneFS('=9.9')) or 
        (isCurrentOneFS('>=9.10') and isCurrentOneFS('<9.10.1.4')) or
        (isCurrentOneFS('>=9.11') and isCurrentOneFS('<9.11.0.6')) or 
        (isCurrentOneFS('>=9.12') and isCurrentOneFS('<9.12.0.1')))
    ){
        $RPS{'Pre Upgrade Patches'}{$patch} = () unless hasPatch('PSP-4737');
    }
} # End checkPatchCertRefresh
sub checkPatchRelease {
    if (wasCalled()) { return }
    if (!isDestinationOneFS('>=9.1')) { return }
    local $target = isDestinationOneFS();
    if (!$Patch_Releases{$target}) { return }
    local $patch = $Patch_Releases{$target}{'GA-RUP'};

    # Extract from destination the release specs
    my ($major, $minor, $service) = split(/\./, $target);
    local $re_versions = qr/^$major\.$minor\.$service\./;

    # If only a patch upgrade and there are DA-RUP/CUSTOM/RESTRICTED patches
    if (isCurrentOneFS($re_versions) and
	hasPatch('DA-RUP|CUSTOM|RESTRICTED')) {

	# Exceptions for DA RUPs that are deprecated by the GA RUP
	if ((hasPatch('_PSP-945$') and  isDestinationOneFS('>=9.1.0.5')) or
	    (hasPatch('_PSP-(?:1022|1080)$') and isDestinationOneFS('>=9.1.0.6')) or
	    (hasPatch('_PSP-1110$') and isDestinationOneFS('>=9.1.0.9')) or
	    (hasPatch('_PSP-1289$') and isDestinationOneFS('>=9.1.0.10')) or
	    (hasPatch('_PSP-1523$') and isDestinationOneFS('>=9.1.0.11')) or
	    (hasPatch('_PSP-1608$') and isDestinationOneFS('>=9.1.0.12')) or
	    (hasPatch('_PSP-862$') and  isDestinationOneFS('>=9.1.0.18')) or
	    (hasPatch('_PSP-2319$') and isDestinationOneFS('>=9.1.0.19')) or
	    (hasPatch('_PSP-2366$') and isDestinationOneFS('>=9.1.0.20')) or
	    (hasPatch('_PSP-1693$') and isDestinationOneFS('>=9.1.0.23')) or
	    (hasPatch('_PSP-(?:645|665|770|843|929|1031|1038|1119|1249|1399|1497|1549|1651|1688|1694|1723|1727|1861|1920|1995|2078|2156|2243|2334|2470|2626|2696|2726)$') and isDestinationOneFS('>=9.1.0.24')) or
	    (hasPatch('_PSP-1312$') and isDestinationOneFS('>=9.2.0.1')) or
	    (hasPatch('_PSP-(?:1252|2241)$') and isDestinationOneFS('>=9.2.1.11')) or
	    (hasPatch('_PSP-2365$') and isDestinationOneFS('>=9.2.1.13')) or
	    (hasPatch('_PSP-2513$') and isDestinationOneFS('>=9.2.1.14')) or
	    (hasPatch('_PSP-(?:2703|2705)$') and isDestinationOneFS('>=9.2.1.15')) or
	    (hasPatch('_PSP-(?:1247|1395|1433|1526|1641|1699|1817|1965|2076|2105|2127|2192|2218|2316|2430|2611|2691|2786)$') and isDestinationOneFS('>=9.2.1.17')) or
	    (hasPatch('_PSP-(?:2374|2466|2503)$') and isDestinationOneFS('>=9.3.0.7')) or
	    (hasPatch('_PSP-2692$') and isDestinationOneFS('>=9.3.0.8')) or
	    (hasPatch('_PSP-(?:2332|2399|2577|2688|2790|2792|2864)$') and isDestinationOneFS('>=9.4.0.7'))) {
	} elsif (defined $Patch_Releases{$target}{'DA-RUP'}) {
	    $patch = $Patch_Releases{$target}{'DA-RUP'};
	}

    }
    local $priority = 10;
    local @reboot = @LNNS;
    checkPatch();
}
sub checkPatchSupportTools8x {
    local $patch = '8.X_GA-TOOLS_2021-05_PSP-1034';
    local $re_versions = qr/^(?:8\.1\.2|8\.2\.2)/;
    local $priority = 1;
    local @requires = ('8.1.2.0_UGA-PATCH-INFRA_2019-09_255624');
    local @replaceable_patches = hasPatch('PSP-(?:1034|587)$');
    checkPatch();

    # If support tools package PSP-587 is installed, it must be uninstalled
    # prior to an OneFS upgrade
    my ($tools_patch) = hasPatch('PSP-587$');
    if ($tools_patch and isCurrentOneFS('<9') and isOneFSUpgrade()) {
	critical("Prior to performing an OneFS upgrade, the support tools package, $tools_patch, must be uninstalled");
	$RPS{'Pre Upgrade Patches'}{"uninstall $tools_patch"} = ();
    }
}
sub checkPatchSupportTools9x {
    local $patch = '9.X_GA-TOOLS_2021-10_PSP-1695';
    local $re_versions = qr/^(?:9\.[0123]\.)/;
    local $priority = 1;
    local @replaceable_patches = hasPatch('PSP-(?:1306|1034|587)$');
    checkPatch();


    # If support tools package PSP-587 is currently installed, it must be
    # uninstalled prior to an OneFS upgrade, however, that would cause service
    # interruptions.  To avoid those interruptions, PSCALE-107422, the latest
    # support tools package should first be installed
    my ($tools_patch) = hasPatch('PSP-587');
    if ($tools_patch and isCurrentOneFS('>=9') and isOneFSUpgrade()) {
	critical("Prior to performing an OneFS upgrade, the support tools package, $tools_patch, must be replaced with the latest support tools package, $patch");
	$RPS{'Pre Upgrade Patches'}{$patch} = ();
    }
}
$CHECKS{'checkPatches'} = {
    'Description' => 'Checks for highly recommended patches, on the current version if no target version, or for the target version',
    'Reference' => 'https://www.dell.com/support/kbdoc/000184794',
};
$MSG{'checkPatches_NO_PREPATCH_3'} = 'A pre-patched OneFS install file is not available for OneFS %s. It will require an upgrade to OneFS %s followed by installation of %s';
sub checkPatches {
    if (wasCalled()) { return }
    checkKB521778();
    checkPartitions();
    printCheck('Highly Recommended Patches');
    gatherPatches();
    gatherNetworkInterfaces();

    my $SPF_ROOT_FIX = 'To avoid encountering errors during the installation of %s, the latest patch infrastructure patch, %s, must be installed prior.';
    my $SPF_ROOT_BAD = 'The following nodes have been identified as having less than 1GB of space allocated to the root [/] partition: %s.  These nodes may encounter errors during patch installs. '.sprintf($MSG{'KB_1'}, '000174169');
    my $SPF_PATCH_HIGH = 'For the patch installation of %s, there must be at least 700MB of available space in the /var partition and the following nodes do not meet this requirement: %s';
    my $SPF_PATCH_STD = 'For the patch installation of %s, there must be at least 200MB of available space in the /var partition and the following nodes do not meet this requirement: %s';
    my $MSG_PARALLEL = 'Parallel method for patch installs was requested, but is not available prior to OneFS 9.1 release, rolling method will be used';
    my $MSG_94_NO_INFRA = 'Do not perform any Roll-Up Patch installs or uninstalls without first installing the latest Patch Infrastructure Patch. Failure to do so will result in node failures. '.sprintf($MSG{'KB_INT_1'}, '000204806');

    # Display all installed patches
    my @patches = hasPatch();
    if (@patches) { details("Installed Patches: @patches") }

    # Check for partially installed patches
    checkPatchesPartial();

    # Check patches reporting a status other than Installed
    checkPatchesNotInstalled();

    # Check if any temporary patches are installed
    checkPatchesTemporary();

    # Check if any of the EncodingCheck job patches are installed
    checkPatchesEncodingCheck();

    # Check if Patch 208294 is installed but missing /lib/libcrypto.so.8.patched
    checkKB520799();

    # Check if any patches are listed as installing
    checkPatchesInstalling();

    # Check if running OneFS 9.4 and the patch infrastructure patch is installed
    if (!isOneFSUpgrade() and
	isCurrentOneFS(qr/^9\.4\.0\./) and
	!hasPatch('UGA-PATCH-INFRA')) {
	critical($MSG_94_NO_INFRA);
    }

    # Do not make patch recommendations when called via HealthCheck
    if ($RPS{'Patches'} eq 'None') {
	printResults();
	return;
    }

    # Support tools patches
    checkPatchCertRefresh();
    checkPatch950Tools();
    checkPatch940Tools();
    checkPatchSupportTools9x();
    checkPatchSupportTools8x();

    # Patch infrastructure patches
    checkPatch950Patch();
    checkPatch940Patch();
    checkPatch921Patch();
    checkPatch910Patch();
    checkPatch820Patch();
    checkPatch8120Patch();

    # HealthCheck patches
    checkPatch9140HC();
    checkPatch9130HC();
    checkPatch9120HC();
    checkPatch9110HC();
    checkPatch9100HC();
    checkPatch990HC();
    checkPatch980HC();
    checkPatch970HC();
    checkPatch961HC();
    checkPatch950HC();
    checkPatch940HC();
    checkPatch930HC();
    checkPatch921HC();
    checkPatch920HC();
    checkPatch910HC();
    checkPatch900HC();
    checkPatch822HC();
    checkPatch821HC();
    checkPatch820HC();
    checkPatch813HC();
    checkPatch812HC();
    checkPatch810HC();
    checkPatch8007HC();

    # Roll up patches
    checkPatchRelease();
    checkPatch900DA();
    checkPatch900GA();
    checkPatch822GA();
    checkPatch822DA();
    checkPatch821GA();
    checkPatch821DA();
    checkPatch820GA();
    checkPatch820Kernel();
    checkPatch820Userspace();
    checkPatch813Userspace();
    checkPatch813Kernel();
    checkPatch8120GA();
    checkPatch8120DA();
    checkPatch8104Userspace();
    checkPatch8104Kernel();
    checkPatch8007Userspace();
    checkPatch8007Kernel();
    checkPatch231386();
    checkPatch229160();
    checkPatch229158();
    checkPatch229156();
    checkPatch228458();
    checkPatch226984();
    checkPatch226323();
    checkPatch224116();
    checkPatch223338();
    checkPatch223311();
    checkPatch222487();
    checkPatch221949();
    checkPatch221947();
    checkPatch221603();
    checkPatch221602();
    checkPatch220176();
    checkPatch220098();
    checkPatch219279();
    checkPatch213287();
    checkPatch213286();
    checkPatch206322();
    checkPatch189133();
    checkPatch189132();
    checkPatch188660();
    checkPatch188239();
    checkPatch181490();
    checkPatch179385();
    checkPatch178793();
    checkPatch175451();
    checkPatch164666();
    checkPatch161166();
    checkPatch147686();
    checkPatch146974();


    if ($RPS{'Post Upgrade Patches'}) {
	my ($patch_rup) = grep(/_[DG]A-RUP_/, keys %{$RPS{'Post Upgrade Patches'}});

	my $direct_to_prepatch = (isOneFSUpgrade() and $Patched_OneFS{isDestinationOneFS()})?1:0;

	if ($patch_rup and
	    isDestinationOneFS('>=9.1') and
	    isOneFSUpgrade() and
	    !$Patched_OneFS{isDestinationOneFS()})
	{   # No pre-patched OneFS install file for the requested patch release

	    # Compile a list of in-family pre-patched releases
	    my ($major, $minor, $service) = split(/\./, isDestinationOneFS());
	    my $re_versions = qr/^$major\.$minor\.$service\./;
	    my @destinations = merge(keys %Patched_OneFS);
	    @destinations = grep {$_ =~ $re_versions and isDestinationOneFS(">=$_")} @destinations;
	    @destinations = sort {compareVersions($b, $a)} @destinations;
	    # Set the destination version to the highest in-family patch release less than the requested destination version
	    my $new_target_onefs;
	    if (@destinations) {
		$new_target_onefs = $destinations[0];
	    } else {
		$new_target_onefs = "$major.$minor.$service.0";
	    }
	    addCustomerInformation('Upgrade Plan',
				   critical(sprintf($MSG{'checkPatches_NO_PREPATCH_3'},
						    isDestinationOneFS(), $new_target_onefs, $patch_rup)));
	    $RPS{'Target'} = $new_target_onefs;
	}

	if ($patch_rup and !$direct_to_prepatch) {

	    # High or standard patch /var partition available space requirement check
	    if ((isDestinationOneFS(qr/^9\.3\.0\./) and isDestinationOneFS('>=9.3.0.2')) or
		(isDestinationOneFS(qr/^9\.2\.1\./) and isDestinationOneFS('>=9.2.1.8')) or
		(isDestinationOneFS(qr/^9\.1\.0\./) and isDestinationOneFS('>=9.1.0.15'))) {
		if (($RPS{'ALL_VARS_EXPANDED'} or isCompliance()) and $RPS{'RQR_PATCH_HIGH'}) {
		    fail(sprintf($SPF_PATCH_HIGH, $patch_rup, compressRange(@{$RPS{'RQR_PATCH_HIGH'}})));
		}
	    } else {
		if ($RPS{'RQR_PATCH_STD'}) {
		    fail(sprintf($SPF_PATCH_STD, $patch_rup, compressRange(@{$RPS{'RQR_PATCH_STD'}})));
		}
	    }

	    # Check if applying patches and nodes have less than 1GB allocated to /
	    if ($RPS{'Root less than 1GB'}) {
		if (isCurrentOneFS('8.1.2.0')) {
		    my $infra_patch = '8.1.2.0_UGA-PATCH-INFRA_2020-08_PSP-268';
		    if (!hasPatch($infra_patch)) {
			critical(sprintf($SPF_ROOT_FIX, $patch_rup, $infra_patch));
		    }
		} elsif (isCurrentOneFS(qr/^9\.1\.0/)) {
		    my $infra_patch = '9.1.0_UGA-PATCH-INFRA_2021-12_PSP-902';
		    if (!hasPatch($infra_patch)) {
			critical(sprintf($SPF_ROOT_FIX, $patch_rup, $infra_patch));
		    }
		} elsif (isCurrentOneFS(qr/^(?:8\.2|9\.0)\./)) {
		    fail(sprintf($SPF_ROOT_BAD, compressRange(keys %{$RPS{'Root less than 1GB'}})));
		}
	    }
	}

    if (isCurrentOneFS(qr/^9\.1\.0/)) {
		    my $infra_patch = '9.1.0_UGA-PATCH-INFRA_2021-12_PSP-902';
		    if (!hasPatch($infra_patch)) {
			critical(sprintf($SPF_ROOT_FIX, $patch_rup, $infra_patch));
		    }
    }
    #$RPS{'Pre Upgrade Patches'}{'9.X_GA-RUP_2025-10_PSP-4737'}{'Impacts'} = '' if !hasPatch('9.X_GA-RUP_2025-10_PSP-4737');

	if (!isOneFSUpgrade()) {
	    # The patches should be Pre Upgrade Patches, not Post Upgrade Patches.
	    if (!$RPS{'Pre Upgrade Patches'}) {
		$RPS{'Pre Upgrade Patches'} = $RPS{'Post Upgrade Patches'};
	    } else {
		foreach my $patch (keys %{$RPS{'Post Upgrade Patches'}}) {
		    next if $RPS{'Pre Upgrade Patches'}{$patch};
		    $RPS{'Pre Upgrade Patches'}{$patch} = $RPS{'Post Upgrade Patches'}{$patch};
		}
	}
	    delete $RPS{'Post Upgrade Patches'};

	    # Flag that parallel was requested but not available
	    if (isCurrentOneFS('<9.1') and
		exists $RPS{'Patch'}{'Method'} and
		$RPS{'Patch'}{'Method'} eq 'parallel') {
		warning($MSG_PARALLEL);
		$RPS{'Patch'}{'Method'} = 'rolling';
	    }
	}
    }

    printResults();
} # End checkPatches
# checkPatchesEncodingCheck: Checks for EncodingCheck job patches installed
sub checkPatchesEncodingCheck {
    if (wasCalled()) { return }

    # Setup
    my $SPF_ENCODING =
	'Prior to performing an OneFS upgrade, patch %s, must be uninstalled';

    # Analyze
    my ($encoding_patch) =
	hasPatch('patch-(185213|185212|185211|185210|185209|185207|185204'.
		 '|185203|176073|176331|176068|176332|176069|176334|176335'.
		 '|176336|176236|176070|176461|176460|176459|176458)');

    # Flag
    if (isDestinationOneFS() and $encoding_patch) {
	critical(sprintf($SPF_ENCODING, $encoding_patch));
    }

} # End checkPatchesEncodingCheck
# checkPatchesInstalling: Checks if any patches are installing
sub checkPatchesInstalling {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LIVE =
	qq[2>&1 ${SUDO} isi_gather_cluster_info -p].
	q[| awk "/'installing'/,/\]/ {print}" ];
    my $CMD_LOGS =
	q[ 2>&1 awk '/<installing>/,/<\/installing>/ {print}' ].
	q[ local/node_info_cache.tar/ifs/_ifsvar/db/node_info_cache/*/*.xml ];
    my $CMD_LOGS2 = q[2>&1 grep -EH '/var/patch/tmp/[0-9a-f]+$' */du ];
    my $SPF_PATCHES =
	q[The following patches were identified in isi_gather_cluster_info].
	q[ -p as installing: %s];

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my %patches_installing = ();
    foreach my $line (@$gather) {
	my @patches = ($line =~ /'([^']+)'/g);
	foreach my $patch (@patches) {
	    if ($patch eq 'installing') { next }
	    if (hasPatch($patch)) { next }
	    $patches_installing{$patch} = 1;
	}
    }

    # In case of a false positive in logs, check elsewhere
    if (!isLive() and %patches_installing) {
	$gather = gatherCluster($CMD_LOGS2);
	if (!%$gather) { %patches_installing = () }
    }

    # Flag
    if (%patches_installing) {
	fail(sprintf($SPF_PATCHES, join(', ', sort keys %patches_installing)));
    }

} # End checkPatchesInstalling
# checkPatchesNotInstalled: Check for patches not in an Installed status
sub checkPatchesNotInstalled {
    if (wasCalled()) { return }

    # Setup
    my $SPF_NOT_INSTALLED = 'The following patches are reporting %s: %s';

    # Analyze
    my %not_installed = ();
    foreach my $patch (hasPatch()) {
	if (!$CLUSTER_INFO{'Patches'}{$patch}{'Status'}) { next }
	my $status = $CLUSTER_INFO{'Patches'}{$patch}{'Status'};
	if ($status ne 'Installed') {
	    push(@{$not_installed{$status}}, $patch);
	}
    }

    # Flag
    if (%not_installed) {
	foreach my $status (keys %not_installed) {
	    fail(sprintf($SPF_NOT_INSTALLED, $status,
			 join(', ', @{$not_installed{$status}})));
	}
    }

} # End checkPatchesNotInstalled
# checkPatchesPartial: Check for partially installed patches
sub checkPatchesPartial {
    if (wasCalled()) { return }

    # Setup
    my $SPF_PARTIAL =
	'The following patches are not installed on all nodes: %s';

    # Analyze
    my $all_nodes = compressRange(@LNNS);
    my @partially_installed = ();
    foreach my $patch (hasPatch()) {
	if (!$CLUSTER_INFO{'Patches'}{$patch}{'Nodes'}) { next }
	my @nodes = @{$CLUSTER_INFO{'Patches'}{$patch}{'Nodes'}};
	my $patched_nodes = compressRange(@nodes);
	if ($patched_nodes ne $all_nodes) {
	    push(@partially_installed, "$patch ($patched_nodes)");
	}
    }

    # Flag
    if (@partially_installed) {
	fail(sprintf($SPF_PARTIAL, join(', ', @partially_installed)));
    }

} # End checkPatchesPartial
# checkPatchesTemporary: Checks if any 'temporary' patches are installed
sub checkPatchesTemporary {
    if (wasCalled()) { return }

    # Setup
    my $SPF_TMP_PATCHES = 'The following packages should be uninstalled: %s';

    # Analyze
    my @temporary_patches = hasPatch('IsiFru_Package|IsiHw_Package'.
				     '|Drive_Support|Drive_Firmware|IsiFw');

    # Flag
    if (@temporary_patches) {
	critical(sprintf($SPF_TMP_PATCHES, join(', ', @temporary_patches)));
    }

} # End checkPatchesTemporary
$CHECKS{'checkSymLink'} = {
    'Description' => 'Checks is /var/patch/catalog or /var/patch/tmp are Sym Linked or if catalog is a file instead of directory.',
    'Reference' => '',
};
# CRITICAL MSG
$MSG{'checkSymLink_1'} = 'Checks Have Identified that /var/patch/catalog or /var/patch/tmp are Sym Linnked on the following nodes: %s';

sub checkSymLink {
    if (wasCalled()) { return }
    if (!isLive()) { return }
    if (isCurrentOneFS('<9.1.0')) { return }
    
    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array "ls -al /var/patch/ | grep -v total"];
    my $gather = gatherCluster(isLive()?$CMD_LIVE:return);
 
    #my $CMD_LIVE_dir = qq[2>&1 ${SUDO} isi_for_array "ls -al /var/patch/ | grep -E \'^d.*catalog$\'"];
    #my $gather_dir = gatherCluster(isLive()?$CMD_LIVE_dir:return);

    my $CMD_LIVE_ifsvar = qq[ls -led /ifs/.ifsvar];
    my $gather_ifsvar = gatherLocal(isLive()?$CMD_LIVE_ifsvar:return);

    printCheck('Patch SymLink & catalog');
    my $fail_list;
    # Gather
    my @nodes__sf = ();
    my @nodes_file = ();
    my @nodes_missing_dir = ();
    my @nodes_perm_issue = ();
    my $ifsvar_perm_issue;
    foreach my $line (@$gather_ifsvar) {
        #next unless ($line =~ /^\s*d[rwx-]+.*(\/ifs\/.ifsvar)\s*$/);
        debug("Directory details: $line\n");
        if($line =~ /^\s*(OWNER|GROUP)\s*(user|group):(\S+)\s*$/){
            $group_assigned = $3;
            if($1 eq 'OWNER' && $3 ne 'root'){
                debug("/ifs/.ifsvar shows incorrect owner assignment, please verify ownership.");    
            }
            if($1 eq 'GROUP' && ($3 ne 'wheel' || $3 ne 'root')){
                debug("/ifs/.ifsvar shows incorrect group assignment, please verify ownership.");
            }
        }
        if($line =~ /^\s*(CONTROL|SYNTHETIC\sACL)\s*.*$/){
            if($1 ne 'SYNTHETIC ACL' and isCurrentOneFS('<9.5')){
                $ifsvar_perm_issue = 1;
                debug("/ifs/.ifsvar directory shows a non Synthetic ACL, Please verify permissions and ownership are correct.\n");
            }
        }
        if ($line =~ /^\s*d[rwx-]+.*(\/ifs\/.ifsvar)\s*$/ && $line !~ /^s*(drwxrwx)[rwx-]+\s*(\+?).*\/ifs\/.ifsvar\s*$/){
            if(isCurrentOneFS('>=9.4')){
                $ifsvar_perm_issue = 1;
                debug("/ifs/.ifsvar does not show correct permissions, expected permissions is 770, current permissions are $1$2\n");
            }
        }
	}

    foreach my $lnn (@LNNS) {
        if (!@{$gather->{$lnn}} || @{$gather->{$lnn}} eq ''){ push(@nodes_missing_dir, $lnn); };
        foreach $l (@{$gather->{$lnn}}){
            if ($l =~ /^\s*l.*(catalog|tmp)$/){
                push(@nodes_sf, $lnn);
            }
            if($l =~ /^\s*-.*\s*catalog$/){
                #catalog is file
                push(@nodes_file, $lnn);
            }
            if($l =~ /^\s*d.*(catalog|tmp)$/){
                debug("FOUND: $l\n");
                if($l =~ /^\s*d.*\s*catalog$/ && $l !~ /^\s*drwxr.*\s*catalog$/){
                    debug("Issues on: $l for node $lnn\n");
                    push(@nodes_perm_issue, $lnn);
                }
                if($l =~ /^\s*d.*\s*tmp$/ && $l !~ /^\s*drwxr?.*\s*tmp$/){
                    debug("Issues on: $l for node $lnn\n");
                    push(@nodes_perm_issue, $lnn);
                }
            }
            if($l =~ /^.*\s*catalog$/ && $l !~ /^\s*d.*\s*catalog$/){
                push(@nodes_missing_dir, $lnn);
            }
        }
    }
    # Flag
    if (@nodes_sf || @nodes_file || @nodes_missing_dir || @nodes_perm_issue || $ifsvar_perm_issue) {
        if(@nodes_sf){
            addCustomerInformation(critical('Checks have identified sym linked files / directories on /var/patch on nodes: '.compressRange(@nodes)));
            info('If you are not currently performing patch install, please follow step 3 and 4 on \'Proactive Workaround section\' within KB 000201666 (https://www.dell.com/support/kbdoc/en-ie/000201666) to unlink the files / directories.');
        }
        if(@nodes_missing_dir){
            addCustomerInformation(critical('Checks have identified that the catalog directory is missing on the following nodes: '.compressRange(@nodes_missing_dir)));
            info('Please follow step 4 within KB 000201666 (https://www.dell.com/support/kbdoc/en-ie/000201666) to create the catalog directory on the affected nodes.');
        }
        if(@nodes_file){
            addCustomerInformation(fail('Checks have identified \'catalog\' within /var/patch/ as being a file and not a directory, Node affected: '.compressRange(@nodes_filecat)));
            info('Please engage Isilon Support to remediate this.');
        }
        if(@nodes_perm_issue){
            addCustomerInformation(fail('Checks have identified permission issues on /var/patch directories that are required for patch installations: '.compressRange(@nodes_perm_issue)));
            info('Please follow step 4 on \'Proactive Workaround section\' within KB 000201666 (https://www.dell.com/support/kbdoc/en-ie/000201666) to correct the folder permissions.');
        }
        if($ifsvar_perm_issue){
            fail('Checks have identified permission issues on /ifs/.ifsvar directory that is required for update / upgrade and installation processes, expected permissions for this directory should be at least 770 with Synthetic ACL and owner/group of root:wheel.');
            info('Please ensure this directory is correctly configured to avoid issues.');
        }
        
    }
     
     printResults();
 } # End checkSymLink
###
# gatherDefinitionsCluster9: Gathers a list of installed Healthcheck Definitions.
sub gatherDefinitionsCluster9 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<9.5')) {
	debug("gatherDefinitionsCluster9: Skipped, version prior to OneFS 9.5");
	return;
    }

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
        if(isCurrentOneFS('<9.10')){
	        $gather = gatherLocal(qq[2>&1 isi ${SUDO} healthcheck definitions list -v | grep 'Package Name:\\|Status']);
        }
        else{
             $gather = gatherLocal(qq[2>&1 isi healthcheck definitions list -v | grep 'Package Name:\\|Status']);
        }
    } else {
	$gather = gatherLocal(qq[2>&1 jq . local/ifsvar_modules.tar/modules/health-check/results/cluster/healthcheck_version.0x0 | grep 'HC version']);
    }

    my $RE_NAME = qr/(?:Package\sName:|\"?HC\sversion)\s+([a-zA-Z0-9_\.]+)\"?,?.*$/;
    my $RE_STATUS = qr/Status:\s+(\S.*)$/;

    my $patch = '';
    foreach my $line (@$gather) {
        if (isLive()){
            if ($line =~ $RE_NAME) { $patch = $1 }
            if ($patch and $line =~ $RE_STATUS) {
                $CLUSTER_INFO{'Patches'}{$patch}{'Status'} = $1;
                debug("CLUSTER_INFO=>Patches=>${patch}=>Status=$1");
            }
        }
        else{

            if ($line =~ $RE_NAME) { 
                $patch = $1;
                $cvf = isCurrentOneFS();
                $cv = $1 if $cvf =~ /^(\d+\.\d+\.\d+).*/;
                $cvl = $1 if $cvf =~ /^(\d+\.\d+).*/;
                if(
                    (
                        isCurrentOneFS('<9.5') or 
                        (
                            isCurrentOneFS('>=9.6') and isCurrentOneFS('<9.7')
                        ) 
                        or 
                            isCurrentOneFS('>= 9.7')
                    )
                ){
                    $patch = 'HealthCheck_'.$cv.'_'.$patch;    
                }
                else{
                    $patch = 'HealthCheck_'.$cvl.'_'.$patch;
                }
            }
            $CLUSTER_INFO{'Patches'}{$patch}{'Status'} = 'Installed';
            $CLUSTER_INFO{'Has_HCF'} = $patch;
            debug("CLUSTER_INFO=>Patches=>${patch}=>Status=Installed");
            
        }
    }

} # End gatherDefinitionsCluster9
###
# gatherPatches: Gathers a list of installed patches.
sub gatherPatches {
    return if wasCalled();
    gatherDefinitionsCluster9();
    gatherPatchesLocal8();
    gatherPatchesCluster8();
    gatherPatchesLocal7();
} # End gatherPatches
###
# gatherPatchesCluster8: Gathers a list of installed patches.
sub gatherPatchesCluster8 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) { return }

    # Setup
    my $CMD_GREP = q[egrep '^ *(Patch Name|Status):'];
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi upgrade patches list -v | $CMD_GREP];
    $CMD_LIVE = qq[2>&1 isi upgrade patches list -v | $CMD_GREP] if isCurrentOneFS('>=9.10');
    my $CMD_LOGS = qq[2>&1 $CMD_GREP local/patch_list];
    my $RE_NAME = qr/Patch Name:\s+(\S.*)$/;
    my $RE_STATUS = qr/Status:\s+(\S.*)$/;

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Parse
    my $patch = '';
    foreach my $line (@$gather) {
	if ($line =~ $RE_NAME) { $patch = $1 }
	if ($patch and $line =~ $RE_STATUS) {
	    $CLUSTER_INFO{'Patches'}{$patch}{'Status'} = $1;
	    debug("CLUSTER_INFO=>Patches=>${patch}=>Status=$1");
	}
    }
} # End gatherPatchesCluster8
###
# gatherPatchesLocal7: Gathers a list of installed patches.
sub gatherPatchesLocal7 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8')) {
	debug("gatherPatchesLocal7: Skipping. OneFS version 8.0 or later");
	return;
    }

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	# if 9.9 or higher then do isi upgrade patches list --local
    # else do below
    $gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array "isi pkg info --forced_local | grep '^[[:alnum:]]'"]);
    } else {
	$gather = gatherCluster(qq[2>&1 grep '^[[:alnum:]]' */packages_local]);
    }

    # Assign CLUSTER_INFO based on gather
    my %patches = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	foreach my $line (@{$gather->{$lnn}}) {
	    next unless $line =~ /^\s*(\S+):/;
	    push(@{$patches{$1}}, $lnn);
	}
    }
    foreach my $patch (keys %patches) {
	@{$CLUSTER_INFO{'Patches'}{$patch}{'Nodes'}} = @{$patches{$patch}};
	debug("CLUSTER_INFO=>Patches=>${patch}=".join(",", @{$CLUSTER_INFO{'Patches'}{$patch}{'Nodes'}}));
    }
} # End gatherPatchesLocal7
###
# gatherPatchesLocal8: Gathers a list of installed patches.
sub gatherPatchesLocal8 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) {
	debug("gatherPatchesLocal8: Skipped, version prior to OneFS 8");
	return;
    }

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array isi upgrade patches list -v --local | grep -E 'Patch Name:']);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -E 'Patch Name:' */patch_list_local]);
    }

    # Assign CLUSTER_INFO based on gather
    my %patches = ();
    foreach my $lnn (@LNNS) {
	foreach my $line (@{$gather->{$lnn}}) {
	    next unless $line =~ /^\s*Patch Name: (\S+)/;
	    push(@{$patches{$1}}, $lnn);
	}
    }
    foreach my $patch (keys %patches) {
	@{$CLUSTER_INFO{'Patches'}{$patch}{'Nodes'}} = @{$patches{$patch}};
	debug("CLUSTER_INFO=>Patches=>${patch}=".join(",", @{$CLUSTER_INFO{'Patches'}{$patch}{'Nodes'}}));
    }
} # End gatherPatchesLocal8
###
# hasPatch: Checks if the cluster has installed a patch that matches the regex
sub hasPatch {
    my ($regex, $lnn) = @_;

    # Gather patches
    gatherPatches();

    # Return an empty list if there are no patches
    if (!$CLUSTER_INFO{'Patches'}) { return () }

    # Generate the list of patches
    my @patches = sort keys %{$CLUSTER_INFO{'Patches'}};

    # Return all patches if no regex
    if (!$regex) { return @patches }

    # Generate the list of patches that match the regex
    my @matched_patches = grep /$regex/, @patches;

    # Return patches that match the provided regex if no lnn
    if (!$lnn) { return @matched_patches }

    # Generate the list of patches for the requested lnn that match the regex
    my @lnn_matched_patches = ();
    foreach my $pkg (@matched_patches) {
	if (!$CLUSTER_INFO{'Patches'}{$pkg}{'Nodes'}) { next }
	my @nodes = @{$CLUSTER_INFO{'Patches'}{$pkg}{'Nodes'}};
	my ($match) = grep {$_ eq $lnn} @nodes;
	if ($match) { push(@lnn_matched_patches, $pkg) }
    }
    return @lnn_matched_patches;

} # End hasPatch
$CHECKS{'checkCloudPools'} = {
    'Description' => 'Checks for CloudPools related issues',
    'Exclude' => 1,
};
sub checkCloudPools {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) {	return unsupported('UNSUP_CUR') }
    if (!hasLicense('CloudPools')) {
	return unsupported('UNSUP_LIC_1', 'CloudPools');
    }
    checkSyncIQ();
    printCheck('CloudPools');
    my $MSG_CPKB = 'When using CloudPools, there are additional steps required'.
      ' by customer alongside an upgrade to OneFS 8.2.2 and later.  '.
      sprintf($MSG{'KB_1'}, '000020736');
    my $MSG_SYNCIQ = 'It is strongly recommended that the OneFS upgrade to be'.
	' committed and the CloudPools conversion process started on Target'.
	' cluster before committing the OneFS upgrade on the Source cluster.'.
	' This allows SyncIQ to send CP1.0 formatted stub files from the'.
	' Source cluster to the Target cluster, where they will be converted'.
	' into CP2.0 formatted stub files.';
    my $SPF_CP_LOW_DEST = 'The provided destination version, OneFS %s, is lower'.
	' than the minimum required when using CloudPools, OneFS 8.2.2 or later.';

	checkKB000217579();

    if (hasCloudPools()) {
		checkCloudPoolsCOIDump();
		checkCloudPoolsSmartLink();
		if ($RPS{'Target'} and isCurrentOneFS('<8.2') and compareVersions($RPS{'Target'}, '8.2') >= 0) {
			if (compareVersions($RPS{'Target'}, '8.2.2') < 0) {
				fail(sprintf($SPF_CP_LOW_DEST, $RPS{'Target'}));
			}
			addCustomerInformation('CloudPools', warning($MSG_CPKB));
			checkCloudPoolsCerts();
			checkCloudPoolsNANON();
			if ($RPS{'SyncIQ'}) {
				addCustomerInformation('CloudPools', $MSG_SYNCIQ);
			}
		}
		if($RPS{'Target'} and isDestinationOneFS('>=9.5.0.0') and isDestinationOneFS('<9.5.0.8')){
			if(isCurrentOneFS('<9.5')){
			fail("Please consider upgrading to 9.5.0.8 or higher which include the latest fixes, some of which are critical to clusters with cloudpools.");
			}
			else{
			warning("Please consider upgrading to 9.5.0.8 or higher which include the latest fixes, some of which are critical to clusters with cloudpools.");
			}
			info("Please reference KB 000224314 (https://www.dell.com/support/kbdoc/en-us/000224314) for further information.");
			info("Please reference KB 000212099 (https://www.dell.com/support/kbdoc/en-us/000212099) for a full list of fixes.");
			addCustomerInformation("Checks have idetified that CloudPools is licensed and configured, please consider upgrading to 9.5.0.8 or higher which include the latest fixes, some of which are critical to clusters with cloudpools.");
		}
	}
    printResults();
} # End checkCloudPools
###
# checkCloudPoolsCOIDump: Checks for active Cloud Object Index dumps and
# existing dump files
sub checkCloudPoolsCOIDump {
    if (wasCalled()) { return }
    if (!isLive()) { return }

    my $CMD_COIDUMPS = q[2>&1 find /ifs/data/Isilon_Support/ -type f -iname].
	q[ '*coi_dump*'];
    my $CMD_NODE = q[screen -ls | grep -i coi_dump];
    my $CMD_COISCREENS = qq[2>&1 ${SUDO} isi_for_array '$CMD_NODE'];
    my $SPF_SCREEN = 'A coi_dump screen session was detected on the following'.
	' node(s): %s';
    my $MSG_COIDUMPS = 'Multiple COI dumps identified under'.
	' /ifs/data/Isilon_Support.  Re-run IOCA with --run=checkCloudPools'.
	' --extra for a list of the COI dump(s) identified.';
    my $SPF_COIDUMP = 'COI dump identifed: %s';
    my $SPF_VERBOSE = '%5s %s';

    # Check for coi dump files in /ifs/data/Isilon_Support
    my $gather =  gatherLocal($CMD_COIDUMPS);
    if (@$gather) {
	if (scalar(@$gather) == 1) {
	    details(sprintf($SPF_COIDUMP, $gather->[0]));
	} elsif (scalar(@$gather) > 1) {
	    details($MSG_COIDUMPS);
	}
	verboseHeader('Cloud Object Index (COI) dump files');
	verbose(@$gather);
    }

    # Check for coi_dump screen sessions
    $gather = gatherCluster($CMD_COISCREENS);
    if (%$gather) {
	warning(sprintf($SPF_SCREEN, compressRange(keys %$gather)));
	verboseHeader('Cloud Object Index (COI) dump screen sessions');
	verbose(sprintf($SPF_VERBOSE, 'LNN', 'Screen session'));
	verbose(sprintf($SPF_VERBOSE, '-'x5, '-'x20));
	foreach my $lnn (@LNNS) {
	    if (!$gather->{$lnn}) { next }
	    foreach (@{$gather->{$lnn}}) {
		s/^\s+|\s+$//g;
		verbose(sprintf($SPF_VERBOSE, $lnn, $_));
	    }
	}
    }
} # End checkCloudPoolsCOIDump
###
# Checks for the existence of additional certificates in
# /ifs/.ifsvar/modules/cloud/cacert
sub checkCloudPoolsCerts {
    if (wasCalled()) { return }

    my $PATH_LOGS = q[local/ifsvar_modules.tar/modules/cloud/cacert];
    my $CMD_LOGS = qq[2>&1 find $PATH_LOGS -type f];
    my $CMD_LIVE = q[2>&1 find /ifs/.ifsvar/modules/cloud/cacert -type f];
    my $MSG_CERTS = 'Found additional certificates under'.
	' /ifs/.ifsvar/modules/cloud/cacert/. If you are using SSL/TLS and a'.
	' certificate that required installation of a root / intermediate'.
	' certificate previously, the certificate will not be automatically'.
	' migrated to the new certificate store after upgrading to OneFS 8.2'.
	' or later. Please make a note to perform this certificate import'.
	' post upgrade. '.sprintf($MSG{'KB_1'}, '000065641');
    my @DEFAULT_CERTS = (
	'BaltimoreCyberTrustRoot.pem',
	'DigiCertHighAssuranceEV-RootCA.pem',
	'DigiCertSHA2HighAssuranceServerCA.pem',
	'GeoTrust_Global_CA.pem',
	'GoogleInternetAuthority_G2.pem',
	'GoogleInternetAuthority_G3.pem',
	'VerisignClass3PublicPrimaryCertificationAuthority.pem',
	);

    # Gather
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal($CMD_LIVE);
    } else {
	$gather = gatherLocal($CMD_LOGS);
    }

    # Analyze
    my @certs = ();
    foreach my $line (@$gather) {
	# Skip filenames that match the default certificates
	my ($is_default) = grep {$line =~ /$_$/i} @DEFAULT_CERTS;
    	if ($is_default) { next }

	# Check file extension for a non-default certificate
	if ($line =~ /\/([^\/]+\.pem)$/i) { push(@certs, $line) }
    }

    # Flag
    if (@certs) {
	addCustomerInformation('CloudPools', critical($MSG_CERTS));
	verboseHeader('CloudPool certificates');
	verbose($_) foreach @certs;
    }
} # End checkCloudPoolsCerts
sub checkCloudPoolsNANON {
    if (wasCalled()) { return }
    if (!isLive()) {
	fail(q[Verification of nodes that do not have connectivity to cloud].
	     q[ providers cannot be completed via logs and must be checked].
	     q[ on cluster with the following command: isi_for_array 'isi].
	     q[ cloud account list' | egrep -vi '(ok|disabled) *$']);
	$RPS{'Requires TC'} = 1;
	return;
    }

    my $CMD_NODE = qq[${SUDO} isi cloud account list -az --format=csv];
    my $CMD_GREP = q[egrep -vi '(ok|disabled) *$'];
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array '$CMD_NODE'|$CMD_GREP];
    my $SPF_FOUND = 'The following nodes may not be able to communicate with'.
	' all CloudPools providers: %s. Nodes that do not have connectivity to'.
	' cloud providers will delay or block ability to upgrade stub files in'.
	' snapshots and may cause the SnapshotDelete job to fail when trying'.
	' to process file fill requests. This requires each node to have cloud'.
	' provider connectivity. If this cannot be accomplished reach out to'.
	' Isilon Support for assistance prior to performing the upgrade.';
    my $SPF_VERBOSE = '%5s %s';

    # Gather
    my $gather = gatherCluster($CMD_LIVE);

    # Analyze
    my %nodes = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line !~ /,/) { next }
	    my ($account) = split /,/, $line;
	    push(@{$nodes{$lnn}}, $account);
	}
    }

    # Flag
    if (%nodes) {
	my $msg = sprintf($SPF_FOUND, compressRange(keys %nodes));
	addCustomerInformation('CloudPools', fail($msg));
	verboseHeader('CloudPool account(s) with connectivity issues by node');
	verbose(sprintf($SPF_VERBOSE, 'LNN', 'CloudPools account(s)'));
	verbose(sprintf($SPF_VERBOSE, '-'x5, '-'x20));
	foreach my $lnn (@LNNS) {
	    if (!$nodes{$lnn}) { next }
	    verbose(sprintf($SPF_VERBOSE, $lnn, join(',', @{$nodes{$lnn}})));
	}
    }
} # End checkCloudPoolsNANON
###
# checkCloudPoolsSmartLink: Checks if there is a running smartlink-upgrade job
sub checkCloudPoolsSmartLink {
    if (wasCalled()) { return }
    if (!isLive()) { return }
    if (isCurrentOneFS('<8.2')) { return }

    # Setup
    my $MSG_UPG_SMARTLINK = 'The CloudPools smartlink-upgrade job must complete prior to OneFS upgrades';
    my $MSG_SMARTLINK = 'CloudPools smartlink-upgrade job is currently running'.
	' and may be monitored with the following command:'.
	' isi cloud jobs view 6';
    my $CMD_JOB = qq[2>&1 ${SUDO} isi cloud jobs view 6];
    my $CMD_GCONFIG;
    if(isCurrentOneFS('>=9.12') and isCompliance()){
        $CMD_GCONFIG = qq[2>&1 ${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig -t cpool_cfg allow_ndu_during_stub_convert];
    }
    else{
        $CMD_GCONFIG = qq[2>&1 ${SUDO} isi_gconfig -t cpool_cfg allow_ndu_during_stub_convert];
    }


    my $CMD_JOB_LOGS = qq[grep -A17 'ID: 6' local/isi_cloud_jobs_settings];
    my $CMD_GCONFIG_LOGS = qq[2>&1 ${SUDO} isi_gconfig -t cpool_cfg allow_ndu_during_stub_convert];

    # Gather
    my $gather_job = gatherLocal($CMD_JOB);
    my $gather_gconfig = gatherLocal($CMD_GCONFIG);

    # Analyze
    my $smartlink_upgrade_remaining_files = 0;
    my $job_id = 0;
    foreach (@$gather_job) {
	if (/^\s*ID:\s+(\d+)$/) { $job_id = $1 }
	if ($job_id ne 6) { next }
	if (/^\s*Total\s(?:Pending|Processing):\s+(\d+)$/) {
	    $smartlink_upgrade_remaining_files += $1;
	}
    }
    my $allow_ndu_during_stub_convert = 0;
    foreach (@$gather_gconfig) {
	my ($key, $value) = parseGconfigItem($_);
	if ($key ne 'allow_ndu_during_stub_convert') { next }
	if ($value eq 'true') {
	  $allow_ndu_during_stub_convert = 1;
      }
    }

    # Flag
    if ($smartlink_upgrade_remaining_files) {
        if(isOneFSUpgrade()){
            fail($MSG_SMARTLINK);
            critical($MSG_UPG_SMARTLINK);
        }
        else{
            warning($MSG_SMARTLINK);    
        }
    }
    if (isOneFSUpgrade() and
	$smartlink_upgrade_remaining_files and
	$allow_ndu_during_stub_convert) {
	    fail($MSG_UPG_SMARTLINK);
    }
} # End checkCloudPoolsSmartLink
###
# checkKB000217579: Checks if there is a running smartlink-upgrade job
sub checkKB000217579 {
    if (wasCalled()) { return }
    if (!hasCloudPools()) { return }
    my $MSG_UPG_KB000217579 = 'CloudPools is enabled and has accounts configured, additional steps are required if upgrading to either 9.5.0.4 (PSP-3524) or 9.5.0.5 (PSP-3793).';

        if (isLive()) {
            $gather = gatherLocal(qq[2>&1 isi cloud accounts list -v -z -a]);
        } else {
            $gather = gatherLocal(qq[2>&1 egrep -E '^\\s*ID:|Name:|State:|Enabled:' local/isi_cloud_accounts_settings]);
        }
        my @accounts_unreachable;
        my %account_detail;
        # account_detail -> counter -> ID -> Name -> State 
        # account_detail -> counter -> ID -> Name -> Enabled 

        my $curr_key;
        my $acc_id;
        my$cur_val;

        foreach my $line (@$gather) {
            #debug("$line");
            if ($line =~ /^\s*$/) { next }
            if ($line =~ /^\s*\-*\s*$/){ next }
            if ($line =~ /^\s*(\S+):\s*(\S+)\s*$/){
                #debug("Matched regex");
                $curr_key = $1;
                $acc_id = $2 if $curr_key eq 'ID';
                $cur_val = $2 if $curr_key ne 'ID';
                $account_detail{$acc_id}{$curr_key} = $acc_id if $curr_key eq 'ID';
                #debug("account_detail -> $acc_id -> $curr_key = $acc_id") if $curr_key eq 'ID';
                $account_detail{$acc_id}{$curr_key} = $cur_val if $curr_key ne 'ID';                
                #debug("account_detail -> $acc_id -> $curr_key = $cur_val") if $1 ne 'ID';
                #debug("checking if $cur_val = 'unreachable'");
                #if($cur_val eq 'unreachable'){debug("storing $acc_id as unreachable"); push(@accounts_unreachable, $acc_id); }
            }
        }
        foreach my $aitem (keys %account_detail){
            debug("$aitem | $account_detail{$aitem}{'Name'} | $account_detail{$aitem}{'State'} | $account_detail{$aitem}{'Enabled'}");
            if($account_detail{$aitem}{'State'} && $account_detail{$aitem}{'State'} ne 'OK' && $account_detail{$aitem}{'Enabled'}){ push(@accounts_unreachable, $aitem); }
        }
    
      if(isDestinationOneFS(qr/^9\.5\.0\.[45]$/) || @accounts_unreachable){
        if(isDestinationOneFS(qr/^9\.5\.0\.[45]$/)){
            debug("Cluster has cloudpools, has accounts configured and is upgrading to 9.5.0.4 or 9.5.0.5");
            critical($MSG_UPG_KB000217579);
        } 
        else{
            if(@accounts_unreachable){
                debug("found unreachable accounts");
                if(isCurrentOneFS(qr/^9\.5\.0\.[45]$/)){
                    debug("Cluster has cloudpools, has accounts configured and are showing unreachable and me be impacted by KB000217579: ". join(', ', @accounts_unreachable));
                    critical("Cluster has cloudpools, has accounts configured and are showing unreachable and me be impacted by KB000217579: ". join(', ', @accounts_unreachable));
                }
                else{
                    warning("Some cloudpools accounts are showing unreachable: ". join(', ', @accounts_unreachable));
                }
            }
        }
        info("Please reference KB 000217579 (https://www.dell.com/support/kbdoc/en-ie/000217579) for further information.");
        info("Please contact Technical Support if assistance is required.");
    }

} # End checkKB000217579
###
# checkKB000260843: Checks if there is a running smartlink-upgrade job
$CHECKS{'checkKB000260843'} = {
    'Description' => 'Checks if the cluster is at risk for KB 000260843',
    'Exclude' => 0,
};
sub checkKB000260843 {
    if (wasCalled()) { return }
    if (!hasCloudPools()) { return }

    #if ((isCurrentOneFS('<=9.0') & isDestinationOneFS('>=9.1')) || isCurrentOneFS('>=9.1')){

    #}
    printCheck('KB 000260843');

        if (isLive()) {
            $gather = gatherCluster(qq[2>&1 isi_for_array 'zgrep "isi_cpool_d.*FAILED ASSERTION"  /var/log/messages*']);
            $gather_cp_log = gatherCluster(qq[2>&1 isi_for_array 'zgrep "CL_CHECKSUM_MISMATCH\\|CBM_INTEGRITY_FAILURE\\|CL_ABORTED_BY_CALLBACK"  /var/log/isi_cpool_d*']);
            $gather_idi_log = gatherCluster(qq[2>&1 isi_for_array 'zgrep "Cache invalidation is attempted over invalid storage layer"  /var/log/idi.log*']);
        } else {
            $gather = gatherCluster(qq[2>&1 zgrep -H 'isi_cpool_d.*FAILED ASSERTION'  */varlog.tar/log/messages*]);
            $gather_cp_log = gatherCluster(qq[2>&1 zgrep -H 'CL_CHECKSUM_MISMATCH\|CBM_INTEGRITY_FAILURE\|CL_ABORTED_BY_CALLBACK\|'  */varlog.tar/log/isi_cpool_d*]);
            $gather_idi_log = gatherCluster(qq[2>&1 zgrep -H 'Cache invalidation is attempted over invalid storage layer'  */varlog.tar/log/idi.log]);
        }
        my %affected = ();
        my %affected_cpl = ();
        my %affected_idil = ();

        foreach my $lnn (@LNNS) {
            foreach my $line (@{$gather->{$lnn}}) {
                debug("$line");
                if ($line =~ /^\s*(\d+-\d+-\d+T\d+:\d+:\d+).*(isi_cpool_d: \*\*\* FAILED ASSERTION).*(isi_cpool_cbm2\/src\/ncoi.c).*$/){
                    debug("ERROR: $1\t$2 $3");
                    $affected{$lnn} = 1;       
                }
            }
            foreach my $cp_line (@{$gather_cp_log->{$lnn}}) {
                debug("$cp_line");
                if ($cp_line =~ /^\s*(\d+-\d+-\d+T\d+:\d+:\d+).*(isi_cpool_cbm error.*CL_CHECKSUM_MISMATCH).*$/){
                    debug("ERROR: $1\t$2");
                    $affected_cpl{$lnn} = 1;       
                }
            }
            foreach my $idi_line (@{$gather_idi_log->{$lnn}}) {
                debug("$idi_line");
                if ($idi_line =~ /^\s*(.*bcm_verify_invalidate_on_valid_storage_layer.*Cache invalidation is attempted over invalid storage layer.*)$/){
                    debug("ERROR: $1");
                    $affected_idil{$lnn} = 1;       
                }
            }
        }
          
    if(%affected || %affected_cpl || %affected_idil){
        debug("FAIL as we can see signs on failures from logs matching this issue.");
        fail('There are log entries on this cluster showing signs of potential issues with CloudPools whereby there may be a risk of multiple LNN\'s / Files writing to the same cloud data objects.');
        info('If you have not already done so, please reference the following KB and perform the resolution steps: https://www.dell.com/support/kbdoc/000260843');
    } 
    elsif(
        (isCurrentOneFS('=9.1') ||
        isCurrentOneFS('=9.2') ||
        isCurrentOneFS('=9.3') ||
        (isCurrentOneFS('=9.4') & !isCurrentOneFS('>=9.4.0.21')) ||
        (isCurrentOneFS('=9.5') & !isCurrentOneFS('>=9.5.1.3')) ||
        (isCurrentOneFS('=9.7') & !isCurrentOneFS('>=9.7.1.7')) ||
        (isCurrentOneFS('=9.10') & !isCurrentOneFS('>=9.10.1.0'))) ||
        (isDestinationOneFS('=9.1') ||
        isDestinationOneFS('=9.2') ||
        isDestinationOneFS('=9.3') ||
        (isDestinationOneFS('=9.4') & !isDestinationOneFS('>=9.4.0.21')) ||
        (isDestinationOneFS('=9.5') & !isDestinationOneFS('>=9.5.1.3')) ||
        (isDestinationOneFS('=9.7') & !isDestinationOneFS('>=9.7.1.7')) ||
        (isDestinationOneFS('=9.10') & !isDestinationOneFS('>=9.10.1.0')))
    ){
        debug("Current and/or destination is exposed to KB 000260843, Please ensure resolution steps are applied to avoid any issue where LNN changes are applied to this cluster.");
        critical("If your cluster has had LNN changes since your upgrade to ".isCurrentOneFS()." or you intend to make LNN changes after your upgrade to ".isDestinationOneFS()." then please ensure correct steps are taken per KB 000260843.");  
        info('Please reference KB 000260843 (https://www.dell.com/support/kbdoc/000260843) for additional information.');        
    }

    # Output
    printResults();

} # End checkKB000260843
$CHECKS{'checkKB496993'} = {
    'Description' => 'Checks if the cluster is at risk for KB 000061504',
    'Exclude' => 1,
};
sub checkKB496993 {
    if (wasCalled()) { return }
    printCheck('KB 496993');

    my $MSG_DU = 'CloudPools is enabled but the isi_cpool_d and/or isi_cpool_io_d service is disabled and may cause Data Unavailability per KB 000061504';
    
    my $CMD_LIVE; 
    if(isCurrentOneFS('>=9.12') and isCompliance()){
        $CMD_LIVE = qq[2>&1 ${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig registry.Services.lwsm.Parameters.Variables.CloudpoolsEnabled];
    }
    else{
        $CMD_LIVE = qq[2>&1 ${SUDO} isi_gconfig registry.Services.lwsm.Parameters.Variables.CloudpoolsEnabled];
    }
    my $CMD_LOGS = qq[2>&1 grep registry.Services.lwsm.Parameters.Variables.CloudpoolsEnabled local/isi_gconfig];

    # If running OneFS versions prior to 8.0,
    #  flag the issue for an upgrade to 8 or later
    if (isCurrentOneFS('<8')) {
	if ($RPS{'Target'} and compareVersions($RPS{'Target'}, '8') >= 0 and
	    hasLicense('CloudPools') and
	    (isServiceDisabled('isi_cpool_d') or
	     isServiceDisabled('isi_cpool_io_d'))) {
	    fail($MSG_DU);
	    info(sprintf($MSG{'KB_1'}, '000061504'));
	}
	printResults();
	return;
    }

    # Gather
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal($CMD_LIVE);
    } else {
	$gather = gatherLocal($CMD_LOGS);
    }

    # Analyze
    my $cloudpools_enabled = 0;
    foreach (@$gather) {
	my ($key, $value) = parseGconfigItem($_);
	if ($key =~ /CloudpoolsEnabled/) {
	    $cloudpools_enabled = $value;
	}
    }

    # Flag
    if ($cloudpools_enabled and
	(isServiceDisabled('isi_cpool_d') or
	 isServiceDisabled('isi_cpool_io_d'))) {
	fail($MSG_DU);
	info(sprintf($MSG{'KB_1'}, '000061504'));
    }

    # Output
    printResults();
}
###
# hasCloudPools: Used to determine CloudPools usage
sub hasCloudPools {
    if (defined $CLUSTER_INFO{'hasCloudPools'}) {
	return $CLUSTER_INFO{'hasCloudPools'};
    }
    $CLUSTER_INFO{'hasCloudPools'} = 0;
    if (!hasLicense('CloudPools')) {
	return $CLUSTER_INFO{'hasCloudPools'};
    }
    my $CMD_GREP = q[egrep '\.(provider_instance_id|account_id) '];
    my $CMD_LIVE;
    if(isCurrentOneFS('>=9.12') and isCompliance()){
        $CMD_LIVE = qq[2>&1 ${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig -t cpool_cfg | $CMD_GREP];
    }
    else{
        $CMD_LIVE = qq[2>&1 ${SUDO} isi_gconfig -t cpool_cfg | $CMD_GREP];
    }
    my $CMD_LOGS = qq[2>&1 sqlite3 local/ifsvar_modules_tardis.tar/modules/tardis/namespaces/Gconfig.cpool_config.sqlite 'select * from kv_table' | $CMD_GREP];
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal($CMD_LIVE);
    } else {
	$gather = gatherLocal($CMD_LOGS);
    }
    my $has_cloud_account = 0;
    my $has_cloud_provider = 0;
    foreach (@$gather) {
	if (/^provider_instance\.\d+\.provider_instance_id/) {
	    $has_cloud_provider = 1;
	}
	if (/^account\.\d+\.account_id/) {
	    $has_cloud_account = 1;
	}
    }
    if ($has_cloud_account and $has_cloud_provider) {
	$CLUSTER_INFO{'hasCloudPools'} = 1;
    }
    return $CLUSTER_INFO{'hasCloudPools'};
} # End hasCloudPools
$CHECKS{'checkJobHistory'} = {
    'Description' => 'Checks job history for issues, currently just MediaScan issues',
    'Exclude' => 1,
};
sub checkJobHistory {
    if (wasCalled()) { return }
    printCheck('Jobs History');

    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array].
	q[ 'grep -hE "MediaScan.*Succeeded$" /var/log/messages'];
    my $CMD_LOGS = q[2>&1 grep -HE 'MediaScan.*Succeeded$'].
	q[ */varlog.tar/log/messages];
    my $MSG_NO = 'No MediaScan job history found';
    my $MSG_90 = 'Media scan not completed in last 90 days';
    my $MSG_60 = 'Media scan not completed in last 60 days';

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my $mr_mediascan = 0;
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	foreach (@{$gather->{$lnn}}) {
	    if (/\s*(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)/) {
		my $timestamp = timelocal($6, $5, $4, $3, $2 - 1, $1);
		debug($timestamp);
		if ($timestamp > $mr_mediascan) { $mr_mediascan = $timestamp }
	    }
	}
    }

    # Flag
    if (!$mr_mediascan) {
	warning($MSG_NO);
    } elsif ($RPS{'Timestamp'} - $mr_mediascan > 90 * 24 * 60 * 60) {
	warning($MSG_90);
    } elsif ($RPS{'Timestamp'} - $mr_mediascan > 60 * 24 * 60 * 60) {
	info($MSG_60);
    }

    printResults();
} # End checkJobHistory
###
# checkJobStatus: Checks for running jobs that would impact an upgrade.
$CHECKS{'checkJobStatus'} = {
    'Description' => 'Checks for running jobs that would impact an upgrade',
    'Reference' => 'https://www.dell.com/support/kbdoc/000039736',
};
$MSG{'checkJobStatus_ONEFS_2'} = '%s is currently %s and must complete prior to any OneFS upgrades';
$MSG{'checkJobStatus_UPG_2'} = '%s is currently %s and must complete prior to any upgrade activities';
$MSG{'checkJobStatus_CANCEL_2'} = '%s is currently %s and must be cancelled prior to any OneFS upgrades';
$MSG{'checkJobStatus_MSNOT_1'} = 'MediaScan fixes available but not in %s';
$MSG{'checkJobStatus_MS'} = 'MediaScan fixes available with OneFS upgrade';
$MSG{'checkJobStatus_UPGRADE_DISABLED'} = 'The Upgrade job is currently disabled and should be enabled by running the following command: isi job type modify upgrade --enabled=true';
sub checkJobStatus {
    if (wasCalled()) { return }
    printCheck('Jobs Status');
    gatherJobStatus();

    # Analyze
    foreach my $type (keys %{$CLUSTER_INFO{'Jobs'}}) {
	if (!$CLUSTER_INFO{'Jobs'}{$type}{'State'}) { next }
	my $state = $CLUSTER_INFO{'Jobs'}{$type}{'State'};
	if ($type eq 'Upgrade') {
	    if (isOneFSUpgrade()) {
		fail(sprintf($MSG{'checkJobStatus_ONEFS_2'}, $type, $state));
	    } else {
		warning(sprintf($MSG{'checkJobStatus_ONEFS_2'}, $type, $state));
	    }
	} elsif ($type =~ /IntegrityScan|FlexProtect/i) {
	    fail(sprintf($MSG{'checkJobStatus_UPG_2'}, $type, $state));
	} elsif ($type =~ /dedupe/i and isDestinationOneFS('>=8')) {
	    critical(sprintf($MSG{'checkJobStatus_CANCEL_2'}, $type, $state));
	}
    }
    if (isDestinationOneFS('<7.1.1.5') or
	isDestinationOneFS(qr/^7\.2\.0\.[012]$/)) {
	warning(sprintf($MSG{'checkJobStatus_MSNOT_1'}, isDestinationOneFS()));
    }
    if (!isDestinationOneFS() and
	(isCurrentOneFS('<7.1.1.5') or
	 isCurrentOneFS(qr/^7\.2\.0\.[012]$/))) {
	warning($MSG{'checkJobStatus_MS'});
    }
    if (isJobDisabled('Upgrade')) { fail($MSG{'checkJobStatus_UPGRADE_DISABLED'}) }

    if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000039736')) }
    printResults();
} # End checkJobStatus
###
# checkJobs: Checks jobs
$CHECKS{'checkJobs'} = {
    'Description' => 'Checks jobs',
    'Exclude' => 1,
};
sub checkJobs {
    if (wasCalled()) { return }
    printCheck('Jobs');
    gatherJobStatus();
    gatherJobTypes();
    gatherJobHistory();
    gatherETA518924();

    # Setup
    my @job_types = sort {$a cmp $b} keys %{$CLUSTER_INFO{'Jobs'}};
    my $max_type = 10;
    my $max_policy = 6;
    foreach my $type (keys %{$CLUSTER_INFO{'Jobs'}}) {
	my $length_type = length($type);
	if ($length_type > $max_type) { $max_type = $length_type }
	if (!defined $CLUSTER_INFO{'Jobs'}{$type}{'Policy'}) { next }
	my $length_policy = length($CLUSTER_INFO{'Jobs'}{$type}{'Policy'});
	if ($length_policy > $max_policy) { $max_policy = $length_policy }
    }

    my $SPF_JOB = "%-${max_type}s %-8s %-3s %-${max_policy}s %-24s %s";

    # Output job details
    if (!$RPS{'JSON'}) {
	details(sprintf($SPF_JOB, 'Type', 'Enabled', 'Pri', 'Policy',
			'Most Recent Completion', 'Schedule'));
	foreach my $type (@job_types) {
	    my %job = %{$CLUSTER_INFO{'Jobs'}{$type}};
	    details(sprintf($SPF_JOB, $type,
			    $job{'Enabled'} || '-',
			    $job{'Priority'} || '-',
			    $job{'Policy'} || '-',
			    $job{'Last'}?scalar(localtime($job{'Last'})):'-',
			    $job{'Schedule'} || '-'));
	}
    }

    # Flag
    if ($RPS{'ETA'}{'518924'}{'Severity'}) {
	if (isJobEnabled('Collect') or isJobEnabled('MultiScan')) {
	    # Flag to disable the Collect/MultiScan jobs per ETA 518924
	}
    } else {
	if (isJobDisabled('Collect') or isJobDisabled('MultiScan')) {
	    # Flag to enable the Collect/MultiScan jobs
	}
    }

    printResults();
} # End checkJobs
$CHECKS{'checkKB537785'} = {
    'Description' => 'Check for criteria of KB 000168829',
    'Reference' => 'https://www.dell.com/support/kbdoc/000168829',
};
sub checkKB537785 {
    if (wasCalled()) { return }
    my $RE_AFFECTED = qr/^8\.2\.[01]\./;
    if (!isDestinationOneFS() and !isCurrentOneFS($RE_AFFECTED)) {
	return unsupported('UNSUP_CUR');
    }
    if (isDestinationOneFS() and !isDestinationOneFS($RE_AFFECTED)) {
	return unsupported('UNSUP_TGT_1', '8.2.0.0, 8.2.1.0');
    }
    if (!isLive() and !isDestinationOneFS(qr/^8\.2\.[01]\./)) { return }

    # Setup
    printCheck('KB 537785');
    my $SYSCTL = 'efs.pdm.check_valid_tag_op';
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'sysctl $SYSCTL'];
    my $MSG_SYSCTL = 'The sysctl setting efs.pdm.check_valid_tag_op should be'.
	' changed to 0 by running the following command: isi_sysctl_cluster'.
	' efs.pdm.check_valid_tag_op=0';
    my $SPF_UPGRADE = 'Immediately following the upgrade to %s, run the'.
	' following command to change the sysctl setting'.
	' efs.pdm.check_valid_tag_op to 0: isi_sysctl_cluster'.
	' efs.pdm.check_valid_tag_op=0';

    # Gather
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster($CMD_LIVE);
    }

    # Analyze
    my $enabled = 0;
    foreach my $lnn (@LNNS) {
	if (!$gather or $enabled) { last }
	if (!$gather->{$lnn}) { next }
	foreach (@{$gather->{$lnn}}) {
	    if (/^\s*${SYSCTL}[:=]\s*(\d)/) { $enabled = $1 }
	}
    }

    # Flag
    if ($enabled) {
	if ($RPS{'Target'}) {
	    critical($MSG_SYSCTL);
	} else {
	    fail($MSG_SYSCTL);
	}
    } elsif (isDestinationOneFS(qr/^8\.2\.[01]\./) and isCurrentOneFS('<8.2')) {
	critical(sprintf($SPF_UPGRADE, $RPS{'Target'}));
    }

    # Output
    if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000168829')) }
    printResults();
}
# gatherJobHistory: Gathers job history from /var/log/messages files
sub gatherJobHistory {
    if (wasCalled()) { return }
    import Parsers qw( parseTime );
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array -Q].
	q[ 'zgrep -a -h Succeeded$ /var/log/messages*'].
	q( | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}.*isi_job_d.*Succeeded$').
	q( | awk '{print $1" "$(NF-1)}').
	q( | sed -e 's/\[.*\]$//').
	q[ | sort -r -k2 | uniq -f1];
    my $CMD_LOGS = q[2>&1 zgrep -a -h Succeeded$ */varlog.tar/log/messages*].
	q( | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}.*isi_job_d.*Succeeded$').
	q( | awk '{print $1" "$(NF-1)}').
	q( | sed -e 's/\[.*\]$//').
	q[ | sort -r -k2 | uniq -f1];

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    foreach (@$gather) {
	my ($time, $type) = split(/ /);
	my $timestamp = parseTime($time);
	$CLUSTER_INFO{'Jobs'}{$type}{'Last'} = $timestamp;
	debug("CLUSTER_INFO->Jobs->${type}->Last=$timestamp");
    }
} # End gatherJobHistory
###
# gatherJobStatus: Gathers job information.
sub gatherJobStatus {
    return if wasCalled();
    gatherJobStatus7();
    gatherJobStatus8();
} # End gatherJobStatus
###
# gatherJobStatus7: Gathers job information in OneFS 7 and earlier
sub gatherJobStatus7 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8')) {
	debug('Supported in OneFS 7 and earlier');
	return;
    }

    # Setup
    my $CMD_LIVE_7 = qq[2>&1 ${SUDO} isi_classic job status -v];
    my $CMD_LIVE_6 = qq[2>&1 ${SUDO} isi job status -v];
    my $CMD_LOGS = q[2>&1 cat local/isi_job_status];

    # Gather
    my $gather = [];
    if (isLive()) {
	if (isCurrentOneFS('>=7')) {
	    $gather = gatherLocal($CMD_LIVE_7);
	} else {
	    $gather = gatherLocal($CMD_LIVE_6);
	}
    } else { $gather = gatherLocal($CMD_LOGS) }

    # Save
    my $state = '';
    foreach (@$gather) {
	if (/^Running jobs:/) { $state = 'Running' }
	if (/^Paused and waiting jobs:/) { $state = 'Paused' }
	if (/^No (running|paused or waiting|failed) jobs/) { $state = '' }
	if (/^Failed jobs:|^Recent job results/) { last }
	if (/^(\w+)\[\d+\]/) {
	    debug("CLUSTER_INFO->Jobs->${1}->State=$state");
	    $CLUSTER_INFO{'Jobs'}{$1}{'State'} = $state;
	}
    }
} # End gatherJobStatus7
###
# gatherJobStatus8: Gathers current jobs in OneFS 8 and later
sub gatherJobStatus8 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) {
	debug('Supported in OneFS 8 and later');
	return;
    }

    # Setup
    my $CMD_LIVE;
	if(isCurrentOneFS('<9.9')){
        $CMD_LIVE = qq[2>&1 ${SUDO} isi job list];
    }
    else{
        $CMD_LIVE = qq[isi job list];
    }
    my $CMD_LOGS = q[2>&1 cat local/isi_job_list];
    my @COLS = ('ID', 'Type', 'State', 'Impact','Running');

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Save
    my $unpack_template = '';
    foreach (@$gather) {
	if (/^ID/) { $unpack_template = generateUnpackTemplate($_, @COLS) }
	if (!$unpack_template or /^-*$/ or !/^\d/) { next }
	eval {
	    my ($id, $type, $state, undef, $running) =
		unpack($unpack_template, $_);
	    debug("CLUSTER_INFO->Jobs->${type}->State=$state");
	    $CLUSTER_INFO{'Jobs'}{$type}{'State'} = $state;
	    debug("CLUSTER_INFO->Jobs->${type}->Running=$running");
	    $CLUSTER_INFO{'Jobs'}{$type}{'Running'} = $running;
	};
    }
} # End gatherJobStatus8
sub gatherJobTypes {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) { return }

    # Setup
    my $CMD_LIVE;
	if(isCurrentOneFS('<9.9')){
        $CMD_LIVE = qq[2>&1 ${SUDO} isi job types list --format list -v --all];
    }
    else{
        $CMD_LIVE = qq[isi job types list --format list -v --all];    
    }
    my $CMD_LOGS = qq[2>&1 cat local/isi_job_schedule];
    my @FIELDS = ('Description','Enabled','Policy','Schedule','Priority');
    my $RE_CAPTURE = '^\s*('.join('|', @FIELDS).'):\s*(\S.*)$';

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Save
    my $type = '';
    foreach (@$gather) {
	if (/^[\s-]*$/) {
	} elsif (/^\s*ID:\s*(\S+)$/) {
	    $type = $1;
	} elsif (!$type) {
	} elsif (/$RE_CAPTURE/) {
	    $CLUSTER_INFO{'Jobs'}{$type}{$1} = $2;
	    debug("CLUSTER_INFO->Jobs->${type}->$1=$2");
	}
    }
} # End gatherJobTypes
sub isJobDisabled {
    my ($type) = @_;
    if (!$type) { return 0 }
    gatherJobTypes();
    if (!defined $CLUSTER_INFO{'Jobs'}{$type}) { return 0 }
    if (!defined $CLUSTER_INFO{'Jobs'}{$type}{'Enabled'}) { return 0 }
    if ($CLUSTER_INFO{'Jobs'}{$type}{'Enabled'} eq 'No') { return 1 }
}
sub isJobEnabled {
    my ($type) = @_;
    if (!$type) { return 0 }
    gatherJobTypes();
    if (!defined $CLUSTER_INFO{'Jobs'}{$type}) { return 0 }
    if (!defined $CLUSTER_INFO{'Jobs'}{$type}{'Enabled'}) { return 0 }
    if ($CLUSTER_INFO{'Jobs'}{$type}{'Enabled'} eq 'Yes') { return 1 }
}
###
# checkKB495911: Checks for an issue where having a large number of
#  SyncIQ report files causes a tar process to delay other upgrade
#  processes potentially leaving the cluster in a temporary DU scenario
#  for an extended period of time.
sub checkKB495911 {
    if (wasCalled()) { return }
    gatherSyncIQ();
    if (!$CLUSTER_INFO{'SyncIQ'}{'Source'}) { return }

    # Setup
    my $SQL_QUERY = q['select policy_id, count(policy_id) from reports group by policy_id'];
    my $FILE_LIVE = '/ifs/.ifsvar/modules/tsm/sched/reports/reports.db';
    my $FILE_LOGS = 'local/ifsvar_modules.tar/modules/tsm/sched/reports/reports.db';
    my $CMD_LIVE = qq[2>&1 sqlite3 $FILE_LIVE $SQL_QUERY];
    my $CMD_LOGS = qq[2>&1 sqlite3 $FILE_LOGS $SQL_QUERY];
    my $SPF_VERBOSE = '%-32s %-30s %-20s %s';
    my $SPF_EST = 'The estimated total number of SyncIQ report files is %s, this may extend the duration of cluster unavailability during an upgrade to OneFS %s.  A fix for this is available in OneFS 8.0.0.6, OneFS 8.0.1.2, and OneFS 8.1.0.1 and later OneFS versions.';
    my $MSG_DBL = 'The following policies have estimated more than double the amount of SyncIQ reports they should have';
    my $SPF_POL = '%-30s %-20s %s';
    my $MAX_FILES_COUNT = 100000;

    # Gather
    my $gather = ();
    if (isLive() and -e $FILE_LIVE) {
	$gather = gatherLocal($CMD_LIVE);
    } elsif (!isLive() and -e $FILE_LOGS) {
	$gather = gatherLocal($CMD_LOGS);
    }

    # Analyze
    my $total_estimated_files_count = 0;
    my %policies_with_report_issues = ();
    verboseHeader('SyncIQ Reports');
    verbose(sprintf($SPF_VERBOSE, 'Policy ID', 'Policy Name',
		    'Report Max Count', 'Estimated Count'));
    foreach my $line (@$gather) {
	if ($line !~ /\|/) { next }
	my ($pid, $estimate) = split(/\|/, $line);
	$total_estimated_files_count += $estimate;
	if (!$CLUSTER_INFO{'SyncIQ'}{'Source'}{$pid}) { next }
	my $policy = $CLUSTER_INFO{'SyncIQ'}{'Source'}{$pid};
	if (!$policy->{'Report Max Count'} or !$policy->{'Name'}) { next }
	verbose(sprintf($SPF_VERBOSE, $pid, $policy->{'Name'},
			$policy->{'Report Max Count'}, $estimate));
	if ($estimate < 2000 or
	    $estimate < $policy->{'Report Max Count'} * 2) { next }
	$policies_with_report_issues{$pid} = $estimate;
    }
    verbose("Total estimated files count: $total_estimated_files_count");

    # Flag
    if (isCurrentOneFS('<8') and
	isDestinationOneFS(qr/^8\.0\.0\.[012345]$|^8\.0\.1\.[01]$|^8\.1\.0\.0$/) and
	$total_estimated_files_count > $MAX_FILES_COUNT) {
	addCustomerInformation(critical(sprintf($SPF_EST,
						$total_estimated_files_count,
						isDestinationOneFS())));
    }
    if (%policies_with_report_issues) {
	warning($MSG_DBL);
	warning(sprintf($SPF_POL, 'Policy Name', 'Report Max Count',
			'Estimated Count'));
	foreach my $pid (keys %policies_with_report_issues) {
	    my $policy = $CLUSTER_INFO{'SyncIQ'}{'Source'}{$pid};
	    warning(sprintf($SPF_POL, $policy->{'Name'},
			    $policy->{'Report Max Count'},
			    $policies_with_report_issues{$pid}));
	}
    }
} # End checkKB495911
$CHECKS{'checkSyncIQ'} = {
    'Description' => 'Gathers source and target SyncIQ information and reports partners of SyncIQ. Checks for an issue where having a large number of SyncIQ report files causes a tar process to delay other upgrade processes potentially leaving the cluster in a temporary DU scenario for an extended period of time',
    'Exclude' => 1,
};
sub checkSyncIQ {
    if (wasCalled()) { return }
    checkKB521778();
    printCheck('SyncIQ');
    gatherSyncIQ();
    gatherFlexNet();
    checkKB495911();

    my $MSG_KB000016953 = 'There is a known issue with SyncIQ and Dynamic pools in OneFS versions 7.2.1.0 - 7.2.1.2. '.sprintf($MSG{'KB_1'}, '000016953');
    my $MSG_DYNAMIC = 'SyncIQ best practice is to use static network pools, and all network pools are dynamic.';
    my $SPF_RESTRICTED = 'SyncIQ best practice is to use static network pools, and the following SyncIQ policies are restricted to use a dynamic network pool: %s';
    my $MSG_COMPLIANCE = 'As this cluster is in Compliance mode and is upgrading from a version prior to OneFS 8.2.x, both the SyncIQ source and SyncIQ target clusters must be upgraded to the same versions of OneFS and patches before restarting SyncIQ backups. If the SyncIQ source and SyncIQ target clusters are not on the same versions, SyncIQ backups will fail. This issue is resolved in OneFS 8.2.x';

    # Analyze
    checkSyncIQTarget();
    checkSyncIQSource();

    # Flag for this cluster being part of SyncIQ, used in the upgrade plan
    if (($CLUSTER_INFO{'SyncIQ'}{'Target'} and
	 keys %{$CLUSTER_INFO{'SyncIQ'}{'Target'}}) or
	($CLUSTER_INFO{'SyncIQ'}{'Source'} and
	 keys %{$CLUSTER_INFO{'SyncIQ'}{'Source'}})) {
	$RPS{'SyncIQ'} = 1;
    }

    my $only_dynamic_pools_available = 0;
    my @restricted_to_dynamic_pool = ();
    if ($CLUSTER_INFO{'FlexNet'}{'Pools'}) {
	# Gather policies that are restricted to use Dynamic IP pools
	if ($CLUSTER_INFO{'SyncIQ'}{'Source'}) {
	    foreach my $pid (keys %{$CLUSTER_INFO{'SyncIQ'}{'Source'}}) {
		my $policy = $CLUSTER_INFO{'SyncIQ'}{'Source'}{$pid};
		if ($policy->{'Enabled'} and
		    $policy->{'Restrict Source Nodes'} and
		    $CLUSTER_INFO{'FlexNet'}{'Pools'}{$policy->{'Restrict Source Nodes'}}{'Allocation Method'} and
		    $CLUSTER_INFO{'FlexNet'}{'Pools'}{$policy->{'Restrict Source Nodes'}}{'Allocation Method'} =~ /dynamic/i) {
		    push(@restricted_to_dynamic_pool, $policy->{'Name'});
		}
	    }
	}
	# Check if there are only Dynamic pools configured
	foreach (keys %{$CLUSTER_INFO{'FlexNet'}{'Pools'}}) {
	    my $pool = $CLUSTER_INFO{'FlexNet'}{'Pools'}{$_};
	    if ($pool->{'Allocation Method'} and
		$pool->{'Allocation Method'} =~ /dynamic/i) {
		$only_dynamic_pools_available = 1;
	    } else {
		$only_dynamic_pools_available = 0;
		last;
	    }
	}
    }
	my $fail_disabled_recent_policies;
	foreach my $p (keys %{$CLUSTER_INFO{'SyncIQ'}{'Recent Policies'}{'disabled'}}){
		debug("CHECKING $p");
		debug("CHECKING $CLUSTER_INFO{'SyncIQ'}{'Recent Policies'}{$p}");
		foreach my $k (keys %{$CLUSTER_INFO{'SyncIQ'}{'Recent Policies'}{'disabled'}{$p}}) {
			debug("CHECKING $k");
	  		$fail_disabled_recent_policies = 1;
		}
	}

    if ($RPS{'SyncIQ'}) {
	if ($only_dynamic_pools_available or
	    @restricted_to_dynamic_pool) {
	    # Flag KB 000016953
	    if ((!isDestinationOneFS() and
		 isCurrentOneFS(qr/^7\.2\.1\.[012]$/)) or
		isDestinationOneFS(qr/^7\.2\.1\.[012]$/)) {
		fail($MSG_KB000016953);
	    }
	    # Warn about not using static pools for replication
	    if ($only_dynamic_pools_available) {
		addCustomerInformation(warning($MSG_DYNAMIC));
	    } else {
		addCustomerInformation(warning(sprintf($SPF_RESTRICTED, join(', ', @restricted_to_dynamic_pool))));
	    }
	}
	if (isCompliance() and
	    isCurrentOneFS('<8.2') and
	    (isDestinationOneFS('>=8.0.1') or
	     ($RPS{'Pre Upgrade Patches'} and isCurrentOneFS(qr/^8\.1/)))) {
	    addCustomerInformation(warning($MSG_COMPLIANCE));
	}
	if($fail_disabled_recent_policies){
		warning("Policies that have run recently are showing disabled, please ensure policy state is as expected.");
	}
	if($CLUSTER_INFO{'SyncIQ'}{'Targets_With_Encrypted_policies'}){
	   if(isCurrentOneFS('>=9.5.1.0') or (isCurrentOneFS('<9.5.1.0') and isDestinationOneFS('>=9.5.1.0'))){
          warning("Policies are enabled and encrypted for the following target clusters: ".join(",", @{$CLUSTER_INFO{'SyncIQ'}->{'Targets_With_Encrypted_policies'}}));
          if(isCurrentOneFS('<9.5.1.0') and isDestinationOneFS('>=9.5.1.0')){
			 info("Please ensure the Target clusters are upgraded to ".isDestinationOneFS()." prior to upgrading the source to avoid issues outlined in KB 000226063.");
			 addCustomerInformation("Your cluster has policies configured with encryption and enabled, please ensure your target clusters are on OneFS ".isDestinationOneFS()." or higher prior to upgrading source clusters to ".isDestinationOneFS()." to avoid potential issues outlined in https://www.dell.com/support/kbdoc/000226063");
		  }
		  else{
			info("Please ensure the Target clusters are upgraded to ".isCurrentOneFS()." or higher to avoid issues outlined in KB 000226063.");
			addCustomerInformation("Your cluster has policies configured with encryption and enabled, please ensure your target clusters are on OneFS ".isCurrentOneFS()." or higher to avoid potential issues outlined in https://www.dell.com/support/kbdoc/000226063");
		  }
		  info("Please reference https://www.dell.com/support/kbdoc/000226063 for additional information.");
        }
	}
    }
    if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000055897')) }
    printResults();
} # End checkSyncIQ
sub checkSyncIQSource {
    if (wasCalled()) { return }
    gatherSyncIQ();
    if (!$CLUSTER_INFO{'SyncIQ'}{'Source'}) { return }

    # Setup
    my $SPF_SYNCIQ = '%-32s %-30s %-10s %-20s %s';
    my $SPF_FAILED = 'The following outgoing SyncIQ policies are reporting failed: %s';
    my $SPF_SOURCE = 'Source to: %s';

    # Analyze
    my @target_clusters = ();
    my @failed_policies = ();
    verboseHeader('Outgoing SyncIQ');
    verbose(sprintf($SPF_SYNCIQ,
		    'Policy ID',
		    'Policy Name',
		    'Timestamp',
		    'Status',
		    'Source Cluster'));
    foreach my $pid (keys %{$CLUSTER_INFO{'SyncIQ'}{'Source'}}) {
	my $policy = $CLUSTER_INFO{'SyncIQ'}{'Source'}{$pid};

	# Remove and skip if the most recent start was over 90 days ago
	if (!$policy->{'Name'} or
	    !$policy->{'Timestamp'} or
	    !$policy->{'Target'} or
	    $RPS{'Timestamp'} - $policy->{'Timestamp'} >= 7776000) {
	    delete $CLUSTER_INFO{'SyncIQ'}{'Source'}{$pid};
	    next;
	}

	# Display
	verbose(sprintf($SPF_SYNCIQ,
			$pid,
			$policy->{'Name'},
			$policy->{'Timestamp'},
			($policy->{'Last Job State'} ||
			 $policy->{'summary.total.state'}),
			$policy->{'Target'}));

	# Gather a list of policies that are failed or needs_attention
	if ($policy->{'Enabled'} and
	    (($policy->{'Last Job State'} and
	      $policy->{'Last Job State'} eq 'failed') or
	     ($policy->{'summary.total.state'} and
	      $policy->{'summary.total.state'} eq 'failed'))) {
	    push(@failed_policies, $policy->{'Name'});
	}

	# Compile a list of target clusters
	my @results = grep(/$policy->{'Target'}/i, @target_clusters);
	if (@results) { next }
	push(@target_clusters, $policy->{'Target'});
    }

    # List target clusters
    if (@target_clusters) {
	details(sprintf($SPF_SOURCE, join(', ', @target_clusters)));
    }

    # Flag policies with failures
    if (@failed_policies and
	$RPS{'CheckList'} eq 'healthcheck' and
	!$RPS{'JSON'}) {
	fail(sprintf($SPF_FAILED, join(', ', @failed_policies)));
    }
}
sub checkSyncIQTarget {
    if (wasCalled()) { return }
    gatherSyncIQ();
    if (!$CLUSTER_INFO{'SyncIQ'}{'Target'}) { return }

    # Setup
    my $SPF_SYNCIQ = '%-32s %-30s %-10s %-20s %s';
    my $SPF_FAILED = 'The following incoming SyncIQ policies are reporting failed: %s';
    my $SPF_TARGET = 'Target for %s';
    my $SPF_TGT_IFS = 'The incoming SyncIQ policy %s has %s as a target path. This may cause unexpected behavior when gathering upgrade assessment details. '.sprintf($MSG{'KB_1'}, '000198373');
    my $RE_CHK_TGT = qr[^/ifs(?:/data)?/?$];

    # Analyze
    my @source_clusters = ();
    my @failed_policies = ();
    my @target_path_policies = ();
    verboseHeader('Incoming SyncIQ');
    verbose(sprintf($SPF_SYNCIQ,
		    'Policy ID',
		    'Policy Name',
		    'Timestamp',
		    'Status',
		    'Source Cluster'));
    foreach my $pid (keys %{$CLUSTER_INFO{'SyncIQ'}{'Target'}}) {
	my $policy = $CLUSTER_INFO{'SyncIQ'}{'Target'}{$pid};

	# Remove and skip if the most recent timestamp is over 90 days ago
	if (!$policy->{'Name'} or
	    !$policy->{'Timestamp'} or
	    !$policy->{'Last Job State'} or
	    !$policy->{'Source'} or
	    $RPS{'Timestamp'} - $policy->{'Timestamp'} >= 7776000) {
	    delete $CLUSTER_INFO{'SyncIQ'}{'Target'}{$pid};
	    next;
	}

	# Display
	verbose(sprintf($SPF_SYNCIQ,
			$pid,
			$policy->{'Name'},
			$policy->{'Timestamp'},
			$policy->{'Last Job State'},
			$policy->{'Source'}));

	# Most recent SyncIQ job failed
	if ($policy->{'Last Job State'} eq 'SIQ_JS_FAILED') {
	    push(@failed_policies, $policy->{'Name'});
	}

	# Add source cluster
	my @results = grep(/^$policy->{'Source'}$/i, @source_clusters);
	if (!@results) { push(@source_clusters, $policy->{'Source'}) }

        # Check the target directory to be /ifs or /ifs/data
        if ($policy->{'Target Directory'} and
            $policy->{'Target Directory'} =~ $RE_CHK_TGT) {
            push(@target_path_policies, $policy);
        }
    }

    # List source clusters
    if (@source_clusters) {
	details(sprintf($SPF_TARGET, join(', ', @source_clusters)));
    }

    # Flag policies with failures
    if (@failed_policies and
	$RPS{'CheckList'} eq 'healthcheck' and
	!$RPS{'JSON'}) {
	fail(sprintf($SPF_FAILED, join(', ', @failed_policies)));
    }

    # Flag /ifs/ /ifs/data/ target path issues for upgrades
    if (@target_path_policies) {
        foreach my $policy (@target_path_policies) {
            warning(sprintf($SPF_TGT_IFS, $policy->{'Name'}, $policy->{'Target Directory'}));
        }
    }
}
###
# gatherSyncIQ: Gathers SyncIQ policies and source and target records.
sub gatherSyncIQ {
    if (wasCalled()) { return }
    gatherSyncIQTargetRecords();
    gatherSyncIQSourceRecords();
    gatherSyncIQSourceReports();
    gatherSyncIQPoliciesGC();
    gatherSyncIQPoliciesList();
    debug('CLUSTER_INFO->SyncIQ='.toJSON($CLUSTER_INFO{'SyncIQ'}));
} # End gatherSyncIQ
# gatherSyncIQPoliciesGC: Gather SyncIQ policy information from GConfig
sub gatherSyncIQPoliciesGC {
    if (wasCalled()) { return }

    # Setup
    my %SECTIONS = (
	'common.name' => 'Name',
	'common.pid' => 'ID',
	'common.state' => 'Enabled',
	'target.dst_cluster_name' => 'Target',
	'target.restrict_by' => 'Restrict Source Nodes',
	'scheduler.max_reports' => 'Report Max Count',
	'scheduler.rotate_report_period' => 'Report Max Age',
	);
    my @SECTIONS = keys %SECTIONS;
    my $RE_ID = qr/^policy\.(\d+)/;
    local $" = '|';
    my $CMD_GREP = qq[egrep -h '(^policy\.[^.]+ |@SECTIONS)'];
    my $CMD_LIVE = qq[2>&1 $CMD_GREP /ifs/.ifsvar/modules/tsm/config/siq-policies.gc];
    my $CMD_LOGS = qq[2>&1 $CMD_GREP local/ifsvar_modules.tar/modules/tsm/config/siq-policies.gc];

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Parse
    my %policies = ();
    my $index = '';
    foreach my $line (@$gather) {
	if ($line =~ $RE_ID) { $index = $1 }
	if (!defined $index or $line !~ /^policy\.$index\./) { next }
	my ($key, $value) = parseGconfigItem($line);
	($key) = grep {$key =~ /$_/} @SECTIONS;
	if (!$key) { next }
	if ($key eq 'common.state' and $value ne 'on') { next }
	if ($key eq 'target.restrict_by' and $value eq '<null>') { next }
	$policies{$index}{$SECTIONS{$key}} = $value;
    }

    # Store
    foreach my $index (keys %policies) {
	my $policy = $policies{$index};
	if (!$policy->{'ID'}) { next }
	my $pid = $policy->{'ID'};
	foreach my $key (keys %{$policy}) {
	    $CLUSTER_INFO{'SyncIQ'}{'Source'}{$pid}{$key} = $policy->{$key};
	    debug("CLUSTER_INFO->SyncIQ->Source->${pid}->${key}=$policy->{$key}");
	}
    }
} # End gatherSyncIQPoliciesGC
# gatherSyncIQPoliciesList: Check SyncIQ policy information
sub gatherSyncIQPoliciesList {
    if (wasCalled()) { return }

    # Setup
    my @SECTIONS = (
	'ID',
	'Name',
	'Action',
	'Enabled',
	'Target',
	'Encrypted',
	'Source Subnet',
	'Source Pool',
	'Last Job State',
	'Last Started',
	'Report Max Count',
	'Report Max Age');
    local $" = '|';
    my $CMD_GREP = qq[egrep '^ *(@SECTIONS):'];
    
	my $CMD_LIVE;
	if(isCurrentOneFS('<9.10')){
		$CMD_LIVE = qq[2>&1 ${SUDO} isi sync pol list -v | $CMD_GREP];
	}
	else{
		$CMD_LIVE = qq[2>&1 isi sync pol list -v | $CMD_GREP];
	}
    
	my $CMD_LOGS = qq[2>&1 $CMD_GREP -h */isi_sync_policy];
    my $RE_DATETIME = qr/(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)/;

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Parse
    my %siq_policies = ();
	my @targets_with_encrypted_policies = ();
    my $pid = '';
    foreach (@$gather) {
	s/^ *//;
	my ($key, $value) = split(/:\s+/, $_, 2);
	if ($value eq '-') { next }
	if ($key eq 'ID') {
	    $pid = $value;
	    if ($siq_policies{$pid}) { last }
	}
	if ($pid and $key and $value) {
	    $siq_policies{$pid}{$key} = $value;
	}
    }

    # Store
    foreach my $pid (keys %siq_policies) {
	my $policy = $siq_policies{$pid};

	# Calculate timestamp using last started
	if ($policy->{'Last Started'} and
	    $policy->{'Last Started'} =~ $RE_DATETIME) {
	    $policy->{'Timestamp'} =
		timelocal($6, $5, $4, $3, $2 - 1, $1);
	}

	# Skip policies last started over 90 days
	if (defined $policy->{'Timestamp'} and
	    $RPS{'Timestamp'} - $policy->{'Timestamp'} > 7776000) {
	    next;
	}
	else{
		debug("RECENT POLICY = $pid -> $key = $policy->{$key}");
		$CLUSTER_INFO{'SyncIQ'}{'Recent Policies'}{$pid}{$key} = $policy->{$key};
	}	

	# Delete the Enabled key when the policy is not enabled
	if ($policy->{'Enabled'} and
	    $policy->{'Enabled'} ne 'Yes') {
		debug("DISABLED POLICY = $pid -> $key = $policy->{$key}");
		$CLUSTER_INFO{'SyncIQ'}{'Disabled Policies'}{$pid}{$key} = $policy->{$key};
		$CLUSTER_INFO{'SyncIQ'}{'Recent Policies'}{'disabled'}{$pid}{$key} = $policy->{$key};
	    delete $policy->{'Enabled'};
	}
	else{
		debug("ENABLED POLICY = $pid -> $key = $policy->{$key}");
		$CLUSTER_INFO{'SyncIQ'}{'Enabled Policies'}{$pid}{$key} = $policy->{$key};
	}

	# Set Restrict Source Nodes to the Subnet:Pool specified
	if ($policy->{'Source Pool'} and
	    $policy->{'Source Subnet'}) {
	    $policy->{'Restrict Source Nodes'} =
		"$policy->{'Source Subnet'}:$policy->{'Source Pool'}";
	}
	# Convert Report Max Age to seconds
	if ($policy->{'Report Max Age'}) {
	    $policy->{'Report Max Age'} =
		convertDurationToSeconds($policy->{'Report Max Age'});
	}
    if($policy->{'Enabled'} and $policy->{'Enabled'} eq 'Yes' and $policy->{'Encrypted'} and $policy->{'Encrypted'} eq 'Yes'){
        push(@targets_with_encrypted_policies, $policy->{'Target'}) unless grep{$_ == $policy->{'Target'}} @targets_with_encrypted_policies;
    };
	# Store to CLUSTER_INFO
	foreach my $key (keys %{$policy}) {
	    $CLUSTER_INFO{'SyncIQ'}{'Source'}{$pid}{$key} = $policy->{$key};
	    debug("CLUSTER_INFO->SyncIQ->Source->${pid}->${key}=$policy->{$key}");
	}
	debug("CLUSTER_INFO->SyncIQ->Source->${pid}=".toJSON($policy));
    }
    if(@targets_with_encrypted_policies){ push(@{$CLUSTER_INFO{'SyncIQ'}{'Targets_With_Encrypted_policies'}},@targets_with_encrypted_policies);};
	
} # End gatherSyncIQPoliciesList
# gatherSyncIQSourceRecords: Gather outgoing SyncIQ information from record files
sub gatherSyncIQSourceRecords {
    if (wasCalled()) { return }

    # Setup
    my @SECTIONS =
	('policy-record .*',
	 'start-sched',
	 'end-sched');
    my @FILES_LIVE =
	('/ifs/.ifsvar/modules/tsm/config/source_record.xml',
	 '/ifs/.ifsvar/modules/tsm/config/source_records/*.xml');
    my @FILES_LOGS =
	('local/ifsvar_modules.tar/modules/tsm/config/source_record.xml',
	 'local/ifsvar_modules.tar/modules/tsm/config/source_records/*.xml');
    local $"='|';
    my $CMD_GREP = qq[egrep '<(@SECTIONS)>'];
    my $RE_PID = qr/policy-id="([^"]*)"/;
    my $RE_SECTION = qr/<(@SECTIONS)>([^<]*)</;
    local $"=' ';
    my $CMD_LIVE = qq[2>&1 $CMD_GREP @FILES_LIVE];
    my $CMD_LOGS = qq[2>&1 $CMD_GREP @FILES_LOGS];

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Parse
    my %siq_outgoing = ();
    my $pid = undef;
    foreach my $line (@$gather) {
	if ($line =~ $RE_PID) { $pid = $1 }
	if (!defined $pid) { next }
	if ($line =~ $RE_SECTION) { $siq_outgoing{$pid}{$1} = $2 }
    }

    # Store
    foreach my $pid (keys %siq_outgoing) {
	# Filter out policies with missing details or more than 90 days since last ran
	if ($siq_outgoing{$pid}{'end-sched'} and
	    $siq_outgoing{$pid}{'start-sched'} and
	    $RPS{'Timestamp'} - $siq_outgoing{$pid}{'end-sched'} <= 7776000) {
	    $CLUSTER_INFO{'SyncIQ'}{'Source'}{$pid} = $siq_outgoing{$pid};
	    debug("CLUSTER_INFO->SyncIQ->Source->${pid}=".toJSON($siq_outgoing{$pid}));
	}
    }
} # End gatherSyncIQSourceRecords
# gatherSyncIQSourceReports: Gather outgoing SyncIQ information from report files
sub gatherSyncIQSourceReports {
    if (wasCalled()) { return }
    if (!$CLUSTER_INFO{'SyncIQ'}{'Source'}) { return }

    # Setup
    my @SECTIONS = ('summary\.total\.state', 'summary\.errors.*msg');
    local $"='|';
    my $CMD_GREP = qq[egrep '^(@SECTIONS)'];
    my $SPF_LIVE = qq[2>&1 $CMD_GREP /ifs/.ifsvar/modules/tsm/sched/reports/%s/report-%s.gc];
    my $SPF_LOGS = qq[2>&1 $CMD_GREP local/ifsvar_modules.tar/modules/tsm/sched/reports/%s/report-%s.gc];

    foreach my $pid (keys %{$CLUSTER_INFO{'SyncIQ'}{'Source'}}) {
	my %policy = %{$CLUSTER_INFO{'SyncIQ'}{'Source'}{$pid}};
	if (!$policy{'end-sched'}) { next }
	my $CMD_LIVE = sprintf($SPF_LIVE, $pid, $policy{'end-sched'});
	my $CMD_LOGS = sprintf($SPF_LOGS, $pid, $policy{'end-sched'});

	# Gather
	my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

	# Parse
	foreach my $line (@$gather) {
	    my ($key, $value) = parseGconfigItem($line);
	    $CLUSTER_INFO{'SyncIQ'}{'Source'}{$pid}{$key} = $value;
	    debug("CLUSTER_INFO->SyncIQ->Source->${pid}->${key}=$value");
	}
    }
} # End gatherSyncIQSourceReports
# gatherSyncIQTargetRecords: Gather incoming SyncIQ info from record files
sub gatherSyncIQTargetRecords {
    if (wasCalled()) { return }

    # Setup
    my %SECTIONS =
	('id' => 'ID',
	 'policy-name' => 'Name',
	 'src-cluster-name' => 'Source',
	 'job-status' => 'Last Job State',
	 'job-status-timestamp' => 'Timestamp',
	 'target-dir' => 'Target Directory');
    my @SECTIONS = keys %SECTIONS;
    my @FILES_LIVE =
	('/ifs/.ifsvar/modules/tsm/config/target_record.xml',
	 '/ifs/.ifsvar/modules/tsm/config/target_records/*.xml');
    my @FILES_LOGS =
	('local/ifsvar_modules.tar/modules/tsm/config/target_record.xml',
	 'local/ifsvar_modules.tar/modules/tsm/config/target_records/*.xml');
    local $"='|';
    my $CMD_GREP = qq[egrep '<(@SECTIONS)>'];
    my $RE_PID = qr/<id>([^<]*)</;
    my $RE_SECTION = qr/<(@SECTIONS)>([^<]*)</;
    local $"=' ';
    my $CMD_LIVE = qq[2>&1 $CMD_GREP @FILES_LIVE];
    my $CMD_LOGS = qq[2>&1 $CMD_GREP @FILES_LOGS];

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Parse
    my %policies = ();
    my $pid = '';
    foreach my $line (@$gather) {
	if ($line =~ $RE_PID) { $pid = $1 }
	if (!$pid) { next }
	if ($line =~ $RE_SECTION) { $policies{$pid}{$SECTIONS{$1}} = $2 }
    }

    # Store
    foreach my $pid (keys %policies) {
	my $policy = $policies{$pid};
	# Filter out policies missing details or 90 days since last ran
	if ($policy->{'Name'} and
	    $policy->{'Source'} and
	    $policy->{'Last Job State'} and
	    $policy->{'Timestamp'} and
	    $RPS{'Timestamp'} - $policy->{'Timestamp'} <= 7776000) {
	    $CLUSTER_INFO{'SyncIQ'}{'Target'}{$pid} = $policy;
	    debug("CLUSTER_INFO->SyncIQ->Target->${pid}=".toJSON($policy));
	}
    }
} # End gatherSyncIQTargetRecords
$CHECKS{'checkAPIAuth'} = {
    'Description' => 'checks for API auth set to basic where upgrading from 8.1.2.0 to 9.2 or higher',
    'Exclude' => 1,
};
sub checkAPIAuth {
    if (wasCalled()) { return }
    #if (isCurrentOneFS('>8.1.2.0')) { return unsupported('UNSUP_CUR') }
    #if (!$RPS{'Target'} or compareVersions($RPS{'Target'}, '9.2') < 0) {
	#return unsupported('UNSUP_TGT_1', '9.2 or later');
    #}

    # Setup
    my $CMD_LIVE; 
    if(isCurrentOneFS('>=9.12') and isCompliance()){
        $CMD_LIVE = qq[2>&1 ${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig -t web-config | grep -I auth];
    }
    else{
        $CMD_LIVE = qq[2>&1 ${SUDO} isi_gconfig -t web-config | grep -I auth];
    }
    my $CMD_LOGS = q[2>&1 grep -H 'auth_basic' local/ifsvar_modules.tar/modules/http/webui_config.gc];

    # Start
    printCheck('API Auth');

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    my $auth_basic_set;

    # Analyze
    if(!$gather || $gather eq ''){
        if(!isLive()){
            warning("Could not obtain WebUI Authentication setting 'auth_basic' from gconfig. Please check on live cluster.");
        }
    }
    foreach my $line (@$gather) {
        if($line =~ /^\s*(\S+)\s*.*=\s*\"?(\S+)\"?$/){
            debug("Param $1 is set to $2");
            if($2 =~ /.*true.*$/){
                $auth_basic_set = 1;
            }
        }
    }

    if($auth_basic_set){
    if((isCurrentOneFS('<8.1.2.0') or (isCurrentOneFS('=8.1.2.0') and isDestinationOneFS('>=9.2')) and !hasPatch('_PSP-1194$'))){
        if(isCurrentOneFS('<8.1.2.0')){
            critical('HTTP basic auth is enabled, this setting is automatically disabled when upgrading to 9.2 or higher.');
            info('If this setting is required to be enabled, please consider upgrading to 8.2.2 prior to upgrading or manually re enable once the upgrade is completed.');
        }
        else{
            critical('HTTP basic auth is enabled, this setting is automatically disabled when upgrading to 9.2 or higher.');
            info('If this setting is required to be enabled, please consider installing the latest RUP prior to upgrading or manually re enable once the upgrade is completed.');        
        }
    }
    info('HTTP API Basic Authentication is enabled. Leaving basic authentication enabled may be less secure than disabling it, depending on your security environment and requirements.');
    info('API Basic auth can be disabled by running \'isi_gconfig -t web-config auth_basic=false\'');
    
        #info("Web Config 'auth_basic' is currently enabled, Please note that basic authentication method is not recommended.");
    
    }

    # Output
    printResults();
} # End checkAPIAuth
$CHECKS{'checkAspera'} = {
    'Description' => 'Checks if any aspera services are enabled. If performing OneFS upgrades it must be reinstalled post-upgrade',
    'Exclude' => 1,
};
$MSG{'checkAspera_REINSTALL'} = 'If running IBM Aspera for Isilon, it must be reinstalled after OneFS upgrades to restore the Aspera service. Refer to IBM Aspera documentation for more details.';
$MSG{'checkAspera_SUPPORT'} = 'If running IBM Aspera for Isilon, it is only supported up to OneFS 8.1.2. Refer to IBM Aspera documentation for more details.';
sub checkAspera {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>8.1.2')) { return unsupported('UNSUP_CUR') }
    if (!isOneFSUpgrade()) { return unsupported('UNSUP_TGT') }
    if (!isServiceEnabled('asperacentral') and
	!isServiceEnabled('asperanoded')) {
	return unsupported('UNSUP_SVC_1', 'asperacenteral or asperanoded');
    }

    # Setup
    printCheck('Aspera');

    # Flag
    if (isDestinationOneFS('>8.1.2')) {
	addCustomerInformation(warning($MSG{'checkAspera_SUPPORT'}));
    } else {
	addCustomerInformation(warning($MSG{'checkAspera_REINSTALL'}));
    }

    # Output
    printResults();
}
###
# checkAuthStatus: Checks authentication status on each node.
#  Warns if any authentication providers are not online or active.
$CHECKS{'checkAuthStatus'} = {
    'Description' => 'Checks authentication status on each node. Warns if any authentication providers are not online or active.  Checks for RFC2307 and Auto-locate of GID/UIDs and points to KB 000028577',
    'Exclude' => 1,
};
sub checkAuthStatus {
    if (wasCalled()) { return }
    printCheck("Authentication Status");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	if (isCurrentOneFS('>=7')) {
	    $gather = gatherCluster("2>&1 ${SUDO} isi_for_array isi auth status --verbose --format list");
		
	} else {
	    $gather = gatherCluster("2>&1 ${SUDO} isi_for_array isi auth status --verbose");
	}
    } else {
	$gather = gatherCluster("2>&1 grep -H . */isi_auth_ads_status */isi_auth");
    }

    # Analyze information for issues.
    foreach my $lnn (@LNNS) {
	verboseHeader($NODE_INFO{$lnn}{'Name'});
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	my $id = "";
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ /^\s*Domain:.*?(\S+)/ or
		$line =~ /^\s*ID:.*?(\S+)/) {
		$id = $1;
	    }
	    next unless $id;
	    if ($line =~ /^\s*Status:.*?(\S+)/) {
		my $status = $1;
		verbose("Provider:$id Status:$status");
		critical("On node $lnn, authentication provider $id is $status") if ($status !~ /active|online|disabled/);
		$id = "";
	    }
	}
    }
    checkRFC2307();
    info(sprintf($MSG{'KB_1'}, '000035640')) if isResults(&WARN);
    # Output Check result
    printResults();
} # End checkAuthStatus
$CHECKS{'checkCapacity'} = {
    'Description' => 'Verifies cluster capacity based on documented numbers the Upgrade Planning and Progress Guide. Warns if approaching',
    'Exclude' => 1,
};
$MSG{'checkCapacity_ALL'} = 'All pools above 90% capacity';
$MSG{'checkCapacity_POOL_2'} = 'Pool %s at %s%% capacity';
$MSG{'checkCapacity_CLUSTER_1'} = 'Cluster is %s%% used';
$MSG{'checkCapacity_NODE_SSD_2'} = 'Node %s SSD Capacity is at %s%%';
$MSG{'checkCapacity_NODE_2'} = 'Node %s is at %s%% capacity';
$MSG{'checkCapacity_INFO'} = 'Per the "Check for free node space" section of the Upgrade Planning and Process Guide (https://dl.dell.com/content/manual81478978): The total amount of used space on the entire cluster must not exceed 90 percent full, and the total amount of used space on each node must not exceed 92 percent full. Do not continue with the OneFS upgrade if the minimum available-space requirement for the cluster is not met.';
sub checkCapacity {
    if (wasCalled()) { return }

    # Start
    printCheck('Cluster Capacity');

    # Gather
    gatherIsiStatPools();
    gatherStat();

    # Analyze
    verboseHeader('Cluster Capacity');
    if ($CLUSTER_INFO{'VHS Size'}) { verbose("Cluster VHS Size: $CLUSTER_INFO{'VHS Size'}") }
    if ($CLUSTER_INFO{'HDD Storage'}) { verbose("Cluster HDD Storage: $CLUSTER_INFO{'HDD Storage'}") }
    if ($CLUSTER_INFO{'HDD Used'}) { verbose("Cluster HDD Used: $CLUSTER_INFO{'HDD Used'}") }
    if ($CLUSTER_INFO{'SSD Storage'}) { verbose("Cluster SSD Storage: $CLUSTER_INFO{'SSD Storage'}") }
    if ($CLUSTER_INFO{'SSD Used'}) { verbose("Cluster SSD Used: $CLUSTER_INFO{'SSD Used'}") }
    my @pools_over_90_percent = ();
    my @pools_over_88_percent = ();
    my @all_pools = ();
    if ($CLUSTER_INFO{'Pools'}) {
	foreach my $name (keys %{$CLUSTER_INFO{'Pools'}}) {
	    my %pool = %{$CLUSTER_INFO{'Pools'}{$name}};
	    if ($pool{'Health'}) { verbose("Pool $name Health: $pool{'Health'}") }
	    if ($pool{'Protection'}) { verbose("Pool $name Protection: $pool{'Protection'}") }
	    if ($pool{'VHS'}) { verbose("Pool $name VHS Size: $pool{'VHS'}") }
	    if ($pool{'Percent Used'}) { verbose("Pool $name Percent Used: $pool{'Percent Used'}%") }
	    if ($pool{'HDD Storage'}) { verbose("Pool $name HDD Storage: $pool{'HDD Storage'}") }
	    if ($pool{'HDD Storage'}) { verbose("Pool $name SSD Storage: $pool{'SSD Storage'}") }
	    if (!$pool{'Percent Used'}) { next }
	    push(@all_pools, $name);
	    if ($pool{'Percent Used'} >= 90) {
		push(@pools_over_90_percent, $name);
	    } elsif ($pool{'Percent Used'} >= 88) {
		push(@pools_over_88_percent, $name);
	    }
	}
    }

    # Flag
    # Storage Pool Usage
    if (@pools_over_90_percent and $#pools_over_90_percent eq $#all_pools) {
	fail($MSG{'checkCapacity_ALL'});
    }
    if (isCurrentOneFS('>=8')) {
	foreach my $pool (@pools_over_90_percent) {
	    fail(sprintf($MSG{'checkCapacity_POOL_2'}, $pool, $CLUSTER_INFO{'Pools'}{$pool}{'Percent Used'}));
	}
    } else {
	foreach my $pool (@pools_over_90_percent) {
	    warning(sprintf($MSG{'checkCapacity_POOL_2'}, $pool, $CLUSTER_INFO{'Pools'}{$pool}{'Percent Used'}));
	}
    }
    foreach my $pool (@pools_over_88_percent) {
	warning(sprintf($MSG{'checkCapacity_POOL_2'}, $pool, $CLUSTER_INFO{'Pools'}{$pool}{'Percent Used'}));
    }

    # Total Cluster Usage
    if ($CLUSTER_INFO{'HDD Percent Used'} and $CLUSTER_INFO{'HDD Percent Used'} >= 90) {
	fail(sprintf($MSG{'checkCapacity_CLUSTER_1'}, $CLUSTER_INFO{'HDD Percent Used'}));
    } elsif ($CLUSTER_INFO{'HDD Percent Used'} and $CLUSTER_INFO{'HDD Percent Used'} >= 88) {
	warning(sprintf($MSG{'checkCapacity_CLUSTER_1'}, $CLUSTER_INFO{'HDD Percent Used'}));
    }

    # Per Node Usage
    foreach my $lnn (@LNNS) {
	if ($NODE_INFO{$lnn}{'SSD Storage'} and $NODE_INFO{$lnn}{'SSD Storage'} =~ /(\d+)%/) {
	    if ($1 >= 92) {
		fail(sprintf($MSG{'checkCapacity_NODE_SSD_2'}, $lnn, $1));
	    } elsif ($1 >= 88) {
		warning(sprintf($MSG{'checkCapacity_NODE_SSD_2'}, $lnn, $1));
	    }
	}
	if (!$NODE_INFO{$lnn}{'Percent Used'}) { next }
	verbose(sprintf($MSG{'checkCapacity_NODE_2'}, $lnn, $NODE_INFO{$lnn}{'Percent Used'}));
	if ($NODE_INFO{$lnn}{'Percent Used'} >= 92) {
	    fail(sprintf($MSG{'checkCapacity_NODE_2'}, $lnn, $NODE_INFO{$lnn}{'Percent Used'}));
	} elsif ($NODE_INFO{$lnn}{'Percent Used'} >= 88) {
	    warning(sprintf($MSG{'checkCapacity_NODE_2'}, $lnn, $NODE_INFO{$lnn}{'Percent Used'}));
	}
    }

    # Output
    if (isResults(&WARN)) {
	addCustomerInformation($MSG{'checkCapacity_INFO'});
	info(sprintf($MSG{'KB_1'}, '000015450'));
    }
    printResults();
} # End checkCapacity
$CHECKS{'checkConfCmtSyntax'} = {
    'Description' => 'Checks sysctl.conf for comments without leading # symbol that would pose an issue in parsing conf file.',
};
sub checkConfCmtSyntax {
    if (wasCalled()) { return }
    
    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array "grep -Ev '.*\#.*' /etc/sysctl.conf | grep -v '.=.*\$' | grep -v '^\$'" ];
    my $CMD_LOGS = q[2>&1; grep -wv '.*#.*' */etc.tar/etc/mcp/override/sysctl.conf | grep -v '.=.*$' | grep -v '^$'];

    # Start
    printCheck('Conf File Syntax');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    
    # Analyze
    my @nodes = ();
    foreach my $lnn (@LNNS) {
        foreach $l (@{$gather->{$lnn}}){
            if($l !~ /^\s*$/){
                push(@nodes, $lnn);
            }
        }
    }


    # Flag
    if(@nodes){
        fail('Nodes have been identified with lines that may pose an issue with parsing in /etc/sysctl.conf: '.compressRange(@nodes));
        info('Please referenced KB 000218020 (https://www.dell.com/support/kbdoc/000218020) for additional information.');
        info('Please engage Technical Support to assist in resolving this issue.');
    }

    # Output
    printResults();
} # End checkConfCmnSyntax
$CHECKS{'checkContact'} = {
    'Description' => 'Displays contact information configured in CELog when run with the --extra argument',
    'Exclude' => 1,
};
$MSG{'checkContact_SWID'} = 'Isilon Software Identifier not found in the Secondary Name field';
sub checkContact {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LIVE;
	if(isCurrentOneFS('>=9.12') and isCompliance()){
		$CMD_LIVE = qq[2>&1 ${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig celog.contact_info];
	}
	else{
    	$CMD_LIVE = qq[2>&1 ${SUDO} isi_gconfig celog.contact_info];
	}
	my $CMD_LOGS = q[2>&1 grep -h celog.contact_info local/isi_gconfig local/gconfig_ifsvar.tar/ifs/_ifsvar/main_config.gc];
    my $RE_SWID = qr/^8\.1\.1\.0$|^8\.1\.0\.[012]$/;

    # Start
    printCheck('Contact Information');
    gatherLicense();

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    verboseHeader('Contact Information');
    my $secondary_name;
    foreach my $line (@$gather) {
	my ($key, $value) = parseGconfigItem($line);
	if ($key eq 'celog.contact_info.company') {
	    verbose("Company Name: $value");
	} elsif ($key eq 'celog.contact_info.location') {
	    verbose("Location: $value");
	} elsif ($key eq 'celog.contact_info.primary_name') {
	    verbose("Primary Name: $value");
	} elsif ($key eq 'celog.contact_info.primary_phone1') {
	    verbose("Primary Phone: $value");
	} elsif ($key eq 'celog.contact_info.primary_email') {
	    verbose("Primary E-mail: $value");
	} elsif ($key eq 'celog.contact_info.secondary_name') {
	    verbose("Secondary Name: $value");
	    $secondary_name = $value;
	} elsif ($key eq 'celog.contact_info.secondary_phone1') {
	    verbose("Secondary Phone: $value");
	} elsif ($key eq 'celog.contact_info.secondary_email') {
	    verbose("Secondary E-mail: $value");
	}
    }
    if ($CLUSTER_INFO{'OneFS License ID'} and
	$secondary_name !~ /$CLUSTER_INFO{'OneFS License ID'}/ and
	isCurrentOneFS($RE_SWID)) {
	warning($MSG{'checkContact_SWID'});
	info(sprintf($MSG{'KB_1'}, '000155886'));
    }

    # Output
    printResults();
} # End checkContact
$CHECKS{'checkCoreDumps'} = {
    'Description' => 'Checks for recent unexpected process restarts reported in /var/log/messages',
    'Exclude' => 1,
};
$MSG{'checkCoreDumps_CORES_1'} = 'Unexpected process restarts seen in the last 14 days on the following nodes: %s';
$MSG{'checkCoreDumps_DETAILS'} = 'For extra information on the times and processes associated with the unexpected process restarts found, run IOCA with the additional arguments --run=checkCoreDumps --extra';
sub checkCoreDumps {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'grep "core dumped" /var/log/messages'];
    my $CMD_LOGS = q[2>&1 grep -H "core dumped" */varlog.tar/log/messages];

    my $CMD_LIVE_CORE = qq[2>&1 ${SUDO} isi_for_array -s "ls -lD "%Y-%m-%dT%H:%m:%S" /var/crash | grep -E 'core' | awk '{print $5" "$6" "$7}'"];
    my $CMD_LOGS_CORE = q[2>&1; for d in *-*/ ; do { ls -hA --full-time ${d}cores.tar | grep -v '^total' | awk '{print $5" "$6"T"$7" "$9}' | while read -r line ; do echo "${d}cores.tar:$line"; done;} ; done;];

    my $EPOCH_14DAYS = $RPS{'Timestamp'} - 14 * 24 * 60 * 60;
    my $EPOCH_6MONTHS = $RPS{'Timestamp'} - 180 * 24 * 60 * 60;
    my $RE_IGNORE = qr/signal\s+[35]\s/;
    my $RE_TIMESTAMP = qr/^\s*(\S+)\s/;
    my $RE_PROCESS = qr/kernel:\s+pid\s+\d+\s+\((\S+)\)/;
    my $SPF_VERBOSE = '%-35s %s';
    my $SPF_VERBOSE_OF = '%-25s %-10s %-10s';
    my @nodes_core_gt2w = ();

    # Start
    printCheck('Core Dumps');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    my $gather_core = gatherCluster(isLive()?$CMD_LIVE_CORE:$CMD_LOGS_CORE);

    # Analyze
    my %coredumps = ();
    my %oldumps = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	foreach my $line (@{$gather->{$lnn}}) {
        if($line !~ /^\s*\d{4}\-\d{2}\-\d{2}T\d{2}:\d{2}:\d{2}.*/){next;}
	    # Ignore signals 3 and 5
	    if ($line =~ $RE_IGNORE) { next }

	    # Capture timestamp
	    if ($line !~ $RE_TIMESTAMP) { next }
	    my $time = $1;
	    my $epoch = parseTime($time);

	    # Ignore core dumps greater than 14 days old
	    if ($epoch < $EPOCH_14DAYS) { next }

	    # Capture process name
	    if ($line !~ $RE_PROCESS) { next }
	    my $process = $1;

	    # Display extra core dump details
	    if (!$coredumps{$lnn}) {
		verboseHeader("Node $lnn Coredumps");
		verbose(sprintf($SPF_VERBOSE, 'Timestamp in /var/log/messages', 'Process'));
	    }
	    verbose(sprintf($SPF_VERBOSE, $time, $process));
	    $coredumps{$lnn}++;
	}
	foreach my $l (@{$gather_core->{$lnn}}) {
            my $size;
            my $dts;
            my $file;
            my $cmpr_dts;
        if ($l =~ /^\s*(\S+)\s+(\S+)\s+(\S+)$/){
            $size = $1;
            $dts = $2;
            $file = $3;
            my $core_epoch;

            if($dts =~ /\s*(\d{4}\-\d{2}\-\d{2}T\d{2}:\d{2}:\d{2}).*/){
                $cmpr_dts = $1;
                $core_epoch = parseTime($dts."+00:00");
            }   
            debug("CHECK if $core_epoch is less than $EPOCH_14DAYS for $file with TimeStamp $dts");
            if ($core_epoch < $EPOCH_14DAYS) {
                push(@nodes_core_gt2w, $lnn);
                if (!$oldumps{$lnn}) {
                    verboseHeader("Node $lnn Old Core Files");
		            verbose(sprintf($SPF_VERBOSE_OF, 'Timestamp of Files', 'Size', 'Filename'));
	            }
                verbose(sprintf($SPF_VERBOSE_OF, $cmpr_dts, $size, $file));
	            $oldumps{$lnn}++;
            }
        }
    }
    }

    # Flag
    if (%coredumps) {
	warning(sprintf($MSG{'checkCoreDumps_CORES_1'}, compressRange(keys %coredumps)));
	warning($MSG{'checkCoreDumps_DETAILS'});
	info(sprintf($MSG{'KB_1'}, '000197933'));
    }
    if(@nodes_core_gt2w){
        warning("Core files found on the following nodes that are greater than 2 weeks old: ".compressRange(@nodes_core_gt2w));
        info("If these core files are no longer required, please ensure you remove them to avoid capacity issues in /var/crash.");    
    }

    # Output
    printResults();
} # End checkCoreDumps
$CHECKS{'checkCrontab'} = {
    'Description' => 'Checks for crontab / crontab.smbtime on /etc/mcp/override and provide recommendations to avoid overwriting / losing non default crontab after an upgrade.',
    'Exclude' => ,
};
sub checkCrontab {
    if (wasCalled()) { return };
    if(isCurrentOneFS('<9.5')){ return };

    # Setup
    checkPatch();
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array ls -l /etc/mcp/override/crontab*];
    my $CMD_LOGS = q[2>&1; for f in *-*/etc.tar/etc/mcp/override/crontab*; do echo "$f: $(basename "$f")"; done];

    # Start
    printCheck('Check Crontab');

    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    
    my @nodes = ();
    my @nodes_wc = ();
    my @nodes_cm = ();
    my %cron_info;
    
    # Analyze
    foreach my $lnn (@LNNS) {
        foreach my $line (@{$gather->{$lnn}}) {
            my $filename;
            debug("Checking file found in override: $line");
            if($line =~ /^\s*crontab$/){
                $filename = $1;
                debug("File matches default 'crontab': $filename");
                $cron_info{$lnn}{'has_cron'} = 1;
                #push(@{$cron_info{$lnn}{"files"}}, $filename);
            }
            if($line =~ /^\s*(crontab\S+)$/){
                $filename = $1;
                debug("Non default crontab found for node $lnn: $filename");
                $cron_info{$lnn}{'has_custom_cron'} = 1;
                push(@{$cron_info{$lnn}{"files"}}, $filename);
            }
        }
        if($cron_info{$lnn}{'has_cron'} and $cron_info{$lnn}{'has_custom_cron'}){
            # Both present!
            debug("Found both crontab and crontab.smbtime - additional steps required prior to upgrading.");
            push(@nodes, $lnn);
            verbose("Node $lnn: ".split(', ', @{$cron_info{$lnn}->{"files"}}));
        }
    }

        # Already on codes, not upgrading (TC for FW only, or, post upg)
        if(isOneFSUpgrade() && isCurrentOneFS('>=9.5') && $CLUSTER_INFO{'Has_HCF'}){
            if(@nodes){
                fail("Multiple crontab files found on the following nodes: ".compressRange(@nodes));                    
                critical("Please ensure you backup crontab file before any OneFS upgrade, please also ensure that any contents from non default (crontab) file moved to crontab file, then remove the non default crontab files.");
                info('To backup the files to Isilon_Support directory, from a node shown above, use: for f in /etc/mcp/override/crontab*; do cp "$f" "/ifs/data/Isilon_Support/$(basename "$f")_$(date +%Y%m%d_%H%M%S)"; done');
                info("Verify files were backed up: ls -al /ifs/data/Isilon_Support/crontab*");
                info("Append contents of non default crontab files to crontab, from a node shown above, example: cat /etc/mcp/override/crontab.smbtime >> /etc/mcp/override/crontab");
                info("Remove crontab.smbtime, for all nodes shown above, example: isi_for_array -n ".compressRange(@nodes)." rm /etc/mcp/override/crontab.smbtime");
                #info("Please reference KB< KB# > (< KB URL >) for more information.");
            }
        }

    # Output
    printResults();
} # End checkCrontab
$CHECKS{'checkEmailSettings'} = {
    'Description' => 'Displays E-mail settings configured in CELog when run with the --extra argument',
    'Exclude' => 1,
};
sub checkEmailSettings {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LIVE;
    if(isCurrentOneFS('>=9.12') and isCompliance()){
        $CMD_LIVE = qq[2>&1 ${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig celog.celog_settings];
    }
    else{
        $CMD_LIVE = qq[2>&1 ${SUDO} isi_gconfig celog.celog_settings];
    }
    my $CMD_LOGS = q[2>&1 grep -h 'celog.celog_settings' local/isi_gconfig local/gconfig_ifsvar.tar/ifs/_ifsvar/main_config.gc];

    # Start
    printCheck('Email Settings');

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    verboseHeader('Email Settings');
    foreach my $line (@$gather) {
	my ($key, $value) = parseGconfigItem($line);
	if ($key eq 'celog.celog_settings.mail_relay') {
	    verbose("SMTP Relay Address: $value");
	} elsif ($key eq 'celog.celog_settings.smtp_port') {
	    verbose("SMTP Relay Port: $value");
	} elsif ($key eq 'celog.celog_settings.mail_sender') {
	    verbose("Send E-mail as: $value");
	} elsif ($key eq 'celog.celog_settings.mail_subject') {
	    verbose("Subject Line: $value");
	} elsif ($key eq 'celog.celog_settings.use_smtp_auth') {
	    verbose("Use SMTP Authentication: $value");
	}
    }

    # Output
    printResults();
} # End checkEmailSettings
$CHECKS{'checkEncoding'} = {
    'Description' => 'Checks exports and cluster configuration for if utf-8/default encoding',
    'Exclude' => 1,
};
$MSG{'checkEncoding_ENC_1'} = 'The cluster is using %s encoding and should not be upgraded to 7.2 or later';
$MSG{'checkEncoding_IDS_1'} = 'The following NFS Export IDs: %s use non-DEFAULT/UTF-8 encoding and should not be upgraded to 7.2 or later';
sub checkEncoding {
    if (wasCalled()) { return }

    # Start
    printCheck('Cluster Encoding');
    verboseHeader('Cluster Encoding');

    # Gather
    gatherArrayXML();
    gatherNFSExport();

    # Analyze
    if ($CLUSTER_INFO{'Encoding'}) {
	verbose("Cluster Encoding: $CLUSTER_INFO{'Encoding'}");
    }
    my @ids = ();
    foreach my $ID (sort {$a <=> $b} keys %{$CLUSTER_INFO{'NFS Export'}}) {
	if (!$CLUSTER_INFO{'NFS Export'}{$ID}{'Encoding'}) { next }
	verbose("NFS Export ID $ID Encoding: $CLUSTER_INFO{'NFS Export'}{$ID}{'Encoding'}");
	if ($CLUSTER_INFO{'NFS Export'}{$ID}{'Encoding'} !~ /DEFAULT|UTF-8/i) {
	    push(@ids, $ID);
	}
    }

    # Flag
    if (isCurrentOneFS('<7.2') and
	isDestinationOneFS('>=7.2') and
	isDestinationOneFS('<8.1.1')) {
	if ($CLUSTER_INFO{'Encoding'} and $CLUSTER_INFO{'Encoding'} !~ /utf-8/) {
	    addCustomerInformation('ETA483840', fail(sprintf($MSG{'checkEncoding_ENC_1'}, $CLUSTER_INFO{'Encoding'})));
	}
	if (@ids) {
	    addCustomerInformation('ETA483840', fail(sprintf($MSG{'checkEncoding_IDS_1'}, join(',', @ids))));
	}
    }

    # Output
    if (isResults(&WARN)) { info(sprintf($MSG{'KB_INT_1'}, '335104')) }
    printResults($CLUSTER_INFO{'Encoding'});
} # End checkEncoding
sub checkEventChannel {
    if (wasCalled()) { return }

    gatherEventChannel();
    gatherRemoteSupport();

    if (!$CLUSTER_INFO{'Event Channels'}) { return }

    my $MSG = 'RemoteSupport (isi remotesupport) is enabled, but the RemoteSupport event channel is disabled and should be enabled for DialHome functionality. To enable the RemoteSupport event channel run the following command: isi event channel modify RemoteSupport --enabled=true';

    # Flag an issue where the RemoteSupport event channel is not enabled and
    #  the cluster is configured for remotesupport
    my $channels = $CLUSTER_INFO{'Event Channels'};
    my $disabled = 0;
    foreach my $id (sort keys %$channels) {
	if ($channels->{$id}{'Name'} eq 'RemoteSupport' and
	    $channels->{$id}{'Enabled'} ne 'Yes') {
	    $disabled = 1;
	}
    }
    if ($CLUSTER_INFO{'RemoteSupport'}{'Enabled'} and $disabled) {
	critical($MSG);
    }

    # Verbose output for event channels
    verboseHeader('Event Channels');
    my $spf = '%-5s %-10s %-10s %s';
    verbose(sprintf($spf, 'ID', 'Type', 'Enabled', 'Name'));
    verbose(sprintf($spf, '-'x5, '-'x10, '-'x10, '-'x20));
    foreach my $id (sort keys %$channels) {
	verbose(sprintf($spf,
			$channels->{$id}{'ID'},
			$channels->{$id}{'Type'},
			$channels->{$id}{'Enabled'},
			$channels->{$id}{'Name'}));
    }
}
$CHECKS{'checkEvents'} = {
    'Description' => 'Checks events on all the nodes, failure if any critical events exist',
    'Exclude' => 1,
};
sub checkEvents {
    return if wasCalled();
    printCheck("Critical Events");
    gatherEvents();

    # Analyze information for issues.
    foreach my $id (sort keys %{$CLUSTER_INFO{'Events'}}) {
	next unless $CLUSTER_INFO{'Events'}{$id}{'Severity'} and
	    $CLUSTER_INFO{'Events'}{$id}{'Ended'} and
	    $CLUSTER_INFO{'Events'}{$id}{'Lnn'} and
	    $CLUSTER_INFO{'Events'}{$id}{'Message'};
	if ($CLUSTER_INFO{'Events'}{$id}{'Severity'} =~ /^c/i and $CLUSTER_INFO{'Events'}{$id}{'Ended'} eq "--") {
	    fail("Critical event $id for node ".join(",", @{$CLUSTER_INFO{'Events'}{$id}{'Lnn'}}).": ".join(",", @{$CLUSTER_INFO{'Events'}{$id}{'Message'}}));
	}
    }

    info(sprintf($MSG{'KB_1'}, '000050913')) if isResults(&WARN);

    # Output Check result
    printResults();
} # End checkEvents
$CHECKS{'checkFileSharing'} = {
    'Description' => 'Checks if Atime is enabled',
    'Reference' => 'https://www.dell.com/support/kbdoc/000021583',
};
$MSG{'checkFileSharing_HOUR'} = 'Access time tracking is enabled and the grace period is less than one hour';
$MSG{'checkFileSharing_DAY'} = 'Acess time tracking is enabled and the grace period is less than one day';
sub checkFileSharing {
    if (wasCalled()) { return }

    # Setup
    printCheck('File Sharing');
    my $CMD_LIVE = "2>&1 ${SUDO} sysctl efs.bam.atime_enabled efs.bam.atime_grace_period";
    my $CMD_LOGS = '2>&1 grep -H atime local/ifsvar_etc.tar/etc/mcp/override/sysctl.conf';
    my $MS_DAY = 86400000;
    my $MS_HOUR = 3600000;

    # Gather information from cluster or from logset
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my $atime_enabled = 0;
    my $atime_grace_period = $MS_DAY;
    foreach (@$gather) {
	if (/atime_enabled.*?(\d+)$/) { $atime_enabled = $1 }
	if (/atime_grace_period.*?(\d+)$/) { $atime_grace_period = $1 }
    }

    # Flag
    if ($atime_enabled) {
	debug('access time tracking (atime) is enabled');
	if ($atime_grace_period < $MS_HOUR) { critical($MSG{'checkFileSharing_HOUR'}) }
	elsif ($atime_grace_period < $MS_DAY) { warning($MSG{'checkFileSharing_DAY'}) }
    }
    if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000021583')) }

    # Output
    printResults();
}
$CHECKS{'checkFirmwarePackages'} = {
    'Description' => 'In OneFS 9.1 and later, confirms firmware packages are available',
    'Reference' => 'https://www.dell.com/support/manuals/isilon-onefs/ifs_pub_event_reference/-powerscale-onefs-event-reference-guide?guid=guid-6975b061-50dd-46d7-9ac0-557a966b0800',
};
$MSG{'checkFirmwarePackages_MISSING_1'} = 'The cluster does not have the available firmware packages. To ensure the cluster is running qualified firmware, firmware packages must be present. Packages missing: %s';
$MSG{'checkFirmwarePackages_DSP'} = 'To install drive firmware, download the latest Drive Support Package, and run the "isi_dsp_install <path to DSP>" command.';
$MSG{'checkFirmwarePackages_NFP'} = 'To install node firmware, download the latest Node Firmware Package, and run the "isi upgrade firmware assess --fw-pkg=<path to NFP>" command.';
sub checkFirmwarePackages {
    if (wasCalled()) { return }
    if (!isLive()) { return unsupported('UNSUP_LOGS') }
    if (isVirtual()) { return unsupported('UNSUP_VIRT') }
    if (isCurrentOneFS('<9.1')) { return unsupported('UNSUP_CUR') }

    # Setup
    printCheck('Firmware Packages');
    my $CMD_LIVE = '2>&1 ls -1 /ifs/.ifsvar/packages/';
    my $RE_DSP = qr/Drive_Support_v\d+(?:\.\d+)*/;
    my $RE_NFP = qr/IsiFw_Package_v\d+(?:\.\d+)*(?:qa\d+)?/;

    # Gather
    my $gather = gatherLocal($CMD_LIVE);

    # Analyze
    my $has_dsp = 0;
    my $has_nfp = 0;
    foreach my $line (@$gather) {
	if ($line =~ $RE_DSP) { $has_dsp = 1 }
	if ($line =~ $RE_NFP) { $has_nfp = 1 }
    }

    # Flag
    if (!$has_dsp and !$has_nfp) {
	warning(sprintf($MSG{'checkFirmwarePackages_MISSING_1'},
			'Drive Support Package, Node Firmware Package'));
	warning($MSG{'checkFirmwarePackages_DSP'});
	warning($MSG{'checkFirmwarePackages_NFP'});
    } elsif (!$has_dsp) {
	warning(sprintf($MSG{'checkFirmwarePackages_MISSING_1'}, 'Drive Support Package'));
	warning($MSG{'checkFirmwarePackages_DSP'});
    } elsif (!$has_nfp) {
	warning(sprintf($MSG{'checkFirmwarePackages_MISSING_1'}, 'Node Firmware Package'));
	warning($MSG{'checkFirmwarePackages_NFP'});
    }

    # Output
    printResults();
} # End checkFirmwarePackages
$CHECKS{'checkFlush'} = {
    'Description' => 'Checks for running flush processes / active pre_flush screen sessions on clusters',
};
sub checkFlush {
    if (wasCalled()) { return }
    if (!isLive()) { return unsupported($MSG{'UNSUP_LOGS'}) }
    
    # Setup
    my $CMD_LIVE_proc = qq[2>&1 ${SUDO} isi_for_array -X 'ps -aux | grep "isi_flush" | grep -v grep | wc -l'];
    my $CMD_LIVE_screen = qq[2>&1 screen -ls | grep proactive_flush | wc -l];

    # Start
    printCheck('Proactive Flush');

    # Gather
    my $gather_proc = gatherCluster($CMD_LIVE_proc);
    my $gather_screen = gatherLocal($CMD_LIVE_screen);
    
    # Analyze
    my @nodes = ();
    my $current_proc_count;
    foreach my $lnn (@LNNS) {
        foreach $l (@{$gather_proc->{$lnn}}){
            debug("FLUSH PROCESS Checking $l");
            if($l =~ /^\s*([0-9]+)\s*$/){
                $current_proc_count = int($1);
                debug("FOUND FLUSH PROC COUNT: $current_proc_count");
                if ($current_proc_count gt 0){
                    debug("Flush Detected on node $lnn");
                    push(@nodes, $lnn);
                }
                else{
                    debug("No Flush Process on node $lnn");
                }
            }
        }
    }
    my ($flsc_res) = grep {$_ =~ /^\s*(\d+)\s*$/} @$gather_screen;
    
    # Flag
    if(@nodes || $flsc_res){
        if(@nodes){
            warning('Do not start another flush while there is one running, detected flush process running on the following nodes: '.compressRange(@nodes));
            info('Flush processes are expected during reboots of nodes, or as part of proactive flushing process.');
        }
        if(int($flsc_res) > 0 ){
            warning('Detected a proactive flush screen session that is still active, do not start another flush while there is one running.');
            info('To show active screen sessions, use \'screen -ls\'.');
        }
    }

    # Output
    printResults();
} # End checkFlush
$CHECKS{'checkGroups'} = {
    'Description' => 'Checks nodes for all enabled protocols.  Fails if group info is reporting that an enabled protocol is not functioning on any node',
    'Exclude' => 1,
};
$MSG{'checkGroups_FAIL_1'} = 'Not all enabled protocols are functioning on the following nodes: %s';
sub checkGroups {
    if (wasCalled()) { return }
    if (!isLive()) { return unsupported($MSG{'UNSUP_LOGS'}) }
    if (isCurrentOneFS('<8')) { return unsupported($MSG{'UNSUP_CUR'}) }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'sysctl efs.gmp.service.all_enabled_protocols.pid'];

    # Start
    printCheck('All Enabled Protocols');

    # Gather
    my $gather = gatherCluster($CMD_LIVE);
    gatherFlexNet();

    # Analyze
    my %not_all_enabled_protocols = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}

	# Skip nodes with no external network
	if ($NODE_INFO{$lnn}{'No External Network'}) {
	    debug("Node $lnn skipped, no external network configured");
	    next;
	}

	my @results = grep(/all_enabled_protocols.pid:\s+-1$/, @{$gather->{$lnn}});
	if (@results) { $not_all_enabled_protocols{$lnn} = 1 }
    }

    # Flag
    if (%not_all_enabled_protocols) {
	fail(sprintf($MSG{'checkGroups_FAIL_1'}, compressRange(keys %not_all_enabled_protocols)));
    }

    # Output
    printResults();
} # End checkGroups
$CHECKS{'checkHDFS'} = {
    'Description' => 'Display HDFS details, only useful when run with --extra',
    'Exclude' => 1,
};
sub checkHDFS {
    return if wasCalled();
    gatherLicense();
    if (!hasLicense('HDFS')) { return unsupported('UNSUP_LIC_1', 'HDFS') }
    printCheck('HDFS');
    checkHDFSAuthenticationMode();
    verboseHeader('HDFS');
    my ($license_href) = getLicense('HDFS');
    verbose("HDFS License Status: $license->{'Status'}") if $license->{'Status'};
    verbose("HDFS License Expiration: $license->{'Expiration'}") if $license->{'Expiration'};

    my $gather = ();
    if (isLive()) {
		if(isCurrentOneFS('>=9.13') and isCompliance()){
			$gather = gatherLocal("2>&1 ${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig -t hdfs");
		}
		else{
			$gather = gatherLocal("2>&1 ${SUDO} isi_gconfig -t hdfs");
		}
	} elsif ( -e 'local/isi_gconfig_hdfs') {
	$gather = gatherLocal('2>&1 cat local/isi_gconfig_hdfs');
    } elsif ( -e 'local/ifsvar_modules.tar/modules/hdfs_d/config/policy.gc') {
	$gather = gatherLocal('2>&1 cat local/ifsvar_modules.tar/modules/hdfs_d/config/policy.gc');
    }
    my %lg_bln_sz =
	( 12 => '4K',
	  13 => '8K',
	  14 => '16K',
	  15 => '32K',
	  16 => '64K',
	  17 => '128K',
	  18 => '256K',
	  19 => '512K',
	  20 => '1M',
	  21 => '2M',
	  22 => '4M',
	  23=> '8M',
	  24=> '16M',
	  25=> '32M',
	  26=> '64M',
	  27=> '128M',
	  28=> '256M',
	  29=> '512M',
	  30=> '1G'
	);
    my %dec_checksum_type = ( 0 => 'none', 1 => 'crc32', 2 => 'crc32c' );
    my %server_log_level = ( 0 => 'emerg', 1 => 'alert', 2 => 'crit', 3 => 'err', 4 => '???', 5 => 'notice', 6 => 'info', 7 => 'debug' );
    foreach my $line (@$gather) {
	if ($line =~ /lg_block_sz.*=\s+(\d+)/) {
	    verbose("HDFS Default Block Size: $lg_bln_sz{$1}");
	}
	if ($line =~ /declared_checksum_type.*=\s+(\d+)/) {
	    verbose("HDFS Default Checksum Type: $dec_checksum_type{$1}");
	}
	if ($line =~ /log_level.*=\s+(\d+)/) {
	    verbose("HDFS Server Log Level: $server_log_level{$1}");
	}
	if ($line =~ /num_threads.*=\s+(\d+)/) {
	    verbose("HDFS Server Threads: $1");
	}
    }

    # Flag upgrade compatibility document for upgrades
    if (isOneFSUpgrade()) {
	addCustomerInformation('For information about OneFS compatibility with Hadoop, review the Using Hadoop with OneFS - Isilon Info Hub [https://www.dell.com/support/article/sln318813/using-hadoop-with-onefs-isilon-info-hub].');
    }

    # Output Check result
    printResults();
} # End checkHDFS
sub checkHDFSAuthenticationMode {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8.2.2')) { return }

    my $CMD_LOGS = q[2>/dev/null egrep '^ *(HDFS settings for zone|Authentication Mode): *' local/isi_hdfs_zone_settings | sed -r -e 's/ *(HDFS settings for zone|Authentication Mode): *//' | paste -d, - -];
    my $CMD_LIVE = qq[for z in \$(2>/dev/null ${SUDO} isi zone zone list --format list | egrep '^ *Name: *' | sed -r -e 's/ *Name: *//'); do a=\$(2>/dev/null ${SUDO} isi hdfs settings view --zone "\$z" | egrep '^ *Authentication Mode: *' | sed -r -e 's/ *Authentication Mode: *//'); echo "\$z,\$a"; done 2>/dev/null];
    my $SPF_ZONES = 'HDFS license is present on cluster and the HDFS setting "authentication_mode" is set to "all" for at least one access zone. If upgrading to version of OneFS 8.2.2 or higher, "authentication_mode" setting value will automatically be migrated to "simple_only". This automatic setting migration can lead to future client connectivity issues if access zones are configured to use kerberos, active directory authentication providers, or HDFS proxy users with HDFS enabled. The authentication_mode is set to all for the following access zones: %s.  For details on the HDFS authentication methods and how to configure them, review the "Configuring HDFS authentication methods" section of the PowerScale OneFS HDFS Reference Guide (https://dl.dell.com/content/manual65758733).';

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my @zones = ();
    foreach (@$gather) {
	my ($zone, $mode) = split /,/;
	if (!$zone or !$mode) { next }
	if ($mode eq 'all') { push(@zones, $zone) }
    }

    # Flag
    if (@zones) {
	my $msg = sprintf($SPF_ZONES, join(', ', @zones));
	warning($msg);
	if (isDestinationOneFS('>=8.2.2')) {
	    addCustomerInformation($msg);
	}
    }
} # End checkHDFSAuthenticationMode
$CHECKS{'checkHardening'} = {
    'Description' => 'Checks if FIPS is enabled on node in the cluster, this needs to be disabled prior to upgrades to 9.5 or higher and re enabled after to avoid assessment failures',
    'Exclude' => ,
};
sub checkHardening {
    if (wasCalled()) { return }
    isHardened();
    if(!$CLUSTER_INFO{'Hardened'} || $CLUSTER_INFO{'Hardened'} == 0){ return }
    
    printCheck('Hardening Status');
    
    if ((isCurrentOneFS('<9.5') and isOneFSUpgrade() and (isDestinationOneFS('>=9.5')) || ($RPS{'Target'} and compareVersions($RPS{'Target'}, '9.5') >= 0))) {
        debug("current is less that 9.5 and no destination OR destination is less than 9.5 OR destination is greater than 9.5");
        addCustomerInformation(critical("Hardening is enabled on this cluster and will need to be disabled prior to upgrading to 9.5 or higher, please use 'isi hardening revert' to disable hardening and once the upgrade is complete you can re enable hardening using 'isi hardening apply' command."));
        addCustomerInformation(critical("If you are using NTLM authentication with hardening enabled, this will stop working after an upgrade to 9.5, NTLM support with hardening enabled is removed from 9.5 onwards."));
        info("please reference https://dl.dell.com/content/manual60210976 for more information.");    
}
    # Output
    printResults();
} # End checkHardening
$CHECKS{'checkHardwareUpgrade'} = {
    'Description' =>  'Checks if there is an in progress hardware upgrade',
    'Reference' => 'https://www.dell.com/support/incidents-online/ContactUs',
};
$MSG{'checkHardwareUpgrade_HW_UPGRADE'} = q[Hardware upgrade in progress];
$MSG{'checkHardwareUpgrade_UPGRADE'} = q[The OneFS upgrade commit will encounter an error while there is a hardware upgrade in progress];
$MSG{'checkHardwareUpgrade_SUPPORT'} = q[Complete the hardware upgrade process or engage Isilon support for further assistance];
sub checkHardwareUpgrade {
    if (wasCalled()) { return }
    printCheck('Hardware Upgrade');

    # Setup
    my $CMD_GREP = q[grep hardware_upgrade];
    my $FILE_LIVE = q[/ifs/.ifsvar/modules/smartpools/drive_purposing.db];
    my $FILE_LOGS = q[local/ifsvar_modules.tar/modules/smartpools/drive_purposing.db];
    my $CMD_LIVE = qq[2>&1 $CMD_GREP $FILE_LIVE];
    my $CMD_LOGS = qq[2>&1 $CMD_GREP $FILE_LOGS];
    my $RE_TRUE = qr/"hardware_upgrade":\s+true/;

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my ($hw_upgrade) = grep {$_ =~ $RE_TRUE} @$gather;
    if ($hw_upgrade) {
	fail($MSG{'checkHardwareUpgrade_HW_UPGRADE'});
	if (isOneFSUpgrade()) { fail($MSG{'checkHardwareUpgrade_UPGRADE'}) }
	fail($MSG{'checkHardwareUpgrade_SUPPORT'});
    }

    # Output
    printResults();
} # End checkHardwareUpgrade
###
# checkHealth: Verifies cluster health status and node health status.
$CHECKS{'checkHealth'} = {
    'Description' => 'Verifies cluster health status and node health status',
    'Exclude' => 1,
};
sub checkHealth {
    return if wasCalled();
    printCheck("Cluster Health Status");
    gatherStatus();
    gatherIsiStatPools();
    gatherStat();

    # Fail if cluster health is not OK
    if ($CLUSTER_INFO{'Health'} and $CLUSTER_INFO{'Health'} ne "OK") {
	fail("The cluster health is $CLUSTER_INFO{'Health'}");
    }

    # Check each node for issues
    foreach my $node (sort {$a <=> $b} keys %NODE_INFO) {
	next if !$NODE_INFO{$node}{'Health'};
	if ($NODE_INFO{$node}{'Health'} =~ /SMARTFAIL/) {
	    # Fail if cluster is reporting SMARTFAIL
	    fail("Node $node is reporting SMARTFAIL");
	} else {
	    if ($NODE_INFO{$node}{'Health'} =~ /.A../) {
		# Fail if cluster is reporting ATTENTION
		fail("Node $node is reporting ATTENTION");
	    }
	    if ($NODE_INFO{$node}{'Health'} =~ /D.../) {
		# Fail if cluster is reporting DOWN
		fail("Node $node is reporting DOWN");
	    }
	    if ($NODE_INFO{$node}{'Health'} =~ /..S./) {
		# Fail if cluster is reporting SMARTFAIL
		fail("Node $node is reporting SMARTFAIL");
	    }
	    if ($NODE_INFO{$node}{'Health'} =~ /...R/) {
		# Fail if cluster is reporting READ-ONLY
		fail("Node $node is reporting READ-ONLY");
	    }
	}
    }
    info(sprintf($MSG{'KB_1'}, '000223412')) if isResults(&WARN);

    printResults();
} # End checkHealth
$CHECKS{'checkIDI'} = {
    'Description' => 'Checks for IDI errors in the past 90 days',
    'Reference' => 'https://www.dell.com/support/manuals/isilon-onefs/ifs_pub_event_reference/-powerscale-onefs-event-reference-guide?guid=guid-81fc3021-c3f0-4f27-990e-4ae7b3d1ead9',
};
$MSG{'checkIDI_NODE_2'} = 'Detected %s IDI errors on node %s in the last 90 days';
sub checkIDI {
    return if wasCalled();
    printCheck("IDI Errors");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array cat /var/log/idi.log 2>&1]);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -H . */varlog.tar/log/idi.log 2>&1]);
    }

    # Analyze information for issues.
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	my $idi_error_count = 0;
	foreach my $line (@{$gather->{$lnn}}) {
	    # Gather timestamp of IDI error
	    next unless ($line =~ /^\s*(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)/);
	    my $timestamp = timelocal($6, $5, $4, $3, $2 - 1, $1);

	    # Ignore if older than 90 days
	    next if $RPS{'Runtime'} - $timestamp > 7776000;

	    # Gather IDI errors
	    if ($line =~ /kernel:.*?(ENOTC\|Stack dump|idi_shallow_verify_failure|CRC|bhv|mmb|ffb|DSR failure)/) {
		$idi_error_count++;
	    }
	}

	# Review gathered IDI error information
	if ($idi_error_count > 0) {
	    critical(sprintf($MSG{'checkIDI_NODE_2'}, $idi_error_count, $lnn));
	}
    }

    # Output Check result
    printResults();
} # End checkIDI
###
# checkISCSI: Checks for iSCSI LUNs being configured in /ifs/.ifsvar/iscsi/iscsi.conf
$CHECKS{'checkISCSI'} = {
    'Description' => 'Checks for iSCSI LUNs being configured in /ifs/.ifsvar/iscsi/iscsi.conf (OneFS prior to 8.x only)',
    'Exclude' => 1,
};
sub checkISCSI {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8')) { return unsupported('UNSUP_CUR') }
    if (!isDestinationOneFS('>=8')) {
	return unsupported('UNSUP_TGT_1', 'OneFS 8 or later releases');
    }
    checkLicense();
    printCheck("iSCSI LUNs");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal("2>&1 grep -n ^LUN /ifs/.ifsvar/iscsi/iscsi.conf");
    } else {
	$gather = gatherLocal("2>&1 grep -n ^LUN local/ifsvar_iscsi.tar/iscsi/iscsi.conf");
    }

    # Analyze information for issues.
    my @iscsiluns = ();
    foreach my $line (@$gather) {
	next unless $line =~ /^\d+:LUN/;
	next if $line =~ / -H/;
	push(@iscsiluns, $line);
    }
    foreach my $iscsilun (@iscsiluns) {
	fail("found active iSCSI LUN in line $iscsilun");
    }
    info('The iSCSI module is removed in OneFS 8.x and later. To avoid issues with data associated with iSCSI LUNs following an upgrade, the LUNs must be inactive or removed prior to completing the upgrade checks. '.sprintf($MSG{'KB_1'}, '000160520')) if isResults(&FAIL);
    # Output Check result
    printResults();
} # End checkISCSI
###
# checkIdentifiers: Gathers node information and SWID to validate again a given identifier using the --validate-id argument.
$CHECKS{'checkIdentifiers'} = {
    'Description' => 'Gathers node information and SWID to validate again a given identifier using the --validate-id argument',
    'Reference' => '',
};
sub checkIdentifiers {
    return if wasCalled();

    # Gather information from cluster or from logset
	gatherStat();
	checkLicense();
printCheck('Validate Identifier');
	if(defined($RPS{'Validate_ID'}) and $RPS{'Validate_ID'} ne ''){
		debug("Validate_ID is present and contains value: $RPS{'Validate_ID'}.");
		if(lc($RPS{'Validate_ID'}) =~ /^elm.*/){
			debug("Identifier provided was a SWID.");
			debug("SWID to check against = $CLUSTER_INFO->{'OneFS License ID'}");
			if(defined($CLUSTER_INFO{'OneFS License ID'}) and $CLUSTER_INFO{'OneFS License ID'} ne ''){
				if(lc($CLUSTER_INFO{'OneFS License ID'}) eq lc($RPS{'Validate_ID'})){
					debug("Provided identifier $RPS{'Validate_ID'} matches cluster SWID $CLUSTER_INFO{'OneFS License ID'}.");	
					exit 1;
					info();
				}
				else{
					debug("!!! Provided identifier $RPS{'Validate_ID'} does not match cluster SWID $CLUSTER_INFO{'OneFS License ID'}.");
					fail();
					exit 1;
				}
			}			
		}
		else{
			debug("Identifier provided was a Serial.");
			my $search_key = 'Serial';
			# Loop through top-level keys
			for my $parent (keys %data) {
				if (exists $data{$parent}{$search_key}) {
					if(exists($data{$parent}{$search_key}{$RPS{'Validate_ID'}})){
						debug("Provided identifier $RPS{'Validate_ID'} matches cluster node serial $data{$parent}{$search_key}{$RPS{'Validate_ID'}} for node $parent.");
						info();
						exit 1;
					}
					else{
						debug("!!! Provided identifier $RPS{'Validate_ID'} does not match any cluster node serial $data{$parent}{$search_key}{$RPS{'Validate_ID'}}.");
						fail();
						exit 1;
					}
				}
			}

			}	
		}
		else{
			debug("!!! ID Validation string not found.");
		}

    # Output
    printResults();

} # End checkIdentifiers
$CHECKS{'checkIndexSnapshotCurrent'} = {
    'Description' => 'Checks for current snapshots that are over 2 weeks old and may contirbute to capacity issues',
    'Reference' => '',
};
sub checkIndexSnapshotCurrent {
    return if wasCalled();
    printCheck("Current Snapshot");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal(q[2>&1 ${SUDO} isi snapshot snapshot list -v --format table -a -z --state all | grep -i 'Index-Snapshot-Current' | awk '{print $1" "$2" "$8" "$9" "$10" "$14 }' 2>&1]);
    } else {
	$gather = gatherLocal(q[2>&1 grep -i 'Index-Snapshot-Current' local/snapshot_usage | awk '{print $1" "$2" "$8" "$9" "$10" "$14 }' 2>&1]);
    }

    # Analyze information for issues.
    my @index_snapshot_current = ();
    my @index_snapshots_current_list = ();
    my $old_isc_count = 0;
    
    my $SPF1 = '%-10s %-40s %-25s %-10s';
    verboseHeader('Old Index Current Snapshots');# if $args{'Verbose'};
    verbose(sprintf($SPF1, 'ID', 'Name', 'Created', 'Size'));# if $args{'Verbose'};
    verbose(sprintf($SPF1, '-'x10, '-'x40, '-'x25, '-'x10));# if $args{'Verbose'};    
    foreach my $line (@$gather) {
    @index_snapshot_current = split(' ', $line);
	debug("Current Snapshot FOUND DETAILS: ID: $index_snapshot_current[0], Name: $index_snapshot_current[1], Created: $index_snapshot_current[2], Size: $index_snapshot_current[4]\n");
        
        next unless ($index_snapshot_current[2] =~ /^\s*(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)/);
	    my $timestamp = timelocal($6, $5, $4, $3, $2 - 1, $1);

	    # Ignore if newer than 14 days ago
	    next if $RPS{'Runtime'} - $timestamp < 1209600;
        #next if $RPS{'Runtime'} - $timestamp < 18410000;

	    # Gather Old Snapshots
	    if ($index_snapshot_current[1] =~ /.*Index\-Snapshot\-Current.*/) {
            if(($index_snapshot_current[3] eq '' || $index_snapshot_current[3] eq '-') && $index_snapshot_current[5] eq 'active'){
		        $old_isc_count++;
                push(@index_snapshots_current_list, $index_snapshot_current[1]);
                verbose(sprintf($SPF1, $index_snapshot_current[0], $index_snapshot_current[1], $index_snapshot_current[2], $index_snapshot_current[4]));# if $args{'Verbose'};
            }
        }
    }
    debug("Old Snapshot count: $old_isc_count\n");
	if ($old_isc_count > 0) {
	    warning("Checks have found a snapshot, or snapshots, of /ifs greater than 14 days old on this cluster: ".join(',', @index_snapshots_current_list));
        info("Snapshots of /ifs that remain on the cluster for prolonged periods may lead or contribute to capacity issues.");
        info("For additional information on the snapshots found, please rerun IOCA with additional arguments -r checkIndexSnapshotCurrent -e");
        info("Additional resources regarding best practices can be found on https://infohub.delltechnologies.com/l/powerscale-onefs-best-practices/snapshot-considerations and https://infohub.delltechnologies.com/p/onefs-filepolicy-job/ .")
    }

    # Output Check result
    printResults();
} # End checkIndexSnapshotCurrent
$CHECKS{'checkInsightIQVersion'} = {
    'Description' => 'Checks for IIQ version 5.x when upgrading to 9.7.1.3, there is a known issue on when upgrading to 9.7.1.3 where IIQ version 5.x is installed, this would break functionality of IIQ monitoring once the cluster is on 9.7.1.3 - reference https://www.dell.com/support/kbdoc/000251368. Additional checks added to notify users if upgrading to 6.1, it would require an intermediate upgrade jump to 6.0.1 prior.',
    'Exclude' => ,
};
sub checkInsightIQVersion {
    if (wasCalled()) { return };

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array "grep 'Isilon InsightIQ' /var/log/apache2/webui_httpd_access.log | tail -n1"];
    my $CMD_LOGS = q[2>&1; for d in *-*/; do grep -H 'Isilon InsightIQ' ${d}varlog.tar/log/apache2/webui_httpd_access.log | tail -n 1; done];

    # Start
    printCheck('IIQ Version');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    my @nodes = ();
    my @nodes_rf = ();

    # Analyze
    foreach my $lnn (@LNNS) {
        debug("node $lnn results: @{$gather->{$lnn}}");
        foreach my $line (@{$gather->{$lnn}}) {
            debug("checking IIQ finding: $line");
            if($line =~ /^.*Isilon\sInsightIQ\/(\d+\.\d+).*$/){
                push(@nodes_rf,$lnn);
                $iiq_vers_found = $1;
                $iiq_maj;
                $iiq_min;
                $iiq_sp;
                if($iiq_vers_found =~ /(\d+)\.(\d)/){
                    $iiq_maj = $1;
                    $iiq_min = $2;
                    $iiq_sp = $3;
                    debug("IIQ results found are Maj: $iiq_maj Min: $iiq_min SP: $iiq_sp");
                }
                debug("IIQ Version found is $iiq_vers_found");
                if(compareVersions($iiq_maj, '5') == 0 and $iiq_min < 2){
                    push(@nodes,$lnn);
                }
                if(compareVersions($iiq_maj, '5') <=0){
                    push(@nodes_lt6,$lnn);
                }                
            }
        }
    }

    if(@nodes){
        if(isCurrentOneFS('9.7.1.3')){
            critical("InsightIQ version 5.X detected from logs, please upgrade to InsightIQ 5.2 as soon as possible to avoid impact to IIQ monitoring.");
            addCustomerInformation("InsightIQ version 5.x detected from logs, please upgrade to InsightIQ 5.2 as soon as possible to avoid impact to IIQ monitoring.");
            info("Please reference KB000214872 (https://www.dell.com/support/kbdoc/000251368) for more information.");        
        }
        if(isDestinationOneFS('9.7.1.3')){
            fail("InsightIQ version 5.x detected from logs, do not upgrade to 9.7.1.3 as there this will impact monitoring, please wait until IIQ version 5.2 is available to be upgraded prior to OneFS.");
            addCustomerInformation("InsightIQ version 5.x detected from logs, do not upgrade to 9.7.1.3 as there this will impact monitoring, please wait until IIQ version 5.2 is available to be upgraded prior to OneFS.");
            info("Please reference KB000214872 (https://www.dell.com/support/kbdoc/000251368) for more information.");        
        }
    }
    if(@nodes_rf){
        addCustomerInformation(info("Please note that if you are upgrading InsightIQ to 6.1 or higher, you would need to upgrade to 6.0.1 as an intermediate step first. Please reference https://www.dell.com/support/kbdoc/000220129 for additional resources."));
    }
    
    # Output
    printResults();
} # End checkInsightIQVersion
$CHECKS{'checkIsiVolCopy'} = {
    'Description' => 'Checks for configured copies with a state of "Restartable" which will cause a pre upgrade assessment to fail.',
    'Exclude' => ,
};

sub checkIsiVolCopy {
    if (wasCalled()) { return }
    if (!isLive()) { return unsupported($MSG{'UNSUP_LOGS'}) }
 
    # Setup
    my $CMD_LIVE;
    
    if(isCurrentOneFS('>=9.9')){
        $CMD_LIVE = qq[2>&1 ${SUDO} isi_vol_copy -list];
    }
    else{
        $CMD_LIVE = qq[2>&1 isi_vol_copy -list];
    }

    # Start
    printCheck('Check isi_vol_copy');

    # Gather
    my $gather = gatherLocal($CMD_LIVE);
 
    # Analyze
    my @mig_id_restartable;
    
    foreach my $line (@$gather) {
        if($line =~ /\s*(\d+)\s*\S+\s*\d+\s*\d+\s*(\S+).*/){ 
            if($2 eq 'Restartable'){
                push(@mig_id_restartable, $1);
            }
        }
    };

    # Flag
    if (@mig_id_restartable) {
	    fail("Checks have found the following Migration instances with a state of 'Restartable': ".join(",", @mig_id_restartable));
        info("isi_vol_copy migration state of 'Restartable' found which will cause an upgrade assessment to fail and these item will need to be cleaned prior to any upgrade taking place.");
    }

    # Output
    printResults();
} # End checkIsiVolCopy
$CHECKS{'checkKB000066019'} = {
    'Description' => 'Checks size of reports.db and flags if over 100MB which may lead to issues outlined on KB 000066019',
    'Exclude' => ,
};
sub checkKB000066019 {
    if (wasCalled()) { return }
    printCheck('KB 000066019');

    # Setup
    my $gd_epoch = '@'.$RPS{'Gather Date'} if !isLive();
    my $CMD_LIVE_FS = qq[2>&1 ls -al /ifs/.ifsvar/modules/jobengine/reports.db];
    my $CMD_LIVE_LF = qq[2>&1 ${SUDO} isi_for_array 'grep -Enwi "^(\$(date -v -2m +%Y-%m)|\$(date -v -1m +%Y-%m)|\$(date +%Y-%m))-*" /var/log/messages | grep -i "job_d.*database is locked" | wc -l'];
    my $CMD_LIVE_RS = qq[2>&1 isi storagepool settings view | grep 'Automatically Manage'];
    my $CMD_LIVE_JL = qq[2>&1 ${SUDO} isi_for_array 'zgrep -Enwi "^(\$(date -v -2m +%Y-%m)|\$(date -v -1m +%Y-%m)|\$(date +%Y-%m)).*Reports database update.*but took [0-9]* ms" /var/log/isi_job_d.log*'];
    

    my $CMD_LOGS_FS = q[2>&1 ls -al local/ifsvar_modules.tar/modules/jobengine/reports.db];
    my $CMD_LOGS_LF = q[2>&1; for d in *-*/ ; do { echo "${d}varlog.tar/log/messages:"; dblck_res=$(grep -Enwi "^($(date --date="].${gd_epoch}.q[" --date='-2 month' +%Y-%m)|$(date --date="].${gd_epoch}.q[" --date='-1 month' +%Y-%m)|$(date --date="].${gd_epoch}.q[" +%Y-%m)).*job_d.*database is locked" ${d}varlog.tar/log/messages | wc -l); echo "$dblck_res"; } | sed ':a;N;s/\\n/ /;ba'; done];
    my $CMD_LOGS_RS = q[2>&1 grep "Automatically Manage" local/isi_storagepool_settings];
    my $CMD_LOGS_JL = q[2>&1 zgrep -Enwi "^($(date --date="].${gd_epoch}.q[" --date='-2 month' +%Y-%m)|$(date --date="].${gd_epoch}.q[" --date='-1 month' +%Y-%m)|$(date --date="].${gd_epoch}.q[" +%Y-%m)).*Reports database update.*but took [0-9]* ms" */varlog.tar/log/isi_job_d.log*];

    # Gather
    my $gather_FS = gatherLocal(isLive()?$CMD_LIVE_FS:$CMD_LOGS_FS);
    debug($CMD_LOGS_LF);
    my $gather_LF = gatherCluster(isLive()?$CMD_LIVE_LF:$CMD_LOGS_LF);
    my $gather_RS = gatherLocal(isLive()?$CMD_LIVE_RS:$CMD_LOGS_RS);
    my $gather_JL = gatherCluster(isLive()?$CMD_LIVE_JL:$CMD_LOGS_JL);

    my $current_size_bytes;
    my $current_size_mb;
    my $byte_size = 1048576;
    my $max_size = 104857600;
    my $log_lock_flg;
    my $FS_res;
    my $LF_res;
    my $RS_res;
    my $current_log_count;
    my $log_count_total;
    my @nodes = ();
    my @log_entry_ts = ();
    # Analyze
    

    foreach my $lnn (@LNNS) {
        #debug($gather->{$lnn});
        if (!$gather_LF->{$lnn} || $gather_LF->{$lnn} eq '') {
           missing($lnn);
           next;
        }
        else{
            foreach $l (@{$gather_LF->{$lnn}}){
                debug("LOG FILE Checking $l");
                if($l =~ /^\s*([0-9]+)\s*$/){
                    $current_log_count = int($1);
                    $log_count_total += $current_log_count;
                    debug("FOUND LOG COUNT: $current_log_count");
                    if ($current_log_count gt 0){
                        debug("LOG COUNT FAIL");
                        $LF_res = 1;
                    }
                    else{
                        debug("LOG COUNT PASS");
                    }
                }
            }
        }


        foreach $jl (@{$gather_JL->{$lnn}}){
            debug("JOB LOG FILE Checking $jl");
            my $tm_joblog = 0;
            my $ts_real;
            if ($jl =~ /^\s*(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).*/) {
		        my $timestamp = timelocal($6, $5, $4, $3, $2 - 1, $1);
		        debug("RPS TIMESTAMP = $RPS{'Timestamp'} AND LOG ENTRY TS = $timestamp");
		        if ($tm_joblog == 0) { $tm_joblog = $timestamp }
                if($timestamp < $tm_joblog){ $tm_joblog = $timestamp }
                my $s = $RPS{'Timestamp'} - $tm_joblog;
                my $m = $s / 60;
                my $h = $m / 60;
                my $d = $h / 24;
                debug("PUSHING $s INTO ARRAY");
                push(@log_entry_ts, int($s));
                debug("SECONDS: $s MINUTE: $m HOUR: $h DAYS: $d");
                if($s < 10 * 24 * 60 * 60){ push(@nodes, $lnn)}
            }
        }
    }
    if(!$gather_FS || $gather_FS eq ''){
        #missing info
        debug("MISSING reports.db file, cannot determine report.db size");
        missing(compressRange(\@LNNS));
    }
    else{
        foreach my $line (@$gather_FS) {
            debug("CHECKING $line");
            if($line =~ /^\s*\D+\d+.*\s+(\d+)\s+[A-Z].*reports\.db$/){
                $current_size_bytes = $1;
                debug("Current Size: $current_size_bytes");
                $current_size_mb = sprintf("%.0f",$current_size_bytes/$byte_size);
                debug("Current Size MB: $current_size_mb");
            }
        }
        if($current_size_bytes >= $max_size){
            $FS_res = 1;
        }
    }
    if(!$gather_RS || $gather_RS eq ''){
        #missing info
        missing(compressRange(@LNNS));
    }
    else{
        foreach my $line (@$gather_RS) {
            debug("CHECKING $line");
            if($line =~ /^\s*[a-zA-Z\s]+\:\s*([a-zA-Z_]+)\s*$/){
                $current_setting = $1;
                debug("Current Replication Setting: $current_setting");
                if($current_setting ne 'files_at_default'){
                    $RS_res = 1;
                    debug("REPLICATION SETTING FAIL, Not Set to 'files_at_default'");
                }
            }
        }
    }

    #get most recent log entry for reports.db latency
    my $newest = 0;
    my $msg_opt;
    if(@nodes && @log_entry_ts){
        foreach my $n (@log_entry_ts) {
            debug("TESTING $n");
            if ($newest == 0){ $newest = $n; };
            if ($n < $newest) { $newest = $n; }
    }
        debug("NEWEST = $newest");
        my $m = int($newest / 60);
        debug("MINUTES = $m"); 
        my $h = int($m / 60);
        debug("HOURS = $h");
        my $d = int($h / 24);
        debug("DAYS = $d");
        $msg_opt = "$m minutes ago" if $m < 60;
        $msg_opt = "$h hours ago" if $h < 24;
        $msg_opt = "$d days ago" if $d >= 1;
    }

    if(((@nodes || $LF_res) && $FS_res)){
        info("Current file size of report.db is: ".$current_size_mb."mb");
        info("Current StoragePool Replication setting is: ".$current_setting);
        info("Entries were found for Report DB updates in isi_job_d log on nodes: ".compressRange(@nodes)." with the most recent being $msg_opt.") if @nodes;
        info("Messages for DB locks: $log_count_total") if $LF_res;
        info("Please reference KB 000066019 (https://www.dell.com/support/kbdoc/en-us/000066019) and KB 000203564 (https://www.dell.com/support/kbdoc/en-us/000203564) for possible mitigations");
    }
    # Output
    printResults();
} # End checkKB000066019
$CHECKS{'checkKB000181818'} = {
    'Description' => 'Checks for criteria of KB 000181818',
    'Exclude' => 1,
};
sub checkKB000181818 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8.2')) { return unsupported('UNSUP_CUR') }
    printCheck('KB 000181818');

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array].
	q[ 'ps auxw | grep -v grep | grep -c isi.hw.fw.proxy.Proxy'];
    my $CMD_LOGS = q[2>&1 grep -H -a -c isi.hw.fw.proxy.Proxy */ps];
    my $MAX_PIDS = 4;
    my $SPF_PIDS = 'The following nodes have more than %s'.
	' isi.hw.fw.proxy.Proxy processes running on them: %s';

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my %nodes = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	foreach (@{$gather->{$lnn}}) {
	    if (/^\s*(\d+)\s*$/ and $1 > $MAX_PIDS) { $nodes{$lnn} = $1 }
	}
    }

    # Flag
    if (%nodes) {
	fail(sprintf($SPF_PIDS, $MAX_PIDS, compressRange(keys %nodes)));
	info(sprintf($MSG{'KB_INT_1'}, '000181818'));
    }

    # Output
    printResults();
} # End checkKB000181818
$CHECKS{'checkKB000192800'} = {
    'Description' => 'Checks for critera of KB 000192800',
    'Reference' => 'https://www.dell.com/support/kbdoc/000192800',
};
sub checkKB000192800 {
    if (wasCalled()) { return }

    # Current OneFS version requirements
    if (isCurrentOneFS('>=9.2.1.5') or
	(isCurrentOneFS(qr/^9\.1\.0/) and
	 isCurrentOneFS('>=9.1.0.12'))) {
	return unsupported
	    ('Supported on OneFS versions prior to 9.1.0.12, 9.2.1.5, or 9.3.0.0');
    }

    # Destination OneFS version requirements
    if (!isDestinationOneFS() or
	!isOneFSUpgrade()) {
	return unsupported('UNSUP_TGT');
    }
    if (isDestinationOneFS('<9.1.0.12') or
	(isDestinationOneFS(qr/^9\.2\.1/) and
	 isDestinationOneFS('<9.2.1.5'))) {
	return unsupported('UNSUP_TGT_1', 'OneFS releases newer than 9.1.0.12, 9.2.1.5, and 9.3.0.0');
    }

    # Setup
    printCheck('KB 000192800');
    my $CMD_GREP = q[egrep '^driveconfigs\.[0-9]+( |\.interface_type)'];
    my $FILE_LIVE = q[/ifs/.ifsvar/modules/hardware/drive_config.gc];
    my $FILE_LOG = q[local/ifsvar_modules.tar/modules/hardware/drive_config.gc];
    my $CMD_LIVE = qq[2>&1 $CMD_GREP $FILE_LIVE];
    my $CMD_LOGS = qq[2>&1 $CMD_GREP $FILE_LOG];
    my $RE_NUM = qr/^driveconfigs\.(\d+)/;
    my $RE_INTERFACE_TYPE = qr/interface_type/;
    my $RE_NONE = qr/none/;
    my $MSG_RISK =
	'Drive config files put this cluster at risk for encountering the'.
	' issues outlined in KB 000192800 during an upgrade to OneFS'.
	' 9.1.0.12, 9.2.1.5, 9.3.0.0 or newer releases';
    my $MSG_WORKAROUND =
	'Prior to starting an OneFS upgrade, perform the proactive measures'.
	' in KB 000192800';

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my $at_risk = 0;
    my $previous = undef;
    my $current = undef;
    my $found = 0;
    foreach my $line (@$gather) {
	if ($at_risk) { last }
	if ($line !~ $RE_NUM) { next }
	$current = $1;
	if (defined $current and
	    defined $previous and
	    $current ne $previous) {
	    if (!$found) { $at_risk = 1 }
	    $found = 0;
	}
	if ($line =~ $RE_INTERFACE_TYPE) { $found = 1 }
	if ($line =~ $RE_NONE) { $at_risk = 1 }
	$previous = $current;
    }
    if (defined $current and !$found) { $at_risk = 1 }

    # Flag
    if ($at_risk) {
	critical($MSG_RISK);
	critical($MSG_WORKAROUND);
	info(sprintf($MSG{'KB_INT_1'}, '000192800'));
    }

    # Output
    printResults();
} # End checkKB000192800
$CHECKS{'checkKB000212387'} = {
    'Description' => 'Checks Authentication providers msDS-SupportedEncryptionTypes attribute to ensure a value is set and assigned, if it is not, there is potential for DU after an upgrade to 9.5 or above.',
    'Exclude' => ,
};
sub checkKB000212387 {
    if (!isLive()) { return }
    if (wasCalled()) { return }
    if ((isCurrentOneFS('<9.5') and !$RPS{'Target'}) || isDestinationOneFS('<9.5') || isCurrentOneFS('>=9.10')) { return }
    #if((isDestinationOneFS('>=9.5') && isDestinationOneFS('>9.5.0.3')) || ($RPS{'Target'} and compareVersions($RPS{'Target'}, '9.5.0.3') < 0)) { return }
    #debug("current is less that 9.5 and no destination OR destination is less than 9.5 OR destination is greater than 9.5 AND less than 9.5.0.3.");
    printCheck('KB 000212387');

    # Setup
    if(isCurrentOneFS('>=9.12') and isCompliance()){
        my $CMD_LIVE_get_ads_list = qq[2>&1 ${SUDO} isi auth ads list -v | awk '\/Name: \/ {print \$NF}'| while read adDomain; do echo "Checking \$adDomain"; samAccountName=\$(\${SUDO}/usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig -t pstore ActiveDirectory._key.\$(echo \$adDomain | sed 's/\\./%2e/g; s/\\-/%2d/g')._sz.SamAccountName.value | awk '{print \$NF}'); \${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_classic auth ads search --domain=\$adDomain --filter "(sAMAccountName=\$samAccountName)" --attribute msDS-SupportedEncryptionTypes; done];
    }
    else{
	    my $CMD_LIVE_get_ads_list = qq[2>&1 ${SUDO} isi auth ads list -v | awk '\/Name: \/ {print \$NF}'| while read adDomain; do echo "Checking \$adDomain"; samAccountName=\$(\${SUDO}isi_gconfig -t pstore ActiveDirectory._key.\$(echo \$adDomain | sed 's/\\./%2e/g; s/\\-/%2d/g')._sz.SamAccountName.value | awk '{print \$NF}'); \${SUDO} isi_classic auth ads search --domain=\$adDomain --filter "(sAMAccountName=\$samAccountName)" --attribute msDS-SupportedEncryptionTypes; done];
    }
    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE_get_ads_list:return);

    my @domains_with_issue = ();   
    #compressRange(@nodes) 
    # Analyze
    if(!$gather || $gather eq ''){
        
    }
    else{
        debug("Cluster Name to be used: $CLUSTER_INFO{'Name'}");
        
        #debug("Command: $CMD_LIVE_get_ads_attribute");
        my $domain = '';
        foreach my $line (@$gather) {
            if ($line =~ /^\s*Checking\s+(\S+)\s*$/){
                $domain = $1;
            }
            if ($line =~ /^\s*msDS\-SupportedEncryptionTypes\s+\=\s+\'?(\d+)\'?\s*$/){
                debug("msDS-SupportedEncryptionTypes value for domain $domain is $1");
            }
            elsif($line =~ /^\s*No\sresults\sfound\!\s*$/){
                debug("Unable to obtain domain attribute / attribute is not set for $domain.");
                push(@domains_with_issue, $domain);
            }
            else{ 
                next; 
            }
        }
    }
         
      if(@domains_with_issue){
        if($RPS{'Target'} and isOneFSUpgrade()){
            if(((isDestinationOneFS('>=9.5') and isDestinationOneFS('<9.5.0.3')) || ($RPS{'Target'} and compareVersions($RPS{'Target'}, '9.5.0.3') < 0)) and isCurrentOneFS('<9.5.0.3')){
                fail("Checks have found no results for msDS-SupportedEncryptionTypes on the following domains: ". join(', ', @domains_with_issue));
                addCustomerInformation("Checks have found no results for msDS-SupportedEncryptionTypes on the following domains: ". join(', ', @domains_with_issue).". You should assign a value to avoid impact during the OneFS upgrade to".isDestinationOneFS().", Please refer to https://www.dell.com/support/kbdoc/en-us/000212387 for more information.");
            }
            else{
                critical("Checks have found no results for msDS-SupportedEncryptionTypes on the following domains: ". join(', ', @domains_with_issue));
                addCustomerInformation("Checks have found no results for msDS-SupportedEncryptionTypes on the following domains: ". join(', ', @domains_with_issue).". You should assign a value to avoid impact during upgrade to 9.5 and later, Please refer to https://www.dell.com/support/kbdoc/en-us/000212387 for more information.");  
            }
            info("For more information refer to KB article 000212387 found at https://www.dell.com/support/kbdoc/en-us/000212387");
        }
      }
    
    # Output
    printResults();
} # End checkKB000212387
$CHECKS{'checkKB000253297'} = {
    'Description' => 'Checks if somaxconn is present on any sysctl.conf file on any nodes, specifically when upgrading to versions affected by KB 000253297 - reference https://www.dell.com/support/kbdoc/000253297.',
    'Exclude' => ,
};
sub checkKB000253297 {
    if (wasCalled()) { return };

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array "grep -H "somaxconn" /etc/mcp/templates/sysctl.conf /etc/mcp/override/sysctl.conf /etc/local/sysctl.conf"];
    my $CMD_LOGS = q[2>&1; grep -H "somaxconn" *-*/etc.tar/etc/mcp/override/sysctl.conf *-*/etc.tar/etc/templates/sysctl.conf *-*/etc.tar/etc/local/sysctl.conf 2>/dev/null];

    # Start
    printCheck('KB 000253297');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    my @nodes = ();

    # Analyze
    foreach my $lnn (@LNNS) {
        debug("node $lnn results: @{$gather->{$lnn}}");
        foreach my $line (@{$gather->{$lnn}}) {
            if($line =~ /^.*somaxconn=.*$/){
                    push(@nodes,$lnn);
            }
        }
    }

    if(@nodes){
        if(isDestinationOneFS('9.7.1.3') or isDestinationOneFS('9.10.0.0')){
            fail("Sysctl settings present for somaxconn, please ensure these custom settings are removed prior to upgrading to ".isDestinationOneFS()." to avoid issues outlined on KB000253297.");
            addCustomerInformation("Sysctl settings present for somaxconn, please ensure these custom settings are removed prior to upgrading to ".isDestinationOneFS()." to avoid issues outlined on KB000253297.");
            info("Please reference KB000253297 (https://www.dell.com/support/kbdoc/000253297) for more information and required steps to follow.");        
        }
    }
    
    # Output
    printResults();
} # End checkKB000253297
###
# checkKB000299293: Checks authentication status on each node and ADS authentication attribute to see if it is enabled or disabled.
#  Warns if any authentication providers are active with Authentication set to no when upgrading to 9.7.1.3 or higher.
$CHECKS{'checkKB000299293'} = {
    'Description' => 'Checks authentication status on each node and ADS authentication attribute to see if it is enabled or disabled.',
    'Exclude' => ,
};
sub checkKB000299293 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<9.7.1.3') and (!isDestinationOneFS() or isDestinationOneFS('<9.7.1.3'))) { return }
    printCheck("KB 000299293");

    # Gather information from cluster or from logset
    $CMD_LIVE = q[2>&1 ${SUDO} isi auth ads list --verbose --format list | grep 'Name:|Authentication:|Status:'];
    $CMD_LOGS = q[2>&1 egrep -A 6 '^\\s*Name:' local/isi_auth];

    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);
    # Analyze information for issues.
    my %adlist_res;
    my $prov_nbr = 0;
    my $cur_name = '';
   foreach my $line (@$gather) {
    debug("$line");
            $prov_nbr++ if ($line =~ /^\s*Name:.*$/);
        if($line =~ /^\s*(Status|Authentication|Name):\s*(.*)$/){
            $cur_name = $2 if ($1 eq 'Name');
            debug("Provider $prov_nbr Param $1 is $2");
            $adlist_res{$prov_nbr}{'name'} = $2 if $1 eq 'Name';
            $adlist_res{$prov_nbr}{'authentication'} = $2 if $1 eq 'Authentication';
            $adlist_res{$prov_nbr}{'status'} = $2 if $1 eq 'Status';
         }
    }

    foreach my $r (keys %adlist_res){
            debug("NAME:$adlist_res{$r}{'name'}\tAUTH:$adlist_res{$r}{'authentication'}\tSTATUS:$adlist_res{$r}{'status'}");
        if(exists($adlist_res{$r}{'authentication'}) and $adlist_res{$r}{'authentication'} eq 'No' and ($adlist_res{$r}{'status'} eq 'active' or $adlist_res{$r}{'status'} eq 'online')){
            debug("FAIL: Provider $r");
            push(@ads_auth_fail, $adlist_res{$r}{'name'});
        }
    }

    if(@ads_auth_fail){
        fail("Check have found active AD Providers with Authenticaiton set to 'No', please ensure that Authentication is set to Yes when upgrading to 9.7.1.3 or higher to avoid issue with AD authentication.");
        info("Providers with Authentication set to No are: ".join(', ', @ads_auth_fail));
        info("Please reference KB 000299293 (https://www.dell.com/support/kbdoc/000299293) for additional information.");
    }

    # Output Check result
    printResults();

} # End checkKB000299293
$CHECKS{'checkKB000354078'} = {
    'Description' => 'Checks for configured CRON job or screen where current version is 9.11 and less than 9.12 to avoid OOM issue.',
};
sub checkKB000354078 {
    if (wasCalled()) { return };
    if (isCurrentOneFS('>=9.12') || isCurrentOneFS('<9.11')) { return };
    
    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array "grep 'isi_flush' /etc/mcp/templates/crontab /etc/mcp/override/crontab"];
    my $CMD_LOGS = q[2>&1; grep -EH 'isi_flush' */etc.tar/etc/mcp/templates/crontab */etc.tar/etc/mcp/override/crontab];

    # Start
    printCheck('KB 000354078');

    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    
    # Analyze
    my @nodes = ();
    my @nodes_w_ct = ();
    my @nodes_w_ct_cmt = ();
    
    foreach my $lnn (@LNNS) {
        if (!$gather->{$lnn} || $gather->{$lnn} eq '') {
            push(@nodes, $lnn);
            next;
        }
        foreach $l (@{$gather->{$lnn}}){
            debug("FLUSH PROCESS Checking $l");
            if($l !~ /^\s*#.*/){
                push(@nodes_w_ct, $lnn) unless $l =~ /isi_for_array/;
            }
            else {
                push(@nodes_w_ct_cmt, $lnn);
            }
        }
    }

    # Flag
    if(!isCurrentOneFS('>=9.11.0.3')){
    if(@nodes){
        # missing records (critical)
        critical("There are no entries found for isi_flush on the following nodes: ".compressRange(@nodes));        
    }
    if(@nodes_w_ct){
        # records present (info)
        info("Entries found in crontab for isi_flush on the following nodes: ".compressRange(@nodes_w_ct));
    }
    if(@nodes_w_ct_cmt){
        # record present but commented out (critical)
        critical("Crontab entry found on the following nodes which are commented out: ".compressRange(nodes_w_ct_cmt));
    }
    info("Please refer to KB 000342005 (https://www.dell.com/support/kbdoc/000342005) for additional information.");
    }
    # Output
    printResults();
} # End checkKB000354078
$CHECKS{'checkKB000464795'} = {
    'Description' => 'Checks for sysctl configuration for dirtybufthresh to avoid node reboots due to memory issue.',
};
sub checkKB000464795 {
    if (wasCalled()) { return };
    #if (isCurrentOneFS('>=9.12') || isCurrentOneFS('<9.11')) { return };
    if(isCompliance()){ return };

# Flag fail for isCurrentOneFS('9.10.1.7') or isCurrentOneFS('9.13.0.1') or isCurrentOneFS('9.13.0.2') or isCurrentOneFS('9.13.1.0') or isDestinationOneFS('9.10.1.7') or isDestinationOneFS('9.13.0.1') or isDestinationOneFS('9.13.0.2') or isDestinationOneFS('9.13.1.0')
# Flag warning for anything else where setting is enabled

    # Setup
    #if (isLive()) {
	    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'grep "vfs.dirtybufthresh" /etc/local/sysctl.conf 2>&- || echo "No Persistent setting found for vfs.dirtybufthresh"'];
    #} else {
	    my $CMD_LOGS = qq[2>&1 grep -H 'vfs.dirtybufthresh' */etc.tar/etc/local/sysctl.conf];
    #}
    # Start
    printCheck('KB 000464795');

    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    
    # Analyze
    my @nodes = ();
    my @nodes_w_cdbcnf = ();
    my @nodes_w_cdbcnfcmt = ();
    
    foreach my $lnn (@LNNS) {
        if (!$gather->{$lnn} || $gather->{$lnn} eq '') {
            push(@nodes, $lnn);
            next;
        }
        foreach $l (@{$gather->{$lnn}}){
            if($l =~ /No Persistent setting found for vfs.dirtybufthresh$/){
                push(@nodes, $lnn);
            }
            elsif($l =~ /^\s*#.*$/){
                push(@nodes_w_cdbcnfcmt, $lnn);
            }
            else {
                push(@nodes_w_cdbcnf, $lnn);
            }
        }
    }

    # Flag
    if(@nodes_w_cdbcnf or @nodes or @nodes_w_cdbcnfcmt){
        if(isCurrentOneFS('9.10.1.7') or isCurrentOneFS('9.13.0.1') or isCurrentOneFS('9.13.0.2') or isCurrentOneFS('9.13.1.0') or isDestinationOneFS('9.10.1.7') or isDestinationOneFS('9.13.0.1') or isDestinationOneFS('9.13.0.2') or isDestinationOneFS('9.13.1.0')){
            if(@nodes or @nodes_w_cdbcnfcmt){
                if(@nodes){
                    # missing records (critical)
                    critical("Unable to determine if Sysctl setting is applied for dirtybufthresh on the following nodes: ".compressRange(@nodes));        
                }
                if(@nodes_w_cdbcnfcmt){
                    # records present but commented out (critical)
                    critical("Sysctl setting for dirtybufthresh found on the following nodes but are commented out: ".compressRange(@nodes_w_cdbcnfcmt));
                }
                info("The Sysctl setting for dirtybufthresh should be applied prior to upgrading, the setting can be removed once upgraded to a remediated OneFS version.");
            }
            if(@nodes_w_cdbcnf){
                # record present but commented out (critical)
                info("Sysctl setting for dirtybufthresh present on the following nodes: ".compressRange(@nodes_w_cdbcnf));
            }
        }
        else{
            if(@nodes_w_cdbcnf){
                if(
                    (isCurrentOneFS('=9.10') and isCurrentOneFS('>9.10.1.7'))
                    or 
                    (isCurrentOneFS('=9.13') and isCurrentOneFS('>9.13.1.0'))
                ){
                    warning("Sysctl setting for dirtybufthresh present on the following nodes:".compressRange(@nodes_w_cdbcnf));
                    info("If this setting was enabled as part of KB 000464795 workaround, it can now be disabled.");
                }
            }
        }
        info("Please refer to KB 000464795 (https://www.dell.com/support/kbdoc/000464795) for additional information.");
    }
    # Output
    printResults();
} # End checkKB000464795
$CHECKS{'checkKB173892'} = {
    'Description' => 'Checks for criteria outlined in KB 000173892',
    'Exclude' => ,
};
sub checkKB173892 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8.2')) { return unsupported('UNSUP_CUR') }
    if (!isLive()){ return unsupported('UNSUP_LOGS') }

    printCheck("KB 173892");

    # Gather information from cluster or from logset
    my $gather = ();
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array sysctl vfs.vnlru_reuse_freevnodes]);

    # Analyze information for issues.
    my @nodes_disabled_reuse = ();
    my @nodes_enabled_reuse = ();

    foreach my $lnn (@LNNS) {
        next unless $gather->{$lnn};
        foreach my $line (@{$gather->{$lnn}}) {
            
            if($line =~ /^\s*vfs\.vnlru_reuse_freevnodes:\s*(\d)$/){
                if($1 eq '1'){
                   if(!$NODE_INFO{$lnn}{'A_Series'}){
                        push(@nodes_enabled_reuse, $lnn);
                    }
                }
                if($1 eq '0'){
                    push(@nodes_disabled_reuse, $lnn);
                }
            }
        }
    }
    
    if (@nodes_disabled_reuse or @nodes_enabled_reuse) {
        if(@nodes_enabled_reuse and ((isCurrentOneFS('=9.2') and isCurrentOneFS('>=9.2.1.25')) or (isCurrentOneFS('=9.4') and isCurrentOneFS('>=9.4.0.17')) or (isCurrentOneFS('=9.5') and isCurrentOneFS('>=9.5.0.7')) or isCurrentOneFS('>=9.7'))){
            warning('Checks have detected vfs.vnlru_reuse_freevnodes enabled on nodes, this setting should be disabled by default.');
            info('The following nodes have vfs.vnlru_reuse_freevnodes setting enabled: '.compressRange(@nodes_enabled_reuse)) if @nodes_enabled_reuse;
            info('Please reference KB 000173892 (https://www.dell.com/support/kbdoc/en-us/000173892) for additional information.');
        }
    }

    # Output Check result
    printResults();
} # End checkKB173892
$CHECKS{'checkKB201666'} = {
    'Description' => 'Checks if it is necessary to perform the proactive workaround from KB 000201666 for a patch installation and whether the pre-requisites are met',
    'Reference' => 'https://www.dell.com/support/kbdoc/000201666',
    'Exclude' => 1,
};
$MSG{'checkKB201666_WA'} = 'Prior to installing any Roll-Up Patch (RUP) on OneFS 9.1.0/9.2.1/9.3.0 releases, perform the proactive workaround from KB 000201666 to avoid encountering issues due to insufficient available space in the /var partition';
$MSG{'checkKB201666_WA_DONE'} = 'The workaround from KB 000201666 has been implemented across all nodes';
$MSG{'checkKB201666_WA_PARTIAL_1'} = 'The workaround from KB 000201666 has been partially implemented, it has not been performed on nodes: %s';
$MSG{'checkKB201666_UPGRADE'} = 'Pre-requisite condition failed: The upgrade framework is currently busy';
$MSG{'checkKB201666_PATCH_INSTALLED_1'} = 'Pre-requisite condition failed: Patches not reporting Installed: %s';
$MSG{'checkKB201666_PATCH_NODES_1'} = 'Pre-requisite condition failed: The patch system is active on nodes: %s';
$MSG{'checkKB201666_PATCH_FILES_1'} = 'Pre-requisite condition failed: Files found under /var/patch/catalog or /var/patch/tmp on nodes: %s';
$MSG{'checkKB201666_PARTITION_3'} = 'In addition to the workaround from KB 000201666, there must be at least %s of available space in the %s partition and the following nodes do not meet this requirement: %s';
sub checkKB201666 {
    if (wasCalled()) { return }
    if (isCompliance()) { return unsupported('UNSUP_COMP') }
    my $RE_AFFECTED = qr/^9\.(?:1\.0|2\.1|3\.0)\./;
    if (isDestinationOneFS()) {
	if (!isDestinationOneFS($RE_AFFECTED)) {
	    return unsupported('UNSUP_TGT_1', 'OneFS 9.1.0, 9.2.1, and 9.3.0 releases');
	}
	if (isOneFSUpgrade() and $Patched_OneFS{isDestinationOneFS()}) {
	    return unsupported('UNSUP_TGT_NO');
	}
    }
    if (!isDestinationOneFS() and !isCurrentOneFS($RE_AFFECTED)) {
	return unsupported('UNSUP_CUR');
    }
    checkPartitions();
    if ($RPS{'ALL_VARS_EXPANDED'}) { return unsupported('UNSUP_VAR_1GB') }

    # Setup
    my $CMD_LIVE_FIND = qq[2>&1 ${SUDO} isi_for_array 'find /var/patch/tmp /var/patch/catalog -ls'];
    my $CMD_LIVE_PATCH = qq[2>&1 ${SUDO} isi_patch isactive];
    my $RE_NODES = qr/The patch system is active.*:\s+(\S.*)/;
    my $RE_WA_TMP = qr[\s+/var/patch/tmp -> /var/crash/patch/tmp$];
    my $RE_WA_CAT = qr[\s+/var/patch/catalog -> /var/crash/patch/catalog$];
    my $RE_FILES_OK = qr[\s+/var/patch/(?:tmp|catalog)$];

    # Start
    printCheck('KB 201666');

    # Check if the workaround has been performed and check for files in
    #  /var/patch/catalog and /var/patch/tmp directories
    my $gather_find = isLive()?gatherCluster($CMD_LIVE_FIND):{};
    my @nodes_wa_done = ();
    my @nodes_wa_needed = ();
    my @nodes_with_other_files = ();
    foreach my $lnn (@LNNS) {
	if (!$gather_find->{$lnn}) {
	    push(@nodes_wa_needed, $lnn);
	    next;
	}
	my $workaround_catalog = 0;
	my $workaround_tmp = 0;
	my $has_other_files = 0;
	foreach my $line (@{$gather_find->{$lnn}}) {
	    if ($line =~ $RE_WA_TMP) {
		$workaround_tmp = 1;
	    } elsif ($line =~ $RE_WA_CAT) {
		$workaround_catalog = 1;
	    } elsif ($line !~ $RE_FILES_OK) {
		$has_other_files = 1;
	    }
	}
	if ($workaround_tmp and $workaround_catalog) {
	    push(@nodes_wa_done, $lnn);
	} else {
	    push(@nodes_wa_needed, $lnn);
	}
	if ($has_other_files) {
	    push(@nodes_with_other_files, $lnn);
	}
    }
    debug("Nodes where the workaround is done: @nodes_wa_done");
    debug("Nodes where the workaround is needed: @nodes_wa_needed");
    debug("Nodes with other files: @nodes_with_other_files");

    # If there is a destination OneFS (patch) release, flag as critical, otherwise just warn
    my $level;
    if (isDestinationOneFS()) {
	$level = \&critical;
    } else {
	$level = \&warning;
    }

    if (!@nodes_wa_needed) {
	details($MSG{'checkKB201666_WA_DONE'});
    } else {
	# Warn about performing the workaround
	&$level($MSG{'checkKB201666_WA'});

	# Flag partially applied workaround
	if (@nodes_wa_done) {
	    &$level(sprintf($MSG{'checkKB201666_WA_PARTIAL_1'},
			    compressRange(@nodes_wa_needed)));
	}

	# Flag when files are under /var/patch/tmp or /var/patch/catalog
	if (@nodes_with_other_files) {
	    &$level(sprintf($MSG{'checkKB201666_PATCH_FILES_1'},
			    compressRange(@nodes_with_other_files)));
	}

	# Flag when the Upgrade Framework is active
	gatherUpgradeCluster();
	if (!$CLUSTER_INFO{'Current Upgrade Activity'} or
	    $CLUSTER_INFO{'Current Upgrade Activity'} !~ /No activity/i) {
	    &$level(sprintf($MSG{'checkKB201666_UPGRADE'}));
	}

	# Flag when there are patches in a status other than Installed
	gatherPatches();
	if ($CLUSTER_INFO{'Patches'}) {
	    my @patches_not_installed = grep
	      {$CLUSTER_INFO{'Patches'}{$_}{'Status'} ne 'Installed'}
	      keys %{$CLUSTER_INFO{'Patches'}};
	    if (@patches_not_installed) {
		&$level(sprintf($MSG{'checkKB201666_PATCH_INSTALLED_1'},
				join(',', @patches_not_installed)));
	    }
	}

	# Flag when the Patch System is active
	my $gather_patch_system = isLive()?gatherLocal($CMD_LIVE_PATCH):[];
	my ($patch_active_nodes) = map {$_ =~ $RE_NODES} @$gather_patch_system;
	if ($patch_active_nodes) {
	    &$level(sprintf($MSG{'checkKB201666_PATCH_NODES_1'},
			    $patch_active_nodes));
	}
    }

    # Partition space checks always apply
    if ($RPS{'KB201666_VAR'}) {
	&$level(sprintf($MSG{'checkKB201666_PARTITION_3'}, '500MB', '/var',
			compressRange(@{$RPS{'KB201666_VAR'}})));
    }
    if ($RPS{'KB201666_VARCRASH'}) {
	&$level(sprintf($MSG{'checkKB201666_PARTITION_3'}, '1GB', '/var/crash',
			compressRange(@{$RPS{'KB201666_VARCRASH'}})));
    }

    # Output
    if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000201666')) }
    printResults();
} # End checkKB201666
$CHECKS{'checkKB203381'} = {
    'Description' => 'Checks for criteria of KB 203381',
    'Reference' => 'https://www.dell.com/support/kbdoc/000203381',
};
$MSG{'checkKB203381_FAIL'} = 'There are known issues with using S3 in OneFS %s, consider instead upgrading to OneFS %s or newer patch release';
sub checkKB203381 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<9')) { return unsupported('UNSUP_CUR') }
    if (!isDestinationOneFS()) { return unsupported('UNSUP_TGT') }
    if (!isServiceEnabled('s3')) { return unsupported('UNSUP_SVC_1', 's3') }
    my $RE_DESTINATION = qr/^(9\.1\.0|9\.2\.1|9\.4\.0)/;
    if (isDestinationOneFS() !~ $RE_DESTINATION) { return unsupported('UNSUP_NO_RISK') }
    my $matched_destination = $1;
    my $RE_AFFECTED = qr/^(?:9\.1\.0\.(?:[0-9]|1[0-9]|2[0-3])|9\.2\.1\.(?:[0-9]|1[0-6])|9\.4\.0\.(?:[0-6]))$/;
    if (!isDestinationOneFS($RE_AFFECTED)) { return unsupported('UNSUP_NO_RISK') }

    # Setup
    my %VERSIONS_FIXED = (
	'9.1.0' => '9.1.0.24',
	'9.2.1' => '9.2.1.17',
	'9.4.0' => '9.4.0.7',
	);

    # Start
    printCheck('KB 203381');

    # Flag
    fail(sprintf($MSG{'checkKB203381_FAIL'},
		 isDestinationOneFS(),
		 $VERSIONS_FIXED{$matched_destination}));
    info(sprintf($MSG{'KB_1'}, '000203381'));

    # Output
    printResults();
} # End checkKB203381
$CHECKS{'checkKB220014'} = {
    'Description' => 'Checks for criteria of KB 220014',
    'Reference' => 'https://www.dell.com/support/kbdoc/000220014',
};
#$MSG{'checkKB220014_FAIL'} = 'There are known issues with using S3 in OneFS %s, consider instead upgrading to OneFS %s or newer patch release';
sub checkKB220014 {
    if (wasCalled()) { return }

    gatherEventChannel();
    gatherRemoteSupport();
    gatherEventAlerts();

    if (isCurrentOneFS('<9.5')) { printCheck('KB 220014'); printResults(); return }
    if (!isServiceEnabled('isi_supportassist')) { printCheck('KB 220014'); printResults(); return }
    #if (!$CLUSTER_INFO{'Event Channels'}) { debug('Skipping As no Event Channels have been found configured');  printCheck('KB 220014'); printResults(); return }
    #if (!$CLUSTER_INFO{'Event Alert'}) { debug('Skipping As no Event Alerts have been found configured');printCheck('KB 220014'); printResults(); return }
    
    # Setup
    my @alert_channels_none = ();
    my @nodes_with_error = ();
    #my @nodes = ();
    my $issue_logged = 0;
    my $channels_none = 0;
    my $disabled = 0;
    my $missing_log_res = 0;
    my $alerts = $CLUSTER_INFO{'Event Alert'};
    my $channels = $CLUSTER_INFO{'Event Channels'};
    
    my $MATCH = q[evaluate_eventgroup KeyError];
    my $CMD_GREP = qq[ zgrep '$MATCH' ];
    my $CMD_LOGS = qq[2>&1 $CMD_GREP */varlog.tar/log/isi_celog_reporting* | wc -l ];
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array $CMD_GREP /var/log/isi_celog_reporting* | wc -l];
    my $CMD_LIVE_PS;
    if(isCurrentOneFS('<9.9')){
        $CMD_LIVE_PS = qq[ 2>&1 ${SUDO} isi supportassist provision view | egrep 'Current|Success|Percent' ];
    }
    else{
        $CMD_LIVE_PS = qq[ 2>&1 isi supportassist provision view | egrep 'Current|Success|Percent' ];
    }
    
    # Start
    printCheck('KB 220014');

    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);
    my $ps_gather = gatherLocal(isLive()?$CMD_LIVE:return);

    # Check if Support Assist is enabled
    foreach my $id (sort keys %$channels) {
        if (($channels->{$id}{'Name'} eq 'SupportAssist' || $channels->{$id}{'Type'} eq 'supportassist') and $channels->{$id}{'Enabled'} ne 'Yes') {
            $disabled = 1;
        }
    }

    my %provisioning_stat;
    foreach my $line (@$ps_gather) {
        if (!$line || $line eq '') {
        }
        else{
            my ($name, $value) = split(/:\s*/, $line, 2);
            $provisioning_stat{$name} = $value;
        }
    };

    my $live_check_provisioning_status = 0;
    if(!isLive()){
        $live_check_provisioning_status = 1;
    }
    if($provisioning_stat{'Current State'} eq 'COMPLETED' and $provisioning_stat{'Success or Fail'} eq 'success' and $provisioning_stat{'Percent Complete'} eq '100%'){
        $live_check_provisioning_status = 1;
    }

    # Check if Alert Channels are configured
    foreach my $id (sort keys %$alerts) {
        if ($alerts->{$id}{'Channel'} =~ /^.*\sNone,?.*$/) {
            $channels_none = 1;
            push(@alert_channels_none, $id);
        }
    }
    foreach my $line (@$gather) {
        if (!$line || $line eq '') {
            $missing_log_res = 1;
        }
        else{
            debug("Checking $line");
            if($line =~ /^\s*(\d+)\s*$/){
                debug("Regex match for $line: $1");
                $error_count = int($1);
                if($error_count gt 0){
                    debug("$error_count is > 0, triggering a failure as results have been found");
                    $issue_logged = 1;
                }
            }
        }
    };

    # Flag
    if(($issue_logged || @alert_channels_none) and !$disabled and $live_check_provisioning_status){
        critical('Checks have identified an issue with your Alert Channel configuration which may be impacting dialhome creation.');
        info('Log entries for \'evaluate_eventgroup KeyError\' errors are detected on log files.') if $issue_logged;
        info('The following Alert groups have a channel showing as \'None\': '.join(", ", @alert_channels_none)) if @alert_channels_none;
        info('Please open a Technical Support Service Request and reference this failure within the description.');
    }
    if($missing_log_res){
        warning('Unable to determine if there are log entries for \'$missing_log_res\'');
        info('Please open a Technical Support Service Request and reference this failure within the description.');
    }
    
    # Output
    printResults();
} # End checkKB220014
$CHECKS{'checkKB496582'} = {
    'Description' => 'Checks for auth rules issues detailed in KB 000160596',
    'Exclude' => 1,
};
sub checkKB496582 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8.0.0.4')) { return unsupported('UNSUP_CUR') }
    if (!isDestinationOneFS('>=8.0.0.4')) {
	return unsupported('UNSUP_TGT_1', 'OneFS 8.0.0.4 or later releases');
    }

    printCheck("KB 496582");
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal("2>&1 ${SUDO} isi_gconfig | grep MappingRules");
    } elsif (-e "local/isi_gconfig") {
	$gather = gatherLocal("2>&1 grep MappingRules local/isi_gconfig");
    } elsif (-e "local/gconfig_ifsvar.tar/ifs/_ifsvar/main_config.gc") {
	$gather = gatherLocal("2>&1 grep MappingRules local/gconfig_ifsvar.tar/ifs/_ifsvar/main_config.gc");
    }

    my %rules = ();
    foreach my $line (@$gather) {
	next unless ($line =~ /Zones\.([^\.]+)\.MappingRules [^\[]+\[ (.+) \]/);
	$rules{$1} = $2;
	debug("checkKB496582: Zone: $1 Rules: $rules{$1}\n");
    }
    my $KB496582 = 0;
    foreach my $zone (keys %rules) {
	foreach my $rule (parseRules($rules{$zone})) {
	    $KB496582 = 1 if ($rule->{'options'} and $rule->{'options'} =~ /group/ and $rule->{'options'} !~ /groups/);
	}
    }
    if ($KB496582) {
	warning("Customer should review KB 000160596 prior to the OneFS upgrade, a User Mapping Rule was found that may be impacted by the change of behavior in OneFS 8.0.0.4.");
	addCustomerInformation("Review KB 000160596 prior to the OneFS upgrade, a User Mapping Rule was found that may be impacted by the change of behavior in OneFS 8.0.0.4.");
    }
    printResults();
}
$CHECKS{'checkKB501267'} = {
    'Description' => 'Checks for the criteria of KB 000026510',
    'Exclude' => 1,
};
sub checkKB501267 {
    if (wasCalled()) { return }

    # Setup
    printCheck('KB 501267');
    my $CMD_NODE = 'sysctl efs.bam.disk_pool_db|grep generation';
    my $CMD_LIVE = "2>&1 ${SUDO} isi_for_array -s '${CMD_NODE}'";
    my $CMD_LOGS = '2>&1 grep -H generation */efs.bam.disk_pool_db';
    my $MSG_MULTI = 'Multiple generations observed in Disk Pools DB';
    my $SPF_GEN = 'generation=%s on nodes: %s';

    # Gather
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster($CMD_LIVE);
    } else {
	$gather = gatherCluster($CMD_LOGS);
    }

    # Analyze
    my %generations = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	foreach (@{$gather->{$lnn}}) {
	    if (/generation=(\d+)/) {
		push(@{$generations{$1}}, $lnn);
	    }
	}
    }

    # Flag
    if (scalar(keys %generations) > 1) {
	warning($MSG_MULTI);
	foreach (sort keys %generations) {
	    warning(sprintf($SPF_GEN, $_, compressRange(@{$generations{$_}})));
	}
	info(sprintf($MSG{'KB_INT_1'}, '000026510'));
    }

    # Output
    printResults();
}
$CHECKS{'checkKB507031'} = {
    'Description' => 'Checks for criteria outlined in KB 000035398',
    'Exclude' => 1,
};
sub checkKB507031 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8.2')) { return unsupported('UNSUP_CUR') }
    printCheck("KB 507031");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array grep -e 'Match' -e 'Ciphers' /etc/mcp/templates/sshd_config]);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -H -e 'Match' -e 'Ciphers' */etc.tar/etc/mcp/templates/sshd_config]);
    }

    # Analyze information for issues.
    my @ciphers_after_mb = ();
    my @mb_without_ciphers = ();
    my @commented_mb_with_ciphers = ();
    foreach my $lnn (@LNNS) {
	next unless $gather->{$lnn};
	my $has_mb = 0;
	my $has_ciphers = 0;
	my $has_commented_mb = 0;
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ /^\s*Match/) {
		$has_mb = 1;
	    }
	    if ($line =~ /^\s*Ciphers/) {
		$has_ciphers = 1;
		if ($has_mb) {
		    my @results = grep {$_ eq $lnn} @ciphers_after_mb;
		    push(@ciphers_after_mb, $lnn) unless @results;
		}
	    }
	    if ($line =~ /^\s*#MB/) {
		$has_commented_mb = 1;
	    }
	}
	push(@mb_without_ciphers, $lnn) if $has_mb and !$has_ciphers;
	push(@commented_mb_with_ciphers, $lnn) if $has_ciphers and $has_commented_mb;
    }
    if (@mb_without_ciphers and isDestinationOneFS()) {
	critical("Follow the 'Preventative Workaround for OneFS upgrades' steps as outlined in KB 000035398 when performing the OneFS upgrade to $RPS{'Target'}.");
    }
    if (@ciphers_after_mb) {
	fail("Directive 'Ciphers' is not allowed within a Match block, review KB 000035398 for resolution.");
    }
    if (@commented_mb_with_ciphers) {
	fail("Follow the 'Preventative Workaround for OneFS upgrades' post-upgrade steps as outlined in KB 000035398.");
    }

    # Output Check result
    printResults();
}
$CHECKS{'checkKB519388'} = {
    'Description' => 'Pre-upgrade check for issues outlined in KB 000162270',
    'Exclude' => 1,
};
sub checkKB519388 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8') or isCurrentOneFS('>=8.1.2')) {
	return unsupported('UNSUP_CUR');
    }
    if (isDestinationOneFS('>=8.1.2')) {
	return unsupported('UNSUP_TGT_1', 'OneFS releases prior to 8.1.2.0');
    }

    printCheck("KB 519388");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array 'sysctl efs.snapshot.default_sub_visible']);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -H default_sub_visible */efs.snapshot]);
    }

    # Analyze information for issues.
    verboseHeader("KB 000162270");
    verbose(sprintf("%-10s %-10s", "LNN", "efs.snapshot.default_sub_visible"));
    verbose(sprintf("%-10s %-10s", "-"x10, "-"x10));
    my $default_sub_visible = 0;
    foreach my $lnn (@LNNS) {
	next unless $gather->{$lnn};
	foreach my $line (@{$gather->{$lnn}}) {
	    next unless $line =~ /efs\.snapshot\.default_sub_visible:\s+(\d+)$/;
	    $default_sub_visible = 1 if $1;
	    verbose(sprintf("%-10s %-10s", $lnn, $1));
	}
    }

    critical("The sysctl setting efs.snapshot.default_sub_visible is currently enabled",
	     "During an upgrade from OneFS $CLUSTER_INFO{'OneFS'} to $RPS{'Target'} the sysctl setting efs.snapshot.default_sub_visible should be disabled, see KB 000162270 details on how to disable this setting.",
	     "Take care to re-enable this setting once the upgrade has completed.") if $default_sub_visible;

    # Output Check result
    printResults();
}
$CHECKS{'checkKB519423'} = {
    'Description' => 'Checks if the cluster config files are in a mixed mode',
    'Reference' => 'https://www.dell.com/support/kbdoc/000170046',
};
$MSG{'checkKB519423_NODATA'} = 'There are cluster config files with no revision data';
$MSG{'checkKB519423_MIXED'} = 'There are cluster config files with mixed revision data, this should only occur during OneFS upgrades';
sub checkKB519423 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) { return unsupported('UNSUP_CUR') }
    if (isCompliance() and isLive()) { return unsupported('UNSUP_COMP') }
    printCheck('KB 519423');

    # Setup
    my $DIR_LIVE = '/ifs/.ifsvar/modules/tardis/namespaces';
    my $DIR_LOGS = 'local/ifsvar_modules_tardis.tar/modules/tardis/namespaces/';
    my $ARGS_FIND = q[ -name '*.sqlite' -exec sh -c 'echo {}; sqlite3 {} "select * from revision_table"' \; ];
    my $CMD_LIVE = qq[2>&1 [ -d '$DIR_LIVE' ] && find '$DIR_LIVE' $ARGS_FIND];
    my $CMD_LOGS = qq[2>&1 [ -d '$DIR_LOGS' ] && find '$DIR_LOGS' $ARGS_FIND];

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my $tardis_file;
    my %revision_count = ();
    verboseHeader('KB 000170046');
    foreach (@$gather) {
	if (/.sqlite$/) {
	    if ($tardis_file and $revision_count{$tardis_file} == 0) {
		verbose('No revision data');
	    }
	    $tardis_file = $_;
	    $revision_count{$tardis_file} = 0;
	    verbose('', '-'x20, $tardis_file, '-'x20);
	} else {
	    verbose($_);
	}
	if (!$tardis_file) { next }
	if (/^\d+\|\d+$/) { $revision_count{$tardis_file}++ }
    }
    if ($tardis_file and $revision_count{$tardis_file} == 0) {
	verbose('No revision data');
    }

    # Review revision data
    my @multiple_revisions = ();
    my @no_revisions = ();
    foreach my $file (keys %revision_count) {
	if ($revision_count{$file} > 1) { push(@multiple_revisions, $file) }
	if ($revision_count{$file} == 0) { push(@no_revisions, $file) }
    }

    # Flag
    if (@multiple_revisions) { fail($MSG{'checkKB519423_MIXED'}) }
    if (@no_revisions) { fail($MSG{'checkKB519423_NODATA'}) }
    if (isResults(&WARN)) { info(sprintf($MSG{'KB_INT_1'}, '000170046')) }

    # Output
    printResults();
}
$CHECKS{'checkKB521778'} = {
    'Description' => 'Checks for criteria outlined in KB 000031948',
    'Exclude' => 1,
};
sub checkKB521778 {
    if (wasCalled()) { return }
    my $RE_AFFECTED = qr/^(8\.0\.0\.[56]|8\.1\.0\.[23])$/;
    if (isCurrentOneFS('>8.1.0.3') or
	(!isDestinationOneFS() and !isCurrentOneFS($RE_AFFECTED))) {
	return unsupported('UNSUP_CUR');
    }
    if (isDestinationOneFS() and !isDestinationOneFS($RE_AFFECTED)) {
	return unsupported('UNSUP_TGT_1', '8.0.0.5, 8.0.0.6, 8.1.0.2, or 8.1.0.3');
    }

    # Skip unless licensed for SmartQuotas and SyncIQ
    return unsupported('UNSUP_LIC_1', 'SmartQuotas and SyncIQ')
	unless hasLicense('SmartQuotas') and hasLicense('SyncIQ');

    # Skip unless a SyncIQ target
    gatherSyncIQ();
    return unsupported('UNSUP_SIQ_TAR')
	unless $CLUSTER_INFO{'SyncIQ'} and $CLUSTER_INFO{'SyncIQ'}{'Target'};

    printCheck("KB 521778");
    gatherPatches();

    warning("This cluster is licensed for SmartQuotas and is a target of SyncIQ.  Incoming SyncIQ may fail if there are more than 100 quotas in sub-directories below a SyncIQ target path.  See KB 000031948 for details on this known issue.");

    if ($RPS{'Target'}) {
	if ($RPS{'Target'} =~ /^(8\.0\.0\.5)$/) {
	    critical("Do NOT perform the SyncIQ Rollup Patch 224399 after performing an upgrade to OneFS $RPS{'Target'}.");
	} elsif ($RPS{'Target'} =~ /^(8\.0\.0\.6)$/) {
	    critical("Do NOT perform the SyncIQ Rollup Patch 215690 or Patch 221638 after performing an upgrade to OneFS $RPS{'Target'}.");
	} elsif ($RPS{'Target'} =~ /^(8\.1\.0\.2)$/) {
	    critical("Do NOT perform the SyncIQ Rollup Patch 222718 after performing an upgrade to OneFS $RPS{'Target'}.");
	} elsif ($RPS{'Target'} =~ /^(8\.1\.0\.3)$/) {
	    fail("An upgrade to OneFS $RPS{'Target'} should NOT be performed.");
	}
    }
    if (hasPatch("patch-(224399|215690|221638|222718)") or
	isCurrentOneFS(qr/^(8\.1\.0\.3)$/)) {
	if ($RPS{'Target'}) {
	    warning("Cluster current OneFS and patch level put it at risk for KB 000031948.");
	} else {
	    fail("Cluster current OneFS and patch level put it at risk for KB 000031948.");
	}
    } elsif (isCurrentOneFS('8.0.0.5')) {
	warning("Do NOT perform the SyncIQ Rollup Patch 224399");
    } elsif (isCurrentOneFS('8.0.0.6')) {
	warning("Do NOT perform the SyncIQ Rollup Patch 215690 or Patch 221638");
    } elsif (isCurrentOneFS('8.1.0.2')) {
	warning("Do NOT perform the SyncIQ Rollup Patch 222718");
    }

    # Output Check result
    printResults();
}
$CHECKS{'checkKB524082'} = {
    'Description' => 'Checks if the cluster is enabled for HTTP clients and flags a compatibility issue caused by a change in Apache versions',
    'Exclude' => 1,
};
sub checkKB524082 {
    if (wasCalled()) { return }

    # Skip if not upgrading to a version with Apache 2.4
    if (!$RPS{'Target'}) { return }
    if (compareVersions($RPS{'Target'}, '8.0.0.6') <= 0 or
	isDestinationOneFS(qr/^8\.0\.1\.[01]/) or
	isDestinationOneFS(qr/^8\.1\.0\.[012]/)) {
	return;
    }

    # Skip if running a version with Apache 2.4
    if (isCurrentOneFS('8.0.0.7') or
	isCurrentOneFS('8.1.0.3') or
	isCurrentOneFS('8.1.0.4') or
	isCurrentOneFS('>=8.1.2')) {
	return;
    }

    # Setup
    printCheck('KB 524082');
    my $CMD_LIVE6 = qq[2>&1 grep "httpdata2" /etc/mcp/override/apache2.xml];
    my $CMD_LOGS6 = q[2>&1 grep "httpdata2"].
	q[ */etc.tar/etc/mcp/override/apache2.xml].
	q[ local/ifsvar_etc.tar/etc/mcp/override/apache2.xml];
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_gconfig -t http-config];
    my $CMD_LOGS = q[2>&1 cat].
	q[ local/ifsvar_modules.tar/modules/http/http_config.gc];
    my $MSG = "During the upgrade from OneFS $CLUSTER_INFO{'OneFS'} to OneFS".
	" $RPS{'Target'}, Apache is upgraded from Apache 2.2 to Apache 2.4.".
	' If .htaccess files are utilized, this can result in HTTP 500 errors'.
	' following and during the upgrade due to invalid existing Apache 2.2'.
	' syntax. '.sprintf($MSG{'KB_1'}, '000156304').'prior to the OneFS'.
	' upgrade to determine whether this may be impactful to your workflow.';

    # Gather
    my $gather = ();
    if (isCurrentOneFS('<7')) {
	if (isLive()) {
	    $gather = gatherLocal($CMD_LIVE6);
	} else {
	    $gather = gatherLocal($CMD_LOGS6);
	}
    } else {
	if (isLive()) {
	    $gather = gatherLocal($CMD_LIVE);
	} else {
	    $gather = gatherLocal($CMD_LOGS);
	}
    }

    # Analyze
    my $enabled_config = 0;
    foreach my $line (@$gather) {
	if ($line =~ /^redirect.*false/ or
	    $line =~ /httpdata2.*>enabled</) {
	    $enabled_config = 1;
	}
    }

    # Flag
    if (isServiceEnabled('apache2') and $enabled_config) {
	addCustomerInformation(warning($MSG));
    }

    # Output
    printResults();
}
$CHECKS{'checkKB527312'} = {
    'Description' => 'Check for criteria of KB 000166965',
    'Exclude' => 1,
};
sub checkKB527312 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8')) { return unsupported('UNSUP_CUR') }
    if (!$RPS{'Target'} or compareVersions($RPS{'Target'}, '8') < 0) {
	return unsupported('UNSUP_TGT_1', '8 or later');
    }

    # Setup
    printCheck('KB 527312');
    my $CMD_LIVE = q[2>&1 cat /ifs/.ifsvar/modules/smartpools].
	q[/drive_purposing.db];
    my $CMD_LOGS = q[2>&1 cat local/ifsvar_modules.tar/modules/smartpools].
	q[/drive_purposing.db];
    my $SPF_UPGRADE = 'Cluster may be affected by KB 000166965 during an upgrade'.
	' from %s to %s.';
    my $MSG_AFFECTED = 'The malformed drive_purposing.db file should be'.
	' corrected prior to performing the OneFS upgrade.';

    # Gather
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal($CMD_LIVE);
    } else {
	$gather = gatherLocal($CMD_LOGS);
    }

    # Analyze
    my $affected = 0;
    foreach my $line (@$gather) {
	# If there are any empty lists they may be affected
	if ($line =~ /\[\s*\]/) { $affected = 1 }

	# Once found affected, drop out of loop
	if ($affected) { last }
    }

    # Flag
    if ($affected) {
	fail(sprintf($SPF_UPGRADE, $CLUSTER_INFO{'OneFS'}, $RPS{'Target'}));
	fail($MSG_AFFECTED);
	info(sprintf($MSG{'KB_INT_1'}, '000166965'));
    }

    # Output
    printResults();
}
$CHECKS{'checkKB530050'} = {
    'Description' => 'Check for criteria of KB 000040987',
    'Reference' => 'https://www.dell.com/support/kbdoc/000040987',
};
sub checkKB530050 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<7.2.1')) { return unsupported('UNSUP_CUR') }
    if (isCurrentOneFS('>=9.5')) { return }
    if (!isHardened()){ return }
    
    # Setup
    printCheck('KB 530050');
    my $CMD_NODE = q[ls /etc/hardening[-_]metadata*];
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array '$CMD_NODE'];
    my $CMD_LOGS = q[2>&1 grep -H . -m 1 */etc.tar/etc/hardening[-_]metadata*];
    my $SPF_NODES = 'The following nodes may be impacted by KB 000040987 during a'.
	' OneFS upgrade: %s';

    # Gather
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster($CMD_LIVE);
    } else {
	$gather = gatherCluster($CMD_LOGS);
    }

    # Analyze
    my @nodes = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) {
	    if (isLive()) {
		missing($lnn);
	    } else {
		push(@nodes, $lnn);
	    }
	    next;
	}
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ /no matches found/) {
		push(@nodes, $lnn);
		last;
	    }
	}
    }

    # Flag
    if (%nodes_with_issues) {
	fail(sprintf($SPF_NODES, compressRange(@nodes)));
	info(sprintf($MSG{'KB_INT_1'}, '000040987'));
    }

    # Output
    printResults();
}
$CHECKS{'checkKB540000'} = {
    'Description' => 'Checks for criteria of KB 000058599',
    'Exclude' => 1,
};
sub checkKB540000 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8.2')) { return unsupported('UNSUP_CUR') }

    # Setup
    printCheck('KB 540000');
    my $GCONFIG = 'fsa.max_count';
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_gconfig $GCONFIG];
    my $CMD_LOGS = qq[2>&1 grep -h $GCONFIG local/isi_gconfig].
	q[ local/gconfig_ifsvar.tar/ifs/_ifsvar/main_config.gc];
    my $SPF_FSA = 'The maximum number of FSA results that can be saved in'.
	' OneFS 8.2 and later is 99. The current value of the fsa.max_count'.
	' is %s and would be changed to a default value of 8 after an upgrade'.
	' to OneFS 8.2.0 or later if left unchanged.';

    # Gather
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal($CMD_LIVE);
    } else {
	$gather = gatherLocal($CMD_LOGS);
    }

    # Parse
    my ($key, $value) = parseGconfigItem(@$gather);

    # Flag
    if ($key eq 'fsa.max_count' and ($value < 1 or $value > 99)) {
	my $msg = sprintf($SPF_FSA, $value);
	warning($msg);
	if ($RPS{'Target'} and compareVersions($RPS{'Target'}, '8.2') >= 0) {
	    addCustomerInformation($msg.sprintf($MSG{'KB_1'}, '000058599'));
	}
	info(sprintf($MSG{'KB_1'}, '000058599'));
    }

    # Output
    printResults();
}
$CHECKS{'checkKB540071'} = {
    'Description' => 'Checks for existence of files under /var/fw/fwpkg when no IsiFw package is installed',
    'Reference' => 'https://www.dell.com/support/kbdoc/000068310',
};
sub checkKB540071 {
    if (wasCalled()) { return }
    if (hasPatch('^IsiFw')) { return unsupported('UNSUP_FWPKG') }
    if (isCurrentOneFS('>=9')) { return unsupported('UNSUP_CUR') }

    # Setup
    printCheck('KB 540071');
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'du -hs /var/fw/fwpkg'];
    my $CMD_LOGS = q[2>&1 grep -H 'fwpkg$' */du];
    my $SPF_NODES = 'The following nodes are at risk for KB 000068310: %s';

    # Gather
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster($CMD_LIVE);
    } else {
	$gather = gatherCluster($CMD_LOGS);
    }

    # Analyze
    my @nodes_at_risk = ();
    foreach my $lnn (@LNNS) {
	foreach (@{$gather->{$lnn}}) {
	    if (/^\s*\S+\s+\/var\/fw\/fwpkg$/) {
		push(@nodes_at_risk, $lnn);
	    }
	}
    }

    # Flag
    if (@nodes_at_risk) {
	critical(sprintf($SPF_NODES, compressRange(@nodes_at_risk)));
	info(sprintf($MSG{'KB_1'}, '000068310'));
    }

    # Output
    printResults();
}
$CHECKS{'checkKB540872'} = {
    'Description' => 'Checks if the cluster may encounter KB 000170982 during an upgrade from OneFS 8.2 releases',
    'Reference' => 'https://www.dell.com/support/kbdoc/000170982',
};
sub checkKB540872 {
    if (wasCalled()) { return }
    if (!isCurrentOneFS('8.2.0.0') and
	!isCurrentOneFS('8.2.1.0') and
	!isCurrentOneFS('8.2.2.0')) {
	return unsupported('UNSUP_CUR');
    }

    # Setup
    printCheck('KB 540872');
    my $GCONFIG = 'smartpools.diskpools.suggested_protection_enabled';
    my $CMD_LIVE_GCONFIG = qq[2>&1 ${SUDO} isi_gconfig $GCONFIG];
    my $CMD_LOG_GCONFIG = qq[2>&1 grep $GCONFIG local/isi_gconfig];
    my $CMD_LIVE_DRIVEDB =
	q[2>&1 cat /ifs/.ifsvar/modules/smartpools/drive_purposing.db];
    my $CMD_LOG_DRIVEDB = q[2>&1 cat local/ifsvar_modules.tar/modules].
	q[/smartpools/drive_purposing.db];
    my $SPF_MTTDL_MISSING = 'The following equivalence entries in the drive'.
	' purposing database are missing the "mttdl" key and puts the cluster'.
	' at risk for encountering KB 000170982 during an OneFS upgrade: %s';
    my $SPF_NO_NODES = 'The following equivalence entries in the drive'.
	' purposing database do not have any active node device ids in'.
	' them and puts the cluster at risk for encountering KB 000170982'.
	' during an upgrade to OneFS 8.2.2: %s';
    my $MSG_PROT = 'Suggested Protection is not enabled';

    # Gather suggested_protection_enabled
    my $suggested_protection_enabled = 1;
    my $gather_gconfig = ();
    if (isLive()) {
	$gather_gconfig = gatherLocal($CMD_LIVE_GCONFIG);
    } else {
	$gather_gconfig = gatherLocal($CMD_LOG_GCONFIG);
    }

    # Analyze suggested_protection_enabled
    foreach (@$gather_gconfig) {
	my ($key, $value) = parseGconfigItem($_);
	if ($key =~ /$GCONFIG/ and $value =~ /false/) {
	    $suggested_protection_enabled = 0;
	}
    }

    # Gather drive_purposing.db
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal($CMD_LIVE_DRIVEDB);
    } else {
	$gather = gatherLocal($CMD_LOG_DRIVEDB);
    }

    # Analyze drive_purposing.db
    my $db = join('', @$gather);
    my $ee_missing_mttdl = '';
    my $ee_no_nodes = '';
    eval {
	require JSON::PP;
	import JSON::PP qw( decode_json );
	my $href_json = decode_json($db);
	my %ee = %{$href_json->{equivalence_entries}};
	my @mttdl_missing = ();
	my @no_nodes = ();
	foreach my $entry (keys %ee) {
	    if (!defined $ee{$entry}{mttdl}) {
		debug("Equivalence entry $entry is missing mttdl");
		push(@mttdl_missing, $entry);
	    }
	    if (!defined $ee{$entry}{node_types}) {
		debug("Equivalence entry $entry is missing node_types");
		next;
	    }
	  TYPE:
	    foreach my $type (@{$ee{$entry}{node_types}}) {
		if (ref $type ne 'ARRAY') { next }
		foreach my $element (@$type) {
		    if (ref $element ne 'ARRAY') { next }
		    foreach my $devid (@$element) {
			if (defined $LNN_FROM_DEVID{$devid}) { next TYPE }
		    }
		}
		push(@no_nodes, $entry);
	    }
	}
	$ee_missing_mttdl = join(', ', sort {$a <=> $b} @mttdl_missing);
	$ee_no_nodes = join(',', sort {$a <=> $b} @no_nodes);
    };

    # Flag
    if ($ee_missing_mttdl) {
	fail(sprintf($SPF_MTTDL_MISSING, $ee_missing_mttdl));
    }
    if ($ee_no_nodes) {
	if (isDestinationOneFS('8.2.2.0')) {
	    fail(sprintf($SPF_NO_NODES, $ee_no_nodes));
	} else {
	    warning(sprintf($SPF_NO_NODES, $ee_no_nodes));
	}
    }
    if (!$suggested_protection_enabled) { info($MSG_PROT) }

    # Output
    if (isResults(&INFO)) { info(sprintf($MSG{'KB_1'}, '000170982')) }
    printResults();
}
$CHECKS{'checkKB544401'} = {
    'Description' => 'Check for critera of KB 000173157',
    'Reference' => 'https://www.dell.com/support/kbdoc/000173157',
};
sub checkKB544401 {
    if (wasCalled()) { return }
    if (!isCurrentOneFS('8.2.0.0')) { return unsupported('UNSUP_CUR') }

    # Setup
    printCheck('KB 544401');
    gatherSRS();
    my $SPF_NODES = 'The following nodes are not considered eligible for SRS'.
	' connectivity and may get stuck during shutdown: %s';
    my $MSG_SRS = 'To avoid this issue, prior to performing any scheduled'.
	' maintenance tasks which would reboot the nodes, disable the'.
	' isi_esrs_d service by running: isi services -a isi_esrs_d disable.'.
	' Once the maintenance tasks have concluded, re-enable the isi_esrs_d'.
	' service by running: isi services -a isi_esrs_d enable';
    my $RE_PATCH = '^8\.2\.0_.*[-_](?:255568|257594|259123|261103|261104'.
	'|262892|262893|264781|264782|266617|266990|268444|269435|270016'.
	'|271507|273332|273851|64|172|320)$';

    # Analyze
    my @nodes = ();
    foreach my $lnn (@LNNS) {
	my ($match) = grep {$_ eq $lnn} @{$CLUSTER_INFO{'SRS'}{'Nodes'}};
	if (!$match) { push(@nodes, $lnn) }
    }

    # Flag
    if (@nodes and isServiceEnabled('isi_esrs_d') and !hasPatch($RE_PATCH)) {
	if ($RPS{'JSON'}) {
	    warning(sprintf($SPF_NODES, compressRange(@nodes)));
	    warning($MSG_SRS);
	} else {
	    critical(sprintf($SPF_NODES, compressRange(@nodes)));
	    critical($MSG_SRS);
	}
	info(sprintf($MSG{'KB_1'}, '000173157'));
    }

    # Output
    printResults();
}
$CHECKS{'checkKB544854'} = {
    'Description' => 'Check for criteria of KB 000173432',
    'Exclude' => 1,
};
sub checkKB544854 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8.2')) { return unsupported('UNSUP_CUR') }
    printCheck('KB 544854');
    gatherArrayXML();

    # Setup
    my $MSG_FILESERIAL = 'Multiple versions of the array config file'.
	' [/etc/ifs/array.xml] detected. To resolve this issue, restart the'.
	' isi_array_d service by running the following command: isi_for_array'.
	' killall isi_array_d';
    my $SPF = '%5s %10s';

    # Analyze
    my %fileserials = ();
    verboseHeader('/etc/ifs/array.xml fileserials');
    verbose(sprintf($SPF, 'LNN', 'fileserial'));
    verbose(sprintf($SPF, '-'x5, '-'x10));
    foreach my $lnn (@LNNS) {
	if (!$NODE_INFO{$lnn}{'fileserial'}) { next }
	verbose(sprintf($SPF, $lnn, $NODE_INFO{$lnn}{'fileserial'}));
	push(@{$fileserials{$NODE_INFO{$lnn}{'fileserial'}}}, $lnn);
    }

    # Flag
    if (scalar(keys %fileserials) > 1) {
	fail($MSG_FILESERIAL);
	info(sprintf($MSG{'KB_1'}, '000173432'));
    }

    # Output
    printResults();
}
$CHECKS{'checkLWIODLog'} = {
    'Description' => 'Checks /var/log/lwiod.log for known errors occuring in the last 30 days',
    'Reference' => 'https://www.dell.com/support/kbdoc/000162012'
};
sub checkLWIODLog {
    return if wasCalled();
    printCheck("LWIOD Log");

    # A list of the last 30 days
    my @date = ();
    for (my $i = 0; $i <= 30; $i++) {
	my ($day, $month, $year) = (localtime($RPS{'Timestamp'} - ($i * 86400)))[3,4,5];
	$year += 1900;
	$month += 1;
	push(@date, sprintf("%04d-%02d-%02d", $year, $month, $day));
    }

    # A list of the errors to gather
    my @detect = ('code:0xc0000205',
		  'GSS-API error calling gss_accept_sec_context:',
		  'Too many open files',
		  'Clock skew too great',
		  'LW_ERROR_CLOCK_SKEW',
		  'An invalid name was supplied',
		  'Configuration file does not specify default realm',
		  'Ticket not yet valid',
		  'Unspecified GSS failure.  Minor code may provide more information',
		  'Illegal cross-realm ticket',
		  '\.pst',
		  '\.ovt',
		  '\.pap',
		  '\.ost',
	);

    # A list of the errors to ignore
    my @ignore = ('LW_ERROR_ACCOUNT_DISABLED',
		  'LW_ERROR_INVALID_PASSWORD',
		  'LW_ERROR_ACCOUNT_LOCKED',
		  'LW_ERROR_ACCOUNT_EXPIRED',
	);

    # Compile greps from the lists
    my $date_grep = join("|", @date);
    my $detect_grep = join("|", @detect);
    my $ignore_grep = join("|", @ignore);

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array 'grep -E "^(${date_grep}).*(${detect_grep})" /var/log/lwiod.log | grep -v -E "(${ignore_grep})"']);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -E "^(${date_grep}).*(${detect_grep})" */varlog.tar/log/lwiod.log | grep -v -E "(${ignore_grep})"]);
    }

    # Analyze information for issues.
    my $pst_errors = 0;
    my %error_counts = ();
    foreach my $lnn (sort {$a <=> $b} keys %NODE_INFO) {
	next unless ($gather->{$lnn});
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ /\.(pst|ovt|pap|ost)/) {
		$pst_errors++;
		next;
	    } elsif ($line =~ /($detect_grep)/) {
		$error_counts{$1}++;
	    }
	}
    }
    foreach my $error (keys %error_counts) {
	warning("Found $error_counts{$error} '$error' errors");
    }
    if ($pst_error) {
	warning('Performance degrading workflow detected. '.sprintf($MSG{'KB_1'}, '000162012'));
    }
    printResults();

} # End checkLWIODLog
$CHECKS{'checkLeakFreeBlocks'} = {
    'Description' => 'Checks for nodes with efs\.lbm\.leak_freed_blocks enabled.',
    'Reference' => 'https://www.dell.com/support/kbdoc/000117899',
};

sub checkLeakFreeBlocks {
    if (wasCalled()) { return }

   my $gather = ();
    if (isLive()) {
	    $gather_crnt = gatherCluster(qq[2>&1 ${SUDO} isi_for_array 'sysctl efs.lbm.leak_freed_blocks']);
        $gather_nprstnt = gatherCluster(qq[2>&1 ${SUDO} isi_for_array 'grep "leak_freed_blocks" /etc/sysctl.conf 2>&- || echo "No Non Persistent setting found"']);
        $gather_prstnt = gatherCluster(qq[2>&1 ${SUDO} isi_for_array 'grep "leak_freed_blocks" /etc/mcp/override/sysctl.conf 2>&- || echo "No Persistent setting found"']);
    } else {
        $gather_crnt = gatherCluster(qq[2>&1 grep -H 'efs.lbm.leak_freed_blocks' */efs.lbm]);
        $gather_nprstnt = gatherCluster(qq[2>&1 grep -H 'efs.lbm.leak_freed_blocks' */etc.tar/etc/sysctl.conf]);
	    $gather_prstnt = gatherCluster(qq[2>&1 grep -H 'efs.lbm.leak_freed_blocks' */etc.tar/etc/mcp/override/sysctl.conf]);
    }
    
    printCheck("Leak Freed Blocks");

    my @nodes = ();
    my @nodes_np = ();
    my @nodes_prst = ();
    my @nodes_all = ();
    # Analyze information for issues.
    my $leak_free_blocks_enabled = 0;
    my $lfb_res;

    foreach my $lnn (@LNNS) {

        next unless ($gather_crnt->{$lnn} || $gather_nprstnt->{$lnn} || $gather_prstnt->{$lnn});
        my $persistent_config;
        foreach my $line (@{$gather_crnt->{$lnn}}) {
            debug("$line");
            next unless $line =~ /^[A-Za-z\._]+[:=]?\s*(\d+)?$/;
            $crnt_res = $1;
            $leak_free_blocks_enabled = 1 if $crnt_res eq '1';
            debug("leak_free_blocks_enabled = $leak_free_blocks_enabled | crnt_res = $crnt_res");
            push(@nodes, $lnn) if $crnt_res eq '1';
            push(@nodes_all, $lnn) if $crnt_res eq '1';
            #verbose(sprintf("%-10s %-10s", $lnn, $lfb_res));
        }
        foreach my $line (@{$gather_prstnt->{$lnn}}) {
            debug("$line");
            next unless $line =~ /^[A-Za-z\._]+[:=]?\s*(\d+)?$/;
            $prst_res = $1;
            $persistent_config = 1;
            push(@nodes_prst, $lnn) if $prst_res eq '1';
            push(@nodes_all, $lnn) if $prst_res eq '1';
            $leak_free_blocks_enabled = 1 if $prst_res eq '1';
            #verbose(sprintf("%-10s %-10s", $lnn, $lfb_res));
        }
        foreach my $line (@{$gather_nprstnt->{$lnn}}) {
            debug("$line");
            next unless $line =~ /^[A-Za-z\._]+[:=]?\s*(\d+)?$/;
            $nprst_res = $1;
            push(@nodes_np, $lnn) if $nprst_res eq '1' and !$persistent_config;
            push(@nodes_all, $lnn) if $nprst_res eq '1';
            $leak_free_blocks_enabled = 1 if $nprst_res eq '1';
            #verbose(sprintf("%-10s %-10s", $lnn, $lfb_res));
        }
    }
verboseHeader("KB 000117899") if $leak_free_blocks_enabled;
verbose(sprintf("%-20s %-10s", "Current State", "LNNs")) if $leak_free_blocks_enabled;
verbose(sprintf("%-20s %-10s", "-"x20, "-"x10)) if $leak_free_blocks_enabled;
verbose(sprintf("%-20s %-10s", "Currently Enabled",compressRange(@nodes))) if @nodes;
verbose(sprintf("%-20s %-10s", "Set Non Persistent",compressRange(@nodes_np))) if @nodes_np;
verbose(sprintf("%-20s %-10s", "Set Persistent",compressRange(@nodes_prst))) if @nodes_prst;


debug('FOUND SETTING ENABLED ON NODES: '.compressRange(@nodes_all)) if $leak_free_blocks_enabled;
fail('Checks have indicated incomplete investigation of data integrity, contact Isilon Support immediately referencing docu52861') if $leak_free_blocks_enabled;

    # Output
    printResults();
} # End checkLeakFreeBlocks
###
# checkLicense: Checks licenses and gives direction based on licensed
#  features.  InsigntIQ and vCenter licenses, gives information from
#  compatibility guide.  iSCSI, instructs to only perform simultaneous
#  OneFS upgrades and that it is not supported in 8.0.
$CHECKS{'checkLicense'} = {
    'Description' => 'Checks licenses and gives direction based on licensed features.  InsigntIQ and vCenter licenses, gives information from compatibility guide.  iSCSI, instructs to only perform simultaneous OneFS upgrades and that it is not supported in 8.0',
    'Reference' => 'https://www.dell.com/support/kbdoc/000039763',
};
sub checkLicense {
    return if wasCalled();
    printCheck("Licenses");
    gatherLicense();
    gatherProductInfo();

    if (hasLicense("iSCSI")) {
	if ($RPS{'Target'} and compareVersions($RPS{'Target'}, "8.0") >= 0) {
	    fail("iSCSI is licensed.  Support for iSCSI is deprecated as of OneFS 8.");
	} else {
	    warning("iSCSI is licensed.  If there are any iSCSI connections to the cluster, you can only perform a simultaneous upgrade. After all the initiator connections are stopped, set the iSCSI LUN to offline until the upgrade is complete." );
	}
    }

	checkSWIFTAccounts();
	if (hasLicense("SWIFT") && !isServiceDisabled(lwswift)) {
	if ($RPS{'Target'} and compareVersions($RPS{'Target'}, "9.5") >= 0 and $RPS{'SWIFT accounts configured'} == 1) {
		
	    if(isCurrentOneFS('<9.5') ){
			fail("SWIFT is licensed and enabled.  Support for SWIFT is deprecated as of OneFS 9.5");
		}
		else{
			warning("SWIFT is licensed and enabled.  If Swift is in use on this cluster, this will stop working after an upgrade to 9.5 as support for SWIFT is deprecated as of OneFS 9.5." );
	
		}
	} else {
	    warning("SWIFT is licensed and enabled.  If Swift is in use on this cluster, this will stop working after an upgrade to 9.5 as support for SWIFT is deprecated as of OneFS 9.5." );
	}
	info("For more information regarding Swift deprecation refer to KB 00067100 found at https://www.dell.com/support/kbdoc/en-ie/00067100.");
	info("For steps required to disable SWIFT, please reference https://www.dell.com/support/kbdoc/000340648");
    }

    if ($RPS{'Target'} and hasLicense("vCenter")) {
	if (compareVersions($RPS{'Target'}, "7.2") >= 0 and
	    isCurrentOneFS('<7.2')) {
	    warning("Isilon for vCenter is licensed. OneFS 7.2 or later does not support Isilon for vCenter plugin.");
	}
	if (compareVersions($RPS{'Target'}, "7.0") >= 0 and
	    isCurrentOneFS('<7')) {
	    warning("Isilon for vCenter is licensed. OneFS 7.0 or later supports Isilon for vCenter version 1.3 or later.");
	}
    }

    # InsightIQ and OneFS Compatibilty
    my $SPF_IIQ = 'InsightIQ %s or later is required for OneFS %s support';
    
	if (isDestinationOneFS() and
	(hasLicense('InsightIQ') or
	 isCurrentOneFS('>=8.1'))) {
	if (isDestinationOneFS('>=9.10') and
	    isCurrentOneFS('<=9.10')) {
	    warning(sprintf($SPF_IIQ, '5.2.x', isDestinationOneFS()));
	}
	elsif ((isDestinationOneFS('>=9.9') and isDestinationOneFS('<9.10')) and
	    isCurrentOneFS('<=9.9')) {
	    warning(sprintf($SPF_IIQ, '5.1.x', isDestinationOneFS()));
	}
	elsif (isDestinationOneFS('>=9.8') and
	    isCurrentOneFS('<=9.8')) {
	    warning(sprintf($SPF_IIQ, '5.1.x', isDestinationOneFS()));
	}
	elsif(
		(isCurrentOneFS('=9.7') and isCurrentOneFS('>=9.7.1.3')) or 
		(isDestinationOneFS('>=9.7.1.3') and isCurrentOneFS('<=9.7.1.3'))){
		warning(sprintf($SPF_IIQ, '5.2.x', isDestinationOneFS()));
	}
	elsif (isDestinationOneFS('>=9.7') and (isDestinationOneFS('<9.7.1.3') or isDestinationOneFS('>9.7.1.3'))
	    and isCurrentOneFS('<=9.7')) {
	    warning(sprintf($SPF_IIQ, '5.0.x', isDestinationOneFS()));
	}	
	elsif (isDestinationOneFS('>=9.5') and
	    isCurrentOneFS('<=9.5')) {
	    warning(sprintf($SPF_IIQ, '4.3.0.x', isDestinationOneFS()));
	} elsif (isDestinationOneFS('>=9.4') and
	    isCurrentOneFS('<=9.4')) {
	    warning(sprintf($SPF_IIQ, '4.2.0.x', isDestinationOneFS()));
	} elsif (isDestinationOneFS('>=9.2') and
	    isCurrentOneFS('<9.2')) {
	    warning(sprintf($SPF_IIQ, '4.1.4.60', isDestinationOneFS()));
	} elsif (isDestinationOneFS('>=8.2') and
		 isCurrentOneFS('<8.2')) {
	    warning(sprintf($SPF_IIQ, '4.1.3', isDestinationOneFS()));
	} elsif ((isDestinationOneFS('>=8.1.2') and
		  isCurrentOneFS('<8.1.2')) or
		 (isDestinationOneFS('>=8.1.1.1') and
		  isCurrentOneFS('<8.1.1.1')) or
		 (isDestinationOneFS('>=8.1.0.3') and
		  isCurrentOneFS('<8.1.0.3')) or
		 (isDestinationOneFS('>=8.0.0.7') and
		  isCurrentOneFS('<8.0.0.7'))) {
	    warning(sprintf($SPF_IIQ, '4.1.2', isDestinationOneFS()));
	} elsif (isDestinationOneFS('>=8') and
		 isCurrentOneFS('<8')) {
	    warning(sprintf($SPF_IIQ, '4.0', isDestinationOneFS()));
	} elsif (isDestinationOneFS('>=7.2') and
		 isCurrentOneFS('<7.2')) {
	    warning(sprintf($SPF_IIQ, '3.1', isDestinationOneFS()));
	} elsif (isDestinationOneFS('>=7') and
		 isCurrentOneFS('<7')) {
	    warning(sprintf($SPF_IIQ, '2.5', isDestinationOneFS()));
	}
    }

    # Flag major/minor/sub upgrades that do not have a valid signature
    if ($RPS{'Target'} and
	isCurrentOneFS('>=8.1') and
	isCurrentOneFS('<9.13.0') and 
	not $CLUSTER_INFO{'Valid Signature'} and
	not isVirtual() and
	upgradeDigit($CLUSTER_INFO{'OneFS'}, $RPS{'Target'}) <= 3) {
	fail("License does not have a valid signature.  The license must be updated prior to performing the upgrade from $CLUSTER_INFO{'OneFS'} to $RPS{'Target'}.");
    }

    # All information above here is in licenses checks should be sent to the customer
    addCustomerInformation($_) foreach (@Logger::WARN, @Logger::FAIL);

    # Flag is iSCSI is disabled but the iSCSI service is enabled as a warning
    if ((!$RPS{'Target'} or compareVersions($RPS{'Target'}, "8.0") < 0)
	and !hasLicense("iSCSI")
	and isServiceEnabled('isi_iscsi_d')) {
	warning("iSCSI license is $CLUSTER_INFO{'License'}{'iSCSI'}{'Status'} and the iSCSI target Daemon service(isi_iscsi_d) is enabled");
    }

    # Flag expired licenses as a warning
    my @expired_licenses = ();
    foreach my $name (keys %{$CLUSTER_INFO{'License'}}) {
	if ($CLUSTER_INFO{'License'}{$name}{'Status'}
	    and $CLUSTER_INFO{'License'}{$name}{'Status'} eq "Expired") {
	    push(@expired_licenses, $name);
	}
    }
    warning("The following licenses have expired: ".join(", ", @expired_licenses)) if @expired_licenses;

    # Output Check result
    info(sprintf($MSG{'KB_1'}, '000039763')) if isResults(&WARN);
    printResults($CLUSTER_INFO{'OneFS License ID'} or "");
} # End checkLicense
$CHECKS{'checkLinMasterPadding'} = {
    'Description' => 'Checks the LIN master padding to be all zeros',
    'Exclude' => 1,
};
sub checkLinMasterPadding {
    if (wasCalled()) { return }
    if (!isLive()) { return unsupported('UNSUP_LOGS') };
    if (isCurrentOneFS('>=8.2') or isCurrentOneFS('<7.1.1')) {
	return unsupported('UNSUP_CUR');
    }

    # Setup
    my $padding_start;
    my @expected_output;
    if (isCurrentOneFS('>=8')) {
	$padding_start = 645;
	@expected_output = (
	    '0000285 0000 0000 0000 0000 0000 0000 0000 0000',
	    '*',
	    '0001ff5',
	    );
    } else {
	$padding_start = 637;
	@expected_output = (
	    '000027d 0000 0000 0000 0000 0000 0000 0000 0000',
	    '*',
	    '0001fed 0000 0000 0000 0000 0000',
	    '0001ff6'
	    );
    }
    my $padding_count = 8182 - $padding_start;
    my $expected_size = scalar(@expected_output);
    my $CMD_SYSCTL = q[sysctl efs.lin.super.cache];
    my $GREP_LIN = q[grep -Eo "[1-9][0-9]*,[0-9]+,[0-9]+:8192"];
    my $CMD_CPR = qq[2>/dev/null ${SUDO} isi_cpr -1rx bARG];
    my $CMD_HEXDUMP = qq[hexdump -s $padding_start -n $padding_count];
    my $CMD_XARGS = qq[xargs -IARG sh -c "$CMD_CPR|$CMD_HEXDUMP"];
    my $CMD_LIVE = qq[2>/dev/null ${SUDO} isi_for_array '$CMD_SYSCTL|$GREP_LIN|$CMD_XARGS'];
    my $MSG_NOT_ZERO_PADDING = 'Failed LIN master zero padding test';

    # Start
    printCheck('LIN Master Zero Padding');

    # Gather
    my $gather = gatherCluster($CMD_LIVE);

    # Analyze
    my $has_zero_padding = 0;
  LNN:
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	my $gather_size = scalar(@{$gather->{$lnn}});
	if ($gather_size % $expected_size) { next }
	my $lin_count = $gather_size / $expected_size;
      LIN:
	foreach my $l (0..$lin_count-1) {
	    foreach my $e (0..$#expected_output) {
		my $g = $l * $expected_size + $e;
		$gather->{$lnn}[$g] =~ s/^\s+|\s+$//g;
		if ($gather->{$lnn}[$g] ne $expected_output[$e]) { next LIN }
	    }
	    $has_zero_padding = 1;
	    last LNN;
	}
    }

    # Flag
    if (!$has_zero_padding) {
	if ((isCurrentOneFS('<8.1') and isDestinationOneFS('>=8.1')) or
	    (isCurrentOneFS('<8.2') and isDestinationOneFS('>=8.2'))) {
	    fail($MSG_NOT_ZERO_PADDING);
	} else {
	    warning($MSG_NOT_ZERO_PADDING);
	}
	info(sprintf($MSG{'KB_INT_1'}, '000082066'));
    }

    # Output
    printResults();
} # End checkLinMasterPadding
$CHECKS{'checkListenQueue'}{'Description'} = "Checks for listen queue overflows to be less than 50,000 per node";
sub checkListenQueue {
    return if wasCalled();
    printCheck("Listen Queue Overflows");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array netstat -sn | grep 'listen queue overflow']);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -H 'listen queue overflow' */netstat]);
    }

    # Analyze information for issues.
    my @listen_queue = ();
    foreach my $lnn (@LNNS) {
	verboseHeader($NODE_INFO{$lnn}{'Name'});
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	foreach my $line (@{$gather->{$lnn}}) {
	    next unless ($line =~ /(\d+)\s+listen queue overflow/);
	    verbose("Listen Queue Overflows: $1");
	    push(@listen_queue, $lnn) if ($1 > 50000);
	}
    }
    warning("Listen Queue Overflows count over 50,000 on the following nodes: ".join(",", @listen_queue)) if @listen_queue;

    printResults();
} # End checkListenQueue
$CHECKS{'checkLocalUsers'} = {
    'Description' => 'Recommends backing up sam.db prior to upgrade to 9.5 or higher where current version is prior to 9.5',
    'Reference' => '',
};
sub checkLocalUsers {
    if (wasCalled()) { return }
    printCheck('Local Users');

    # Analyze
    my $active_ndmp = 0;

	if(isCurrentOneFS('<9.5') and 
    (
        (isDestinationOneFS('>=9.5') and 
        isDestinationOneFS('<=9.10.1.4'))
        or
        (isDestinationOneFS('>=9.11.0.0') and isDestinationOneFS('<9.13'))
    )
    ){
		addCustomerInformation("Please ensure sam.db is backed up prior to upgrading OneFS by using the following command: cp /ifs/.ifsvar/modules/auth/sam/sam.db /ifs/.ifsvar/modules/auth/sam/sam.db-bak");
		critical("Please ensure sam.db is backed up prior to upgrading OneFS.");
		info("Please use the following to perform the backup: cp /ifs/.ifsvar/modules/auth/sam/sam.db /ifs/.ifsvar/modules/auth/sam/sam.db-bak");
        info("Please reference KB 000225723 (https://www.dell.com/support/kbdoc/000225723) for additional information.");
	}

    # Output Check result
    printResults();
} # End checkLocalUsers
$CHECKS{'checkLogLevel'} = {
    'Description' => 'Checks LWSM log levels for NFS, SMB, HDFS, and Authentication',
    'Exclude' => 1,
};
sub checkLogLevel {
    if (wasCalled()) { return }
    if (!isLive()) { return }
    if (isCompliance()) { return }
    if ($RPS{'JSON'}) { return }
    if (isCurrentOneFS('<8')) { return }

    # Setup
    printCheck('Log Level');
    my %services = (
	'lsass' => 'authentication (lsass)',
	'netlogon' => 'authentication (netlogon)',
	'nfs' => 'NFS protocol',
	'lwio' => 'SMB protocol',
	'hdfs' => 'HDFS protocol',
	);
    my $SPF_CMD = q[2>&1 isi_for_array '/usr/likewise/bin/lwsm get-log %s'];
    my $RE_DEBUG = qr/TRACE|DEBUG/i;
    my $SPF_DEBUG = 'Debug log levels found for %s on the following nodes: %s';

    # Gather
    my %gather = ();
    foreach my $service (keys %services) {
	$gather{$service} = gatherCluster(sprintf($SPF_CMD, $service));
    }

    # Analyze
    my %debug_logging = ();
    foreach my $service (keys %gather) {
	foreach my $lnn (@LNNS) {
	    if (!$gather{$service}{$lnn}) {
		missing($lnn);
		next;
	    }
	    foreach my $line (@{$gather{$service}{$lnn}}) {
		if ($line =~ $RE_DEBUG) {
		    push(@{$debug_logging{$service}}, $lnn);
		}
	    }
	}
    }

    # Flag
    if (%debug_logging) {
	info(sprintf($MSG{'KB_1'}, '000008264'));
	foreach my $service (keys %debug_logging) {
	    fail(sprintf($SPF_DEBUG, $services{$service},
			 compressRange(@{$debug_logging{$service}})));
	}
    }

    # Output
    printResults();
} # End checkLogLevel
$CHECKS{'checkLogs'} = {
    'Description' => 'Checks Log file presence and flags if any log file specified in list is not present',
    'Exclude' => '',
};
sub checkLogs {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'find /var/log -name "messages" -o -name "audit_protocol.log" -o -name "isi_firmware.log" -o -name "isi_job_d.log" -o -name "isi_radish.log" -o -name "isi_tardis_d.log" -o -name "lsassd.log" -o -name "lwiod.log"'];
    my $CMD_LOGS = q[2>&1; for d in *-*/ ; do { echo ${d}'varlog.tar/log:'; log_opt=$(find  ${d}varlog.tar/log/ \( -name 'messages' -o -name 'audit_protocol.log' -o -name 'isi_firmware.log' -o -name 'isi_job_d.log' -o -name 'isi_radish.log' -o -name 'isi_tardis_d.log' -o -name 'lsassd.log' -o -name 'lwiod.log' \) -printf '%f '); echo $log_opt; } | sed ':a;N;s/\n//;ba'| sed 's|^./||'; done];
    my $SPF = '%5s  %-10s';
    # Start
    printCheck('Check Log Presence');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
	
    # Analyze
    my @expected_logs = ('audit_protocol.log','isi_firmware.log','isi_job_d.log','isi_radish.log','isi_tardis_d.log','lsassd.log','lwiod.log','messages');
	my @nodes_missing_files = ();
    my %files_missing;

    my $SPF = '%5s %-30s';
    
    verboseHeader('Missing Log Files');
    verbose(sprintf($SPF, 'LNN', 'Missing Log Files'));
    verbose(sprintf($SPF, '-'x5, '-'x30));

    foreach my $lnn (@LNNS) {
        my $missing_logs = '';
        if (!$gather->{$lnn}) {
            missing($lnn);
            next;
        }
        my @files_present = ();
        my @local_files = ();
        my %file_on_nodes;
        my $f;
        if(isLive()){
            foreach my $line (@{$gather->{$lnn}}) {
                debug("LINE = $line");
                $f = $1 if $line =~ /^\s*\/var\/log\/(\S+)\s*$/;
                #debug("F = $f");
                $file_on_nodes{$lnn}{$f} = $f;
                debug("file_on_nodes -> $lnn -> $f");
            }
            #create an array from hash to compare again pre defined file list
            foreach my $lf (keys %file_on_nodes){
                foreach my $fn (keys %{ $file_on_nodes{$lf} }){
                    #debug("file_on_nodes -> $lf -> $fn ");
                    if ($fn =~ /^\s*$/){ next }
                    #debug("FILE CHECK -> $fn");
                    push(@files_present, $fn);   
                }
            }
            foreach my $el (@expected_logs){
                if( !grep( /^\s*$el\s*$/, @files_present )){
                    push(@nodes_missing_files, $lnn);
                    debug("$el not present on node $lnn");
                    $missing_logs = $el if $missing_logs eq '';
                    $missing_logs .= ', '.$el unless $missing_logs eq '';
                }
            }
            verbose(sprintf($SPF, $lnn, $missing_logs)) unless $missing_logs eq '';
        }
        else{
            foreach my $line (@{$gather->{$lnn}}) {
                @local_files = split(' ', $line);
                foreach my $f (@local_files){
                if ($f =~ /^\s*$/){ next }
                push(@files_present, $f);
                }
                foreach my $el (@expected_logs){
                    if( !grep( /^\s*$el\s*$/, @files_present )){
                        push(@nodes_missing_files, $lnn);
                        debug("$el not present on node $lnn");
                        $missing_logs = $el if $missing_logs eq '';
                        $missing_logs .= ', '.$el unless $missing_logs eq '';
                    }
                }
                verbose(sprintf($SPF, $lnn, $missing_logs)) unless $missing_logs eq '';
            }
        }

        #foreach my $n (keys %files_missing){
        #
        #    my @missing_from = ();
        #    foreach my $l (keys %{$files_missing{$n}}){
        #        debug("NODE NUM MISSING: $l");
        #        debug("$var_partitions{$n}->{$l}");
        #        push(@missing_from, $l);
        #    };
        #}
        
    }
    if(@nodes_missing_files){
        warning('Log files are missing from the following nodes: '.compressRange(@nodes_missing_files));
        info('Use the following command to show the nodes and missing: perl IOCA -r checkLogs -e');
        info('Please open a Technical Support SR referencing the above failure to have an engineer engaged to remediate.');
    }
    printResults();
} # End checkLogs

$CHECKS{'checkMaintenanceMode'}{'Description'} =
    'Checks if the cluster is currently in maintenance mode';
sub checkMaintenanceMode {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8') || isCurrentOneFS('>=9.9')) {	return unsupported('UNSUP_CUR') }
    printCheck('Maintenance Mode');

    # Setup
    my $MSG_MAINT =
	q[The cluster is currently in maintenance mode. Review 'isi event].
	q[ settings view' for the current maintenance mode settings.  Use the].
	q[ following command to take the cluster out of maintenance mode:].
	q[ isi event settings modify --clear-maintenance-start];
    my $MSG_MAINT_92 =
	q[The cluster is currently in maintenance mode. Use the following].
	q[ command to take the cluster out of maintenance mode:].
	q[ isi event maintenance modify disable];

    my $CMD_LIVE = qq[2>&1 ${SUDO} isi --debug event settings view];
    my $CMD_LOGS = q[2>&1 sqlite3 local/ifsvar_modules_tardis.tar/modules/tardis/namespaces/celog.settings.sqlite 'select * from kv_table'];
    my $CMD_LIVE_92 = qq[2>&1 ${SUDO} isi event maintenance list --format json];
    my $CMD_LOGS_92 = q[2>&1 sqlite3 local/ifsvar_modules_tardis.tar/modules/tardis/namespaces/celog_maintenance.sqlite 'select * from kv_table'];

    if (isCurrentOneFS('>=9.2')) {
	# Gather
	my $gather = gatherLocal(isLive()?$CMD_LIVE_92:$CMD_LOGS_92);

	# Analyze
	my $enabled = 0;
	foreach (@$gather) { if (/\"end\":\s*0\D/) { $enabled = 1 } }

	# Flag
	if ($enabled) { warning($MSG_MAINT_92) }
    } else {
	# Gather
	my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

	# Analyze
	my $start = 0;
	my $duration = 0;
	foreach (@$gather) {
	    if (/\"duration\"\s*:\s*(\d+)\D/) { $duration = $1 }
	    if (/\"start\"\s*:\s*(\d+)\D/) { $start = $1 }
	    if (/maintenance_duration\|(\d+)\|/) { $duration = $1 }
	    if (/maintenance_start\|(\d+)\|/) { $start = $1 }
	}

	# Flag
	if ($start + $duration > $RPS{'Runtime'}) { warning($MSG_MAINT) }
    }

    # Display
    printResults();
} # End checkMaintenanceMode
$CHECKS{'checkMessagesLog'} = {
    'Description' => "Checks /var/log/messages.log for known errors occuring in the last 30 days",
    'Exclude' => 1,
};
sub checkMessagesLog {
    return if wasCalled();
    printCheck("Messages Log");

    # A list of the last 30 days
    my @date = ();
    for (my $i = 0; $i <= 30; $i++) {
	my ($day, $month, $year) = (localtime($RPS{'Timestamp'} - ($i * 86400)))[3,4,5];
	$year += 1900;
	$month += 1;
	push(@date, sprintf("%04d-%02d-%02d", $year, $month, $day));
    }

    # A list of the error types to gather
    my @detect =
	("bus",
	 "changed linkrate",
	 "dsr",
	 "dwr",
	 "dws",
	 "dwt",
	 "ecc",
	 "error",
	 "fail",
	 "free",
	 "inquiry",
	 "invalid hostname to sm_mon",
	 "isi_flexnet_d",
	 "isi_migrate",
	 "lock",
	 "mca",
	 "memory",
	 "nlm",
	 "non-fatal",
	 "onefs",
	 "panic",
	 "quota",
	 "rbmn",
	 "seconds in syslog",
	 "stack",
	 "stall",
	 "terminated",
	 "unrecovered",
	 "upgrade",
	 "userspace",
	 "watchdog",
	);

    my $awkbegin = "BEGIN{IGNORECASE=1}";
    my $awkstring = "";
    my $awkend = "END {";
    foreach my $i (@detect) {
	my $key = $i;
	$key =~ s/[^a-zA-Z0-9]/_/g;
	$awkstring .= qq[/^[^[:space:]]+[[:space:]]+[^[:space:]]+[[:space:]]+[^[:space:]]+[[:space:]].*${i}/{${key}++; ${key}MR=\\\$1}];
	$awkend .= qq[if(${key}){print \\\"${key}:\\\" ${key} \\\" MR:\\\" ${key}MR};];
    }
    $awkend .= "}";

    # A list of the errors to ignore
    my @ignore = ('last message repeated');

    # Compile greps from the lists
    my $date_grep = join("|", @date);
    my $detect_grep = join("|", @detect);
    my $ignore_grep = join("|", @ignore);

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array 'awk "].
				qq[$awkbegin].
				qq[(\\\$0 !~ /(${date_grep}).*\\.[01234]>/){next}].
	 			qq[/(${ignore_grep})/{next}].
	 			qq[$awkstring].
				qq[$awkend].
	 			qq[" /var/log/messages']);
    } else {
	$awkstring =~ s/\\//g;
	$awkend =~ s/\\//g;
	$gather = gatherCluster(qq[for i in */varlog.tar/log/messages; do 2>&1].
				qq[ grep -H -E '^(${date_grep}).*\.[01234]>' \$i].
				qq[|grep -v -E '(${ignore_grep})'].
				qq[|awk '$awkstring $awkend'].
				qq[|awk 'BEGIN{FS=":";OFS=":"}{LNN=\$3;\$3=\$2;\$2=\$1;\$1=LNN;print}'].
				qq[;done]);
    }

    # Analyze information for issues.
    my %error_counts = ();
    foreach my $lnn (sort {$a <=> $b} keys %NODE_INFO) {
	next unless ($gather->{$lnn});
	foreach my $line (@{$gather->{$lnn}}) {
	    next unless $line =~ /^\s*([^:]+):(\d+) MR:(\S+)$/;
	    $error_counts{$lnn}{$1}{'Count'} = $2;
	    $error_counts{$lnn}{$1}{'Most Recent'} = $3;
	}
    }
    foreach my $lnn (sort {$a <=> $b} keys %NODE_INFO) {
	next unless $error_counts{$lnn};
	foreach my $error (keys %{$error_counts{$lnn}}) {
	    warning("Node $lnn: Found $error_counts{$lnn}{$error}{'Count'} '$error' errors. Most recent log entry of this type was: $error_counts{$lnn}{$error}{'Most Recent'}");
	}
    }
    printResults();
} # End checkMessagesLog
$CHECKS{'checkNDMP'} = {
    'Description' => 'Checks for running NDMP sessions',
    'Reference' => 'https://elabnavigator.dell.com/vault/pdf/PowerScale_Isilon_OneFS_Backup_Archive.pdf',
};
sub checkNDMP {
    if (wasCalled()) { return }
    printCheck('NDMP');

    my $CMD_SESSIONS_7 = qq[2>&1 ${SUDO} isi ndmp list];
    my $CMD_SESSIONS_8 = qq[2>&1 ${SUDO} isi ndmp sessions list];
    my $CMD_SESSIONS_9 = qq[2>&1 isi ndmp sessions list];
    my $CMD_LIVE_TRACE = q[2>&1 find /ifs/.ifsvar/modules/ndmp/trace/ -mtime -365 -type f -name '*.trace' | wc -l];
    my $CMD_LOGS_TRACE = q[2>&1 find ARG/local/ifsvar_modules.tar/modules/ndmp/trace/ -mtime -365 -type f -name '*.trace' | wc -l];
    my $MSG_ACTIVE = 'There are active NDMP sessions';
    my $MSG_STOPNDMP = 'Prior to any upgrade activity, all NDMP sessions must complete or be stopped from the associated backup application.';
    my $SPF_COMPATIBILITY = 'Make sure to verify compatibility of your NDMP backup application with the destination OneFS %s prior to the scheduled upgrade. For details on compatibility review the Isilon OneFS for Backup & Archive Simple Support Matrix [ https://elabnavigator.dell.com/vault/pdf/PowerScale_Isilon_OneFS_Backup_Archive.pdf ]';

    # Gather
    my $gather = [];
    if (isLive()) {
	if(isCurrentOneFS('>=9.9')){
        $gather = gatherLocal($CMD_SESSIONS_9);    
    }
    elsif (isCurrentOneFS('>=8')) {
	    $gather = gatherLocal($CMD_SESSIONS_8);
	} else {
	    $gather = gatherLocal($CMD_SESSIONS_7);
	}
    }

    # Analyze
    my $active_ndmp = 0;
    foreach my $line (@$gather) {
	if ($line =~ /^\s*\d.*ACTIVE/ or
	    $line =~ /^\s*\d.* A /) {
	    $active_ndmp = 1;
	    last;
	}
    }

    # Gather
    $gather = gatherLocal(isLive()?$CMD_LIVE_TRACE:$CMD_LOGS_TRACE);

    # Analyze
    my $recent_trace = 0;
    foreach my $line (@$gather) {
	if ($line =~ /^(\d+)$/) {
	    $recent_trace = $1;
	}
    }

    # Flag
    if ($active_ndmp) { critical($MSG_ACTIVE) }
    if (isServiceEnabled('ndmpd')) {
	addCustomerInformation(info($MSG_STOPNDMP));
	if (isOneFSUpgrade()) {
	    addCustomerInformation
		(info(sprintf($SPF_COMPATIBILITY, isDestinationOneFS())));
	}
    }

    # Output Check result
    printResults();
} # End checkNDMP
$CHECKS{'checkNDMP16GB'} = {
    'Description' => 'Checks for LNN changes that have occurred since the isi_ndmp_d processes started which can cause issues during the HookDataMigrationUpgrade phase of an OneFS upgrade',
    'Exclude' => 1,
};
$MSG{'checkNDMP16GB_NODES_2'} = 'Due to an increased memory usage for NDMP backups in %s, if they are performed on the following nodes that only have 16GB of RAM they may fail: %s';
sub checkNDMP16GB {
    if (wasCalled()) { return }
    if (!isServiceEnabled('ndmpd')) {
	return unsupported('UNSUP_SVC_1', 'ndmpd');
    }
    if (isCurrentOneFS('<8.1') and isCurrentOneFS('>=9.2')) {
	return unsupported('UNSUP_CUR');
    }
    if (!isOneFSUpgrade()) { return unsupported('UNSUP_TGT') }
    if (isDestinationOneFS('<9.2')) {
	return unsupported('UNSUP_TGT_1', 'OneFS 9.2 or later releases');
    }

    # Pre-requisite
    checkHardwareStatus();

    # Start
    printCheck('NDMP - 16GB Nodes');
    # Flag
    if ($CLUSTER_INFO{'16GB Nodes'}) {
	warning(sprintf($MSG{'checkNDMP16GB_NODES_2'}, isDestinationOneFS(), compressRange(@{$CLUSTER_INFO{'16GB Nodes'}})));
	info(sprintf($MSG{'KB_1'}, '000197802'));
	addCustomerInformation(sprintf($MSG{'checkNDMP16GB_NODES_2'}, isDestinationOneFS(), compressRange(@{$CLUSTER_INFO{'16GB Nodes'}})).'. '.sprintf($MSG{'KB_1'}, '000455629'));
    }

    # Output
    printResults();
} # End checkNDMP16GB
$CHECKS{'checkNDMPUpgradeTimeout'} = {
    'Description' => 'Checks for LNN changes that have occurred since the isi_ndmp_d processes started which can cause issues during the HookDataMigrationUpgrade phase of an OneFS upgrade',
    'Reference' => 'https://www.dell.com/support/kbdoc/000199064',
    'Parameters' => { 'name' => 'target_version', 'mandatory' => 'true', 'default' => '' }
};
$MSG{'checkNDMPUpgradeTimeout_KB'} = q[A Logical Node Number (LNN) has changed since the isi_ndmp_d processes started which will cause issues during the HookDataMigrationUpgrade phase of an OneFS upgrade. As a workaround, confirm there are no running NDMP sessions using the command "isi ndmp sessions list" and then restart the isi_ndmp_d processes on all nodes by running: isi_for_array 'killall isi_ndmp_d'];
sub checkNDMPUpgradeTimeout {
    if (wasCalled()) { return }
    if (!isServiceEnabled('ndmpd')) {
	return unsupported('UNSUP_SVC_1', 'ndmpd');
    }
    if (isCurrentOneFS('<8')) { return unsupported('UNSUP_CUR') }
    if (!isOneFSUpgrade()) { return unsupported('UNSUP_TGT') }

    # Setup
    my $CMD_LIVE =
	qq[2>&1 ${SUDO} isi_for_array '].
	q[ pid=$(2>/dev/null ps -ax -o sid,comm | grep -m1 isi_ndmp_d | grep -E -o "[0-9]+");].
	q[ [ "$pid" ] || exit; ].
	q[ 2>/dev/null zgrep -E -h -o -m1 "^.*].$CLUSTER_INFO{'Name'}.q[-[1-9]+.*isi_ndmp_d.$pid.:" /var/log/*ndmp* | sort | head -1'];
    my $CMD_LOGS =
	q[for i in *-*/; do].
	q[ pid=$(2>/dev/null grep -E -h isi_ndmp_d $i/ps|awk '$8 ~ /s/ {print $2}'|head -1);].
	q[ [ "$pid" ] || continue;].
	q[ results=$(2>/dev/null zgrep -E -h -o -m1 "^.*].$CLUSTER_INFO{'Name'}.q[-[1-9]+.*isi_ndmp_d.$pid.:" $i/varlog.tar/log/*ndmp* | sort | head -1);].
	q[ [ "$results" ] || continue;].
	q[ echo "$i/ndmp: $results";].
	q[done 2>&1];
    my $RE_LNN = qr/$CLUSTER_INFO{'Name'}-(\d+)/;

    # Start
    printCheck('NDMP - Upgrade Timeout');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my @nodes = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	my ($match_lnn) = map { $_ =~ $RE_LNN} @{$gather->{$lnn}};
	if ($match_lnn and $match_lnn ne $lnn) { push(@nodes, $lnn) }
    }

    # Flag
    if (@nodes) { critical($MSG{'checkNDMPUpgradeTimeout_KB'}) }

    # Output
    printResults();
} # End checkNDMPUpgradeTimeout
# checkNFS: Runs nfsstat and looks for Encountered RPC error
$CHECKS{'checkNFS'} = {
    'Description' => 'Uses nfsstat to identify RPC errors',
    'Exclude' => 1,
};
sub checkNFS {
    if (wasCalled()) { return }
    printCheck('NFS');
    if (isServiceDisabled('nfs')) { return printResults('Disabled') }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'nfsstat'].
	q[| grep 'Encountered RPC error'];
    my $CMD_LOGS = q[2>&1 grep -H 'Encountered RPC error' */nfsstat];
    my $SPF_ISSUES = q[NFS issues detected on the following nodes: %s];

    checkNFS8004();

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my %rpc_errors = ();
    foreach my $lnn (@LNNS) {
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ /Encountered RPC error/) {
		$rpc_errors{$lnn} = 1;
	    }
	}
    }

    # Flag
    if (%rpc_errors) {
	critical(sprintf($SPF_ISSUES, compressRange(keys %rpc_errors)));
    }

    # Output
    printResults();
} # End checkNFS
$CHECKS{'checkNTLM'} = {
    'Description' => 'Checks if NTLM Auth is enabled and the configuration of zones',
    'Exclude' => ,
};
sub checkNTLM {
    if (wasCalled()) { return };
    if((isCurrentOneFS('<9.10.0.1') or isCurrentOneFS('>=9.10.1.2')) and (!isDestinationOneFS() or isDestinationOneFS('<=9.10.0.0') or isDestinationOneFS('>=9.10.1.2'))) { return };
    
    # Setup
    my $CMD_LIVE;
    my $CMD_get_IO_LIVE;
    my $CMD_LIVE_Zones;

    if(isCurrentOneFS('<9.9')){
        $CMD_LIVE = qq[2>&1 ${SUDO} isi auth file list --verbose | egrep 'Name:|NTLM Support:|Status:'];
        $CMD_get_IO_LIVE = qq[2>&1 ${SUDO} isi smb sessions list]; 
        $CMD_LIVE_Zones = q[2>&1 ${SUDO} isi smb shares list --no-header --no-footer --format=list | grep 'Name:' | awk '{print $3}' | while read line; do ${SUDO} isi smb shares view $line | egrep 'Share Name:|Path:|Impersonate Guest:|Impersonate User:|Zone|Guest'; done];
    }
    else{
        $CMD_LIVE = qq[2>&1 isi auth file list --verbose | egrep 'Name:|NTLM Support:|Status:'];
        $CMD_get_IO_LIVE = qq[2>&1 isi smb sessions list]; 
        $CMD_LIVE_Zones = q[2>&1 isi smb shares list --no-header --no-footer --format=list | grep 'Name:' | awk '{print $3}' | while read line; do isi smb shares view $line | egrep 'Share Name:|Path:|Impersonate Guest:|Impersonate User:|Zone|Guest'; done];
    
    }
    my $CMD_LOGS = q[2>&1 egrep -i '^\\s*name:|^\\s*ntlm support:|^\\s*status:' local/isi_auth];
    my $CMD_get_IO_LOGS = q[2>&1 grep 'smb' -h local/isi_statistics_smb];
    my $CMD_LOGS_Zones = q[2>&1 egrep -i '^\\s*\\(.*\\)|^\\s*Share Name:|^\\s*Path:|^\\s*Impersonate Guest:|^\\s*Impersonate User:|^\\s*Zone|^Guest' local/isi_smb];
    my $CMD_LIVE_GetGuest = qq[2>&1 isi auth users view guest | grep -i enabled];
    
    # Start
    printCheck('NTLM Auth');

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);
    my $gather_IO = gatherLocal(isLive()?$CMD_get_IO_LIVE:$CMD_get_IO_LOGS);
    my $gather_Zones = gatherLocal(isLive()?$CMD_LIVE_Zones:$CMD_LOGS_Zones);
    my $get_guest = gatherLocal($CMD_LIVE_GetGuest) if isLive();

    my $smb1_enabled = '';
    my %auth_cfg;
    my %zone_cfg;
    my @nodes = ();
    my $nm_trck = '';
    my $guest_enabled;
    my $ntlm_sessions;
    my $ntlm_enabled;
    my $guest_impersonation;

    # Analyze
    foreach my $line (@$gather) {
        my $key;
        my $value;
        debug("Checking: $line");
        if ($line =~ /^\s*(\S*\s?\S*):\s*(\S+)\s*$/){
            $key = $1;
            $value = $2;
        }
        $nm_trck = $value if $key eq 'Name';
        $auth_cfg{$value}{'Name'} = $value if $key eq 'Name';
        if($key eq 'Status' or $key eq 'NTLM Support'){
            if($key eq 'Status'){
                $auth_cfg{$nm_trck}{'Status'} = defined $value && $value ne '' ? $value : '';
            }
            if($key eq 'NTLM Support'){
                $auth_cfg{$nm_trck}{'NTLM'} = defined $value && $value ne '' ? $value : '';
                if(defined($value) and $value eq 'all'){
                    debug("trigger NTLM support");
                    $ntlm_enabled = 1;
                }
            }
        }
    }
    foreach my $k (keys %auth_cfg) {
        debug("Records Found: $k has vals: Name: $auth_cfg{$k}->{'Name'}\tStatus: $auth_cfg{$k}->{'Status'}\tNTLM: $auth_cfg{$k}->{'NTLM'}");
    }
    my $zone;
    my $share_nm;

    foreach my $line (@$gather_Zones) {
        my $key;
        my $value;
        my $gst_root_perm;
        my $gst_en;
        debug("Checking: $line");
        if ($line =~ /^\s*Zone\s\'(\S*)\':.*$/){
            if($zone ne $1){
                debug("Found $1");
                $zone = $1;
                #$share_nm = '';
            }
        }
        if ($line =~ /^\s*\(\S*\)\s*Share\s*\'(\S*)\':.*$/){
                    $share_nm = $1;
        }
        if ($line =~ /^\s*\(\S*\).*$/){
            if($zone ne $1){
                debug("Found $1");
                $zone = $1;
                $share_nm = '';
            }
        }
        if ($line =~ /^\s*(\S*\s?\S*):\s*(\S+)\s*$/){
            $key = $1;
            $value = $2;
            if ($key =~ /^(\S*)\s(\S*)$/){
                $key = $1."_".$2;
            }
        }
        if ($line =~ /^\s*Guest\s*\S*\s*(\d)\s*(\S*).*$/ | defined($get_guest)){
            if (defined($get_guest)){
                foreach my $gl (@$get_guest){
                    if($gl =~/^\s*Enabled:\s*Yes\s*$/){
                        debug("trigger for guest enablement");
                        $guest_enabled = 1;
                    }
                }
            }
            else{
            $gst_root_perm = $1;
            $gst_en = $2;
            $zone_cfg{$zone}{$share_nm}{'Guest_root_perm'} = defined $gst_root_perm && $gst_root_perm ne '' ? $gst_root_perm : 'N/A';
            $zone_cfg{$zone}{$share_nm}{'Guest_Allowed'} = defined $gst_en && $gst_en ne '' ? $gst_en : 'N/A';
            }
            if(defined($gst_en) and $gst_en eq 'allow'){
                debug("trigger for guest enablement");
                $guest_enabled = 1;
            }
        }
        if(defined($value) and $key eq 'Impersonate_Guest' and $value ne 'never'){
            debug("trigger for guest impersonation");
            $guest_impersonation = 1;
        }
        $zone_cfg{$zone}{$share_nm}{$key} = defined $value && $value ne '' ? $value : 'N/A';
    }
    foreach my $k (keys %zone_cfg) {
        foreach my $v (keys %{$zone_cfg{$k}}){
                foreach my $p (keys %{$zone_cfg{$k}{$v}}){
            debug("Records Found for Zone: $k has vals: Share: $v \t$p : $zone_cfg{$k}{$v}->{$p}")
    }
        }
    }

    
#    $ntlm_sessions;
    if($guest_enabled and $guest_impersonation){
        if(isCurrentOneFS('<9.10.0.1') and $ntlm_enabled and $guest_enabled and $guest_impersonation){
            fail("NTLM support is enabled with Guest User enabled, Do not proceed with this upgrade if you use NTLM authentication.");
            info("Please disable Guest User to proceed with the upgrade to ".isDestinationOneFS().", or upgrade to 9.10.1.2 or higher.");
        }
        else{
            critical("NTLM support is enabled with Guest User enabled, Please note that on you current version of ".isCurrentOneFS().", you may experiance disruption to client connectivity if you use NTLM authentication.");
            info("You should disable guest user or upgrade to a fixed version of OneFS (9.10.1.2 or higher).")
        }
        info("Please reference KB 000309008 (https://www.dell.com/support/kbdoc/000309008) for further information.");
    }
    # Output
    printResults();
} # End checkNTLM
$CHECKS{'checkNamespace'} = {
    'Description' => 'Checks tardis DB siize for any DB exceeding 8GB',
    'Exclude' => 1,
};
sub checkNamespace {
    if (wasCalled()) { return }
	# Setup
    my $CMD_LIVE = qq[2>&1 du -sh  /ifs/.ifsvar/modules/tardis/namespaces/* | grep -i journal];
    my $CMD_LOGS = q[2>&1; du -sh  local/ifsvar_modules_tardis.tar/modules/tardis/namespaces/* | grep -i journal];
    my $size_exceed_8g = 8;
    my $size_exceed_10g = 10;

    # Start
    printCheck('Namespace');

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);
    # Analyze
    my @size_exceed_8 = ();
    my @size_exceed_10 = ();
	foreach my $line (@$gather) {
		if($line =~ /^\s*(\d+\.?\d?)(\S+)\s*\S*\/(\S+)\.sqlite$/){
			my $cdbs = $1;
			my $cdbq = $2;
			my $dbtc = $3;
			
			debug("checking size of $dbtc, Current size is ".$cdbs.$cdbq);
			
			if(defined($cdbq) and lc($cdbq) !~/[bkm]/ and $cdbq ne ''){
				if(lc($cdbq) =~ /^g/){
					if(int($cdbs) > int($size_exceed_8g) and int($cdbs) < int($size_exceed_10g)){
						# INFO
						push(@size_exceed_8, $dbtc);
						debug("INFO: DB $dbtc exceeds 8GB, current size is ".$cdbs.$cdbq);
					}
					elsif(int($cdbs) >= int($size_exceed_10g)){
						# WARN
						push(@size_exceed_10, $dbtc);
						debug("WARN: DB $dbtc is at or exceeds 10GB in size, current size is ".$cdbs.$cdbq);
					}
				}	
			}
		}
	}

    if (@size_exceed_8 or @size_exceed_10) {
		if(@size_exceed_8){
			info("The following DB's are currently exceeding 8GB: ".join(', ', @size_exceed_8));
		}
		if(@size_exceed_10){
			warning("The following DB's are currently exceeding 10GB: ".join(', ',@size_exceed_10));
		}
		info();
	}
    printResults();
} # End checkNamespace
$CHECKS{'checkNetBIOS'} = {
    'Description' => 'Checks if the Isilon NetBIOS Name Service (nbns) is enabled when updating to OneFS 8.0.1 and later',
    'Exclude' => 1,
};
sub checkNetBIOS {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8.0.1')) { return unsupported('UNSUP_CUR') }
    if (!$RPS{'Target'} or compareVersions($RPS{'Target'}, '8') <= 0) {
	return unsupported('UNSUP_TGT_1', 'OneFS 8.0.1 and later releases');
    }

    # Setup
    printCheck('NetBIOS EOL');
    my $MSG_EOSL = 'The Isilon NetBIOS Name Service (nbns) is enabled and is'.
	' not supported beyond OneFS 8.0.0.x.  Review'.
	' https://dl.dell.com/content/docu69808 - End of Life (EOL) Announcement'.
	' for the EMC Isilon NetBIOS Name Service.pdf for details.';

    # Flag
    if (isServiceEnabled('nbns')) {
	addCustomerInformation(fail($MSG_EOSL));
    }

    # Output
    printResults();
}
$CHECKS{'checkOpenFiles'} = {
    'Description' => 'Checks each node for the number of open files [sysctl kern.openfiles] and compares that against the number of max open files [sysctl kern.maxfiles].  The check will warn at over 80% of the max and fail at over 90% of the max',
    'Exclude' => 1,
};
sub checkOpenFiles {
    if (wasCalled()) { return }
    printCheck('Kernel Open Files Count');

    # Setup
    my $MAX = 315000;
    my $SPF = '%5s %15s %15s';
    my $CMD_LIVE =
	qq[2>&1 ${SUDO} isi_for_array 'sysctl kern.openfiles kern.maxfiles'];
    my $CMD_LOGS = q[2>&1 zgrep -HE 'kern.openfiles|kern.maxvnodes'].
	q[ */kern.openfiles */varlog.tar/log/vmlog */varlog.tar/log/vmlog.0.gz];
    my $SPF_APP = qq[The following nodes are approaching their max number of].
	q[ open files: %s];
    my $SPF_OVER = qq[The following nodes have exceeded their max number of].
	q[ open files: %s];
    my $MSG_DOC = q[For details review: https://dl.dell.com/content/docu94152].
	q[ - PowerScale OneFS Technical Specifications Guide];

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my @over_80 = ();
    my @over_90 = ();
    my @over_100 = ();
    verboseHeader(sprintf($SPF, 'LNN', 'Open Files', 'Max Open Files'));
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { missing($lnn); next; }
	my $maxfiles = $MAX;
	my $openfiles = 0;
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ /kern.openfiles:\s+(\d+)$/) { $openfiles = $1 }
	    if ($line =~ /kern.maxfiles:\s+(\d+)$/) { $maxfiles = $1 }
	    if ($line =~ /kern.maxvnodes:\s+(\d+)$/) { $maxfiles = $1 * 0.9 }
	}
	verbose(sprintf($SPF, $lnn, $openfiles, $maxfiles));
	if ($openfiles > $maxfiles) { push(@over_100, $lnn) }
	elsif ($openfiles > $maxfiles * 0.9) { push(@over_90, $lnn) }
	elsif ($openfiles > $maxfiles * 0.8) { push(@over_80, $lnn) }
    }

    # Flag
    if (@over_80) { warning(sprintf($SPF_APP, compressRange(@over_80))) }
    if (@over_90) { fail(sprintf($SPF_APP, compressRange(@over_90))) }
    if (@over_100) { fail(sprintf($SPF_OVER, compressRange(@over_100))) }
    if (isResults(&WARN)) { info($MSG_DOC) }

    # Output
    printResults();
} # End checkOpenFiles
$CHECKS{'checkPSCALE136276'} = {
    'Description' => 'Check for criteria of PSCALE-136276',
    'Parameters' => {'name' => 'target_version', 'default' => '', 'mandatory' => 'false'},
};
sub checkPSCALE136276 {
    if (wasCalled()) { return }

    # Setup
    my $RE_AFFECTED = qr/^9\.2\.0\.|^9\.2\.1\.(?:[0-9]|10)$|^9\.3\.0\.[0-6]$|^9\.4\.0\.0$/;
    my $CMD_GREP = q[grep -q 'Security Type:.*krb'];
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi nfs export list -v | ${CMD_GREP} && echo Kerberos Found];
    my $CMD_LOGS = qq[2>&1 ${CMD_GREP} local/isi_nfs_exports && echo Kerberos Found];
    my $SPF_RISK = 'NFS processes will unexpectedly stop and interrupt data access when NFS clients connect using Kerberos authentication in OneFS %s and it was found that there are NFS exports configured with a security type which includes Kerberos';
    my $MSG_UPGRADE = 'If there are any NFS clients are using Kerberos authentication to connect to this cluster, it is recommended to upgrade to a version which includes the fixes for PSCALE-136276: 9.2.1.10_DA-CUSTOM_2022-03_PSP-2241, 9.2.1.11 and later 9.2.1 releases, or 9.4.0.1 and later releases';
    my $MSG_KB = 'For details escalate to support and reference PSCALE-136276';

    # Start
    if (!isDestinationOneFS($RE_AFFECTED) and !isCurrentOneFS($RE_AFFECTED)) {
	return unsupported('UNSUP_NO_RISK');
    }
    printCheck('PSCALE-136276');

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my ($has_kerberos) = grep {$_ =~ /Kerberos Found/} @$gather;

    # Flag
    if ($has_kerberos) {
	if (isCurrentOneFS($RE_AFFECTED)) {
	    warning(sprintf($SPF_RISK, isCurrentOneFS()));
	}
	if (isDestinationOneFS($RE_AFFECTED)) {
	    if (!isCurrentOneFS($RE_AFFECTED)) {
		fail(sprintf($SPF_RISK, isDestinationOneFS()));
		fail($MSG_UPGRADE);
	    } else {
		warning(sprintf($SPF_RISK, isDestinationOneFS()));
		warning($MSG_UPGRADE);
	    }
	}
	info($MSG_KB);
    }

    # Output
    printResults();
} # End checkPSCALE136276
$CHECKS{'checkPartitions'} = {
    'Description' => 'Checks system partition space',
    'Exclude' => 1,
};
sub checkPartitions {
    if (wasCalled()) { return }
	gatherProductInfo();
    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array 'df -Pli'];
    my $CMD_LOGS = q[2>&1 grep -H . */df];
    my $CMD_mirror_LIVE = qq[2>&1 ${SUDO} isi_for_array 'df -hli /dev/mirror/var1 /dev/mirror/var0'];
    my $CMD_mirror_LOGS = q[2>&1; for d in *-*/ ; do { echo "${d}gmirror_list:"; mirror_opt=$(grep -A1 'mirror/var[01]' ${d}gmirror_list; grep 'mirror/var[01]' ${d}df | awk '{print $1" " $5}'); echo "$mirror_opt"; } | sed ':a;N;s/\n/ /;ba'; done];
    my $RE_MIRRORS = qr[^\s*/dev/mirror/];
    my $RE_PARTITION = qr/^\s*(\S.*?)\s+(\d+)\s+(\d+)\s+(-?\d+)\s+(\d+)%\s+(\d+)\s+(-?\d+)\s+(\d+)%\s+(\S+.*)$/;
    my $RE_BLOCKSIZE = qr/Filesystem\s+(\S+)/;
    my $SPF_PARTITION = '%-25s %20s %20s %20s %4s%% %4s%% %s';
    my $FS_IGNORE = qr[^\s*(?:devfs|/dev/md)];
    my $CONV_512K = 0.00048828125;
    my $CONV_1K = 0.0009765625;
    my $RQR_DSP = 200;
    my $RQR_NF8 = 500;
    my $RQR_NF9 = 200;
    my $KB201666_VAR = 500;
    my $KB201666_VARCRASH = 1000;
    my $LARGE_VAR = 1900;
    my $RQR_PATCH_HIGH = 700;
    my $RQR_PATCH_STD = 200;
    my $SPF_OVERFULL = 'The %s partition is over %s%% full on the following nodes: %s';
    my $SPF_INODE = 'The %s partition inode usage is over %s%% on the following nodes: %s';

    # Start
    printCheck('System Partition Free Space');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
	my $gather_mirrors = gatherCluster(isLive()?$CMD_mirror_LIVE:$CMD_mirror_LOGS);
    # Analyze
    my @root_95 = ();
    my @var_90 = ();
    my @varcrash_90 = ();
    my @varcrash_50 = ();
    my %inode = ();
	my %var_partitions = ();
	my @size_diff_fail = ();
	my @size_diff_warn = ();
	my @var_tmp_mnt;
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	verboseHeader($NODE_INFO{$lnn}{'Name'});
	verbose(sprintf($SPF_PARTITION, 'Filesystem', 'Size (MB)', 'Used (MB)', 'Available (MB)', 'Capa', 'Iuse', 'Mount Point'));
	my $has_mirrors = 0;
	my $blocksize = $CONV_512K;
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ $RE_MIRRORS) { $has_mirrors = 1 }
	    if ($line =~ $FS_IGNORE) { next }
	    if ($line =~ $RE_BLOCKSIZE) {
		if ($1 eq '512-blocks') {
		    $blocksize = $CONV_512K;
		} elsif ($1 eq '1K-blocks') {
		    $blocksize = $CONV_1K;
		}
	    }
	    if ($line !~ $RE_PARTITION) { next }
	    my ($fs, $capacity, $iused, $mount) = ($1, $5, $8, $9);
	    my $total = int($2 * $blocksize);
	    my $used = int($3 * $blocksize);
	    my $available = int($4 * $blocksize);
	    verbose(sprintf($SPF_PARTITION, $fs, $total, $used, $available, $capacity, $iused, $mount));

	    # root (/) partition over 95%
	    if ($mount eq '/' and $capacity > 95) {
		push(@root_95, $lnn);
	    }
		if($mount =~ /\/\tmp\// or $mount =~ /\/df\./){
			push(@var_tmp_mnt, $lnn);
		}

	    # /var partition over 90%
	    if ($mount eq '/var' and $capacity > 90) {
		push(@var_90, $lnn);
	    }

	    # /var/crash partition over 50/90% full
	    if ($mount eq '/var/crash' and $capacity > 90) {
		push(@varcrash_90, $lnn);
	    } elsif ($mount eq '/var/crash' and $capacity > 50) {
		push(@varcrash_50, $lnn);
	    }

	    # inode usage over 90%
	    if ($iused > 97 and
		($mount eq '/' or
		 $mount eq '/var' or
		 $mount eq '/var/crash')) {
		push(@{$inode{$mount}}, $lnn);
	    }

	    # Node compatibility check for 8.0.0.7 and IQ nodes
	    if ($mount eq '/' and $total < 500) {
		$RPS{'Root less than 500MB'}{$lnn} = 1;
	    }

	    # Node compatibility check for A100 nodes
	    if ($mount eq '/' and $total < 1000) {
		$RPS{'Root less than 1GB'}{$lnn} = 1;
	    }

	    # /var available space requirements for NFP/DSP/Patches
	    if ($mount eq '/var' and $available < $RQR_NF8) {
		push(@{$RPS{'RQR_NF8'}}, $lnn);
	    }
	    if ($mount eq '/var' and $available < $RQR_NF9) {
		push(@{$RPS{'RQR_NF9'}}, $lnn);
	    }
	    if ($mount eq '/var' and $available < $RQR_DSP) {
		push(@{$RPS{'RQR_DSP'}}, $lnn);
	    }
	    if ($mount eq '/var' and $available < $RQR_PATCH_HIGH) {
		push(@{$RPS{'RQR_PATCH_HIGH'}}, $lnn);
	    }
	    if ($mount eq '/var' and $available < $RQR_PATCH_STD) {
		push(@{$RPS{'RQR_PATCH_STD'}}, $lnn);
	    }

	    # KB 201666 requirements
	    if ($mount eq '/var' and $available < $KB201666_VAR) {
		push(@{$RPS{'KB201666_VAR'}}, $lnn);
	    }
	    if ($mount eq '/var/crash' and $available < $KB201666_VARCRASH) {
		push(@{$RPS{'KB201666_VARCRASH'}}, $lnn);
	    }

	    # Expanded var check
	    if ($mount eq '/var' and $total > $LARGE_VAR) {
		push(@{$RPS{'LARGE_VAR'}}, $lnn);
	    }
	}
	
	if ($has_mirrors) {
		my $partition;
		my $p1;
		my $p2;
		my $s1;
		my $s2;
		my $pitem;
		my $mcap;
		foreach my $line (@{$gather_mirrors->{$lnn}}) {
			#if(grep { $lnn eq $_ } @{$CLUSTER_INFO{'Gen6'}}){ debug("Node $lnn is Gen6");}
			#else{debug("Node $lnn is not Gen6 / MLK, skipping.. "); next;};
		if ($line =~ /^\s*df\:|Filesystem.*$/){ next; }
		if($line =~ /^\s*\/dev\/mirror\/(var[01])\s*(\S+)\s*(\S+)\s+(\S+)\s*(\d+).*(\/var|%)$/){
			$p2 = $1 if $p1 ne '';
			$p1 = $1 if $p1 eq '';
			$s2 = $2 if $s1 ne '';
			$s1 = $2 if $s1 eq '';
			$pitem = $6 if $6 ne '%';
			$mcap = $5 if $6 ne '%';
			#debug("$1 $2 $3 $4 $5 $6");
			$var_partitions{$lnn}{$p1}{'size'} = $s1;
			debug("$p1 on node $lnn = $var_partitions{$lnn}{$p1}{'size'}");
			$var_partitions{$lnn}{$p2}{'size'} = $s2;
			$var_partitions{$lnn}{$pitem}{'capacity'} = $mcap;
			debug("$p2 on node $lnn = $var_partitions{$lnn}{$p2}{'size'}");
			debug("Mounted on $pitem with capacity used = $mcap") if $6 ne '%';
			debug("P1 = $p1 | P2 = $p2 | Pitem = $pitem | S1 = $s1 | S2 = $s2");
			if(($pitem && (($s1 eq '2.0G' && $s2 eq '1.0G') || ($s1 eq '1.9G' && $s2 =~ /^\d+M$/) || ($s2 eq '2.0G' && $s1 eq '1.0G') || ($s2 eq '1.9G' && $s1 =~ /^\d+M$/))) || ($pitem && (($s2 eq '2.0G' && $s1 eq '1.0G') || ($s2 eq '1.9G' && $s1 =~ /^\d+M$/) || ($s1 eq '2.0G' && $s2 eq '1.0G') || ($s1 eq '1.9G' && $s2 =~ /^\d+M$/)))){
			#if(($p1 eq $pitem && (($s1 eq '2.0G' && $s2 eq '1.0G') || ($s1 eq '1.9G' && $s2 =~ /^\d+M$/) || ($s2 eq '2.0G' && $s1 eq '1.0G') || ($s2 eq '1.9G' && $s1 =~ /^\d+M$/))) || ($p2 eq $pitem && (($s2 eq '2.0G' && $s1 eq '1.0G') || ($s2 eq '1.9G' && $s1 =~ /^\d+M$/) || ($s1 eq '2.0G' && $s2 eq '1.0G') || ($s1 eq '1.9G' && $s2 =~ /^\d+M$/)))){
				push(@size_diff_warn, $lnn);
				if($mcap >= 50){
						push(@size_diff_fail, $lnn);
				}
				if(grep { $lnn eq $_ } @{$CLUSTER_INFO{'Gen6'}}){ debug("Node $lnn is Gen6 so setting LKG EXPANSION FLAG to 1");
				$RPS{'LKG EXPANSION FLAG'} = 1;	}
				else{debug("Node $lnn is not Gen6 / MLK, not setting global LKG EXPANSION FLAG.. ");};
			}
		}
		if($line =~ /^\s*\d?\.?\s*\S+\s*mirror\/(\S+)\s*\S+\s*(\d+)\s*\((\S+)\)\s*\S+\s*\S+\s*\S+\s*mirror\/(\S+)\s*\S+\s*(\d+)\s*\((\S+)\)\s*\/dev\/mirror\/(\S+)\s*(\d+)%$/){
			$p1 = $1;
			$p2 = $4;
			$s1 = $3;
			$s2 = $6;
			$pitem = $7;
			$mcap = $8;
			$var_partitions{$lnn}{$p1}{'size'} = $s1;
			debug("$p1 on node $lnn = $var_partitions{$lnn}{$p1}{'size'}");
			$var_partitions{$lnn}{$p2}{'size'} = $s2;
			$var_partitions{$lnn}{$pitem}{'capacity'} = $mcap;
			debug("$p2 on node $lnn = $var_partitions{$lnn}{$p2}{'size'}");
			debug("Mounted on $pitem with capacity used = $mcap");
			if(($p1 eq $pitem && (($s1 eq '2.0G' && $s2 eq '1.0G') || ($s1 eq '1.9G' && $s2 =~ /^\d+M$/) || ($s2 eq '2.0G' && $s1 eq '1.0G') || ($s2 eq '1.9G' && $s1 =~ /^\d+M$/))) || ($p2 eq $pitem && (($s2 eq '2.0G' && $s1 eq '1.0G') || ($s2 eq '1.9G' && $s1 =~ /^\d+M$/) || ($s1 eq '2.0G' && $s2 eq '1.0G') || ($s1 eq '1.9G' && $s2 =~ /^\d+M$/)))){
				if($mcap >= 50){
						push(@size_diff_fail, $lnn);
				}
				else{
					push(@size_diff_warn,$lnn);
				}
				if(grep { $lnn eq $_ } @{$CLUSTER_INFO{'Gen6'}} ){ debug("Node $lnn is Gen6 so setting LKG EXPANSION FLAG to 1");
				$RPS{'LKG EXPANSION FLAG'} = 1;	}
				else{debug("Node $lnn is not Gen6 / MLK, not setting global LKG EXPANSION FLAG.. ");};
				
			}
		}
	}
	    $NODE_INFO{$lnn}{'Bootflash'} = 1;
	    debug("NODE_INFO->${lnn}->Bootflash=1");
	    push(@{$CLUSTER_INFO{'Bootflash'}}, $lnn);
	    debug("CLUSTER_INFO->Bootflash=$lnn");
	}
    
	}

    # If all nodes have an expanded var partition
    if ($RPS{'LARGE_VAR'} and $#{$RPS{'LARGE_VAR'}} eq $#LNNS) {
	$RPS{'ALL_VARS_EXPANDED'} = 1;
    }

    # Flag
	if(@var_tmp_mnt){
		fail("Temporary /var mount showing on the following nodes which may pose an issue for upgrading: ".compressRange(@var_tmp_mnt));
	}
    if (@root_95) {
	fail(sprintf($SPF_OVERFULL, '/', 95, compressRange(@root_95)));
    }
	
	if (@size_diff_fail || @size_diff_warn){
		push (@{$RPS{'LKG Issue Nodes'}{'Nodes'}},@size_diff_fail,@size_diff_warn);
		if (@size_diff_fail) {
			fail("The following nodes have /var mirrors of different sizes and have a 2GB mounted partition of 50% or more utilization: ".compressRange(@size_diff_fail));
		}
		if (@size_diff_warn) {
			critical("The following nodes have /var mirrors of different sizes: ".compressRange(@size_diff_warn));
		}
		info("Please reference KB 000213248 (https://www.dell.com/support/kbdoc/000213248) for further information.");
	}
	
    if (@var_90) {
	fail(sprintf($SPF_OVERFULL, '/var', 90, compressRange(@var_90)));
    }

    if (@varcrash_90) {
	fail(sprintf($SPF_OVERFULL, '/var/crash', 90,
		     compressRange(@varcrash_90)));
    }
    if (@varcrash_50) {
	warning(sprintf($SPF_OVERFULL, '/var/crash', 50,
			compressRange(@varcrash_50)));
    }
    if (%inode) {
	foreach my $mount (sort keys %inode) {
	    fail(sprintf($SPF_INODE, $mount, 97,
			 compressRange(@{$inode{$mount}})));
	}
    }

    if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000041465')) }
    printResults();
} # End checkPartitions
$CHECKS{'checkPerformance'} = {
    'Description' => "Checks cluster performance",
    'Exclude' => 1,
};
sub checkPerformance {
    return if wasCalled();
    checkHardwareStatus();
    printCheck("Performance");

    # Pre-defined threshold for performance checks
    my $THRESHOLD = 80;

    # A list of the last 7 days
    my @abbr = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
    my @date = ();
    for (my $i = 0; $i <= 7; $i++) {
	my ($day, $month) = (localtime($RPS{'Timestamp'} - ($i * 86400)))[3,4];
	push(@date, sprintf("%3s %2s", $abbr[$month], $day));
    }
    my $dategrep = join("|", @date);

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array "zgrep -H -E '^Mon|^Tue|^Wed|^Thu|^Fri|^Sat|^Sun|vm.loadavg' /var/log/vmlo* | paste - - | grep -E '$dategrep'"]);
    } else {
	$gather = gatherCluster(qq[2>&1 zgrep -H -E '^Mon|^Tue|^Wed|^Thu|^Fri|^Sat|^Sun|vm.loadavg' */varlog.tar/log/vmlo* | paste - - | grep -E '$dategrep']);
    }

    # Analyze information for issues.
    my %stats = ();
    my %average_above_threshold = ();
    my ($total_of_averages, $total_of_highest, $node_count);
    foreach my $lnn (@LNNS) {
	verboseHeader($NODE_INFO{$lnn}{'Name'});
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}

	# Parse command output
	foreach my $line (@{$gather->{$lnn}}) {
 	    next unless $line =~ /vm.loadavg: \{ \S+ \S+ ([0-9.]+) \}/;
	    $stats{$lnn}{'load'} += $1;
	    $stats{$lnn}{'count'}++;
	    if (!$stats{$lnn}{'highest'} or $1 > $stats{$lnn}{'highest'}) {
		$stats{$lnn}{'highest'} = $1;
	    }
	}

	next unless
	    exists $stats{$lnn}{'count'} and
	    exists $stats{$lnn}{'highest'} and
	    exists $stats{$lnn}{'load'};

	# Calculate the average load
	$stats{$lnn}{'average'} =
	    $stats{$lnn}{'load'} / $stats{$lnn}{'count'};

	next unless $NODE_INFO{$lnn}{'Cores'};

	# Assign the number of cores for this node
	my $core_count = $NODE_INFO{$lnn}{'Cores'};

	# Turn the performance loads into percents based on the number of cores for the node
	my $highest = sprintf("%d", ($stats{$lnn}{'highest'} / $core_count) * 100);
	my $average = sprintf("%d", ($stats{$lnn}{'average'} / $core_count) * 100);

	# Set a flag if the average is above threshold
	if ($average > $THRESHOLD) {
	    $average_above_threshold{$lnn} = 1;
	}

	# Increment cluster totals
	$total_of_averages += $average;
	$total_of_highest += $highest;
	$node_count++;

	# Verbose output
	verbose("Highest: ${highest}%");
	verbose("Average: ${average}%");
    }

    # Warn if any flag is set for an average being above threshold
    if (%average_above_threshold) {
	warning("The following nodes have an average load of over ${THRESHOLD}%: ".
		compressRange(keys %average_above_threshold));
    }

    # Assign average performance across cluster
    my ($average_of_averages, $average_of_highest);
    $average_of_averages = int($total_of_averages / $node_count)
	if defined $total_of_averages and defined $node_count;
    $average_of_highest = int($total_of_highest / $node_count)
	if defined $total_of_highest and defined $node_count;

    # Verbose output
    verboseHeader($CLUSTER_INFO{'Name'});
    verbose("Average of highest across the cluster: ${average_of_highest}%")
	if defined $average_of_highest;
    verbose("Average of averages across the cluster: ${average_of_averages}%")
	if defined $average_of_averages;

    # Output Check result
    printResults();
} # End checkPerformance
$CHECKS{'checkProcesses'} = {
    'Description' => 'Checks for OpenSM master, MCP, isi_mca_dump, and isi_upgrade_d process related issues',
    'Exclude' => 1,
};
sub checkProcesses {
    return if wasCalled();
    printCheck("Processes");
    gatherNetworkInterfaces();

    # Gather information
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array 'ps auxww | grep -E "isi_mcp|opensm|isi_upgrade_d|isi_mca_dump" | grep -v grep']);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -H -E "isi_mcp|opensm|isi_upgrade_d|isi_mca_dump" */ps]);
    }

    # Analyze information for issues.
    my $opensm_master = 0;
    my @nodes_with_upgrade_process = ();
    my @nodes_with_not_enough_isi_mcp = ();
    my @nodes_with_high_mca_process = ();
    foreach my $lnn (@LNNS) {
	verboseHeader($NODE_INFO{$lnn}{'Name'});
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	my $mcp_processes = 0;
    my $mca_dump = 0;
	foreach my $line (@{$gather->{$lnn}}) {
	    verbose($line);
	    # Check for two isi_mcp processes on each node:
	    $mcp_processes++ if ($line =~ /isi_mcp/);

	    # Check for an OpenSM Master
	    if ($line =~ /opensm:\s+(0x\S+)\s+master/) {
		$opensm_master++;
	    }
        
        if($line =~ /^.*isi_mca_dump.*$/){
                $mca_dump++;
        }

	    # Check for isi_upgrade_d processes, there should be none running
	    push(@nodes_with_upgrade_process, $lnn) if ($line =~ /isi_upgrade_d/);
	}

    # Check if count of mca processes is greater than 2
    push(@nodes_with_high_mca_process, $lnn) if ($mca_dump > 1);

	# Check that we found a master and child isi_mcp process
	push(@nodes_with_not_enough_isi_mcp, $lnn) if ($mcp_processes < 2);
    }
    if(isCurrentOneFS('<9.2.1.22') || isCurrentOneFS('^9.3')|| (isCurrentOneFS('^9.4') && isCurrentOneFS('<9.4.0.13')) || (isCurrentOneFS('^9.5') && isCurrentOneFS('<9.5.0.1'))){
        critical("Multiple isi_mca_dump processes running on the following nodes: ".join(",", @nodes_with_high_mca_process)) if (@nodes_with_high_mca_process);
    }
    warning("isi_upgrade_d process running on the following nodes: ".join(",", @nodes_with_upgrade_process)) if (@nodes_with_upgrade_process);
    fail("isi_mcp process count less than 2 on the following nodes: ".join(",", @nodes_with_not_enough_isi_mcp)) if (@nodes_with_not_enough_isi_mcp);

    # Check that opensm master processes matches the number of IB Interfaces on each node
    if ($CLUSTER_INFO{'Active IB Interfaces'} and $opensm_master != $CLUSTER_INFO{'Active IB Interfaces'}) {
	fail("Number of OpenSM Masters does not match the number of InfiniBand Interfaces in use");
    }

    info(sprintf($MSG{'KB_1'}, '000030127')) if isResults(&WARN);

    # Output Check result
    printResults();
} # End checkProcesses
$CHECKS{'checkProtectionLevel'} = {
    'Description' => 'Check storage pools protection levels',
    'Reference' => '',
    'Exclude' => 1,
};

sub checkProtectionLevel {
    if (wasCalled()) { return }

    gatherIsiStatPools();
    printCheck('Protection Levels');
    my $protection_level_list;
    $protection_level_list.= "  ========================\n"."  System Protection Levels\n"."  ========================\n";
    my $SPF1 = '%6s %45s %10s';
    my $SPF11 = '%0s %5s %23s %10s';
    $protection_level_list .= sprintf($SPF1, 'Name', 'Protection Level', 'Nodes')."\n";
    #$protection_level_list .= sprintf($SPF11, 'Name','Nodes', 'Protection Level')."\n";
    $protection_level_list .= sprintf($SPF11, ' ', '-'x26, '-'x16, '-'x5)."\n"; 
    #my $SPF2 = '%0s %5s %10s %18s';
    my $prv_str_ln = 30;
    my $prv_str_ln1 = 3;
    foreach my $name (keys %{$CLUSTER_INFO{'Pools'}}) {
        my %pool = %{$CLUSTER_INFO{'Pools'}{$name}};
        my $str_ln = $prv_str_ln - length($name);
        $str_ln = $str_ln+10;
        my $str_ln1 = $prv_str_ln1 - length(compressRange(@{$pool{'Members'}}));
        $str_ln1 = 18-$str_ln1;
        my $SPF2 = '%0s %5s %'.$str_ln.'s %'.$str_ln1.'s';
        
        $protection_level_list .= sprintf($SPF2,' ',  $name, $pool{'Protection'}, compressRange(@{$pool{'Members'}}))."\n";

    };
    $protection_level_list .= "\n";
    #$CLUSTER_INFO{'Protection Levels'} = print $protection_level_list;
    info('');
    printResults();
	print $protection_level_list;
} # End checkProtectionLevel
sub checkRFC2307 {
    return if wasCalled();
    verboseHeader("RFC2307 Settings");

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
		if(isCurrentOneFS('>=9.12') and isCompliance()){
			$gather = gatherLocal("2>&1 ${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig -a |grep ActiveDirectory");
		}
		else{
			$gather = gatherLocal("2>&1 ${SUDO} isi_gconfig -a|grep ActiveDirectory");
		}
    } elsif (-e "local/isi_gconfig") {
	$gather = gatherLocal("2>&1 grep ActiveDirectory local/isi_gconfig");
    } elsif (-e "local/gconfig_ifsvar.tar/ifs/_ifsvar/main_config.gc") {
	$gather = gatherLocal("2>&1 grep ActiveDirectory local/gconfig_ifsvar.tar/ifs/_ifsvar/main_config.gc");
    }

    # Analyze information for issues.
    my ($sfu_support_global, $allocate_gids_global, $allocate_uids_global, $store_sfu_mappings_global);
    my %ADS = ();
    foreach (@$gather) {
	if (/ActiveDirectory\.Instance\._key\.([^\.]+)\./) {
	    my $id = $1;
	    if (/$id\._name .*= (\S+)$/) {
		$ADS{$id}{'Name'} = $1;
	    } elsif (/$id\._dword\.AllocateGids\.value .*= (\S+)$/) {
		$ADS{$id}{'AllocateGids'} = $1;
	    } elsif (/$id\._dword\.AllocateUids\.value .*= (\S+)$/) {
		$ADS{$id}{'AllocateUids'} = $1;
	    } elsif (/$id\._sz\.SFUSupport\.value .*= (\S+)$/) {
		$ADS{$id}{'SFUSupport'} = $1;
	    } elsif (/$id\._dword\.StoreSFUMappings\.value .*= (\S+)$/) {
		$ADS{$id}{'StoreSFUMappings'} = $1;
	    }
	} elsif (/ActiveDirectory\.AllocateGids .*= (\S+)$/) {
	    $allocate_gids_global = $1;
	} elsif (/ActiveDirectory\.AllocateUids .*= (\S+)$/) {
	    $allocate_uids_global = $1;
	} elsif (/ActiveDirectory\.SFUSupport .*= (\S+)$/) {
	    $sfu_support_global = $1;
	} elsif (/ActiveDirectory\.StoreSFUMappings .*= (\S+)$/) {
	    $store_sfu_mappings_global = $1;
	}
    }
    # Setup global/defaults if they were not gathered
    $allocate_gids_global = "1"      unless defined $allocate_gids_global;
    $allocate_uids_global = "1"      unless defined $allocate_uids_global;
    $sfu_support_global = "none"     unless defined $sfu_support_global;
    $store_sfu_mappings_global = "0" unless defined $store_sfu_mappings_global;

    # Update ADS values if they were not gathered to the global/defaults and then see if it meets criteria to be impacted by mapping issues
    my @ads_with_issues = ();
    verbose(sprintf("%-20s %15s %15s %15s %16s", "Provider Name", "AllocateGids", "AllocateUids", "SFUSupport", "StoreSFUMappings"));
    verbose(sprintf("%-20s %15s %15s %15s %16s", "Global Setttings", $allocate_gids_global, $allocate_uids_global, $sfu_support_global, $store_sfu_mappings_global));
    foreach my $id (keys %ADS) {
	$ADS{$id}{'Name'} = $id                                    unless defined $ADS{$id}{'Name'};
	$ADS{$id}{'AllocateGids'} = $allocate_gids_global          unless defined $ADS{$id}{'AllocateGids'};
	$ADS{$id}{'AllocateUids'} = $allocate_uids_global          unless defined $ADS{$id}{'AllocateUids'};
	$ADS{$id}{'SFUSupport'} = $sfu_support_global              unless defined $ADS{$id}{'SFUSupport'};
	$ADS{$id}{'StoreSFUMappings'} = $store_sfu_mappings_global unless defined $ADS{$id}{'StoreSFUMappings'};
	verbose(sprintf("%-20s %15s %15s %15s %16s", $ADS{$id}{'Name'}, $ADS{$id}{'AllocateGids'}, $ADS{$id}{'AllocateUids'}, $ADS{$id}{'SFUSupport'}, $ADS{$id}{'StoreSFUMappings'}));
	next unless $ADS{$id}{'SFUSupport'} =~ /rfc2307/i;
	next unless $ADS{$id}{'AllocateGids'} =~ /1/ or $ADS{$id}{'AllocateUids'} =~ /1/;
	push(@ads_with_issues, $ADS{$id}{'Name'});
    }
    warning("The following ADS Authentication Providers are showing as configured for RFC2307 and are also enabled to auto-assign UIDs/GIDs: ".join(", ", @ads_with_issues)) if @ads_with_issues;
    addCustomerInformation("It was detected that an ADS Authentication Provider is configured for RFC2307 support and Auto-assign UIDs/GIDs are also enabled.  This may lead to mapping issues which may impact client authentication to the cluster.  ".sprintf($MSG{'KB_1'}, '000028577')) if @ads_with_issues;
} # End checkRFC2307
$CHECKS{'checkRemoteSupport'} = {
    'Description' => 'Checks is Restricted Shell is enabled and isi_supportassist, if both are enabled and you upgrade to 9.7 then it will cause an issue for support assist services to restart.',
    'Reference' => '',
};

sub checkRemoteSupport {
    if (wasCalled()) { return }

    gatherEventChannel();
    gatherRemoteSupport();
    gatherEventAlerts();

    if(!isOneFSUpgrade()){ return }
    #if (isCurrentOneFS('<9.5')) { return }
    if (!isServiceEnabled('isi_supportassist')) { return }
    my $CMD_LIVE;
    if(isCurrentOneFS('>=9.12') and isCompliance()){
        $CMD_LIVE = qq[2>&1 /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig -t security_config restricted_shell_enabled];
    }
    else{
        $CMD_LIVE = qq[2>&1 isi_gconfig -t security_config restricted_shell_enabled];
    }
    my $CMD_LOGS = qq[2>&1; grep -i 'restricted_shell_enabled' local/ifsvar_modules_tardis.tar/modules/tardis/gconfig/Gconfig.security_config.*];

    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Setup
    my $sev_isi_rem_supp = 1 if isServiceEnabled('isi_supportassist');
    my $channels = $CLUSTER_INFO{'Event Channels'};
    my $channel_configured = 0;
    my $channel_enabled = 0;
    my $res_sh_enabled = 0;

    # Start
    printCheck('SupportAssist');

    # Check if Support Assist is enabled
    foreach my $id (sort keys %$channels) {
        if ($channels->{$id}{'Name'} eq 'SupportAssist' || $channels->{$id}{'Type'} eq 'supportassist'){
            $channel_configured = 1;   
            if ($channels->{$id}{'Enabled'} eq 'Yes'){
                $channel_enabled = 1;        
            }
        } 
    }

    foreach my $line (@$gather) {
        next unless ($line !~ /^\s*$/);
        debug("Settings Found: $line\n");
        if($line =~ /restricted_shell_enabled\s*\(bool\)\s*\=\s*\"?([a-zA-Z]+)\"?$/ || $line =~ /restricted_shell_enabled\s*[\(\{]token:\d+[\)\}]\s*\=\s*\"?([a-zA-Z]+)\"?$/){
            if($1 eq 'true'){
                $res_sh_enabled = 1;
            }
        }
    }
    
    # Flag
    if($sev_isi_rem_supp && $res_sh_enabled){
        critical('Checks have identified that Support Assist is enabled and Restricted Shell is enabled.');
        info('Having Restricted Shell enabled where SupportAssist in enabled / in use, whilst upgrading would lead to a failure of Support Assist services to start correctly after the upgrade.');
        info('Restricted Shell should be disabled prior to upgrading to ').$RPS{'Target'};
    }
    
    # Output
    printResults();
} # End checkRemoteSupport
$CHECKS{'checkSFTP'} = {
    'Description' => 'Checks permissions of openssl.cnf file to ensure they are set correctly post upgrade but also to call out pre upgrade the actions that are required post upgrade per KB000226508',
    'Exclude' => ,
};
sub checkSFTP {
    if (wasCalled()) { return }
    #if(isCurrentOneFS('<9.7.1.0')){ return }
    if (!isOneFSUpgrade()) {return };
    printCheck('KB 000226508');

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array '[ -e "/etc/ssl/openssl.cnf" ] && ls -l /etc/ssl/openssl.cnf || echo "file does not exist"'];
        
    # Gather    
    my @nodes_with_file_present = ();
    my $current_perms = '';
    my @nodes = ();
    
    # Analyze
    if(!isLive()){
        #This is a live only check but we still want to present a warning for checks run on logsets to ensure visibility.
        addCustomerInformation(warning("Upgrading to ".isDestinationOneFS()." may lead to permission changes of openssl.cnf file and may require updating permissions after the upgrade is completed."));
        info("Checks will identify any permission issues post upgrade and will provide steps to remediate.");
        info("Please reference KB 000226508 (https://www.dell.com/support/kbdoc/000226508) for additional information.");
    }
    else{
        my $gather = gatherCluster($CMD_LIVE);
        foreach my $lnn (@LNNS) {
            if (isLive() and !@{$gather->{$lnn}}) {
                missing($lnn);
                next;
            }
            else{
                foreach $l (@{$gather->{$lnn}}){
                    debug("Checking $l");
                    if($l =~ /^\s*\-{1}((?:r|-){1}(?:w|-){1}(?:x|-){1})((?:r|-){1}(?:w|-){1}(?:x|-){1})((?:r|-){1}(?:w|-){1}(?:x|-){1}).*openssl\.cnf$/){
                        push(@nodes_with_file_present, $lnn);
                        $current_perms = $1.$2.$3;
                        debug("Current permissions for openssl.cnf on node $lnn are $current_perms");
                        if ($current_perms !~ /rw(?:[x-])r(?:[w-])(?:[x-])r(?:[w-])(?:[x-])/){
                            debug("Permissions of openssl.cnf do not meet minimum required (644) on node $lnn: $current_perms");
                            push(@nodes, $lnn);
                        }
                        else{
                            debug("Permissions meet minium required (644): $current_perms");
                        }
                    }
                    else{
                        if($l =~ /^\s*file\sdoes\snot\sexist\s*$/){
                            debug("Moving on as openssl.cnf does not exist for node $lnn");
                        }
                    }
                }
            }
        }
    }

    if(@nodes_with_file_present){
        if((isDestinationOneFS() eq isCurrentOneFS() or !isDestinationOneFS()) and isCurrentOneFS('>=9.7.1.0') and @nodes){
            # This indicates the customer is already on 9.7.1.0 or higher so we can assume, this is post upgrade, this will trigger a critical where we observe permission issues           
                addCustomerInformation(critical("Checks have found permission issues with openssl.cnf file on the following nodes: ".compressRange(@nodes)));
                info("Please ensure permissions are set to at least minimum required (644) to avoid connectivity issues for SFTP if in use.");
                info("Please reference KB 000226508 (https://www.dell.com/support/kbdoc/000226508) for additional information.");        
        }
        else{
            # Current is less than 9.7.1 so we can assume this is pre upgrade / TC
            if(isCurrentOneFS('<9.7.1.0') and isDestinationOneFS() and isDestinationOneFS('>=9.7.1.0')){
                addCustomerInformation(warning("Upgrading to ".isDestinationOneFS()." may lead to permission changes of openssl.cnf file and may require updating permissions after the upgrade is completed."));
                info("Checks will identify any permission issues post upgrade and will provide steps to remediate.");
                info("Please reference KB 000226508 (https://www.dell.com/support/kbdoc/000226508) for additional information.");
            }
        }
    }

    # Output
    printResults();

} # End checkSFTP
$CHECKS{'checkSMB'} = {
    'Description' => 'Checks if SMB1 support is enabled and advises to disable if it is currently enabled',
    'Exclude' => ,
};
sub checkSMB {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LIVE;
    if(isCurrentOneFS('>=9.12') and isCompliance()){
        $CMD_LIVE = qq[2>&1 ${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig registry.Services.lwio.Parameters.Drivers.srv.SupportSmb1];
    }
    else{
        $CMD_LIVE = qq[2>&1 ${SUDO} isi_gconfig registry.Services.lwio.Parameters.Drivers.srv.SupportSmb1];
    }
    my $CMD_LOGS = q[2>&1 grep -h 'registry.Services.lwio.Parameters.Drivers.srv.SupportSmb1' local/isi_gconfig local/gconfig_ifsvar.tar/ifs/_ifsvar/main_config.gc];
    my $CMD_get_IO_LIVE;
    if(isCurrentOneFS('<9.9')){
         $CMD_get_IO_LIVE = qq[2>&1 ${SUDO} isi statistics protocol list -v --format=table --protocols smb1]; 
    }
    else{
         $CMD_get_IO_LIVE = qq[2>&1 isi statistics protocol list -v --format=table --protocols smb1]; 
    }
    my $CMD_get_IO_LOGS = q[2>&1 grep 'smb1' -h local/isi_statistics_smb];

    # Start
    printCheck('SMB Version');

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);
    my $gather_IO = gatherLocal(isLive()?$CMD_get_IO_LIVE:$CMD_get_IO_LOGS);
    my $smb1_enabled = '';
    my @nodes = ();

    # Analyze
    foreach my $line (@$gather) {
        my ($key, $value) = parseGconfigItem($line);
        if ($key eq 'registry.Services.lwio.Parameters.Drivers.srv.SupportSmb1') {
            if($value eq '1'){
                $smb1_enabled = 1;
            }
        }
    }

    foreach my $line (@$gather_IO) {
        my $time = '';
        my $in = '';
        my $out = '';
        my $node = '';
        my $vers = '';
        debug("$line");
        if($line =~ /^\s*(\d+)\s*\S+\s+\S+\s+\S+\s+\S+\s+(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s*/){
            $time = $1;
            $in = $2;
            $out = $3;
            $node = $4;
            $vers = $5;
            debug("$time\t$in\t$out\t$node\t$vers");
            if($vers eq 'smb1' and ($in > 0 or $in !~ /0.0k?/ or $out > 0 or $out !~ /0.0k?/)){
                push(@nodes, $node);
            }
        }
    }

    if($smb1_enabled or @nodes){
        if($smb1_enabled){
            if(@nodes and isDestinationOneFS('>=9.6') and isCurrentOneFS('<9.6')){
                fail("SMB1 is disabled by default on OneFS 9.6 and newer so upgrading to ".isDestinationOneFS()." will, by default, disable SMB1 which will cause impact if you have clients configured using this version of SMB.") if (isDestinationOneFS('>=9.6'));
                fail("Nodes showing SMB1 IO on this cluster: ".compressRange(@nodes)) if (isDestinationOneFS('>=9.6') and @nodes);
                addCustomerInformation("SMB1 is disabled by default on OneFS 9.6 and newer so upgrading to ".isDestinationOneFS()." will by default disable SMB1 which will cause impact if you have clients configured using this version of SMB if in use.") if (isDestinationOneFS('>=9.6'));
            }
            else{
                warning("SMB1 support is enabled on this cluster, please consider disabling SMB1 per security best practices. SMB1 is disabled by default on 9.6 and newer OneFS versions.");
                addCustomerInformation("SMB1 support is currently enabled on this cluster, you should consider disabling this per security best practices. This protocol version is disabled by default on OneFS 9.6 and newer code versions, please refer https://dl.dell.com/content/manual60210976 to for more information.");
            }
        }
        info("Please reference the OneFS Security Configuration Guide (https://dl.dell.com/content/manual60210976) for more information.");
    }

    # Output
    printResults();
} # End checkSMB
$CHECKS{'checkSNMPDConfig'} = {
    'Description' => 'Checks both SNMPD.config and also isilon_serial_number to make sure they are not 0 bytes',
};
sub checkSNMPDConfig {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LIVE_sn = qq[2>&1 ${SUDO} isi_for_array 'wc -c /etc/isilon_serial_number'];
    my $CMD_LIVE_snmpd = qq[2>&1 ${SUDO} isi_for_array 'wc -c /etc/snmpd.config'];
    my $CMD_LOGS_sn = q[2>&1; for d in *-[0-9]*/etc\.tar/etc ; do echo -n "${d}: "; wc -c ${d}/isilon_serial_number ; done];
    my $CMD_LOGS_snmpd = q[2>&1; for d in *-[0-9]*/etc\.tar/etc ; do echo -n "${d}: "; wc -c ${d}/snmpd.config ; done];

    # Start
    printCheck('SNMPD Config');

    # Gather
    my $gather_sn = gatherCluster(isLive()?$CMD_LIVE_sn:$CMD_LOGS_sn);
	my $gather_snmpd = gatherCluster(isLive()?$CMD_LIVE_snmpd:$CMD_LOGS_snmpd);
    
    # Analyze
	my @sn_file_size_issue = ();
    my @snmpd_file_size_issue = ();
    
    foreach my $lnn (@LNNS) {
	if (!$gather_sn->{$lnn} || !$gather_snmpd->{$lnn}) {
	    missing($lnn);
	    next;
	}
    
    my $file_size;
    my $file_name;

	foreach my $line (@{$gather_sn->{$lnn}}) {
        if ($line =~ /^\s*(\d+)\s*.*etc\/(\S+)\s*$/){
            $file_size = $1;
            $file_name = $2;
            debug("CHECKING FILE: $file_name CURRENT SIZE: $file_size");
            if(defined($file_size) && int($file_size) eq 0){
                push(@sn_file_size_issue, $lnn);
            }
        }
        else{
            debug("CANNOT PARSE STRING: $line");
        }
    }
    foreach my $line (@{$gather_snmpd->{$lnn}}) {
        if ($line =~ /^\s*(\d+)\s*.*etc\/(\S+)\s*$/){
            $file_size = $1;
            $file_name = $2;
            debug("CHECKING FILE: $file_name CURRENT SIZE: $file_size");
            if(defined($file_size) && int($file_size) eq 0){
                push(@snmpd_file_size_issue, $lnn);
            }
        }
        else{
            debug("CANNOT PARSE STRING: $line");
        }
    }
    }
    # Flag
    if (@sn_file_size_issue || @snmpd_file_size_issue) {
	fail("The following nodes have /etc/isilon_serial_number showing as 0 bytes: ".compressRange(@sn_file_size_issue)) if @sn_file_size_issue;
    fail("The following nodes have /etc/snmpd.config showing as 0 bytes: ".compressRange(@snmpd_file_size_issue)) if @snmpd_file_size_issue;
    info("Please raise a Technical Support case referencing the above error.");
    }
	
	    printResults();
} # End checkPartitions
$CHECKS{'checkSPNs'} = {
    'Description' => 'Displays a list of SPNs, only useful when run with --extra',
    'Exclude' => 1,
};
sub checkSPNs {
    return if wasCalled();
    printCheck("SPN List");
    if (isCurrentOneFS('<7.1')) {
	verbose("This check has not been validated for OneFS versions prior to OneFS 7.1.");
	printResults();
	return;
    }

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	if (isCurrentOneFS('<8')) {
	    $gather = gatherLocal(qq[for i in `${SUDO} isi auth ads list -a -z|awk '{print \$1}'`; do ${SUDO} isi auth ads spn list --domain=\$i; done 2>&1]);
	} elsif(isCurrentOneFS('<9.9')) {
	    $gather = gatherLocal(qq[for i in `${SUDO} isi auth ads list -a -z|awk '{print \$1}'`; do ${SUDO} isi auth ads spn list \$i; done 2>&1]);
	}
    else{
        $gather = gatherLocal(qq[for i in `isi auth ads list -a -z|awk '{print \$1}'`; do isi auth ads spn list \$i; done 2>&1]);    
    }
    } else {
	$gather = gatherLocal(qq[2>&1 awk '/auth ads spn list/, /ads trusts list/' local/isi_auth]);
    }

    # Analyze information for issues.
    verboseHeader("SPN List");
    foreach my $line (@$gather) {
	next if $line =~ /auth ads trusts list/;
	verbose($line);
    }

    # Output Check result
    printResults();
} # End checkSPNs
$CHECKS{'checkSRS'} = {
    'Description' => 'Checks the remote connectivity configuration for issues',
    'Reference' => 'https://www.dell.com/support/kbdoc/000043617',
};
sub checkSRS {
    if (wasCalled()) { return }

    checkUpgradePath();
    printCheck('DialHome & Remote Connectivity');

    gatherRemoteSupport();
    gatherSRS();
    gatherProductInfo();

    checkSRSPools();
    checkEventChannel();

    my $MSG_SRS_SVC = 'SRS (isi esrs) is enabled, but the SRS daemon service is disabled and should be enabled. To enable the SRS Daemon service run the following command: isi services -a isi_esrs_d enable';
    my $MSG_RS_SVC = 'RemoteSupport (isi remotesupport) is enabled, but the ConnectEMC service is disabled and should be enabled. To enable the ConnectEMC service run the following command: isi services -a connectemc enable';
    my $MSG_BOTH = 'Both SRS (isi esrs) and RemoteSupport (isi remotesupport) methods for remote connectivity are enabled.  The RemoteSupport (isi remotesupport) method should be disabled by running the following command: isi remotesupport connectemc modify --enabled=false';
    my $MSG_PS_NODES = 'DialHomes and RemoteSupport (isi remotesupport) will not work on PowerScale F200 and PowerScale F600 nodes in OneFS 9.0.0.0 and later.  Consider migrating to SRS (isi esrs) method for remote connectivity.';
    my $MSG_RS_TO_SRS = 'RemoteSupport (isi remotesupport) is enabled, and the cluster is running a version of OneFS 8.1 or later. In OneFS 8.1 and later, SRS (isi esrs) is the recommended method for remote support functionality. Review the "Remote support" section of the administration guides for your current OneFS version for details on how to enable this functionality.';
    my $MSG_91 = 'The SRSv2 method for remote connectivity via "isi remotesupport connectemc" commands is enabled, this method must be disabled before upgrading to OneFS 9.1 or later.  It is recommended to configure the SRSv3 method for remote connectivity via "isi esrs" commands or in the WebUI. See https://dl.dell.com/content/manual73126448 for more information on how to configure SRSv3.';
 	my $MSG_SRS_TO_SCG = 'ESRS (isi esrs) is enabled, and the cluster is running a version of OneFS 9.11 or later. In OneFS 9.11 and later, Dell Technologies connectivity services (isi connectivity) is the recommended method for remote support functionality. Review the "Dell Technologies connectivity services" section of the administration guides for your current OneFS version for details on how to enable this functionality.';
    
    # SRSv3 (isi esrs) is enabled
    if ($CLUSTER_INFO{'SRS'}{'Enabled'}) {
		details('SRS (isi esrs) is enabled');

		if (isCurrentOneFS('>=9.11')){
			addCustomerInformation('Remote Connectivity',fail('The SRS method for remote connectivity via "isi esrs" commands is enabled, this method must be disabled as it is deprecated by Dell Technologies connectivity services method. Please configure Dell Technologies connectivity services method is used for remote connectivity via "isi connectivity" commands or in the WebUI. See https://dl.dell.com/content/manual73126448 for more information on how to configure Dell Technologies connectivity services.'));
		}
		if (isCurrentOneFS('<9.11') and isDestinationOneFS('>=9.11')) {
			addCustomerInformation('Remote Connectivity',warning('The SRS method for remote connectivity via "isi esrs" commands is enabled, this method is deprecated as of 9.11 and will automatically be migrated to Dell Technologies connectivity services method during the upgrade, The SRS service must remain enabled to allow the migration complete.'));
		}

	# Flag if the isi_esrs_d service is disabled
	if (isServiceDisabled('isi_esrs_d')) { critical($MSG_SRS_SVC) }
    }

    # Remote Support (isi remotesupport) is enabled
    if ($CLUSTER_INFO{'RemoteSupport'}{'Enabled'}) {
	details('RemoteSupport (isi remotesupport) is enabled');

	# Flag if the connectemc service is disabled
	if (isServiceDisabled('connectemc')) { critical($MSG_RS_SVC) }

	# Flag if there are any Gen6.5 nodes and RemoteSupport is being used
	if ($CLUSTER_INFO{'Gen6.5'}) { fail($MSG_PS_NODES) }

	# Flag if the cluster is running OneFS 8.1+ and not using SRS
	if (isCurrentOneFS('>=8.1') and
	    !$CLUSTER_INFO{'SRS'}{'Enabled'}) {
	    if (isDestinationOneFS('>=9.1')) {
		addCustomerInformation('Remote Connectivity',
				       fail($MSG_91));
	    } else {
		addCustomerInformation('Remote Connectivity',
				       warning($MSG_RS_TO_SRS));
	    }
	}

    }

    # Both are enabled
    if ($CLUSTER_INFO{'SRS'}{'Enabled'} and
	$CLUSTER_INFO{'RemoteSupport'}{'Enabled'}) {
	fail($MSG_BOTH);
    }

    # Neither is enabled
    if (!$CLUSTER_INFO{'RemoteSupport'}{'Enabled'} and
	!$CLUSTER_INFO{'SRS'}{'Enabled'}) {
	if (isCurrentOneFS('<7.1')) {
	    details('RemoteSupport/SRS is not supported prior to OneFS 7.1');
	} elsif (isCurrentOneFS('<9.1')) {
	    details('RemoteSupport/SRS is not enabled');
	} else {
	    details('SRS is not enabled');
	}
    }

    # Verbose output for isi esrs
    if (isCurrentOneFS('>=8.1')) {
	verboseHeader('SRSv3/SRS (isi esrs)');
	verbose('Enabled: '.
		($CLUSTER_INFO{'SRS'}{'Enabled'} or 'No'));
	verbose('Primary SRS Gateway: '.
		($CLUSTER_INFO{'SRS'}{'Primary Gateway'} or '-'));
	verbose('Secondary SRS Gateway: '.
		($CLUSTER_INFO{'SRS'}{'Secondary Gateway'} or '-'));
	verbose('Gateway Access Pools: '.
		($CLUSTER_INFO{'SRS'}{'Pools'}?
		 "@{$CLUSTER_INFO{'SRS'}{'Pools'}}":'-'));
    }

    # Verbose output for isi remotesupport
    if (isCurrentOneFS('>=7.1') and
	isCurrentOneFS('<9.1')) {
	verboseHeader('SRSv2/RemoteSupport (isi remotesupport):');
	verbose('Enabled: '.
		($CLUSTER_INFO{'RemoteSupport'}{'Enabled'} or 'No'));
	verbose('Primary SRS Gateway: '.
		($CLUSTER_INFO{'RemoteSupport'}{'Primary Gateway'} or '-'));
	verbose('Secondary SRS Gateway: '.
		($CLUSTER_INFO{'RemoteSupport'}{'Secondary Gateway'} or '-'));
	if (isCurrentOneFS('>=8')) {
	    verbose('Gateway Access Pools: '.
		    ($CLUSTER_INFO{'RemoteSupport'}{'Pools'}?
		     "@{$CLUSTER_INFO{'RemoteSupport'}{'Pools'}}":'-'));
	} else {
	    verbose('Remote Support Subnet: '.
		    ($CLUSTER_INFO{'RemoteSupport'}{'Subnet'} or '-'));
	}
    }

    # Output results
    if (isResults(&WARN)) { info(sprintf($MSG{'KB_1'}, '000043617')) }
    printResults();
} # End checkSRS
sub checkSRSPools {
    if (wasCalled()) { return }
    gatherFlexNet();
    gatherRemoteSupport();
    gatherSRS();

    my $MSG_SRS_NO_POOLS = 'SRS (isi esrs) is enabled, but no Gateway Access Pools are configured';
    my $MSG_RS_NO_POOLS = 'Remote Support (isi remotesupport) is enabled, but no Gateway Access Pools are configured';
    my $MSG_RS_NO_SUBNET = 'Remote Support (isi remotesupport) is enabled, but no Remote Support Subnet is configured';
    my $SPF_SRS_MISSING_POOLS = 'SRS (isi esrs) has configured the following Gateway Access Pool(s) which could not be found: %s';
    my $SPF_SRS_NON_SYSTEM = 'SRS (isi esrs) has configured the following Gateway Access Pool(s) which are not configured with a System Access Zone: %s';
    # my $SPF_SRS_NON_STATIC = 'SRS (isi esrs) has configured the following Gateway Access Pool(s) which are not configured with a Static Allocation Method: %s';
    my $SPF_RS_MISSING_POOLS = 'Remote Support (isi remotesupport) has configured the following Gateway Access Pool(s) which could not be found: %s. '.sprintf($MSG{'KB_1'}, '000082040');
    my $SPF_RS_MISSING_POOLS_UPG = 'If left unresolved the upgrade to OneFS %s will encounter an error shortly after the first node reboots.';
    my $SPF_RS_NON_SYSTEM = 'Remote Support (isi remotesupport) has configured the following Gateway Access Pool(s) which are not configured with the System Access Zone: %s';
    # my $SPF_RS_NON_STATIC = 'Remote Support (isi remotesupport) has configured the following Gateway Access Pool(s) which are not configured with a Static Allocation Method: %s';
    my $SPF_RS_MISSING_SUBNET = 'Remote Support (isi remotesupport) has configured the following Remote Support Subnet which could not be found: %s';

    # Flag if enabled but no pool or subnet configured
    if ($CLUSTER_INFO{'SRS'}{'Enabled'} and
	!$CLUSTER_INFO{'SRS'}{'Pools'}) {
	critical($MSG_SRS_NO_POOLS);
    }
    if ($CLUSTER_INFO{'RemoteSupport'}{'Enabled'} and
	!$CLUSTER_INFO{'RemoteSupport'}{'Pools'} and
	isCurrentOneFS('>=8') and
	isCurrentOneFS('<8.2')) {
	critical($MSG_RS_NO_POOLS);
    }
    if ($CLUSTER_INFO{'RemoteSupport'}{'Enabled'} and
	!$CLUSTER_INFO{'RemoteSupport'}{'Subnet'} and
	isCurrentOneFS('<8')) {
	critical($MSG_RS_NO_SUBNET);
    }

    # Checks that the network pools/subnets configured for remote connectivity
    # exist and are in the System access zone and Static allocation method
    if ($CLUSTER_INFO{'SRS'}{'Pools'} and $CLUSTER_INFO{'FlexNet'}{'Pools'}) {
	my @missing_pools = ();
	my @non_system_pools = ();
	my @non_static_pools = ();
	foreach my $pool (@{$CLUSTER_INFO{'SRS'}{'Pools'}}) {
	    my @pools = grep(/\.$pool$/, keys %{$CLUSTER_INFO{'FlexNet'}{'Pools'}});
	    if (!@pools) {
		push(@missing_pools, $pool);
		next;
	    }
	    my $found_system_pool = 0;
	    foreach my $found_pool (@pools) {
		if ($CLUSTER_INFO{'FlexNet'}{'Pools'}{$found_pool}{'Auth Zone'} and
		    $CLUSTER_INFO{'FlexNet'}{'Pools'}{$found_pool}{'Auth Zone'} eq 1) {
		    $found_system_pool = 1;
		    last;
		}
	    }
	    if (!$found_system_pool) { push(@non_system_pools, $pool) }

	    # if ($CLUSTER_INFO{'FlexNet'}{'Pools'}{$found_pool}{'Allocation Method'} and
	    # 	$CLUSTER_INFO{'FlexNet'}{'Pools'}{$found_pool}{'Allocation Method'} !~ qr/^static$/i) {
	    # 	push(@non_static_pools, $pool);
	    # }
	}
	if ($CLUSTER_INFO{'SRS'}{'Enabled'} and @missing_pools) {
	    warning(sprintf($SPF_SRS_MISSING_POOLS, join(', ', @missing_pools)));
	}
	if ($CLUSTER_INFO{'SRS'}{'Enabled'} and @non_system_pools) {
	    warning(sprintf($SPF_SRS_NON_SYSTEM, join(', ', @non_system_pools)));
	}
	# if ($CLUSTER_INFO{'SRS'}{'Enabled'} and @non_static_pools) {
	#     warning(sprintf($SPF_SRS_NON_STATIC, join(', ', @non_static_pools)));
	# }
    }
    if ($CLUSTER_INFO{'RemoteSupport'}{'Pools'} and
	$CLUSTER_INFO{'FlexNet'}{'Pools'} and
	isCurrentOneFS('<8.2')) {
	my @missing_pools = ();
	my @non_system_pools = ();
	my @non_static_pools = ();
	foreach my $pool (@{$CLUSTER_INFO{'RemoteSupport'}{'Pools'}}) {
	    my ($found_pool) = grep(/\.$pool$/, keys %{$CLUSTER_INFO{'FlexNet'}{'Pools'}});
	    if (!$found_pool) {
		push(@missing_pools, $pool);
		next;
	    }
	    if ($CLUSTER_INFO{'FlexNet'}{'Pools'}{$found_pool}{'Auth Zone'} and
		$CLUSTER_INFO{'FlexNet'}{'Pools'}{$found_pool}{'Auth Zone'} ne 1) {
		push(@non_system_pools, $pool);
	    }
	    # if ($CLUSTER_INFO{'FlexNet'}{'Pools'}{$found_pool}{'Allocation Method'} and
	    # 	$CLUSTER_INFO{'FlexNet'}{'Pools'}{$found_pool}{'Allocation Method'} !~ qr/static/i) {
	    # 	push(@non_static_pools, $pool);
	    # }
	}
	if (@missing_pools) {
	    my $msg_pools = sprintf($SPF_RS_MISSING_POOLS,
				    join(', ', @missing_pools));
	    if (isOneFSUpgrade()) {
		my $source;
		foreach my $target (@{$RPS{'Upgrade Path'}}) {
		    if ($source and $target and
			$target =~ /^8\.2\.|^9\.0\./) {
			fail($msg_pools.'. '.
			     sprintf($SPF_RS_MISSING_POOLS_UPG,
				     $target));
			last;
		    }
		    $source = $target;
		}
	    } else {
		warning($msg_pools);
	    }
	}
	if ($CLUSTER_INFO{'RemoteSupport'}{'Enabled'} and @non_system_pools) {
	    warning(sprintf($SPF_RS_NON_SYSTEM, join(', ', @non_system_pools)));
	}
	# if ($CLUSTER_INFO{'RemoteSupport'}{'Enabled'} and @non_static_pools) {
	#     warning(sprintf($SPF_RS_NON_STATIC, join(', ', @non_static_pools)));
	# }
    }
    if ($CLUSTER_INFO{'RemoteSupport'}{'Subnet'} and
	$CLUSTER_INFO{'FlexNet'}{'Subnets'} and
	!$CLUSTER_INFO{'FlexNet'}{'Subnets'}{$CLUSTER_INFO{'RemoteSupport'}{'Subnet'}}) {
	warning(sprintf($SPF_RS_MISSING_SUBNET,
			$CLUSTER_INFO{'RemoteSupport'}{'Subnet'}));
    }
}
$CHECKS{'checkSSHDConfig'} = {
    'Description' => 'Checks the /etc/mcp/templates/sshd_config files for known issues',
    'Exclude' => 1,
};
sub checkSSHDConfig {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8.2')) { return }
    if (isCurrentOneFS('<7') and !isLive()) { return }
    printCheck("SSHD Config Compatibility");

    checkSSHDConfigSHA256();

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	my $cmd = q[cat /etc/mcp/templates/sshd_config];
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array '$cmd']);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -H . */etc.tar/etc/mcp/templates/sshd_config]);
    }

    # MACs no longer supported in OpenSSH 7.6
    my @OpenSSH76RemovedMACs = (
	'hmac-ripemd160',
	'hmac-ripemd160@openssh.com',
	'hmac-ripemd160-etm@openssh.com',
	);

    # Ciphers no longer supported in OpenSSH 7.6
    my @OpenSSH76RemovedCiphers = (
	'blowfish-cbc',
	'cast128-cbc',
	'arcfour',
	'arcfour128',
	'arcfour256',
	);

    # Analyze information for issues.
    my %bad_macs = ();
    my %bad_ciphers = ();
    my %appends = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ /^\s*MACs\s*(\S+)$/) {
		my @MACs = split(/,/,$1);
		foreach my $MAC (@MACs) {
		    my ($results) = grep {$_ eq $MAC} @OpenSSH76RemovedMACs;
		    $bad_macs{$results} = 1 if $results;
		}
	    }
	    if ($line =~ /^\s*Ciphers\s*(\S+)$/) {
		my @Ciphers = split(/,/,$1);
		foreach my $Cipher (@Ciphers) {
		    my ($results) = grep {$_ eq $Cipher} @OpenSSH76RemovedCiphers;
		    $bad_ciphers{$results} = 1 if $results;
		}
	    }
	    if ($line =~ /^(\S+)\s+\+/) {
		$appends{$lnn}{$1}++;
	    }
	}
    }

    # Flag issues detected
    if (%bad_ciphers or %bad_macs) {
	my $version = "";
	if ($RPS{'Target'}) {
	    $version = $RPS{'Target'};
	} elsif (isCurrentOneFS('8.1.0.4') or isCurrentOneFS('8.1.2.0')) {
	    $version = $CLUSTER_INFO{'OneFS'};
	}
	if ($version eq "8.1.0.4" or $version eq "8.1.2.0") {
	    fail("Support for the following Ciphers is removed in OpenSSH 7.7 and later which is included in the Kernel Rollup Patch for OneFS $version: ".join(", ", keys %bad_ciphers)) if %bad_ciphers;
	    fail("    Support for the following MACs is removed in OpenSSH 7.7 and later which is included in the Kernel Rollup Patch for OneFS $version: ".join(", ", keys %bad_macs)) if %bad_macs;
	    fail("    The compatibility issues found in the /etc/mcp/templates/sshd_config file should be resolved before applying the Kernel Rollup Patch.");
	    fail("    If left unresolved the sshd service will fail to start.");
	} elsif ($RPS{'Target'} and isCurrentOneFS('<8.2') and compareVersions($RPS{'Target'}, "8.2") >= 0) {
	    fail("Support for the following Ciphers is removed in OpenSSH 7.7 and later which is included in the upgrade to OneFS $RPS{'Target'}: ".join(", ", keys %bad_ciphers)) if %bad_ciphers;
	    fail("    Support for the following MACs is removed in OpenSSH 7.7 and later which is included in the upgrade to OneFS $RPS{'Target'}: ".join(", ", keys %bad_macs)) if %bad_macs;
	    fail("    The compatibility issues found in the /etc/mcp/templates/sshd_config file should be resolved before upgrading to $RPS{'Target'}.");
	    fail("    If left unresolved the sshd service will fail to start.");
	}
    }
    my $multiple_append_issue = 0;
    foreach my $lnn (keys %appends) {
	foreach my $keyword (keys %{$appends{$lnn}}) {
	    if ($appends{$lnn}{$keyword} > 1) {
		$multiple_append_issue = 1;
	    }
	}
    }
    if ($multiple_append_issue and isCurrentOneFS('<8.2')) {
	if ($RPS{'Target'} and compareVersions($RPS{'Target'}, "8.2") >= 0) {
	    fail("Multiple lines with appending values detected in /etc/mcp/templates/sshd_config");
	} else {
	    warning("Multiple lines with appending values detected in /etc/mcp/templates/sshd_config");
	}
    }

    # Output Check result
    info(sprintf($MSG{'KB_1'}, '000060486')) if isResults(&WARN);
    printResults();
} # End checkSSHDConfig
sub checkSSHDConfigSHA256 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8.2')) { return }
    if (!isDestinationOneFS('>=8.2')) { return }
    if (!isLive()) { return }
    if (isHardened()) { return }

    # Setup
    my $CMD_LIVE =
	"2>&1 ${SUDO} isi_for_array 'sha256 -q /etc/ssh/sshd_config'";
    my $MSG_FAIL =
      'A mismatch of /etc/ssh/sshd_config files was found. '.
      sprintf($MSG{'KB_1'}, '000169490');

    # Gather
    my $gather = gatherCluster($CMD_LIVE);

    # Analyze
    my %sha256sums = ();
    my $spf = '%5s %s';
    verboseHeader('/etc/ssh/sshd_config SHA256 Checksum Values');
    verbose(sprintf($spf, 'LNN', 'SHA256 Checksum Value'));
    verbose(sprintf($spf, '-'x5, '-'x64));
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { missing($lnn); next; }
	foreach my $checksum (@{$gather->{$lnn}}) {
	    $checksum =~ s/^\s*|\s*$//;
	    if ($checksum !~ /^[a-f0-9]{64}$/) { next }
	    verbose(sprintf($spf, $lnn, $checksum));
	    $sha256sums{$checksum}++;
	}
    }

    # Flag
    if (scalar(keys %sha256sums) > 1) { fail($MSG_FAIL) }
} # End checkSSHDconfigSHA256
$CHECKS{'checkSWIFTAccounts'} = {
    'Description' => 'checks for SWIFT accounts which is then used to set prioirity of flag when SWIFT is licensed and an upgrade to 9.5 or higher is being performed',
    'Exclude' => 1,
};
sub checkSWIFTAccounts {
    if (wasCalled()) { return }
    if (!isDestinationOneFS() or isDestinationOneFS('<9.5')) { return }
    checkLicense();
    # Setup
    my $CMD_LIVE;
    if(isCurrentOneFS('<9.9')){
        $CMD_LIVE = qq[2>&1 ${SUDO} isi swift accounts list];
    }
    else{
        $CMD_LIVE = qq[2>&1 isi swift accounts list];
    }
    my $CMD_LOGS = q[2>&1 find local/ifsvar_modules\.tar/modules/swift/ -maxdepth 2 -type f | wc -l];

    my $CMD_LIVE_FW = qq[2>&1 isi network firewall policies list | grep lwswift];
    my $CMD_LOGS_FW = q[2>&1 grep -H lwswift local/isi_firewall_policies];

    printCheck("Check LWSWIFT");

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);
    my $gather_fw = gatherLocal(isLive()?$CMD_LIVE_FW:$CMD_LOGS_FW);

    my $swift_accounts;

    # Analyze
    if(!$gather || $gather eq ''){
        if(!isLive()){
            warning("Could not determine if SWIFT accounts are configured, Please check on live cluster using 'isi swift accounts list'.");
        }
    }
    foreach my $line (@$gather) {
        if(isLive()){
        if($line =~ /^.*Total:\s*(\d+).*$/){
            if($1 > 0){
                $RPS{'SWIFT accounts configured'} = 1;
            }
        }
        }
        elsif(!isLive()){
            if($line =~ /^\s*(\d+)\s*$/){
                if($1 > 0){ 
                    $RPS{'SWIFT accounts configured'} = 1;
                }
            }
        }
    }

    foreach my $line (@$gather_fw) {
        debug("Checking $line");
        if($line =~ /^.*rule_lwswift.*$/){
            debug("Setting Firewall rule to 1 for LWSWIFT rule: $line");
            $RPS{'SWIFT Firewall Rule configured'} = 1;
        }
    }

    if($RPS{'SWIFT Firewall Rule configured'}){
        fail("Firewall rule found configured for lwswift, this needs to be removed prior to upgrading to 9.5 or higher where lwswift is deprecated.");
        info("To obtain all rules associated with lwswift, please run the following command: isi_gconfig -t firewall | grep -Eo '.*rules\\.rule_lwswift' | grep -v '^(empty' | uniq");
        info("Use the following command to remove the firewall rule: isi_gconfig -t firewall -r <lwswift records returned from the above command>");
        info("Please reference KB000331351 https://www.dell.com/support/kbdoc/000331351 for additional information.");
    }

    printResults();

    return;
} # End checkSWIFTAccounts
$CHECKS{'checkScreenDTA219904'} = {
    'Description' => 'Checks for running _lldpd restart processes / active screen sessions for DTA 219904 on clusters',
};
sub checkScreenDTA219904 {
    if (wasCalled()) { return }
    if (!isLive()) { return unsupported($MSG{'UNSUP_LOGS'}) }
    
    # Setup
    my $CMD_LIVE_screen = qq[2>&1 screen -ls | grep RPS_DTA219904 | wc -l];

    # Start
    printCheck('DTA 219904 Screen Session');

    # Gather
    my $gather_screen = gatherLocal($CMD_LIVE_screen);
    
    # Analyze
    my @nodes = ();
    my $current_proc_count;

    my ($flsc_res) = grep {$_ =~ /^\s*(\d+)\s*$/} @$gather_screen;
    
    # Flag
    if($flsc_res){
        if(int($flsc_res) > 0 ){
            warning('Detected a screen session for RPS_DTA219904 that is still active, do not start another screen for this workaround.');
            info('To show active screen sessions, use \'screen -ls\'.');
        }
    }

    # Output
    printResults();
} # End checkScreenDTA219904
$CHECKS{'checkServices'}{'Description'} =
    'Checks common services to make sure they are in the expected states';
sub checkServices {
    if (wasCalled()) { return }
    printCheck('Cluster Services');
    gatherServices();
    if (!$CLUSTER_INFO{'Services'}) {
	fail('Unable to gather cluster services information');
	printResults();
	return;
    }
    gatherSyncIQ();

    # Output details about the status of the cluster services
    verboseHeader('Cluster Services');
    verbose(sprintf('%-30s %s', 'Name', 'Status'));
    verbose(sprintf('%-30s %s', '-'x30, '-'x10));
    verbose(sprintf('%-30s %s', $_, $CLUSTER_INFO{'Services'}{$_}))
	foreach (sort keys %{$CLUSTER_INFO{'Services'}});

    # Check if critical services are disabled
    fail('Authentication (auth) service is disabled.')
	if isServiceDisabled('auth');
    fail('SmartConnect (isi_smartconnect) service is disabled.')
	if isServiceDisabled('isi_smartconnect');
	fail('Cluster Event Log (celog) service is disabled.')
	if isServiceDisabled('celog');
    fail('Cluster Event Log Coalescer (celog_coalescer) service is disabled.')
	if isServiceDisabled('celog_coalescer');
    fail('Cluster Event Log System Monitor (celog_monitor) service is disabled.')
	if isServiceDisabled('celog_monitor');
    fail('Cluster Event Log Notification Daemon (celog_notification) service is disabled.')
	if isServiceDisabled('celog_notification');
    fail('Filesystem Daemon (ifsd) service is disabled.')
	if isServiceDisabled('ifsd');
    fail('Drive Daemon (isi_drive_d) service is disabled.')
	if isServiceDisabled('isi_drive_d');
    fail('Drive Repurposing Daemon (isi_drive_repurpose_d) service is disabled.')
	if isServiceDisabled('isi_drive_repurpose_d');
    fail('Platform API Daemon (isi_papi_d) service is disabled.')
	if isServiceDisabled('isi_papi_d');
    fail('Group Change Daemon (isi_group_change_d) service is disabled.')
	if isServiceDisabled('isi_group_change_d');
    fail('Job Daemon (isi_job_d) service is disabled.')
	if isServiceDisabled('isi_job_d');
    fail('MCP Failsafe Daemon (isi_mcp) service is disabled.')
	if isServiceDisabled('isi_mcp');
    fail('Protocol Availability (isi_protohealth_d) service is disabled.')
	if isServiceDisabled('isi_protohealth_d');
    fail('RPC Daemon (isi_rpc_d) service is disabled.')
	if isServiceDisabled('isi_rpc_d');
    fail('Likewise Service Manager (lwsm) service is disabled.')
	if isServiceDisabled('lwsm');
    warning('Tardis Configuration (isi_tardis_d) service is disabled.')
	if isServiceDisabled('isi_tardis_d');
    warning('Service isi_upgrade_d is currently enabled.',
	    '  The isi_upgrade_d service should only be enabled when performing'.
	    ' a OneFS upgrade and coming from a OneFS version prior to OneFS 8.')
	if isServiceEnabled('isi_upgrade_d');
	warning("Audit Daemon is (isi_audit_d) disabled.") 
	if isServiceDisabled('isi_audit_d');
    if (isServiceDisabled('isi_upgrade_agent_d')) {
	my $msg = 'Service isi_upgrade_agent_d is currently disabled. The'.
	    ' isi_upgrade_agent_d service must be enabled prior to performing'.
	    ' any upgrade activities in OneFS 8 and later codes.';
	if (isDestinationOneFS() or $RPS{'UpgradePlan'}) {
	    critical($msg);
	} else {
	    warning($msg);
	}
    }

    if (isServiceDisabled('isi_migrate') and
	($CLUSTER_INFO{'SyncIQ'}{'Source'} or
	 $CLUSTER_INFO{'SyncIQ'}{'Target'})) {
	critical('The SyncIQ Service (isi_migrate) is disabled.  This service'.
		 ' is required for SyncIQ functionality.  To enable the'.
		 ' service run the command: isi services isi_migrate enable');
    }

    # Issue where upgrade commit will error if isi_cpool_d or isi_cpool_io_d services are disabled
    if (isCurrentOneFS('<8.2') and
	$RPS{'Target'} and compareVersions($RPS{'Target'}, '8.2') >= 0) {
	my @services = ();
	if (isServiceDisabled('isi_cpool_d')) { push(@services, 'isi_cpool_d') }
	if (isServiceDisabled('isi_cpool_io_d')) { push(@services, 'isi_cpool_io_d') }
	if (@services) {
	    critical('For upgrades going to OneFS 8.2 and later, CloudPools services should be enabled to avoid encountering an error during the upgrade commit. '.
		     "The following CloudPools services were found as disabled: @services");
	}
    }

    # Issue where post HealthCheck package install the isi_healthcheck service is disabled
    critical('Healthcheck Daemon (isi_healthcheck) is not enabled and will need'.
	     ' to be enabled to run any health checks via isi healthcheck commands.')
	if hasPatch('HealthCheck') and isServiceDisabled('isi_healthcheck');

    # Issue where post upgrade to OneFS 8, isi_cbind_d service goes missing, KB 000065646
    fail('isi_cbind_d service is missing in isi services -a, manually verify and,'.
	 ' if missing, escalate and reference KB 000065646')
	if isCurrentOneFS(qr/^8/) and !$CLUSTER_INFO{'Services'}{'isi_cbind_d'};

    # Display the check results
    printResults();

} # End checkServices
$CHECKS{'checkServicesMonitoring'}{'Description'} =
    'Checks that enabled services are being monitored';
sub checkServicesMonitoring {
    if (wasCalled()) { return }
    printCheck('Services - Monitoring');
    gatherServices();

    # Setup
    my $CMD_LIVE =
	qq[2>&1 ${SUDO} isi_for_array 'grep -H ignore /etc/mcp/override/*'];
    my $CMD_LOGS = q[for i in $(grep -l ignore */etc.tar/etc/mcp/override/*);].
	q[ do echo $i: $(grep -H ignore $i); done 2>&1];
    my $SPF_IGNORE = q[The service %s is enabled but set to ignore on the].
	q[ following nodes: %s];
    my $MSG_INFO = q[The Master Control Process (MCP|isi_mcp) will not].
	q[ monitor services flagged for ignore.  Those services must be].
	q[ manually started/stopped on each node.  This also implies that if a].
	q[ node is rebooted, MCP will not automatically start the service].
	q[ on that node and it must be manually started.  To allow MCP to].
	q[ resume monitoring a service run the following command replacing].
	q[ <service> with the appropriate service name: isi services -a].
	q[ <service> monitor];

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my %ignored_services = ();
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	foreach (@{$gather->{$lnn}}) {
	    if (!/attribute="ignore"/ or !/value="1"/) { next }
	    my $service = '';
	    if (/\/etc\/mcp\/override\/(\S+):/) { $service = $1 }
	    debug("Ignored service: $service");
	    if (isServiceEnabled($service)) {
		push(@{$ignored_services{$service}}, $lnn);
	    }
	}
    }

    # Flag
    foreach my $service (keys %ignored_services) {
	fail(sprintf($SPF_IGNORE, $service,
		     compressRange(@{$ignored_services{$service}})));
    }

    # Output
    if (isResults(&FAIL)) { info($MSG_INFO) }
    printResults();
} # End checkServicesMonitoring
$CHECKS{'checkSnapshot'} = {
    'Description' => 'Checks if the Snapshot count is approaching cluster limit of 20,000, whether Autodelete is set to yes, and checks snapshot logs. Checks snapshot logs for EIN/EIO/EDEADLK/Failed to create snapshot',
    'Exclude' => 1,
};
sub checkSnapshot {
    return if wasCalled();
    checkLicense();
    checkStoragePools();
    printCheck("Snapshot");
    gatherSnapshotCount();
    gatherSnapshotSettings();
    gatherSnapshotUsage();
    checkSnapshotLog();
    
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array sysctl -a efs.snapshot.stf_populate_illogical_lin_enabled];
    my $CMD_LOGS = q[2>&1; grep -H 'efs.snapshot.stf_populate_illogical_lin_enabled' ./*/efs.snapshot];
    
    my @nodes;
    if(isCurrentOneFS('>=9.10')){
        my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
        
        foreach my $lnn (@LNNS) {
            foreach $l (@{$gather->{$lnn}}){
                debug("Sysctl param for node $lnn is $l");
                if($l =~ /efs.snapshot.stf_populate_illogical_lin_enabled[:=]\s*(None|\d)/){
                    debug("Result to check = $1");
                    push(@nodes, $lnn) if $1 eq 'None' or $1 eq '1';
                }
            }
        }
    };

    if ($CLUSTER_INFO{'Snapshots'}{'Count'} and $CLUSTER_INFO{'Snapshots'}{'Count'} >= 20000) {
	critical("Total snapshot count is $CLUSTER_INFO{'Snapshots'}{'Count'}, more than the limit for snapshots per cluster of 20,000.");
    } elsif ($CLUSTER_INFO{'Snapshots'}{'Count'} and $CLUSTER_INFO{'Snapshots'}{'Count'} >= 19000) {
	critical("Total snapshot count is $CLUSTER_INFO{'Snapshots'}{'Count'}, approaching the limit for snapshots per cluster of 20,000.");
    } elsif ($CLUSTER_INFO{'Snapshots'}{'Count'} and $CLUSTER_INFO{'Snapshots'}{'Count'} >= 15000) {
	warning("Total snapshot count is $CLUSTER_INFO{'Snapshots'}{'Count'}, approaching the limit for snapshots per cluster of 20,000.");
    }
    if ($CLUSTER_INFO{'Snapshots'}{'Autodelete'} and $CLUSTER_INFO{'Snapshots'}{'Autodelete'} !~ /Yes/) {
	warning("Snapshot Autodelete not set to Yes");
    }
    if($CLUSTER_INFO{'StoragePools_Count'} > 1 and isServiceEnabled('isi_snapshot_d') and $CLUSTER_INFO{'License'}{'SNAPSHOTIQ'}{'Status'} ne 'Unlicensed'){
        if(isCurrentOneFS('<9.10') and ((isDestinationOneFS('>=9.10') and isDestinationOneFS('<9.10.1.4')) or (isDestinationOneFS('>=9.11') and isDestinationOneFS('<9.11.0.5')))){
            warning("This cluster may be at risk of encountering a bug outlined on KB 000337012 after upgrading to ".isDestinationOneFS().", Please ensure resolution steps are performed after you upgrade.");
            info("Please reference KB 000337012 (https://www.dell.com/support/kbdoc/000337012) for additional information.");
            addCustomerInformation("After upgrading to ".isDestinationOneFS()." please ensure that 'stf_populate_illogical_lin_enabled' is disabled. Please reference https://www.dell.com/support/kbdoc/000337012 for additional information.");
        }
        if((isCurrentOneFS('>=9.10') and isCurrentOneFS('<9.10.1.4')) or (isCurrentOneFS('>=9.11') and isCurrentOneFS('<9.11.0.5'))){
            if(@nodes){
                addCustomerInformation(critical("This cluster may be at risk of encountering a bug outlined on KB 000337012. Please ensure that stf_populate_illogical_lin_enabled is disabled."));
                info("Please reference KB 000337012 (https://www.dell.com/support/kbdoc/000337012) for additional information.");
            }
            else{
                # INFO info("Please note that 'efs.snapshot.stf_populate_illogical_lin_enabled' setting is currently disabled.");
                info("Please reference KB 000337012 (https://www.dell.com/support/kbdoc/000337012) for additional information.");
            }
        }    
    }

    # Output Check result
    printResults();
} # End checkSnapshot
sub checkSnapshotLog {
    return if wasCalled();

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array 'awk "].
				q[/EINVAL/ {EINVAL++; EINVALMR=\$1}].
				q[/EIO/ && !/ifs_snap_delete/{EIO++; EIOMR=\$1}].
				q[/EDEADLK/ {EDEADLK++; EDEADLKMR=\$1}].
				q[/Failed to create snapshot/ {FAIL++; FAILMR=\$1}].
				q[END {].
				q[print \"EINVAL:\" EINVAL;].
				q[print \"EINVALMR:\" EINVALMR;].
				q[print \"EIO:\" EIO;].
				q[print \"EIOMR:\" EIOMR;].
				q[print \"EDEADLK:\" EDEADLK;].
				q[print \"EDEADLKMR:\" EDEADLKMR;].
				q[print \"FAIL:\" FAIL;].
				q[print \"FAILMR:\" FAILMR;].
				q[}].
				q[" /var/log/isi_snapshot_d.log']);
    } else {
	$gather = gatherCluster(qq[for i in */varlog.tar/log/isi_snapshot_d.log;do ].
				qq[echo -n \$i; awk '/EINVAL/ {EINVAL++; EINVALMR=\$1} END {print ":EINVAL:" EINVAL " EINVALMR:" EINVALMR}' \$i;].
				qq[echo -n \$i; awk '/EIO/ && !/ifs_snap_delete/ {EIO++; EIOMR=\$1} END {print ":EIO:" EIO " EIOMR:" EIOMR}' \$i;].
				qq[echo -n \$i; awk '/EDEADLK/ {EDEADLK++; EDEADLKMR=\$1} END {print ":EDEADLK:" EDEADLK " EDEADLKMR:" EDEADLKMR}' \$i;].
				qq[echo -n \$i; awk '/Failed to create snapshot/ {FAIL++; FAILMR=\$1} END {print ":FAIL:" FAIL " FAILMR:" FAILMR}' \$i;].
				qq[done]);
    }

    # Analyze information for issues.
    my %errors = ();
    foreach my $lnn (sort {$a <=> $b} keys %NODE_INFO) {
	next unless $gather->{$lnn};
	foreach my $line (@{$gather->{$lnn}}) {
	    $errors{$lnn}{'EINVAL'}{'Count'} = $1 if ($line =~ /EINVAL:(\d+)/);
	    $errors{$lnn}{'EINVAL'}{'MR'} = $1 if ($line =~ /EINVALMR:(\S+)/);
	    $errors{$lnn}{'EIO'}{'Count'} = $1 if ($line =~ /EIO:(\d+)/);
	    $errors{$lnn}{'EIO'}{'MR'} = $1 if ($line =~ /EIOMR:(\S+)/);
	    $errors{$lnn}{'EDEADLK'}{'Count'} = $1 if ($line =~ /EDEADLK:(\d+)/);
	    $errors{$lnn}{'EDEADLK'}{'MR'} = $1 if ($line =~ /EDEADLKMR:(\S+)/);
	    $errors{$lnn}{'FAIL'}{'Count'} = $1 if ($line =~ /FAIL:(\d+)/);
	    $errors{$lnn}{'FAIL'}{'MR'} = $1 if ($line =~ /FAILMR:(\S+)/);
	}
    }
    foreach my $lnn (keys %errors) {
	foreach my $type (keys %{$errors{$lnn}}) {
	    warning("Node $lnn: Found $errors{$lnn}{$type}{'Count'} $type errors, the most recent was $errors{$lnn}{$type}{'MR'}");
	}
    }
} # End checkSnapshotLog
$CHECKS{'checkStoragePools'} = {
    'Description' => 'Check storage pools for health/capacity/unprovisioned drive issues',
    'Reference' => 'https://dl.dell.com/content/docu48119',
};
sub checkStoragePools {
    if (wasCalled()) { return }

    # Setup
    printCheck('Storage Pools');
    gatherIsiStatPools();
    gatherNeighborhoods();
    my $WARN_PERCENT = 90;
    my $FAIL_PERCENT = 95;
    my $SPF_UNPROV = 'There are unprovisioned drives: %s';
    my $SPF_UNHEALTHY = 'The following pools are unhealthy: %s';
    my $SPF_MISSING = 'The following pools are missing drives: %s';
    my $SPF_HDD_FAIL = 'The following pools have HDD usage over 95%: %s';
    my $SPF_HDD_WARN = 'The following pools have HDD usage over 90%: %s';
    my $SPF_SSD_FAIL = 'The following pools have SSD usage over 95%: %s';
    my $SPF_SSD_WARN = 'The following pools have SSD usage over 90%: %s';
    my $SPF_USED_FAIL = 'The following pools have usage over 95%: %s';
    my $SPF_USED_WARN = 'The following pools have usage over 90%: %s';

    # Analyze
    my @fail_unhealthy_pools = ();
    my @missing_drives = ();
    my @fail_hdd_used_pools = ();
    my @warn_hdd_used_pools = ();
    my @fail_ssd_used_pools = ();
    my @warn_ssd_used_pools = ();
    my @fail_used_pools = ();
    my @warn_used_pools = ();
    $CLUSTER_INFO{'StoragePools_Count'} = scalar keys %{$CLUSTER_INFO{'Pools'}};
    
    foreach my $name (keys %{$CLUSTER_INFO{'Pools'}}) {

	if ($pool{'Health'}) {
	    if ($pool{'Health'} =~ /S|D|R|U/) {
		push(@fail_unhealthy_pools, $name);
	    }
	    if ($pool{'Health'} =~ /M/) {
		push(@missing_drives, $name);
	    }
	}
	if ($pool{'Percent HDD Used'}) {
	    if ($pool{'Percent HDD Used'} >= 95) {
		push(@fail_hdd_used_pools, $name);
	    } elsif ($pool{'Percent HDD Used'} >= 90) {
		push(@warn_hdd_used_pools, $name);
	    }
	} elsif ($pool{'Percent Used'}) {
	    if ($pool{'Percent Used'} >= 95) {
		push(@fail_used_pools, $name);
	    } elsif ($pool{'Percent Used'} >= 90) {
		push(@warn_used_pools, $name);
	    }
	}
	if ($pool{'Percent SSD Used'}) {
	    if ($pool{'Percent SSD Used'} >= 95) {
		push(@fail_ssd_used_pools, $name);
	    } elsif ($pool{'Percent SSD Used'} >= 90) {
		push(@warn_ssd_used_pools, $name);
	    }
	}
    }

    # Flag
    if ($CLUSTER_INFO{'Unprovisioned drives'}) {
	warning(sprintf($SPF_UNPROV, $CLUSTER_INFO{'Unprovisioned drives'}));
    }
    if (@fail_unhealthy_pools) {
	fail(sprintf($SPF_UNHEALTHY, join(', ', @fail_unhealthy_pools)));
    }
    if (@missing_drives) {
	warning(sprintf($SPF_MISSING, join(', ', @missing_drives)));
    }
    if (@fail_hdd_used_pools) {
	critical(sprintf($SPF_HDD_FAIL, join(', ', @fail_hdd_used_pools)));
    }
    if (@warn_hdd_used_pools) {
	warning(sprintf($SPF_HDD_WARN, join(', ', @warn_hdd_used_pools)));
    }
    if (@fail_ssd_used_pools) {
	critical(sprintf($SPF_SSD_FAIL, join(', ', @fail_ssd_used_pools)));
    }
    if (@warn_ssd_used_pools) {
	warning(sprintf($SPF_SSD_WARN, join(', ', @warn_ssd_used_pools)));
    }
    if (@fail_used_pools) {
	critical(sprintf($SPF_USED_FAIL, join(', ', @fail_used_pools)));
    }
    if (@warn_used_pools) {
	warning(sprintf($SPF_USED_WARN, join(', ', @warn_used_pools)));
    }

    # Output

    printResults();

} # End checkStoragePools
$CHECKS{'checkSupportAssist'} = {
    'Description' => 'Checks if ESE user has provider of lsa-file-provider, if not it fails and requires a change prior to performing an upgrade / RUP installation',
    'Reference' => '',
};
sub checkSupportAssist {
    if (wasCalled()) { return }
    if(!isLive()) { return }

    # Setup
    my $CMD_LIVE;
    if(isCurrentOneFS('<9.9')){
       $CMD_LIVE = qq[2>&1 ${SUDO} isi auth user view ese];
    }
    else{
       $CMD_LIVE = qq[2>&1 isi auth user view ese];
    } 

    my $gather;
    if(isLive()){
        $gather = gatherLocal($CMD_LIVE);
    }

    printCheck('Remote Assist');

    # Analyze
    my $SA_Enabled = 1 if isServiceEnabled('isi_supportassist');
    my $SA_user_conf = 0;
    my $SA_provider = 0;
    my $SA_HD = 0;
    my $SA_SH = 0;
    foreach $line (@{$gather}){
        if($line =~ /^\s*Provider:\s*(.*):.*$/){
            debug("found Provider for ESE: $1"); 
            if($1 =~ /.*lsa-local-provider.*/){
                $SA_provider = 1;
            }
        }
        if($line =~ /^\s*Home Directory:\s*(.*):.*$/){
            debug("found Provider for ESE: $1"); 
            if($1 =~ /.*lsa-local-provider.*/){
                $SA_HD = 1;
            }
        }
        if($line =~ /^\s*Shell:\s*(.*):.*$/){
            debug("found Provider for ESE: $1"); 
            if($1 =~ /.*lsa-local-provider.*/){
                $SA_SH = 1;
            }
        }
    }    
    
    if($SA_provider and ($SA_HD or $SA_SH)){ $SA_user_conf = 1; }

    # Flag
    if($SA_user_conf and isCurrentOneFS('>=9.5.0.0') and isCurrentOneFS('<9.5.0.8') and isDestinationOneFS('>=9.5.0.8')){    
        fail("Checks have identified that the ESE user has a provider other than file-provider configured which will cause a RUP or bundle install to fail.");
        critical("Please use the following command to update the ESE user PRIOR to a RUP install / Bundle upgrade: echo '{ \"shell\": \"/usr/local/bin/bash\",\"home_directory\": \"/usr/local/ese\" }' | isi_papi_tool PUT \"/16/auth/users/USER:ese?force=true\"");
        info("Please reference KB 000225333 (https://www.dell.com/support/kbdoc/en-us/000225333) for more information.");
    }
  
    # Output
    printResults();
} # End checkSupportAssist
$CHECKS{'checkSystemFlag'} = {
    'Description' => 'checks for diskpools with system flag set',
    'Exclude' => 1,
};
sub checkSystemFlag {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} disi -I diskpools ls -v | tail -n +3 | head -n8 | awk '{print \$1"\\t"\$3"\\t"\$5}'];
    my $CMD_LOGS = q[2>&1 cat local/isi_diskpool | grep -v '^\\s' | tail -n +3 | head -n -8 | awk '{print $1"\\t"$3"\\t"$5}'];

    # Start
    printCheck('DP System Flag');

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my @system_flg_dp = ();
    my $diskpoolname;
    foreach my $line (@$gather) {
	my $dp;
    my $setting_val;
    debug("checking $line");
    if($line =~ /^.*G\s*((?:S|H|D|T|M|E|I|R|G|\-|\*)+)\s*.*$/){
        debug("first match");
        if($line =~ /^\s*(\S*)\s*G\s*((?:S|H|D|T|M|E|I|R|G|\-|\*)+)\s*$/){
            debug("second match");
        $dp = $1;
        $setting_val = $2;
        $diskpoolname = $dp;
        debug("Diskpool result is $diskpoolname Setting result is $setting_val");
        if($dp !~ /^(\S+)-$/){
            debug("Checking $dp");
            if($setting_val =~/^S.*$/){
                debug("Adding $dp to list - SYSTEM flag is set in $setting_val");
                push(@system_flg_dp, $dp);
            }
        }
    }
    }
    if($line =~ /^(\S+:\d+)$/){
        debug("diskpool was $diskpoolname");
        $diskpoolname.=$1;
        debug("appended diskpool name is $diskpoolname");
        push(@system_flg_dp, $diskpoolname) if $setting_val;
        $diskpoolname = '';
        $setting_val = '';
    }
}
 info("The following diskpools have system flag set: ".join(",", @system_flg_dp)) if @system_flg_dp;

    # Output
    printResults();
} # End checkSystemFlag
$CHECKS{'checkTardis'} = {
    'Description' => 'Checks size of reports.db and flags if over 100MB which may lead to issues outlined on KB 000066019',
    'Exclude' => ,
};
sub checkTardis {
    if (wasCalled()) { return }
    printCheck('Tardis');

    # Setup
    my $gd_epoch = '@'.$RPS{'Gather Date'} if !isLive();
    my $CMD_LIVE_FS = qq[2>&1 ls -al /ifs/.ifsvar/modules/tardis/namespaces | grep sqlite | awk '{print $5" "$9}'];

    my $CMD_LOGS_FS = q[2>&1 ls -al local/ifsvar_modules_tardis.tar/modules/tardis/namespaces/ | grep sqlite | awk '{print $5" "$9}'];

    # Gather
    my $gather_FS = gatherLocal(isLive()?$CMD_LIVE_FS:$CMD_LOGS_FS);
    
    my $tdb;
    my $current_size_bytes;
    my $byte_size = 1048576;
    my $max_size = 10737418240;
    my @large_tdb = ();
    # Analyze
    if(!$gather_FS || $gather_FS eq ''){
        #missing info
        debug("MISSING Tardis DB size output, Please verify Tardis DB size manually");
        missing(compressRange(\@LNNS));
    }
    else{
        verboseHeader('Tardis DB Size');
        verbose(sprintf('%-45s %s', 'Name', 'Size'));
        verbose(sprintf('%-45s %s', '-'x40, '-'x10));
        foreach my $line (@$gather_FS) {
            debug("CHECKING $line");
            if($line =~ /^\s*(\d+)\s*(\S+)$/){
                $tdb = $2;
                $current_size_bytes = int($1);
                $CLUSTER_INFO{'Tardis'}{$tdb} = $current_size_bytes;
                if($current_size_bytes >= $max_size){
                        debug("FAIL: Tardis DB at or exceeding 10GB.");
                        push(@large_tdb, $tdb);
                };
                if ($current_size_bytes >= 1024**3) {
                    $value = int(($current_size_bytes / (1024**3)) * 10 + 0.5) / 10;
                    $unit  = 'GB';
                }
                elsif ($current_size_bytes >= 1024**2) {
                    $value = int(($current_size_bytes / (1024**2)) * 10 + 0.5) / 10;
                    $unit  = 'MB';
                }
                else {
                    $value = int(($current_size_bytes / 1024) * 10 + 0.5) / 10;
                    $unit  = 'KB';
                }
                $size_cmbd = $value.$unit;
                debug("Current Size: $size_cmbd for $tdb");
                verbose(sprintf('%-45s %s', $tdb, $size_cmbd));
            }
        }
    }

    if(@large_tdb and ($args{'OneFS'} or $args{'Node Firmware'})){
        fail("Tardis Database(s) found that are at or exceeding 10GB in size, the following DB's were found: ".join(", ",@large_tdb));
        info("To view a full list of tardis databases and associated sizes, please rerun IOCA using -r checkTardis -e");
        info("Please contact Technical Support to remediate the large databases prior to upgrading");
    }
    # Output
    printResults();
} # End checkTardis
###
# checkTimeDrift: Determines if the times on nodes are out of sync
$CHECKS{'checkTimeDrift'} = {
    'Description' => "Checks time drift between nodes",
    'Exclude' => 1,
};
sub checkTimeDrift {
    return if wasCalled();
    return unless isLive();
    printCheck("Cluster Time Drift");
    gatherUptime();

    # Compare times between nodes, maintain a lowest and highest value
    my $min_time;
    my $max_time;
    foreach my $lnn (@LNNS) {
	next unless $NODE_INFO{$lnn}{'time'};
	my $time = 0;
	$time += 12 * 60 if $NODE_INFO{$lnn}{'time'} =~ /PM/;
	$time += 12 * $1 + $2 if $NODE_INFO{$lnn}{'time'} =~ /^(\d+):(\d+)/;
	if (not defined $min_time) {
	    $min_time = $max_time = $time;
	}
	if ($time < $min_time) {
	    $min_time = $time;
	}
	if ($time > $max_time) {
	    $max_time = $time;
	}
    }

    # Flag issues with time drift
    my $time_drift;
    my $results = "Unknown";
    if (not defined $min_time or not defined $max_time) {
	fail("Unable to determine time drift");
    } else {
	$time_drift = $max_time - $min_time;
	$results = "$time_drift Minutes";
	if ($time_drift >= 4) {
	    fail("Time drift between nodes is greater than 5 minutes");
	} elsif ($time_drift >= 3) {
	    warning("Time drift between nodes is between 3 and 5 minutes");
	}

    }

    # Output Check result
    printResults($time_drift);
} # End checkTimeDrift
###
# checkTimeSync: Checks if the cluster is enabled to sync to an external server
$CHECKS{'checkTimeSync'} = {
    'Description' => 'Checks if the cluster is enabled to sync to an external server',
    'Exclude' => 1,
};
sub checkTimeSync {
    return if wasCalled();
    printCheck("Cluster Time Sync");
    gatherSMBTime();
    gatherNTPConfig();
    gatherNTPdXML();

    # Warn if no external time sync is found
    if (!$CLUSTER_INFO{'NTP Servers'} and !$CLUSTER_INFO{'SMBTime'}) {
	warning("This cluster is not configured to synchronize time externally.");
    }

    # Output Check result
    printResults();
} # End checkTimeSync
###
# checkTimeZone: Checks for missing time zone in target OneFS code level.
$CHECKS{'checkTimeZone'} = {
    'Description' => 'Checks for missing time zone in target OneFS code level',
    'Exclude' => 1,
};
sub checkTimeZone {
    if (wasCalled()) { return }

    printCheck("Cluster Time Zone");
    gatherArrayXML();

    # No target OneFS version, not impacted.
    if (!$RPS{'Target'}) {
	debug("checkTimeZone: No target OneFS version, not impacted.");
    }
    # Target OneFS version not a version known as impacted
    elsif ($RPS{'Target'} !~ /^8\.0\.0\.[012345]$|^8\.0\.1\.[01]$|^8\.1\.0\.0$/) {
	debug("checkTimeZone: Target OneFS version not a version known as impacted");
    }
    # Time zone was not gathered
    elsif (!$CLUSTER_INFO{'Timezone'}) {
	fail("Unable to determine time zone, manually verify");
    }
    # Time zones missing in OneFS 8.0.0.1, OneFS 8.0.0.2, OneFS 8.0.0.3, OneFS 8.0.0.4, OneFS 8.0.0.5, OneFS 8.0.1.0, OneFS 8.0.1.1, and OneFS 8.1.0.0
    elsif ($RPS{'Target'} =~ /^8\.0\.0\.[12345]$|^8\.0\.1\.[01]$|^8\.1\.0\.0$/) {
	my @Timezone = ("Brazil/East","Brazil/DeNoronha","Brazil/West","Brazil/Acre","Pacific/Samoa","PRC","Mexico/General","Mexico/BajaSur","Mexico/BajaNorte","Singapore","Canada/Yukon","Canada/Pacific","Canada/Central","Canada/Eastern","Canada/Mountain","Canada/Atlantic","Canada/Newfoundland","Canada/East-Saskatchewan","Canada/Saskatchewan","America/Santa_Isabel","America/Ensenada","Chile/Continental","Chile/EasterIsland","GMT0","US/Arizona","US/Pacific","US/Central","US/Alaska","US/Eastern","US/Samoa","US/East-Indiana","US/Mountain","US/Aleutian","US/Indiana-Starke","US/Michigan","US/Hawaii","Antarctica/South_Pole","Asia/Rangoon");
	if (grep {$_ eq $CLUSTER_INFO{'Timezone'}} @Timezone) {
	    fail("$CLUSTER_INFO{'Timezone'} timezone found on this cluster, an upgrade to OneFS 8.0.0.[012345], 8.0.1.[01], or 8.1.0.0 should not be performed.");
	}
    }
    # Time zones missing in OneFS 8.0.0.0
    elsif ($RPS{'Target'} =~ /^8\.0\.0\.0$/) {
	my @Timezone = ("Europe/Tiraspol","Europe/Belfast","Brazil/East","Brazil/DeNoronha","Brazil/West","Brazil/Acre","W-SU","GB-Eire","Pacific/Ponape","Pacific/Samoa","Pacific/Truk","Pacific/Yap","Israel","Portugal","ROC","Universal","PRC","Kwajalein","Iceland","ROK","Mexico/General","Mexico/BajaSur","Mexico/BajaNorte","Egypt","Navajo","Poland","Jamaica","Singapore","Africa/Timbuktu","Africa/Asmera","Australia/Canberra","Australia/South","Australia/NSW","Australia/North","Australia/Tasmania","Australia/West","Australia/Yancowinna","Australia/Victoria","Australia/ACT","Australia/LHI","Australia/Queensland","Libya","Canada/Yukon","Canada/Pacific","Canada/Central","Canada/Eastern","Canada/Mountain","Canada/Atlantic","Canada/Newfoundland","Canada/East-Saskatchewan","Canada/Saskatchewan","NZ","Japan","Zulu","Turkey","GMT+0","America/Jujuy","America/Buenos_Aires","America/Montreal","America/Rosario","America/Virgin","America/Cordoba","America/Mendoza","America/Knox_IN","America/Indianapolis","America/Shiprock","America/Catamarca","America/Argentina/ComodRivadavia","America/Atka","America/Coral_Harbour","America/Fort_Wayne","America/Ensenada","America/Porto_Acre","America/Louisville","Chile/Continental","Chile/EasterIsland","GMT0","UCT","Cuba","Hongkong","Atlantic/Faeroe","Atlantic/Jan_Mayen","GMT","Eire","US/Arizona","US/Pacific","US/Central","US/Alaska","US/Eastern","US/Samoa","US/East-Indiana","US/Mountain","US/Aleutian","US/Indiana-Starke","US/Michigan","US/Hawaii","Iran","Greenwich","NZ-CHAT","Antarctica/South_Pole","Asia/Katmandu","Asia/Kashgar","Asia/Calcutta","Asia/Thimbu","Asia/Ashkhabad","Asia/Dacca","Asia/Saigon","Asia/Tel_Aviv","Asia/Chungking","Asia/Ulan_Bator","Asia/Ujung_Pandang","Asia/Harbin","Asia/Macao","Asia/Chongqing","GMT-0","GB");
	if (grep {$_ eq $CLUSTER_INFO{'Timezone'}} @Timezone) {
	    fail("$CLUSTER_INFO{'Timezone'} timezone found on this cluster, an upgrade to OneFS 8.0.0.0 should not be performed.");
	}
    }

    # Output Check result
    info(sprintf($MSG{'KB_INT_1'}, '502177')) if isResults(&WARN);
    printResults($CLUSTER_INFO{'Timezone'} or "Unknown");
} # End checkTimeZone
$CHECKS{'checkUIDGID'} = {
    'Description' => 'Checks for UID/GID values greater than 262143 for files located in / and /var',
    'Exclude' => 1,
};
sub checkUIDGID {
    if (wasCalled()) { return }
    return unless $RPS{'Target'};
    return unless isLive();
    return if isCompliance();
    if (isCurrentOneFS('>=8.2')) { return }
    printCheck("uid_gid_check");

    # Gather information from cluster
    my $gather = gatherCluster
	(qq[2>&1 ${SUDO} isi_for_array "].
	 q[ find -x / /var -print0 ].
	 q[ | xargs -0 ls -lnd ].
	 q[ | awk '{if (\$3 > 262143 || \$4 > 262143) print}'" ]);

    # Gather a list of files to preserve during upgrades, used for $root_include
    my $preserves = gatherLocal(q[2>&1 awk -F\" '/file name/ {print $2}' /etc/mcp/sys/user_preserve_files.xml |sed -e 's/^\///']);

    # Versions where the uid_gid_check was introduced may incorrectly
    # flag some files not touched during the upgrade.
    my $include_false_positive = 0;
    if ($RPS{'Target'} =~ /^8\.0\.0\.7$|^8\.1\.0\.[34]$|^8\.1\.1\.[01]$|^8\.1\.[23]\.0$/) {
	$include_false_positive = 1;
    }

    # Regex used to identify files
    my $var_exclude = q[^/var/(run|crash|db/versions|db/samba|db/isilon-pkg|db/pkg|apache2/run|lib/likewise/run)];
    my $root_include = q[^/(].join("|", @$preserves).q[)];

    # Analyze information for issues.
    my %uid_gid_issues = ();
    foreach my $lnn (@LNNS) {
	next unless $gather->{$lnn};
	foreach my $line (@{$gather->{$lnn}}) {
	    next unless $line =~ qr[^\s*\S+\s+\S+\s+(\d+)\s+(\d+)[^/]+(/.*)$];
	    my ($uid, $gid, $path) = ($1, $2, $3);
	    if ($path =~ qr[^/var] and
		$path !~ qr[$var_exclude]) {
		$uid_gid_issues{$lnn}{$path} = 1;
	    } elsif ($path =~ qr[$root_include]) {
		$uid_gid_issues{$lnn}{$path} = 1;
	    } elsif ($include_false_positive and ($uid > 2097151 or $gid > 2097151)) {
		$uid_gid_issues{$lnn}{$path} = 1;
	    }
	}
    }

    if (%uid_gid_issues) {
	fail("Files were identified as having a user/group id greater than 262143 and may cause an error in the OneFS upgrade.");
	fail("To review the list of files identified, re-run with the additional arguments: -e --run=checkUIDGID $RPS{'Target'}");
	fail(sprintf($MSG{'KB_INT_1'}, '527935'));

	my $sprintf = "%5s %s";
	verbose(sprintf($sprintf, "LNN", "Path"));
	verbose(sprintf($sprintf, "-----", "--------------------"));
	foreach my $lnn (@LNNS) {
	    next unless $uid_gid_issues{$lnn};
	    foreach my $path (keys %{$uid_gid_issues{$lnn}}) {
		verbose(sprintf($sprintf, $lnn, $path));
	    }
	}
    }

    # Output Check result
    printResults();
} # End checkUIDGID
$CHECKS{'checkUpgrade'} = {
    'Description' => 'Checks for in progress upgrade related issues. Warns if the isi_upgrade_d service is enabled. Fails if not in a committed state. Fails if an upgrade activity is already in progress.Checks the fs_fmt_version, an odd or zero fs_fmt_version is problematic',
    'Reference' => 'https://www.dell.com/support/kbdoc/000033892',
};
$MSG{'checkUpgrade_STATE_1'} = 'Cluster Upgrade State is %s';
$MSG{'checkUpgrade_ACT_1'} = 'Current Upgrade Activity is %s';
$MSG{'checkUpgrade_TSG8'} = 'See also the Isilon Customer Troubleshooting Guide for upgrades from OneFS 8 and later: https://dl.dell.com/content/docu83028';
$MSG{'checkUpgrade_FSFMT'} = 'An odd fs_fmt_version was identified which indicates that the Upgrade job must succeed before any further OneFS upgrade may be performed';
$MSG{'checkUpgrade_ZERO_FSFMT_1'} = 'The following nodes are reporting a fs_fmt_version of 0: %s';
$MSG{'checkUpgrade_NODE_STATE_2'} = 'The following nodes are reporting an upgrade status of "%s": %s';
sub checkUpgrade {
    if (wasCalled()) { return }
    printCheck('Upgrade Status');
    gatherUpgradeCluster();
    if ($CLUSTER_INFO{'Cluster Upgrade State'} and
	$CLUSTER_INFO{'Cluster Upgrade State'} ne 'committed') {
	fail(sprintf($MSG{'checkUpgrade_STATE_1'},
		     $CLUSTER_INFO{'Cluster Upgrade State'}));
    }
    if ($CLUSTER_INFO{'Current Upgrade Activity'} and
	$CLUSTER_INFO{'Current Upgrade Activity'} ne '-' and
	$CLUSTER_INFO{'Current Upgrade Activity'} !~ /No activity/i) {
	fail(sprintf($MSG{'checkUpgrade_ACT_1'},
		     $CLUSTER_INFO{'Current Upgrade Activity'}));
    }
    if ($CLUSTER_INFO{'Node Upgrade States'}) {
	foreach my $state (keys %{$CLUSTER_INFO{'Node Upgrade States'}}) {
	    if ($state eq 'committed') { next }
	    fail(sprintf($MSG{'checkUpgrade_NODE_STATE_2'},
			 $state, compressRange(@{$CLUSTER_INFO{'Node Upgrade States'}{$state}})));
	}
    }
    checkFileSystemFormatVersion();
    #if (isResults(&WARN)) {
	#info(sprintf($MSG{'KB_1'}, '000033892'));
	#if (isCurrentOneFS('>=8')) {
	#    info($MSG{'checkUpgrade_TSG8'});
	#}
    #}
    printResults();
} # End checkUpgrade

sub checkFileSystemFormatVersion {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array sysctl efs.gmp.fs_fmt_version];
    my $CMD_LOGS = q[2>&1 grep -H fs_fmt_version */efs.gmp.current_info];
    my $RE_LOGS = qr/^\s{0,8}fs_fmt_version\s*=\s*(\d+)$/;
    my $RE_LIVE = qr/fs_fmt_version:\s*(\d+)$/;

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my @zero_fs_fmt_version = ();
    my $fs_fmt_version_odd;
    verboseHeader('File System Format Versions');
    verbose(sprintf('%5s %5s', 'LNN', 'fs_fmt_version'));
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { next }
	foreach my $line (@{$gather->{$lnn}}) {
	    if ($line =~ $RE_LIVE or $line =~ $RE_LOGS) {
		verbose(sprintf('%5s %5s', $lnn, $1));
		if ($1 eq '0') { push(@zero_fs_fmt_version, $lnn) }
		if ($1 % 2 == 1) { $fs_fmt_version_odd = 1 }
	    }
	}
    }

    # Setup Upgrade job check
    my $CMD_LIVE_gcfj;
    if(isCurrentOneFS('>=9.12') and isCompliance()){
        $CMD_LIVE_gcfj = qq[2>&1 /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig -t job-status |egrep -A2 "failed.+type.+ = 16" | grep -v version];
    }
    else{
        $CMD_LIVE_gcfj = qq[2>&1 isi_gconfig -t job-status |egrep -A2 "failed.+type.+ = 16" | grep -v version];
    }
    my $CMD_LOGS_gcfj = q[2>&1 egrep -A 2 "failed.+type.+ = \"16\""  local/ifsvar_modules_tar/modules/jobengine/status.gc | grep -v version];
    my $gather_gcfj = gatherLocal(isLive()?$CMD_LIVE_gcfj:$CMD_LOGS_gcfj);

    # parse gconfig info for failed upg job (if one exists)
    my %gcfj = ();
    #my $intval = 0;
    #my $id = 1;
    foreach my $line (@$gather_gcfj) {
    if ($line !~ /^\s*failed\.\d+\.(?:type|id).*$/) {
	    next;
	}
    else{
        #$intval++;
        if($line =~ /^\s*failed\.\d+\.(\S+)\s*\S+\s*\=\s*\"?(\d+)\"?$/){
            $gcfj{$1} = $2;
            #$id++ if $intval == 2;
        }   
        #$id++ if $intval == 2;
        #$intval = 0 if $intval == 2;
    }

    }

    # use above job ID to check the job reports db
    my $CMD_LIVE_rptdb = qq[2>&1 for f in /ifs/.ifsvar/modules/jobengine/reports* ; do sqlite3 $f -header -list -nullvalue ' ' "select job_id,job_type,progress,create_time,update_time,start_time,end_time,status,current_phase from jobs where job_type=16 and job_id=].$gcfj{'id'}.qq[ order by job_id DESC"; done;];
    my $CMD_LOGS_rptdb = q[2>&1; for f in local/ifsvar_modules[_.]tar/modules/jobengine/reports* ; do sqlite3 $f -header -list -nullvalue ' ' "select job_id,job_type,progress,create_time,update_time,start_time,end_time,status,current_phase from jobs where job_type=16 and job_id=].$gcfj{'id'}.q[ order by job_id DESC"; done;];

    my @header = ();
    my $failed_job_found = 0;
    my $gather_rptdb = gatherLocal(isLive()?$CMD_LIVE_rptdb:$CMD_LOGS_rptdb) if $gcfj{'id'};
    foreach my $line (@$gather_rptdb) {
    if ($line !~ /^\s*(?:job_id\||\d+\|).*$/) {
	    next;
	}
    else{
        $failed_job_found = 1;
    }
    }

    # Flag if failed upgrade job is found in reports DB and gconfig
    if($failed_job_found and $gcfj{'id'}){
        if(isOneFSUpgrade()){
        fail("A failed upgrade job record (Job ID: $gcfj{'id'}) has been found in gconfig and job reports DB, please open a technical support SR referencing this error for assistance.");
        #info("Please reference KB () for further information.");
        }
        else{
        warning("A failed upgrade job record (Job ID: $gcfj{'id'}) has been found in gconfig and job reports DB, please open a technical support SR referencing this error for assistance.");    
        }
    }

    # Flag if fs_fmt_version is zero on any nodes
    if (@zero_fs_fmt_version) {
	fail(sprintf($MSG{'checkUpgrade_ZERO_FSFMT_1'},
		     compressRange(@zero_fs_fmt_version)));
    info(sprintf($MSG{'KB_1'}, '000033892'));
	if (isCurrentOneFS('>=8')) {
	    info($MSG{'checkUpgrade_TSG8'});
	}
    }

    # Flag if fs_fmt_version is odd on any nodes
    if ($fs_fmt_version_odd) {
	if (isOneFSUpgrade()) {
	    fail($MSG{'checkUpgrade_FSFMT'});
	} else {
	    warning($MSG{'checkUpgrade_FSFMT'});
        
	}
    info(sprintf($MSG{'KB_1'}, '000033892'));
	if (isCurrentOneFS('>=8')) {
	    info($MSG{'checkUpgrade_TSG8'});
	}
    }
} # End checkFileSystemFormatVersion
$CHECKS{'checkUpgradeAgentPort'} = {
    'Description' => 'Checks the port used by the isi_upgrade_agent_d daemon to ensure it is not in use by other processes',
    'Reference' => 'https://www.dell.com/support/kbdoc/000160126',
};
$MSG{'checkUpgradeAgentPort_PROCESS_2'} = 'A process other than the isi_upgrade_agent_d is on port %s on nodes: %s';
sub checkUpgradeAgentPort {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) {	return unsupported('UNSUP_CUR') }
    printCheck('Upgrade Agent Port');

    # Setup
    my $CMD_LIVE = qq[ 2>&1 ${SUDO} isi_for_array 'sockstat -P udp -p 15100' ];
    my $CMD_LOGS = q[ 2>&1 grep -H -E '^USER|:15100' */sockstat ];
    my $PORT = 15100;
    my $NUM_FIELDS = 7;
    my $RE_HEADER = qr/^USER/;
    my $RE_PORT = qr/:$PORT$/;
    my $RE_PROTO = qr/udp/;
    my $RE_COMMAND = qr/isi_upgrad/;
    my $FIELD_COMMAND = 1;
    my $FIELD_PID = 2;
    my $FIELD_PROTO = 4;
    my $FIELD_PORT = 5;

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my %nodes = ();
    foreach my $lnn (@LNNS) {
	verboseHeader($NODE_INFO{$lnn}{'Name'});
	if (!$gather->{$lnn}) {
	    missing($lnn);
	    next;
	}
	foreach my $line (@{$gather->{$lnn}}) {
	    $line =~ s/^\s*//;

	    # Header line
	    if ($line =~ $RE_HEADER) {
		verbose($line);
		next;
	    }

	    # Split the line into fields
	    my @fields = split(/\s+/, $line);

	    # Skip if there are less than 7 fields
	    if (scalar(@fields) < $NUM_FIELDS) { next }

	    # Skip unless the PROTO field is udp
	    if ($fields[$FIELD_PROTO] !~ $RE_PROTO) { next }

	    # Skip unless the LOCAL ADDRESS field ends with the port
	    if ($fields[$FIELD_PORT] !~ $RE_PORT) { next }

	    # Add this line to the verbose output
	    verbose($line);

	    # Skip if the COMMAND is isi_upgrad (short for isi_upgrade_agent_d)
	    if ($fields[$FIELD_COMMAND] =~ $RE_COMMAND) { next }

	    # Beyond this point there must be a process other than
	    # isi_upgrade_agent_d that is listening on the port

	    # Gather for each node the problematic PID and COMMAND
	    $nodes{$lnn}{$fields[$FIELD_PID]} = $fields[$FIELD_COMMAND];
	}
    }

    # Flag
    if (%nodes) {
	fail(sprintf($MSG{'checkUpgradeAgentPort_PROCESS_2'},
		     $PORT, compressRange(keys %nodes)));
	foreach my $lnn (keys %nodes) {
	    my @pidlist = map {"$nodes{$lnn}{$_} ($_)"} keys %{$nodes{$lnn}};
	    fail("Node $lnn: ".join(', ', @pidlist));
	}
	info(sprintf($MSG{'KB_1'}, '000160126'));
    }

    # Output
    printResults();
} # End checkUpgradeAgentPort
$CHECKS{'checkUptime'} = {
    'Description' => 'Checks node uptime, warning over 200 days, flags uptime ETAs',
    'Exclude' => 1,
};
sub checkUptime {
    if (wasCalled()) { return }
    printCheck("Node Uptime");
    gatherUptime();
    gatherETA202452();
    gatherETA209918();
    gatherETA491747();
    gatherDTA526050();

    # ETA 209918
    if ($RPS{'ETA'}{'209918'} and $RPS{'ETA'}{'209918'}{'Message'}) {
	info(@{$RPS{'ETA'}{'209918'}{'Message'}});
    }

    # ETA 202452
    if ($RPS{'ETA'}{'202452'} and $RPS{'ETA'}{'202452'}{'Message'}) {
	info(@{$RPS{'ETA'}{'202452'}{'Message'}});
    }

    # ETA 491747
    if ($RPS{'ETA'}{'491747'} and $RPS{'ETA'}{'491747'}{'Message'}) {
	info(@{$RPS{'ETA'}{'491747'}{'Message'}});
    }

    # DTA 526050
    if ($RPS{'ETA'}{'526050'} and $RPS{'ETA'}{'526050'}{'Message'}) {
	info(@{$RPS{'ETA'}{'526050'}{'Message'}});
    }

    my $highest_uptime = 0;
    foreach my $lnn (@LNNS) {
	verboseHeader($NODE_INFO{$lnn}{'Name'});
	verbose("Gathered uptime: $NODE_INFO{$lnn}{'uptime_gathered'}") if $NODE_INFO{$lnn}{'uptime_gathered'};
	next unless $NODE_INFO{$lnn}{'uptime'};
	my $uptime_seconds = $NODE_INFO{$lnn}{'uptime'};
	my @calculated_uptime = ();
	if ($uptime_seconds > 86400) {
	    my $days = int($uptime_seconds / 86400);
	    $uptime_seconds -= $days * 86400;
	    push(@calculated_uptime, "$days days")
	}
	if ($uptime_seconds > 3600) {
	    my $hours = int($uptime_seconds / 3600);
	    $uptime_seconds -= $hours * 3600;
	    push(@calculated_uptime, "$hours hours")
	}
	if ($uptime_seconds > 60) {
	    my $minutes = int($uptime_seconds / 60);
	    $uptime_seconds -= $minutes * 60;
	    push(@calculated_uptime, "$minutes minutes")
	}
	if ($uptime_seconds > 0) {
	    push(@calculated_uptime, "$uptime_seconds seconds")
	}
	verbose("Calculated uptime: ".join(", ", @calculated_uptime)) if @calculated_uptime;
	$highest_uptime = $NODE_INFO{$lnn}{'uptime'} if ($NODE_INFO{$lnn}{'uptime'} > $highest_uptime);
    }
    # Not used, but may come in handy
    debug("checkUptime: Predicted date for 248.5 day uptime bug: ".scalar(localtime($RPS{'Runtime'} + 21384000 - $highest_uptime)));

    # Warn about/provide information for node uptime and ETAs
    if ($highest_uptime > 42940800) {
	if ($RPS{'ETA'}{'202452'}) {
	    warning("ETA202452: Nodes over 497 days uptime.");
	    push(@{$RPS{'Proactive Reboot'}{'Priority'}}, "During any activity that requires node reboots it is possible that all remaining nodes in the cluster restart simultaneously, without warning, due to ETA 202452.");
	}
	if ($RPS{'ETA'}{'526050'}) {
	    warning("DTA526050: Nodes over 497 days uptime.");
	}
    } elsif ($highest_uptime > 41731200) {
	if ($RPS{'ETA'}{'202452'}) {
	    warning("ETA202452: Nodes approaching 497 days uptime.");
	}
	if ($RPS{'ETA'}{'526050'}) {
	    warning("DTA526050: Nodes approaching 497 days uptime.");
	}
    }
    if ($highest_uptime > 21470400 and $RPS{'ETA'}{'209918'}) {
	warning("ETA209918: Nodes over 248.5 days uptime.");
	push(@{$RPS{'Proactive Reboot'}{'Priority'}}, "During any activity that requires node reboots it is possible that all remaining nodes in the cluster restart simultaneously, without warning, due to ETA 209918.");
    } elsif ($highest_uptime > 20217600 and $RPS{'ETA'}{'209918'}) {
	warning("ETA209918: Nodes approaching 248.5 days uptime.");
    }
    if ($highest_uptime > 21470400 and $RPS{'ETA'}{'491747'}) {
	warning("ETA491747: Nodes over 248.5 days uptime.");
    } elsif ($highest_uptime > 20217600 and $RPS{'ETA'}{'491747'}) {
	warning("ETA491747: Nodes approaching 248.5 days uptime.");
    }

    # Proactive reboot only for versions of OneFS prior to 8.0
    if (isCurrentOneFS('<8') and $highest_uptime > 17280000) {
	push(@{$RPS{'Proactive Reboot'}{'Priority'}}, "A proactive reboot preceding all other activities is strongly recommended due to the high uptime of nodes.");
	warning("Highest node uptime over 200 days.") if !isResults(&WARN);
	@{$RPS{'Proactive Reboot'}{'Nodes'}} = keys %NODE_INFO;
    }

    info(sprintf($MSG{'KB_INT_1'}, '335097')) if isResults(&WARN);
    my $highest_uptime_days = int($highest_uptime / 86400);
    printResults("$highest_uptime_days days");
} # End checkUptime
$CHECKS{'checkVSFTP'} = {
    'Description' => 'Checks if VSFTP is enabled and configured and if it is, it will reference https://www.dell.com/support/kbdoc/000214872 as VSFTP impact may occur when upgrading to 9.1.0.29 or higher if writable chroot is enabled.',
    'Exclude' => ,
};
sub checkVSFTP {
    if (wasCalled()) { return };
    gatherServices();

    if(!isServiceEnabled('vsftpd')){ return };

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array egrep -H '^allow_writeable_chroot=' /etc/mcp/templates/vsftpd.conf];
    my $CMD_LOGS = q[2>&1 egrep -H '^allow_writeable_chroot=' */etc.tar/etc/mcp/templates/vsftpd.conf];

    # Start
    printCheck('Check VSFTP');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    my @nodes = ();

    # Analyze
    foreach my $lnn (@LNNS) {
        my $allow_writeable_chroot_present = 0;
        foreach my $line (@{$gather->{$lnn}}) {
            debug("checking: $line");
            if($line =~ /^\s*allow_writeable_chroot\s*=\s*(\S*)/){
                $allow_writeable_chroot_present = $1;
                my $val = lc($1);
                debug("allow_writeable_chroot value is $val");
                if($val eq 'yes' or $val eq '1'){
                    push(@nodes,$lnn);
                }
            }
        }
        debug("Node $lnn value for precense of allow_writeable_chroot = $allow_writeable_chroot_present");
        #if (!$allow_writeable_chroot_present) 
        #{ 
        #    debug("Adding node $lnn to list.");
        #    push(@nodes, $lnn); 
        #};
    }

    if(
        (isServiceEnabled('vsftpd') and isDestinationOneFS('>=9.5.1.0')) or 
        (@nodes and ((
                    isDestinationOneFS('<9.2') and isDestinationOneFS('>=9.1.0.29')
                ) or 
                (
                    isDestinationOneFS('<9.3') and isDestinationOneFS('>=9.2.1.23')
                ) or 
                (
                    isDestinationOneFS('<9.5') and isDestinationOneFS('>=9.4.0.14')
                ) or 
                (
                    isDestinationOneFS('>=9.5.0.4')
                )
            )
        )
    ){
        if(isDestinationOneFS('>=9.5.1.0') and @nodes){
            critical("VSFTP is enabled on this cluster, when upgrading to ".isDestinationOneFS().", writable chroot will be disabled by default and may cause impact if you are using this setting.");
            info("The setting can be re-enabled after the upgrade to ".isDestinationOneFS." but this setting does not persist through OneFS upgrades.");
            addCustomerInformation("VSFTP is enabled on this cluster, when upgrading to ".isDestinationOneFS().", writable chroot will be disabled by default and may cause impact if your are using this setting. The setting can be re-enabled after the upgrade to ".isDestinationOneFS." but this setting does not persist through OneFS upgrades.");
            addCustomerInformation("Further information on writable chroot changes/steps to re-enable can be found on KB000214872 (https://www.dell.com/support/kbdoc/000214872)");
        }
        else{
            warning("VSFTP is enabled on this cluster, when upgrading to ".isDestinationOneFS().", if writable chroot is enabled then this will cause impact unless 'allow_writeable_chroot' is configured.");
            addCustomerInformation("VSFTP is enabled on this cluster, when upgrading to ".isDestinationOneFS().", if writable chroot is enabled then this will cause impact unless 'allow_writeable_chroot' is configured.");
        }
        info("Please reference KB000214872 (https://www.dell.com/support/kbdoc/000214872) for more information.");
        
    }

    # Output
    printResults();
} # End checkVSFTP
$CHECKS{'checkVNODE'} = {
    'Description' => 'Checks VNODE consumption against the maximum configured VNODE\'s in OneFS 9.11',
    'Exclude' => ,
};
sub checkVNODE {
    if (wasCalled()) { return };
    if(isCurrentOneFS('<9.11') and isDestinationOneFS('<9.11')){ return };

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array "sysctl vfs.numvnodes; sysctl kern.maxvnodes"];
    my $CMD_LOGS = q[2>&1; for node in $(nodenames); do egrep -H 'maxvnodes|numvnodes' $node/varlog.tar/log/vmlog | tail -n2; done;];

    my $CMD_LIVE_CRON = qq[2>&1 ${SUDO} isi_for_array "grep -v '^#' /etc/mcp/override/crontab | grep 'isi_flush'"];
    my $CMD_LOGS_CRON = q[2>&1; grep -v '^#' */etc.tar/etc/mcp/override/crontab* | grep 'isi_flush'];

    # Start
    printCheck('Check VNODE');

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    my $gather_cron = gatherCluster(isLive()?$CMD_LIVE_CRON:$CMD_LOGS_CRON);
    
    my @nodes = ();
    my @nodes_wc = ();
    my @nodes_cm = ();
    my %vnode_info;
    
    # Analyze
    foreach my $lnn (@LNNS) {
        foreach my $line (@{$gather->{$lnn}}) {
            my $vnode_rec;
            my $vnode_val;
            if($line =~ /^\s*(?:vfs|kern).((?:num|max)vnodes):\s*(\d+)\s*$/){
                $vnode_rec = $1;
                $vnode_val = int($2);
                $vnode_info{$lnn}{$vnode_rec} = $vnode_val;
            }
        }
        push(@nodes_cm, $lnn) if !$gather_cron{$lnn};
        foreach my $line (@{$gather_cron->{$lnn}}) {
            if($line != ''){
                push(@nodes_wc, $lnn);
            }
            else{
                push(@nodes_cm, $lnn);
            }
        }
    }

    foreach my $vni (keys %vnode_info){
        if($vnode_info{$vni}{'numvnodes'} >= $vnode_info{$vni}{'maxvnodes'}){
            push(@nodes, $vni);
        }
    }

        # Already on codes, not upgrading (TC for FW only, or, post upg)
        if((isCurrentOneFS('>=9.11') and isCurrentOneFS('<9.11.0.3') and !isOneFSUpgrade() and !isDestinationOneFS())){
            if($CLUSTER_INFO{'Node Count'} != scalar(@nodes_wc)){
                if(@nodes){
                    critical("VNODE consumption on the following nodes are nearing or in excess of the configured maximum vnode count: ".compressRange(@nodes));                    
                }
                critical("No isi_flush CRON job detected for the following nodes: ".compressRange(@nodes_cm));
                info("Please ensure that the actions in the 'Resolution' section of the KB provided below are implemented as soon as possible.");
                info("Always seek permission from customer before running this KB.");
                info("Please reference KB000342005 (https://www.dell.com/support/kbdoc/000342005) for more information.");
            }
        }
        
        # Upgrading to affected codes - call out that the remediation needs to be applied POST upgrade
        if(isDestinationOneFS('>=9.11') and isDestinationOneFS('<9.11.0.3')){
            if($CLUSTER_INFO{'Node Count'} != scalar(@nodes_wc)){
                critical("Upgrading to ".isDestinationOneFS()." will require additional steps POST upgrade to avoid an issue outlined in KB 000342005.");
                info("Please ensure that the actions in the 'Resolution' section of the KB provided below are implemented after the upgrade to ".isDestinationOneFS().".");
                info("Always seek permission from customer before running this KB.");
                info("Please reference KB000342005 (https://www.dell.com/support/kbdoc/000342005) for more information.");
            }
        }

        # On fixed code, no need to apply CRON, check CRON and remove if not present ASAP
        if(isCurrentOneFS('>=9.11.0.3')){
            if(@nodes_wc){
                warning("The following nodes have CRON jobs configured for isi_flush: ".compressRange(@nodes_wc));
                info("If this was configured as part of the workaround for KB 000342005, the CRON job should now be removed as your current version (".isCurrentOneFS().") contains the fix for this issue.");
                info("Always seek permission from customer before running this KB.");
                info("Please reference KB000342005 (https://www.dell.com/support/kbdoc/000342005) for more information.");
            }
        }

    # Output
    printResults();
} # End checkVNODE
$CHECKS{'checkZoneLocalAuth'} = {
    'Description' => 'For upgrades to OneFS 8.2 and later, checks if local providers are associated with other access zones',
    'Exclude' => 1,
};
sub checkZoneLocalAuth {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8') or isCurrentOneFS('>=8.2')) {
	return unsupported('UNSUP_CUR');
    }
    if (!isDestinationOneFS('>=8.2')) {
	return unsupported('UNSUP_TGT_1', 'OneFS 8.2 and later releases');
    }

    # Setup
    my $NAME = 'Access Zone Local Authentication Providers';
    my $GREP = 'egrep "^ *(Name|Auth Providers):"';
    my $CMD_LIVE; 
	if(isCurrentOneFS('<9.9')){ 
		$CMD_LIVE= qq[${SUDO} isi zone zones list --verbose | $GREP];
	}
	else{
		$CMD_LIVE= qq[isi zone zones list --verbose | $GREP];
	}
    my $CMD_LOGS = qq[$GREP local/isi_auth | grep -B1000 "Auth Providers"];
    my $MSG_WARN = 'Warning: local providers (with the exception of System'.
	' local provider) may no longer be added to other access zones'.
	' (beyond the one that shares their name) after upgrade to OneFS'.
	' 8.2.0+.  Local providers from the list below that are currently'.
	' added to other access zones will continue to work; however, if they'.
	'  are removed, they will not be permitted to be re-added. Please use'.
	' only the System local provider to share local users between access'.
	' zones.';
    my $SPF_MISMATCH = 'Zone: %s, Providers: %s';
    printCheck($NAME);

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my $zone_name = '';
    my %mismatch = ();
    foreach my $line (@$gather) {
	if ($line =~ /^\s*Name:\s*(\S+)$/) { $zone_name = $1 }
	if (!$zone_name) { next }
	if ($line !~ /^\s*Auth Providers:\s*(\S.*)\s*$/) { next }
	my @providers = split(/, /, $1);
	foreach my $provider (@providers) {
	    my ($type, $zone) = split(/:/, $provider);
	    if ($zone eq 'System' or $zone eq $zone_name) { next }
	    if ($type !~ /local/) { next }
	    push(@{$mismatch{$zone_name}}, $provider);
	}
    }
    if (%mismatch) {
	addCustomerInformation($NAME, warning($MSG_WARN));
	foreach my $zone_name (keys %mismatch) {
	    my $msg = sprintf($SPF_MISMATCH, $zone_name,
			      join(', ', @{$mismatch{$zone_name}}));
	    addCustomerInformation($NAME, warning($msg));
	}
	info(sprintf($MSG{'KB_1'}, '000165335'));
    }
    printResults();
} # End checkZoneLocalAuth
$CHECKS{'checkZoneProviders'} = {
    'Description' => '',
    'Exclude' => 1,
};
sub checkZoneProviders {
    if (wasCalled()) { return }
	if(!isOneFSUpgrade()){ return }
	if(
		isDestinationOneFS('<9.10')
		or (isDestinationOneFS('=9.10') and isDestinationOneFS('>=9.10.1.7')) 
		or isDestinationOneFS('=9.11')
		or isDestinationOneFS('=9.12')
		or (isDestinationOneFS('=9.13') and isDestinationOneFS('>=9.13.1.1')) 
		or isDestinationOneFS('>=9.14.0.1')
		){ return }

    # Setup
    my $NAME = 'Access Zone Authentication Providers';
    my $GREP = 'egrep "^ *(Name|Auth Providers):"';
    my $CMD_LIVE; 
	if(isCurrentOneFS('<9.9')){ 
		$CMD_LIVE= qq[${SUDO} isi zone zones list --verbose | $GREP];
	}
	else{
		$CMD_LIVE= qq[isi zone zones list --verbose | $GREP];
	}
    my $CMD_LOGS = qq[$GREP local/isi_auth | grep -B1000 "Auth Providers"];
    my $MSG_Crit = 'Critical: Zone local providers (with the exception of System Zone'.
	') would temporarily need the \'System\' or identified providers removed to enable the OneFS upgrade to complete.';
    my $SPF_MISMATCH = 'Zone: %s, Providers: %s';
    printCheck($NAME);

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Analyze
    my $zone_name = '';
    my %mismatch = ();
    foreach my $line (@$gather) {
	if ($line =~ /^\s*Name:\s*(\S+)$/) { $zone_name = $1 }
	if (!$zone_name) { next }
	if ($line !~ /^\s*Auth Providers:\s*(\S.*)\s*$/) { next }
	my @providers = split(/, /, $1);
	foreach my $provider (@providers) {
	    my ($type, $zone) = split(/:/, $provider);
	    if ($zone_name eq 'System') { next }
	    if ($type !~ /local/) { next }
		#if ($zone =~ /System/) { push(@{$mismatch{$zone_name}}, $provider) }
		if (lc($zone) =~ /system/ or $zone ne $zone_name) { push(@{$mismatch{$zone_name}}, $provider) }
	}
    }
    if (%mismatch) {
	addCustomerInformation($NAME, fail($MSG_Crit));
	foreach my $zone_name (keys %mismatch) {
	    my $msg = sprintf($SPF_MISMATCH, $zone_name,
			      join(', ', @{$mismatch{$zone_name}}));
	    addCustomerInformation($NAME, fail($msg));
	}
	info(sprintf($MSG{'KB_1'}, '000447994'));
    }
    printResults();
} # End checkZoneProviders
###
# gatherArrayXML: Gathers information from /etc/ifs/array.xml
sub gatherArrayXML {
    if (wasCalled()) { return }

    # A bug may cause the array.xml files to be inconsistent across nodes
    # So, revert to gathering committed_version from each node
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array].
	q[ 'egrep "<(committed_version|fileserial)>" /etc/ifs/array.xml'];
    my $CMD_LOGS = q[2>&1 egrep -H "<(committed_version|fileserial)>"].
	q[ */etc.tar/etc/ifs/array.xml */etcifs.tar/ifs/array.xml];
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);
    if (!%$gather) { return }
    foreach my $lnn (keys %$gather) {
	if (!$gather->{$lnn}) { next }
	foreach (@{$gather->{$lnn}}) {
	    if (/<(committed_version|fileserial)>([^<]+)</) {
		$NODE_INFO{$lnn}{$1} = $2;
		debug("NODE_INFO->${lnn}->${1}=${2}");
	    }
	}
    }

    # Gather
    my $grep = '</?(name|encoding|timezone|interface|netmask|low|high|mtu|'.
	'device|array|guid|compliance|type|onefs_version)';
    $gather = [];
    if (isLive()) {
	$gather = gatherLocal(qq[2>&1 grep -h -E '$grep' /etc/ifs/array.xml]);
    } else {
	my ($file) = gatherLocal(q[2>/dev/null ls -t].
				 q[ */etc.tar/etc/ifs/array.xml].
				 q[ */etcifs.tar/ifs/array.xml|head -1]);
	if (-e $file) {
	    $gather = gatherLocal(qq[2>&1 grep -h -E '$grep' '$file']);
	}
    }

    # Analyze
    my %interface = ();
    my %device = ();
    my $gather_array = 0;
    my $gather_interfaces = 0;
    foreach (@$gather) {
	if (/<array>/) { $gather_array = 1 }
	if (/<\/array>/) { $gather_array = 0 }
	if (/<interfaces>/) { $gather_interfaces = 1 }
	if (/<\/interfaces>/) { $gather_interfaces = 0 }
	if ($gather_interfaces) { # Gather interface details
	    if (/(name)=\"([^\"]+)\"/) { $interface{$1} = $2 }
	    if (/(flags)=\"([^\"]+)\"/) { $interface{$1} = $2 }
	    if (/<(netmask|low|high|mtu)>([^<]+)</) { $interface{$1} = $2 }
	    if (/<\/interface>/) {
		if ($interface{'name'}) {
		    my $name = $interface{'name'};
		    if ($interface{'flags'}) {
			$CLUSTER_INFO{'Interfaces'}{$name}{'Flags'} =
			    $interface{'flags'};
			debug("CLUSTER_INFO->Interfaces->${name}->Flags=".
			      $interface{'flags'});
		    }
		    if ($interface{'netmask'}) {
			$CLUSTER_INFO{'Interfaces'}{$name}{'Netmask'} =
			    $interface{'netmask'};
			debug("CLUSTER_INFO->Interfaces->${name}->Netmask=".
			      $interface{'netmask'});
		    }
		    if ($interface{'low'}) {
			$CLUSTER_INFO{'Interfaces'}{$name}{'Low'} =
			    $interface{'low'};
			debug("CLUSTER_INFO->Interfaces->${name}->Low=".
			      $interface{'low'});
		    }
		    if ($interface{'high'}) {
			$CLUSTER_INFO{'Interfaces'}{$name}{'High'} =
			    $interface{'high'};
			debug("CLUSTER_INFO->Interfaces->${name}->High=".
			      $interface{'high'});
		    }
		    if ($interface{'mtu'}) {
			$CLUSTER_INFO{'Interfaces'}{$name}{'MTU'} =
			    $interface{'mtu'};
			debug("CLUSTER_INFO->Interfaces->${name}->MTU=".
			      $interface{'mtu'});
		    }
		}
		%interface = ();
	    }
	} elsif ($gather_array) { # Gather device details
	    if (/<(array_lnn|array_id|type|onefs_version)>([^<]+)</) {
		$device{$1} = $2;
	    }
	    if (/<\/device>/) {
		if ($device{'array_lnn'}) {
		    my $lnn = $device{'array_lnn'};
		    if ($CLUSTER_INFO{'Name'}) {
			$NODE_INFO{$lnn}{'Name'} = "$CLUSTER_INFO{'Name'}-$lnn";
			debug("NODE_INFO->${lnn}->Name=".
			      $NODE_INFO{$lnn}{'Name'});
		    }
		    if ($device{'type'}) {
			$NODE_INFO{$lnn}{'type'} = $device{'type'};
			debug("NODE_INFO->${lnn}->type=$device{'type'}");
		    }
		    if ($device{'onefs_version'}) {
			$NODE_INFO{$lnn}{'onefs_version'} =
			    $device{'onefs_version'};
			debug("NODE_INFO->${lnn}->onefs_version=".
			      $device{'onefs_version'});
		    }
		    if ($device{'array_id'}) {
			$NODE_INFO{$lnn}{'ID'} = $device{'array_id'};
			debug("NODE_INFO->${lnn}->ID=$NODE_INFO{$lnn}{'ID'}");
			$LNN_FROM_DEVID{$device{'array_id'}} = $lnn;
			debug("LNN_FROM_DEVID->${device{'array_id'}}=$lnn");
		    }
		}
		%device = ();
	    }
	} else {
	    # Gather cluster details
	    if (/<name>([^<]+)</) {
		# If gathered once before, stop
		if ($CLUSTER_INFO{'Name'}) { last }
		$CLUSTER_INFO{'Name'} = $1;
		debug("CLUSTER_INFO->Name=$1");
	    }
	    if (/<encoding>([^<]+)</) {
		$CLUSTER_INFO{'Encoding'} = $1;
		debug("CLUSTER_INFO->Encoding=$1");
	    }
	    if (/<timezone>([^<]+)</) {
		$CLUSTER_INFO{'Timezone'} = $1;
		debug("CLUSTER_INFO->Timezone=$1");
	    }
	    if (/<guid>([^<]+)</) {
		$CLUSTER_INFO{'GUID'} = $1;
		debug("CLUSTER_INFO->GUID=$1");
	    }
	    if (/<compliance>([^<]+)</) {
		$CLUSTER_INFO{'Compliance'} = $1;
		debug("CLUSTER_INFO->Compliance=$1");
	    }
	}
    }

    # Populate the number of nodes
    $CLUSTER_INFO{'Node Count'} = scalar(keys %NODE_INFO);
    debug("CLUSTER_INFO->Node Count=$CLUSTER_INFO{'Node Count'}");

    # Create a sorted list of logical node numbers [lnns]
    @LNNS = sort {$a <=> $b} keys %NODE_INFO;
    debug("Sorted lnn list: [@LNNS]");
} # End gatherArrayXML
sub gatherEventAlerts {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) { return }

    my $MATCH = q[Name|Channel];
    my $CMD_GREP = qq[ egrep '$MATCH' ];
    my $CMD_LOGS = qq[2>&1 $CMD_GREP local/isi_event_alerts | grep -B1 '^\\s*Channel:.*None.*\$' ];
    my $CMD_LIVE;
    if(isCurrentOneFS('<9.9')){
        $CMD_LIVE = qq[2>&1 ${SUDO} isi event alert list -v | $CMD_GREP | grep -B1 '^\\s*Channel:.*None.*\$' ];

    }
    else{
        $CMD_LIVE = qq[2>&1 isi event alert list -v | $CMD_GREP | grep -B1 '^\\s*Channel:.*None.*\$' ];
    }
    
    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal($CMD_LIVE);
    } else {
	$gather = gatherLocal($CMD_LOGS);
    }

    # Store relavant information
    my $id = q[];
    foreach my $line (@$gather) {
	# Skip unless the line is a match
	if ($line !~ /^\s*(?:$MATCH)/) { next }

	# Remove any leading whitespaces
	$line =~ s/^\s*//;

	# Split into a name/value pair
	my ($name, $value) = split(/:\s*/, $line, 2);

	# Assign the ID
	if ($name eq 'Name') { $id = $value }

	# Skip if no ID is found
	if (!$id) { next }

	# Assign values
	$CLUSTER_INFO{'Event Alert'}{$id}{$name} = $value;
	debug("CLUSTER_INFO->Event Alert->${id}->${name}=$value");
    }
} # End gatherEventAlerts
sub gatherEventChannel {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) { return }

    my $MATCH = q[ID|Name|Type|Enabled];
    my $CMD_GREP = qq[ egrep '$MATCH' ];
    my $CMD_LOGS = qq[2>&1 $CMD_GREP local/isi_event_channel];
    my $CMD_LIVE;
    if(isCurrentOneFS('<9.9')){ 
       $CMD_LIVE = qq[2>&1 ${SUDO} isi event channel list -v | $CMD_GREP];
    }
    else{
       $CMD_LIVE = qq[2>&1 isi event channel list -v | $CMD_GREP];    
    }

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal($CMD_LIVE);
    } else {
	$gather = gatherLocal($CMD_LOGS);
    }

    # Store relavant information
    my $id = q[];
    foreach my $line (@$gather) {
	# Skip unless the line is a match
	if ($line !~ /^\s*(?:$MATCH)/) { next }

	# Remove any leading whitespaces
	$line =~ s/^\s*//;

	# Split into a name/value pair
	my ($name, $value) = split(/:\s*/, $line, 2);

	# Assign the ID
	if ($name eq 'ID') { $id = $value }

	# Skip if no ID is found
	if (!$id) { next }

	# Assign values
	$CLUSTER_INFO{'Event Channels'}{$id}{$name} = $value;
	debug("CLUSTER_INFO->Event Channels->${id}->${name}=$value");
    }
} # End gatherEventChannel
###
# gatherEventGroups8: Gathers events on OneFS 8+ clusters.
sub gatherEventGroups8 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8') or isCurrentOneFS('>=9')) { return }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi event groups list | grep critical | grep - --];
    my $CMD_LOGS = q[2>&1 grep -h critical */isi_event_groups | grep - --];
    my $RE_EVENTGROUP = qr/^(\d+)\s+([\d\/]+\s+[\d:]+)\s+(--)\s+(\S+)\s+(\d+)\s+(\d+)\s+(critical)$/;

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    foreach my $line (@$gather) {
	if ($line !~ $RE_EVENTGROUP) { next }
	my ($id, $started, $ended, $causes, $lnn, $events, $severity) = ($1, $2, $3, $4, $5, $6, $7);

	# Store
	$CLUSTER_INFO{'Events'}{$id}{'Started'} = $started;
	debug("CLUSTER_INFO->Events->${id}->Started=$started");
	$CLUSTER_INFO{'Events'}{$id}{'Ended'} = $ended;
	debug("CLUSTER_INFO->Events->${id}->Ended=$ended");
	$CLUSTER_INFO{'Events'}{$id}{'Severity'} = $severity;
	debug("CLUSTER_INFO->Events->${id}->Severity=$severity");
    }
} # End gatherEventGroups8
###
# gatherEventGroups9: Gathers events on OneFS 9+ clusters.
sub gatherEventGroups9 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<9')) { return }

    # Setup
    my $CMD_LIVE;
    if(isCurrentOneFS('<9.9')){ 
      $CMD_LIVE = qq[2>&1 ${SUDO} isi event groups list | grep critical | grep - --];
    }
    else{
       $CMD_LIVE = qq[isi event groups list | grep critical | grep - --];
    }
    my $CMD_LOGS = q[2>&1 grep -h critical */isi_event_groups | grep - --];
    my $RE_EVENTGROUP = qr/^(\d+)\s+([\d\/]+\s+[\d:]+)\s+(--)\s+(\S+)\s+(\d+)\s+(\d+)\s+(critical)$/;

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    foreach my $line (@$gather) {
	if ($line !~ $RE_EVENTGROUP) { next }
	my ($id, $started, $ended, $causes, $lnn, $events, $severity) = ($1, $2, $3, $4, $5, $6, $7);

	# Store
	$CLUSTER_INFO{'Events'}{$id}{'Started'} = $started;
	debug("CLUSTER_INFO->Events->${id}->Started=$started");
	$CLUSTER_INFO{'Events'}{$id}{'Ended'} = $ended;
	debug("CLUSTER_INFO->Events->${id}->Ended=$ended");
	$CLUSTER_INFO{'Events'}{$id}{'Severity'} = $severity;
	debug("CLUSTER_INFO->Events->${id}->Severity=$severity");
    }
} # End gatherEventGroups9
###
# gatherEvents: Gathers events
sub gatherEvents {
    return if wasCalled();
    gatherEvents7();
    gatherEvents8();
    gatherEvents9();
} # End gatherEvents
###
# gatherEvents7: Gathers events on OneFS 7 and earlier clusters.
sub gatherEvents7 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8')) {
	debug("gatherEvents7: Skipping for OneFS versions 8 and later.");
	return;
    }

    # Gather event group information
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal(qq[2>&1 ${SUDO} isi events list -w | grep '\\-\\- .* C ']);
    } else {
	$gather = gatherLocal(qq[2>&1 grep -h '\\-\\- .* C ' */isi_alerts */isi_events | sort | uniq]);
    }

    # Iterate through gather
    foreach my $line (@$gather) {
	next unless ($line =~ /^([\d\.]+)\s+([\d\/]+\s+[\d:]+)\s+(--)\s+(C)\s+(\S+)\s+(.*?)$/);
	my ($id, $started, $ended, $severity, $lnn, $message) = ($1, $2, $3, $4, $5, $6);

	# Skip event if already recorded
	next if $CLUSTER_INFO{'Events'}{$id};

	# Assign out variables
	$CLUSTER_INFO{'Events'}{$id}{'Started'} = $started;
	debug("CLUSTER_INFO->Events->${id}->Started=$started");
	$CLUSTER_INFO{'Events'}{$id}{'Ended'} = $ended;
	debug("CLUSTER_INFO->Events->${id}->Ended=$ended");
	$CLUSTER_INFO{'Events'}{$id}{'Severity'} = $severity;
	debug("CLUSTER_INFO->Events->${id}->Severity=$severity");
	push(@{$CLUSTER_INFO{'Events'}{$id}{'Lnn'}}, $lnn);
	debug("CLUSTER_INFO->Events->${id}->Lnn=$lnn");
	push(@{$CLUSTER_INFO{'Events'}{$id}{'Message'}}, $message);
	debug("CLUSTER_INFO->Events->${id}->Message=$message");
    }

} # End gatherEvents7
###
# gatherEvents8: Gathers events on OneFS 8+ clusters.
sub gatherEvents8 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8') or isCurrentOneFS('>=9')) { 
		debug("gatherEvents8: Skipping for OneFS versions 9 and later.");
	return;}

    gatherEventGroups8();
    if (!$CLUSTER_INFO{'Events'}) { return }

    # Setup
    my $EVENTS_EGREP = join('|', keys %{$CLUSTER_INFO{'Events'}});
    my $EVENTS_SQL = join(',', keys %{$CLUSTER_INFO{'Events'}});
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi event events list --severity=critical | egrep '$EVENTS_EGREP'];
    my $CMD_LOGS = qq[2>&1 sqlite3 local/celog_ifsvar_db.tar/ifs/_ifsvar/db/celog/events.db 'select devid, coalesce_instanceid, message from events where coalesce_instanceid in ($EVENTS_SQL)'];
    my $RE_LOGS = qr/^(\d+)\|(\d+)\|(.*)$/;
    my $RE_LIVE = qr/^\S+\s+\S+\s+\S+\s+\S+\s+(\S+)\s+(\S+)\s+(.*)$/;

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Iterate through gather
    foreach my $line (@$gather) {
	my ($lnn, $eventgroup, $message);
	if ($line =~ $RE_LOGS) {
	    ($lnn, $eventgroup, $message) = ($1, $2, $3);
	    if ($LNN_FROM_DEVID{$lnn}) { $lnn = $LNN_FROM_DEVID{$1} }
	} elsif ($line =~ $RE_LIVE) {
	    ($lnn, $eventgroup, $message) = ($1, $2, $3);
	}

	if (defined $lnn and $lnn eq '0') { $lnn = 'All' }
	if (!$lnn or !$eventgroup or !$message) { next }

	# Store
	my @match_lnn = grep {$_ eq $lnn or $_ eq 'All'} @{$CLUSTER_INFO{'Events'}{$eventgroup}{'Lnn'}};
	if (!@match_lnn) {
	    push(@{$CLUSTER_INFO{'Events'}{$eventgroup}{'Lnn'}}, $lnn);
	    debug("CLUSTER_INFO->Events->${eventgroup}->Lnn=$lnn");
	}
	my @match_message = grep {$_ eq $message} @{$CLUSTER_INFO{'Events'}{$eventgroup}{'Message'}};
	unless (@match_message) {
	    push(@{$CLUSTER_INFO{'Events'}{$eventgroup}{'Message'}}, $message);
	    debug("CLUSTER_INFO->Events->${eventgroup}->Message=$message");
	}
    }
} # End gatherEvents8
###
# gatherEvents9: Gathers events on OneFS 9+ clusters.
sub gatherEvents9 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<9')) { return }

    gatherEventGroups9();
    if (!$CLUSTER_INFO{'Events'}) { return }

    # Setup
    my $EVENTS_EGREP = join('|', keys %{$CLUSTER_INFO{'Events'}});
    my $EVENTS_SQL = join(',', keys %{$CLUSTER_INFO{'Events'}});
    my $CMD_LIVE;
	if(isCurrentOneFS('<9.9')){
		$CMD_LIVE = qq[2>&1 ${SUDO} isi event events list | awk -F' ' '\$4=="C"' | egrep '$EVENTS_EGREP'];
	}
	else {
			$CMD_LIVE = qq[isi event events list | awk -F' ' '\$4=="C"' | egrep '$EVENTS_EGREP'];
	}
	my $CMD_LOGS = qq[2>&1 sqlite3 local/celog_ifsvar_db.tar/ifs/_ifsvar/db/celog/events.db 'select devid, coalesce_instanceid, message from events where coalesce_instanceid in ($EVENTS_SQL)'];
    my $RE_LOGS = qr/^(\d+)\|(\d+)\|(.*)$/;
    my $RE_LIVE = qr/^\S+\s+\S+\s+\S+\s+\S+\s+(\S+)\s+(\S+)\s+(.*)$/;

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Iterate through gather
    foreach my $line (@$gather) {
	my ($lnn, $eventgroup, $message);
	if ($line =~ $RE_LOGS) {
	    ($lnn, $eventgroup, $message) = ($1, $2, $3);
	    if ($LNN_FROM_DEVID{$lnn}) { $lnn = $LNN_FROM_DEVID{$1} }
	} elsif ($line =~ $RE_LIVE) {
	    ($lnn, $eventgroup, $message) = ($1, $2, $3);
	}

	if (defined $lnn and $lnn eq '0') { $lnn = 'All' }
	if (!$lnn or !$eventgroup or !$message) { next }

	# Store
	my @match_lnn = grep {$_ eq $lnn or $_ eq 'All'} @{$CLUSTER_INFO{'Events'}{$eventgroup}{'Lnn'}};
	if (!@match_lnn) {
	    push(@{$CLUSTER_INFO{'Events'}{$eventgroup}{'Lnn'}}, $lnn);
	    debug("CLUSTER_INFO->Events->${eventgroup}->Lnn=$lnn");
	}
	my @match_message = grep {$_ eq $message} @{$CLUSTER_INFO{'Events'}{$eventgroup}{'Message'}};
	unless (@match_message) {
	    push(@{$CLUSTER_INFO{'Events'}{$eventgroup}{'Message'}}, $message);
	    debug("CLUSTER_INFO->Events->${eventgroup}->Message=$message");
	}
    }
} # End gatherEvents9
sub gatherIsiStatPools {
    if (wasCalled()) { return }

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
		if (isCurrentOneFS('<8')) {
			$gather = gatherLocal(qq[2>&1 ${SUDO} isi stat -d -v]);
		} else {
			if(isCurrentOneFS('<9.9')){
				$gather = gatherLocal(qq[2>&1 ${SUDO} isi --timeout=300 stat -p -v]);
			}
			else{
				$gather = gatherLocal(qq[2>&1 isi --timeout=300 stat -p -v]);	
			}
		}
    } else {
	$gather = gatherLocal(qq[2>&1 cat */isi_stat-d */isi_stat-p]);
    }

    # Analyze information for issues.
    my $node_group_name = "";
    my $seen_cluster_name = 0;
    foreach my $line (@$gather) {
	# Gather cluster name
	if ($line =~ /^Cluster Name:\s+(\S+)$/) {
	    last if $seen_cluster_name;
	    $CLUSTER_INFO{'Name'} = $1;
	    $seen_cluster_name = 1;
	    debug("CLUSTER_INFO->Name=$CLUSTER_INFO{'Name'}");
	}
	# Gather cluster health
	if ($line =~ /^Cluster Health:\s+\[\s*(\S+)\s*\]$/) {
	    if (!$CLUSTER_INFO{'Health'}) {
		$CLUSTER_INFO{'Health'} = $1;
		debug("CLUSTER_INFO->Healthy=$CLUSTER_INFO{'Health'}");
	    }
	}
	# Start gathering pool information
	if ($line =~ /^(?:Node|Disk) (?:Group|Pool) Name:\s+(\S+)(?:\s+Protection:\s+(\S+))?/) {
	    $node_group_name = $1;
	    if (defined $2) {
		$CLUSTER_INFO{'Pools'}{$node_group_name}{'Protection'} = $2;
		debug("CLUSTER_INFO->Pools->${node_group_name}->Protection=$2");
	    }
	}
	# Only proceed from this point if we have gathered a group name
	next unless $node_group_name;
	# Gather VHS details for the group
	if ($line =~ /^VHS Size:\s+(\S+)/) {
	    $CLUSTER_INFO{'Pools'}{$node_group_name}{'VHS'} = $1;
	    debug("CLUSTER_INFO->Pools->${node_group_name}->VHS=$1");
	}
	# Gather %Used details for the group
	if ($line =~ /^Used:.*?(\d+)%(?:.*?(\d+)%)?/) {
	    $CLUSTER_INFO{'Pools'}{$node_group_name}{'Percent Used'} = $1;
	    debug("CLUSTER_INFO->Pools->${node_group_name}->Percent Used=$1");
	    if (defined $2) {
		$CLUSTER_INFO{'Pools'}{$node_group_name}{'Percent HDD Used'} = $1;
		debug("CLUSTER_INFO->Pools->${node_group_name}->Percent HDD Used=$1");
		$CLUSTER_INFO{'Pools'}{$node_group_name}{'Percent SSD Used'} = $2;
		debug("CLUSTER_INFO->Pools->${node_group_name}->Percent SSD Used=$2");
	    }
	}

	next if ($line =~ /^Name/);
	if ($line =~ /^\S+\s*\|/) {
	    my @columns = split(/\s*\|\s*/, $line);
	    $CLUSTER_INFO{'Pools'}{$node_group_name}{'Health'} = $columns[1];
	    debug("CLUSTER_INFO->Pools->${node_group_name}->Health=$columns[1]");

	    $CLUSTER_INFO{'Pools'}{$node_group_name}{'HDD Storage'} = $columns[5];
	    debug("CLUSTER_INFO->Pools->${node_group_name}->HDD Storage=$columns[5]");

	    $CLUSTER_INFO{'Pools'}{$node_group_name}{'SSD Storage'} = $columns[$#columns];
	    debug("CLUSTER_INFO->Pools->${node_group_name}->SSD Storage=$columns[$#columns]");
	}
	next unless ($line =~ /^\s*(\d+)\|/);
	my $lnn = $1;

	push(@{$CLUSTER_INFO{'Pools'}{$node_group_name}{'Members'}}, $lnn);
	debug("CLUSTER_INFO->Pools->${node_group_name}->Members=$lnn");

	$NODE_INFO{$lnn}{'Pool'} = $node_group_name;
	debug("NODE_INFO->${lnn}->Pool=$node_group_name");

	my @columns = split(/\s*\|\s*/, $line);
	$NODE_INFO{$lnn}{'Health'} = $columns[2];
	debug("NODE_INFO->${lnn}->Health=$NODE_INFO{$lnn}{'Health'}");

	$NODE_INFO{$lnn}{'HDD Storage'} = $columns[6];
	debug("NODE_INFO->${lnn}->HDD Storage=$NODE_INFO{$lnn}{'HDD Storage'}");

	$NODE_INFO{$lnn}{'SSD Storage'} = $columns[$#columns];
	debug("NODE_INFO->${lnn}->SSD Storage=$NODE_INFO{$lnn}{'SSD Storage'}");

	if ($NODE_INFO{$lnn}{'HDD Storage'} =~ /(\d+)%/ or $NODE_INFO{$lnn}{'SSD Storage'} =~ /(\d+)%/) {
	    $NODE_INFO{$lnn}{'Percent Used'} = $1;
	    debug("NODE_INFO->${lnn}->Percent Used=$1");
	}

	if ($NODE_INFO{$lnn}{'SSD Storage'} =~ /L3/) {
	    $NODE_INFO{$lnn}{'L3'} = 1;
	    debug("NODE_INFO->${lnn}->L3=1");
	}
    }
} # End gatherIsiStatPools
###
# gatherLicense: Gather and store license details
sub gatherLicense {
    gatherLicense7();
    gatherLicense80();
    gatherLicense81();
    gatherLicense913();
} # End gatherLicense
###
# gatherLicense7: Gather and store license details
sub gatherLicense7 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('>=8')) { return }

    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal("2>&1 ${SUDO} isi license");
    } else {
	$gather = gatherLocal("2>&1 cat local/isi_license");
    }

    # Analyze information for issues.
    my $unpack_template = "";
    my $min_length = 0;
    foreach my $line (@$gather) {

	# Generate an unpack template from the header line
	if ($line =~ /^Module/) {
	    $min_length = length($line);
	    $unpack_template = generateUnpackTemplate($line, "Module", "License", "Configuration", "Expiration");
	}

	# Skip header/foot lines and skip until an unpack templace is available
	next if $line =~ /^\s*-/;
	next if $line =~ /^Module/;
	next unless $unpack_template;

	# Expand the line to be at least as long as the header line to avoid unpack issues
	$line = sprintf("%-${min_length}s", $line);

	# Unpack the line
	my @unpacked_line = ();
	eval { @unpacked_line = unpack($unpack_template, $line); };
	$CLUSTER_INFO{'License'}{$unpacked_line[0]}{'Status'} = $unpacked_line[1];
	debug("CLUSTER_INFO->License->${unpacked_line[0]}->Status=$unpacked_line[1]");
	if ($unpacked_line[3]) {
	    $CLUSTER_INFO{'License'}{$unpacked_line[0]}{'Expiration'} = $unpacked_line[3];
	    debug("CLUSTER_INFO->License->${unpacked_line[0]}->Expiration=$unpacked_line[3]");
	}
    }
} # End gatherLicense7
###
# gatherLicense80: Gather and store license details
sub gatherLicense80 {
    if (wasCalled()) { return }
    if ((isCurrentOneFS('=8') and isCurrentOneFS('<8.1')) or isCurrentOneFS('>=9')) { return }

    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal("2>&1 ${SUDO} isi license list");
    } else {
	$gather = gatherLocal("2>&1 cat local/isi_license");
    }

    # Analyze information for issues.
    my $unpack_template = "";
    my $min_length = 0;
    foreach my $line (@$gather) {

	# Generate an unpack template from the header line
	if ($line =~ /^Name/) {
	    $min_length = length($line);
	    $unpack_template = generateUnpackTemplate($line, "Name", "Status", "Expiration");
	}

	# Skip header/foot lines and skip until an unpack templace is available
	next if $line =~ /^\s*-/;
	next if $line =~ /^Total/;
	next if $line =~ /^Name/;
	next unless $unpack_template;

	# Expand the line to be at least as long as the header line to avoid unpack issues
	$line = sprintf("%-${min_length}s", $line);

	# Unpack the line
	my @unpacked_line = ();
	eval { @unpacked_line = unpack($unpack_template, $line); };
	$CLUSTER_INFO{'License'}{$unpacked_line[0]}{'Status'} = $unpacked_line[1];
	debug("CLUSTER_INFO->License->${unpacked_line[0]}->Status=$unpacked_line[1]");
	if ($unpacked_line[2] ne "-") {
	    $CLUSTER_INFO{'License'}{$unpacked_line[0]}{'Expiration'} = $unpacked_line[2];
	    debug("CLUSTER_INFO->License->${unpacked_line[0]}->Expiration=$unpacked_line[2]");
	}
    }
} # End gatherLicense80
###
# gatherLicense81: Gather and store license details
sub gatherLicense81 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8.1')) { return }

    my $gather = ();
    if (isLive()) {
		if(isCurrentOneFS('<9.9')){
			$gather = gatherLocal("2>&1 ${SUDO} isi license list");
		}
		else {
			$gather = gatherLocal("isi license list");	
		}
    } else {
	$gather = gatherLocal("2>&1 cat local/isi_license");
    }

    # Analyze information for issues.
    my $unpack_template = "";
    my $min_length = 0;
    foreach my $line (@$gather) {
	# Generate an unpack template from the header line
	if ($line =~ /^Module/) {
	    $min_length = length($line);
	    $unpack_template = generateUnpackTemplate($line, "Module", "Licensed", "Actual", "Status", "Expiration");
	}

	# Gather OneFS License ID
	if ($line =~ /OneFS (?:License|Software) ID:\s*(\S+)$/ and $1 ne "-") {
	    $CLUSTER_INFO{'OneFS License ID'} = $1;
	    debug("CLUSTER_INFO->OneFS License ID=$1");
	}

	# Gather if cluster has a "Valid Signature"
	if ($line =~ /Valid Signature:\s*Yes$/) {
	    $CLUSTER_INFO{'Valid Signature'} = "Yes";
	    debug("CLUSTER_INFO->Valid Signature=Yes");
	}

	# Skip header/foot lines and skip until an unpack templace is available
	next if $line =~ /^[\s-]*$/;
	next if $line =~ /^Total/;
	next if $line =~ /^Module/;
	next unless $unpack_template;

	# Unpack the line
	eval {
	    # Expand the line to be at least as long as the header line to avoid unpack issues
	    $line = sprintf("%-${min_length}s", $line);

	    my @unpacked_line = ();
	    @unpacked_line = unpack($unpack_template, $line);

	    # Assign values out
	    $CLUSTER_INFO{'License'}{$unpacked_line[0]}{'Status'} = $unpacked_line[3];
	    debug("CLUSTER_INFO->License->${unpacked_line[0]}->Status=$unpacked_line[3]");

	    if ($unpacked_line[4] ne "-") {
		$CLUSTER_INFO{'License'}{$unpacked_line[0]}{'Expiration'} = $unpacked_line[4];
		debug("CLUSTER_INFO->License->${unpacked_line[0]}->Expiration=$unpacked_line[4]");
	    }
	};
    }
} # End gatherLicense81
###
# gatherLicense913: Gather and store license details
sub gatherLicense913 {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<9.13' or isCurrentOneFS('>=9.13.1.0'))) { return }

    my $gather = ();
    if (isLive()) {
		$gatherCluster = gatherCluster("2>&1 ${SUDO} isi_for_array -sX ls -la /ifs/.ifsvar/isi_license.xml | wc -l");
    } else {
		$gatherLocal = gatherLocal('if [ -f "local/isi_license.xml" ]; then echo "File exists"; else echo "File Missing"; fi');
    }

    # Analyze information for issues.
    my $unpack_template = "";
    my $min_length = 0;
	$CLUSTER_INFO{'OneFS License File Presence'} = @LNNS;
    if($gatherCluster){
		foreach my $lnn (@LNNS) {
			#if (!$gatherCluster->{$lnn}) { missing($lnn); next; }
			foreach my $l (@{$gatherCluster->{$lnn}}) {
				if($l eq '0' or int($l) < 1){
					$CLUSTER_INFO{'OneFS License File Presence'}--;
					debug("CLUSTER_INFO->OneFS License File Presence = 0 || License File Missing for node $lnn");
				}
				else{
					debug("CLUSTER_INFO->OneFS License File Presence incremented as presence verified for node");
				}
			}
		}
		if($CLUSTER_INFO{'OneFS License File Presence'} < @LNNS){ 
			$CLUSTER_INFO{'OneFS License File Missing'} = 1;
		}
		else{
			$CLUSTER_INFO{'OneFS License File Missing'} = 0;
		};
	}
	if($gatherLocal){
		foreach my $line (@$gather) {
			if ($line =~ /File Missing/) {
				$CLUSTER_INFO{'OneFS License File Missing'} = 1;
				debug("CLUSTER_INFO->OneFS License File Presence = 1");
			}
			else{
				$CLUSTER_INFO{'OneFS License File Missing'} = 0;
				debug("CLUSTER_INFO->OneFS License File Presence = 0 || License File Missing");
			}
		}
	}
} # End gatherLicense913
###
# gatherNFSExport: Gathers NFS export information
sub gatherNFSExport {
    return if wasCalled();
    my $gather = ();
    if (isLive()) {
		if(isCurrentOneFS('<9.9')){
			$gather = gatherLocal("2>&1 ${SUDO} isi nfs exports list --verbose");
		}
		else{
			$gather = gatherLocal("2>&1 isi nfs exports list --verbose");
		}
    
	} else {
	$gather = gatherLocal("2>&1 cat local/isi_nfs_exports");
    }
    my $ID = "";
    foreach my $line (@$gather) {
	$ID = $1 if ($line =~ /^\s*ID: (.*)$/ or $line =~ /^Export (.*):$/);
	next unless $ID;
	$ID = 0 if $ID =~ /default/i;
	if ($line =~ /^\s*Zone: (.*)$/) {
	    $CLUSTER_INFO{'NFS Export'}{$ID}{'Zone'} = $1;
	    debug("CLUSTER_INFO->NFS Export->${ID}->Zone=$1");
	} elsif ($line =~ /^\s*Encoding: (.*)$/ or $line =~ /data encoding is (.*)$/) {
	    $CLUSTER_INFO{'NFS Export'}{$ID}{'Encoding'} = $1;
	    debug("CLUSTER_INFO->NFS Export->${ID}->Encoding=$1");
	} elsif ($line =~ /^\s*Paths: (.*)$/) {
	    $CLUSTER_INFO{'NFS Export'}{$ID}{'Paths'} = $1;
	    debug("CLUSTER_INFO->NFS Export->${ID}->Paths=$1");
	} elsif ($line =~ /^\s*Security Type: (.*)$/) {
	    $CLUSTER_INFO{'NFS Export'}{$ID}{'Security Type'} = $1;
	    debug("CLUSTER_INFO->NFS Export->${ID}->Security Type=$1");
	}
    }
} # End gatherNFSExport
###
# gatherNTPConfig: Gathers NTP Time Server details from the ntp_config.gc file
sub gatherNTPConfig {
    return if wasCalled();

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal(qq[2>&1 cat /ifs/.ifsvar/modules/ntp/ntp_config.gc /var/ifs/ntp_config.gc ]);
    } else {
	$gather = gatherLocal(qq[2>&1 cat local/ifsvar_modules.tar/modules/ntp/ntp_config.gc */gconfig_local.tar/var/ifs/ntp_config.gc]);
    }
    my @ntp_servers = ();
    foreach my $line (@$gather) {
	next unless ($line =~ /^servers.*?\[\s*(.*?)\s*\]/);
	my $server_list = $1;
	$server_list =~ s/\\"//g;
	my @servers = split(/, /, $server_list);
	foreach my $server (@servers) {
	    my @results = grep {$_ eq $server} @ntp_servers;
	    next if @results;
	    push(@ntp_servers, $server);
	}
    }
    if (@ntp_servers) {
	@{$CLUSTER_INFO{'NTP Servers'}} = @ntp_servers;
	debug("CLUSTER_INFO->NTP Servers=".join(",", @ntp_servers));
    }
} # End gatherNTPConfig
###
# gatherNTPdXML: Gathers NTP Time Server details from the ntpd.xml file
sub gatherNTPdXML {
    return if wasCalled();

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal(qq[2>&1 cat /ifs/.ifsvar/etc/mcp/override/ntpd.xml /etc/mcp/override/ntpd.xml]);
    } else {
	$gather = gatherLocal(qq[2>&1 cat local/ifsvar_etc.tar/etc/mcp/override/ntpd.xml */local_configs.tar/etc/mcp/override/ntpd.xml */override.tar/override/ntpd.xml]);
    }
    my @ntp_servers = ();
    foreach my $line (@$gather) {
	next unless ($line =~ /<server>(.*?)<\/server>$/);
	my $server = $1;
	my @results = grep {$_ eq $server} @ntp_servers;
	next if @results;
	push(@ntp_servers, $server);
    }
    if (@ntp_servers) {
	@{$CLUSTER_INFO{'NTP Servers'}} = @ntp_servers;
	debug("CLUSTER_INFO->NTP Servers=".join(",", @ntp_servers));
    }
} # End gatherNTPdXML
###
# gatherNeighborhoods: Gather neighborhoods from disk pool information
sub gatherNeighborhoods {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) { return }

    # Setup
    my @COLS = ('Name', 'Id', 'Type', 'Prot', 'Flags',
		'Members', 'VHS', 'HDD Used', 'SSD Used');
    my $CMD_LIVE = qq[2>&1 ${SUDO} disi -I diskpool list -v];
    my $CMD_LOGS = qq[2>&1 cat local/isi_diskpool];

    # Gather
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal($CMD_LIVE);
    } else {
	$gather = gatherLocal($CMD_LOGS);
    }

    # Analyze
    my $header;
    foreach my $line (@$gather) {
                if ($line =~ /^\s*#*$/ or $line =~ /^\s*WARNING.*$/ or $line =~ /^\s*\'disi\'\s.*$/ or $line =~ /^\s*Dell\s.*$/) { next; }
                $header .= $line;
    }
    if (!$header) { return }
    my $header_length = length($header);
    my $members_length = index($header, 'VHS') - index($header, 'Members') - 1;
    my $template = generateUnpackTemplate($header, @COLS);
    my %Pools = ();
    my $id = 0;
    foreach my $line (@$gather) {
	if ($line =~ /^\s*-*$/) { next }
	if ($line =~ /Unprovisioned drives:(?:\s+(.*))?$/) {
	    if (defined $1 and $1 !~ /none/i) {
		$CLUSTER_INFO{'Unprovisioned drives'} = $1;
		debug("CLUSTER_INFO->Unprovisioned drives=$1");
	    }
	    last;
	}
	$line = sprintf("%-${header_length}s", $line);
	my %unpacked_line;
	eval { @unpacked_line{@COLS} = unpack($template, $line) };
	if (!%unpacked_line) { next }
	if ($unpacked_line{'Type'}) { $id++ }
	foreach (keys %unpacked_line) {
	    if ($_ eq 'Members' and
		length($unpacked_line{$_}) ne $members_length and
		substr($unpacked_line{$_}, -1) eq ',') {
		# PSCALE-48039
		$unpacked_line{$_} .= ' ';
	    }
	    $Pools{$id}{$_} .= $unpacked_line{$_};
	}
    }
    if (%Pools) { debug('Pools='.toJSON(\%Pools)) }
    my %neighborhoods = ();
  POOL:
    foreach my $id (sort keys %Pools) {
	if (!$Pools{$id}{'Type'} or
	    !$Pools{$id}{'Members'} or
	    $Pools{$id}{'Type'} ne 'D') { next }
	my @members = split(/:bay\S+\s*/, $Pools{$id}{'Members'});
	my @neighbors = ();
	foreach (@members) {
	    push(@neighbors, expandRange($_));
	}
	@neighbors = sort {$a <=> $b} @neighbors;
	foreach my $n (keys %neighborhoods) {
	    if ($neighborhoods{$n}[0] eq $neighbors[0]) { next POOL }
	}
	@{$neighborhoods{$id}} = @neighbors;
    }
    if (%neighborhoods) {
	%{$CLUSTER_INFO{'Neighborhoods'}} = %neighborhoods;
	debug('CLUSTER_INFO->Neighborhoods='.toJSON(\%neighborhoods));
    }
} # End gatherNeighborhoods
###
# gatherRemoteSupport: Gather remotesupport config
sub gatherRemoteSupport {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<7.1')) { return }
    if (isCurrentOneFS('>=9.1')) { return }

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal(qq[2>&1 ${SUDO} isi_gconfig -t remote-support]);
    } elsif (-e 'local/isi_remote_support') {
	$gather = gatherLocal(q[2>&1 cat local/isi_remote_support]);
    } elsif (-e 'local/ifsvar_modules_tardis.tar/modules/tardis/namespaces/Gconfig.remotesupport_config.sqlite') {
	$gather = gatherLocal(q[2>&1 sqlite3 local/ifsvar_modules_tardis.tar/modules/tardis/namespaces/Gconfig.remotesupport_config.sqlite 'select * from kv_table']);
    } elsif (isCurrentOneFS('<8')) {
	$gather = gatherLocal(q[2>&1 cat */gconfig_local.tar/var/ifs/remote_support.gc]);
    }

    # Store relavant information
    foreach my $line (@$gather) {
	my ($key, $value) = parseGconfigItem($line);
	if (!$key or !$value) {
	    next;
	} elsif ($key eq 'connectemc.enabled' and $value eq 'true') {
	    $CLUSTER_INFO{'RemoteSupport'}{'Enabled'} = 'Yes';
	    debug('CLUSTER_INFO->RemoteSupport->Enabled=Yes');
	} elsif ($key eq 'connectemc.primary_esrs_gateway') {
	    $CLUSTER_INFO{'RemoteSupport'}{'Primary Gateway'} = $value;
	    debug("CLUSTER_INFO->RemoteSupport->Primary Gateway=$value");
	} elsif ($key eq 'connectemc.secondary_esrs_gateway') {
	    $CLUSTER_INFO{'RemoteSupport'}{'Secondary Gateway'} = $value;
	    debug("CLUSTER_INFO->RemoteSupport->Secondary Gateway=$value");
	} elsif ($key eq 'connectemc.gateway_access_pools') {
	    my ($key, @values) = parseGconfigList($line);
	    foreach my $pool (@values) { $pool =~ s/:/./ }
	    @{$CLUSTER_INFO{'RemoteSupport'}{'Pools'}} = @values;
	    debug("CLUSTER_INFO->RemoteSupport->Pools=[ @values ]");
	} elsif ($key eq 'connectemc.remote_support_subnet') {
	    $CLUSTER_INFO{'RemoteSupport'}{'Subnet'} = $value;
	    debug("CLUSTER_INFO->RemoteSupport->Subnet=$value");
	}
    }
} # End gatherRemoteSupport
sub gatherSMBTime {
    return if wasCalled();

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	$gather = gatherCluster(qq[2>&1 ${SUDO} isi_for_array grep running /etc/mcp/override/smbtime.xml]);
    } else {
	$gather = gatherCluster(qq[2>&1 grep -H running */override.tar/override/smbtime.xml]);
    }

    # Analyze information for issues.
    foreach my $lnn (sort {$a <=> $b} keys %NODE_INFO) {
	next unless $gather->{$lnn};
	foreach my $line (@{$gather->{$lnn}}) {
	    last if defined $CLUSTER_INFO{'SMBTime'};
	    if ($line =~ /<running>(.*?)<\/running>/) {
		$CLUSTER_INFO{'SMBTime'} = $1;
	    }
	}
    }

} # End gatherSMBTime
###
# gatherSRS: Gathers SRS details
sub gatherSRS {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8.1')) { return }

    # Setup
    my $CMD_LIVE;
	if(isCurrentOneFS('>=9.12') and isCompliance()){
		$CMD_LIVE = qq[2>&1 ${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi_gconfig -t esrs|grep -v 'api_key'];
	}
	else{
    	$CMD_LIVE = qq[2>&1 ${SUDO} isi_gconfig -t esrs|grep -v 'api_key'];
	}
	my $CMD_LOGS = q[2>&1 cat local/isi_esrs|grep -v '^[#+-]'];

    # Gather
    my $gather = ();
    if (isLive()) {
	$gather = gatherLocal($CMD_LIVE);
    } else {
	$gather = gatherLocal($CMD_LOGS);
    }

    # Analyze
    my %gateway_access_pools = ();
    foreach my $line (@$gather) {
	my ($key, $value) = parseGconfigItem($line);
	if (!$key and !$value) {
	    next;
	} elsif ($key eq 'esrs.enabled' and $value eq 'true') {
	    $CLUSTER_INFO{'SRS'}{'Enabled'} = 'Yes';
	    debug('CLUSTER_INFO->SRS->Enabled=Yes');
	} elsif ($key eq 'esrs.primary_esrs_gateway') {
	    $CLUSTER_INFO{'SRS'}{'Primary Gateway'} = $value;
	    debug("CLUSTER_INFO->SRS->Primary Gateway=$value");
	} elsif ($key eq 'esrs.secondary_esrs_gateway') {
	    $CLUSTER_INFO{'SRS'}{'Secondary Gateway'} = $value;
	    debug("CLUSTER_INFO->SRS->Secondary Gateway=$value");
	} elsif ($key =~ /^esrs\.gateway_access_pools\.(\d+)\.subnet/) {
	    $gateway_access_pools{$1}{'Subnet'} = $value;
	} elsif ($key =~ /^esrs\.gateway_access_pools\.(\d+)\.pool/) {
	    $gateway_access_pools{$1}{'Pool'} = $value;
	} elsif ($key eq 'esrs.eligible_lnns') {
	    (undef, @{$CLUSTER_INFO{'SRS'}{'Nodes'}}) = parseGconfigList($line);
	    debug('CLUSTER_INFO->SRS->Nodes='.
		  "[ @{$CLUSTER_INFO{'SRS'}{'Nodes'}} ]");
	}
    }
    foreach (keys %gateway_access_pools) {
	if (!$gateway_access_pools{$_}{'Subnet'} or
	    !$gateway_access_pools{$_}{'Pool'}) {
	    next;
	}
	my $pool = sprintf('%s.%s',
			   $gateway_access_pools{$_}{'Subnet'},
			   $gateway_access_pools{$_}{'Pool'});
	push(@{$CLUSTER_INFO{'SRS'}{'Pools'}}, $pool);
    }
    if ($CLUSTER_INFO{'SRS'}{'Pools'}) {
	debug('CLUSTER_INFO->SRS->Pools='.
	      "[ @{$CLUSTER_INFO{'SRS'}{'Pools'}} ]");
    }
} # End gatherSRS
sub gatherServices {
    return if wasCalled();
    my $gather = ();
    if (isLive()) {
        if(isCurrentOneFS('>=9.12') and isCompliance()){
            $gather = gatherLocal(qq[2>&1 ${SUDO} /usr/sbin/isi_mpa_check -- /usr/bin/isi services -a]);
        }
        else{
	        $gather = gatherLocal(qq[2>&1 ${SUDO} isi services -a]);
        }
    } else {
	$gather = gatherLocal(qq[2>&1 cat local/isi_services]);
    }
    foreach (@$gather) {
	next unless /^\s*(\S+)\s.*(Enabled|Disabled)$/;
	$CLUSTER_INFO{'Services'}{$1} = $2;
	debug("CLUSTER_INFO->Services->${1}=${2}");
    }
}
sub gatherSnapshotCount {
    return if wasCalled();

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
        if(isCurrentOneFS('<9.9')){
	        $gather = gatherLocal("2>&1 ${SUDO} isi snap list | grep '^Total'");
        }
        else{
            $gather = gatherLocal("2>&1 isi snap list | grep '^Total'");    
        }
    } else {
	$gather = gatherLocal("2>&1 cat local/snapshot_list | grep '^Total'");
    }

    # Analyze information for issues.
    foreach my $line (@$gather) {
	if ($line =~ /Total:\s+(\d+)$/) {
	    $CLUSTER_INFO{'Snapshots'}{'Count'} = $1;
	    debug("CLUSTER_INFO->Snapshots->Count=$1");
	}
    }
} # End gatherSnapshotCount
sub gatherSnapshotSettings {
    return if wasCalled();

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
        if(isCurrentOneFS('<9.9')){
	        $gather = gatherLocal("2>&1 ${SUDO} isi snap settings view");
        }
        else{
            $gather = gatherLocal("2>&1 isi snap settings view");    
        }
    } else {
	$gather = gatherLocal("2>&1 cat local/snapshot_settings");
    }

    # Analyze information for issues.
    foreach my $line (@$gather) {
	if ($line =~ /Autodelete:\s+(\S+)$/) {
	    $CLUSTER_INFO{'Snapshots'}{'Autodelete'} = $1;
	    debug("CLUSTER_INFO->Snapshots->Autodelete=$1");
	}
    }
} # End gatherSnapshotSettings
sub gatherSnapshotUsage {
    return if wasCalled();

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
		if(isCurrentOneFS('<9.9')){
			$gather = gatherLocal("2>&1 ${SUDO} isi snap snap list -v --format table --state all | grep '^Total'");
		}
		else{
			$gather = gatherLocal("2>&1 isi snap snap list -v --format table --state all | grep '^Total'");
		}
    } else {
	$gather = gatherLocal("2>&1 cat local/snapshot_usage | grep '^Total'");
    }

    # Analyze information for issues.
    foreach my $line (@$gather) {
	if ($line =~ /^Total:\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/) {
	    $CLUSTER_INFO{'Snapshots'}{'Count'} = $1;
	    debug("CLUSTER_INFO->Snapshots->Count=$1");
	    $CLUSTER_INFO{'Snapshots'}{'Size'} = $2;
	    debug("CLUSTER_INFO->Snapshots->Size=$2");
	    $CLUSTER_INFO{'Snapshots'}{'Reserve'} = $3;
	    debug("CLUSTER_INFO->Snapshots->Reserve=$3");
	    $CLUSTER_INFO{'Snapshots'}{'Filesystem'} = $4;
	    debug("CLUSTER_INFO->Snapshots->Filesystem=$4");
	}
    }
} # End gatherSnapshotUsage
###
# gatherStat: Gathers node information from isi stat -n/isi stat -a output.
sub gatherStat {
    return if wasCalled();

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
	if (isCurrentOneFS('>=8')) {
		if(isCurrentOneFS('<9.10')){
	    	$gather = gatherLocal("2>&1 ${SUDO} isi stat -a | grep -E '^(Member|Node|Available|Used)'");
		}
		else{
			$gather = gatherLocal("2>&1 isi stat -a | grep -E '^(Member|Node|Available|Used)'");
		}
	} else {
	    $gather = gatherLocal("2>&1 ${SUDO} isi stat -n | grep -E '^(Member|Node|Available|Used)'");
	}
    } else {
	$gather = gatherLocal("2>&1 grep -E -h '^(Member|Node|Available|Used)' */isi_stat");
    }

    # Analyze information for issues.
    my $lnn = 0;
    my $first_lnn = 0;
    foreach my $line (@$gather) {
	next unless $line =~ /:/;
	my ($name, $value) = split(/:\s*/, $line, 2);
	if ($name eq "Node LNN") {
	    next unless $value =~ /^\d+$/;
	    $lnn = $value;
	    last if $first_lnn == $lnn;
	    $first_lnn = $lnn unless $first_lnn;
	}
	next unless $lnn;
	if ($name eq "Node ID") {
	    $NODE_INFO{$lnn}{'ID'} = $value;
	    debug("NODE_INFO->${lnn}->ID=$value");
	} elsif ($name eq "Node Name") {
	    $NODE_INFO{$lnn}{'Name'} = $value;
	    debug("NODE_INFO->${lnn}->Name=$value");
	} elsif ($name eq "Node Health") {
	    $NODE_INFO{$lnn}{'Health'} = $value;
	    debug("NODE_INFO->${lnn}->Health=$value");
	} elsif ($name eq "Node SN") {
	    $NODE_INFO{$lnn}{'Serial'} = $value;
	    debug("NODE_INFO->${lnn}->Serial=$value");
	} elsif ($name eq "Member of (Node|Disk) (Pool|Pools|Group)") {
	    $NODE_INFO{$lnn}{'Pool'} = $value;
	    debug("NODE_INFO->${lnn}->Pool=$value");
	} elsif ($name eq "Node Capacity") {
	    $NODE_INFO{$lnn}{'Capacity'} = $value;
	    debug("NODE_INFO->${lnn}->Capacity=$value");
	} elsif ($name eq "Available") {
	    $NODE_INFO{$lnn}{'Available'} = $value;
	    debug("NODE_INFO->${lnn}->Available=$value");
	    if ($value =~ /\D([0-9\.]+)\%/) {
		$NODE_INFO{$lnn}{'Percent Available'} = $1;
		debug("NODE_INFO->${lnn}->Percent Available=$1");
	    }
	} elsif ($name eq "Used") {
	    $NODE_INFO{$lnn}{'Used'} = $value;
	    debug("NODE_INFO->${lnn}->Used=$value");
	    if ($value =~ /\D([0-9\.]+)\%/) {
		$NODE_INFO{$lnn}{'Percent Used'} = $1;
		debug("NODE_INFO->${lnn}->Percent Used=$1");
	    }
	}
    }

} # End gatherStat
###
# gatherStatus: Gathers cluster information from isi status
sub gatherStatus {
    return if wasCalled();

    # Gather information from cluster or from logset
    my $gather = ();
    if (isLive()) {
		if(isCurrentOneFS('<9.10')){
			$gather = gatherLocal("2>&1 ${SUDO} isi status");
		}
		else{
			$gather = gatherLocal("2>&1 isi status");
		}

    } else {
	$gather = gatherLocal("2>&1 cat */isi_status");
    }

    # Analyze information for issues.
    my $seen_cluster_name = 0;
    foreach my $line (@$gather) {
	my ($hdd, $ssd, $quantifier, $numeric) = "";
	if ($line =~ /^Cluster Name:\s+(\S+)$/) {
	    last if $seen_cluster_name;
	    $seen_cluster_name = 1;
	    $CLUSTER_INFO{'Name'} = $1;
	    debug("CLUSTER_INFO->Name=$CLUSTER_INFO{'Name'}");
	} elsif ($line =~ /^Cluster Health:\s+\[\s*(\S+)\s*\]$/) {
	    $CLUSTER_INFO{'Health'} = $1;
	    debug("CLUSTER_INFO->Health=$CLUSTER_INFO{'Health'}");
	} elsif ($line =~ /^VHS Size:\s+(\S+)$/) {
	    $CLUSTER_INFO{'VHS Size'} = $1;
	    debug("CLUSTER_INFO->VHS Size=$CLUSTER_INFO{'VHS Size'}");
	} elsif ($line =~ /^Size:\s+(\S+\s+\(.*\))\s+(\S+\s+\(.*\))$/) {
	    $CLUSTER_INFO{'HDD Storage'} = $1;
	    debug("CLUSTER_INFO->HDD Storage=$1");
	    $CLUSTER_INFO{'SSD Storage'} = $2;
	    debug("CLUSTER_INFO->SSD Storage=$2");
	    if ($CLUSTER_INFO{'HDD Storage'} =~ /\((\S+)\s/) {
		$CLUSTER_INFO{'HDD Size'} = convertToNumeric($1);
		debug("CLUSTER_INFO->HDD Size=$CLUSTER_INFO{'HDD Size'}");
	    }
	    if ($CLUSTER_INFO{'SSD Storage'} =~ /\((\S+)\s/) {
		$CLUSTER_INFO{'SSD Size'} = convertToNumeric($1);
		debug("CLUSTER_INFO->SSD Size=$CLUSTER_INFO{'SSD Size'}");
	    }
	} elsif ($line =~ /^Used:\s+(\S+\s+\(.*\))\s+(\S+\s+\(.*\))$/) {
	    $CLUSTER_INFO{'HDD Used'} = $1;
	    debug("CLUSTER_INFO->HDD Used=$1");
	    $CLUSTER_INFO{'SSD Used'} = $2;
	    debug("CLUSTER_INFO->SSD Used=$2");
	    if ($CLUSTER_INFO{'HDD Used'} =~ /\((\d+)%/) {
		$CLUSTER_INFO{'HDD Percent Used'} = $1;
		debug("CLUSTER_INFO->HDD Percent Used=$CLUSTER_INFO{'HDD Percent Used'}");
	    }
	    if ($CLUSTER_INFO{'SSD Used'} =~ /\((\d+)%/) {
		$CLUSTER_INFO{'SSD Percent Used'} = $1;
		debug("CLUSTER_INFO->SSD Percent Used=$CLUSTER_INFO{'SSD Percent Used'}");
	    }
	} elsif ($line =~ /^\s*(\d+)\|/) {
	    my $lnn = $1;
	    my @columns = split(/\s*\|\s*/, $line);
	    foreach (@columns) {
		if (/([\d\.]+)%/) {
		    $NODE_INFO{$lnn}{'Percent Used'} = $1;
		    debug("NODE_INFO->${lnn}->Percent Used=$1");
		    last;
		}
	    }
	    $NODE_INFO{$lnn}{'SSD Storage'} = $columns[$#columns];
	    debug("NODE_INFO->${lnn}->SSD Storage=$NODE_INFO{$lnn}{'SSD Storage'}");
	}
    }
} # End gatherStatus
###
# gatherUpgradeCluster: Gathers information from isi upgrade cluster view
sub gatherUpgradeCluster {
    if (wasCalled()) { return }
    if (isCurrentOneFS('<8')) { return }

    # Setup
    my $CMD_LIVE;
	if(isCurrentOneFS('>=9.9')){
		$CMD_LIVE = qq[isi upgrade cluster view];
	}
	else{
		$CMD_LIVE = qq[2>&1 ${SUDO} isi upgrade cluster view];
	}
    my $CMD_LOGS = q[2>&1 cat */isi_upgrade_cluster_view local/isi_upgrade_view];
    my $RE_NODE_STATE = qr/^([1-9]\S*)\s+.*(?:unknown|[0-9.]+)\s+(\S.*)/i;

    # Gather
    my $gather = gatherLocal(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Save
    my %node_states;
    foreach (@$gather) {
	if (/Cluster Upgrade State: (.*)/) {
	    if ($CLUSTER_INFO{'Cluster Upgrade State'}) { next }
	    $CLUSTER_INFO{'Cluster Upgrade State'} = $1;
	    debug("CLUSTER_INFO->Cluster Upgrade State=$1");
	}
	if (/Current Upgrade Activity: (.*)/) {
	    if ($CLUSTER_INFO{'Current Upgrade Activity'}) { next }
	    $CLUSTER_INFO{'Current Upgrade Activity'} = $1;
	    debug("CLUSTER_INFO->Current Upgrade Activity=$1");
	}
	if (/$RE_NODE_STATE/) {
	    push(@{$node_states{$2}}, expandRange($1));
	}
    }
    if (%node_states) {
	$CLUSTER_INFO{'Node Upgrade States'} = \%node_states;
	debug("CLUSTER_INFO->Node Upgrade States=".toJSON(\%node_states));
    }
} # End gatherUpgradeCluster
###
# gatherUptime: Gathers and calculates each nodes uptime
sub gatherUptime {
    if (wasCalled()) { return }

    # Setup
    my $CMD_LIVE = qq[2>&1 ${SUDO} isi_for_array uptime];
    my $CMD_LOGS = q[2>&1 grep -H . */uptime];

    # adjustment: the difference between the log gather time and script run time
    my $adjustment = 0;
    $adjustment = $RPS{'Runtime'} - $RPS{'Timestamp'};
    debug("adjustment=$adjustment seconds");

    # Gather
    my $gather = gatherCluster(isLive()?$CMD_LIVE:$CMD_LOGS);

    # Anaylze
    foreach my $lnn (@LNNS) {
	if (!$gather->{$lnn}) { missing($lnn); next; }
	foreach my $line (@{$gather->{$lnn}}) {
	    $NODE_INFO{$lnn}{'uptime_gathered'} = $line;
	    debug("NODE_INFO->${lnn}->uptime_gathered=$line");

	    # Parse current time
	    if ($line =~ /(\d+:\d+[AP]M)/) {
		$NODE_INFO{$lnn}{'time'} = $1;
		debug("NODE_INFO->${lnn}->time->$NODE_INFO{$lnn}{'time'}");
	    }

	    # uptime_seconds: the number of seconds a node has been up for
	    my $uptime_seconds = 0;
	    if ($line =~ /\s(\d+)\s+day/) {
		$uptime_seconds += $1 * 86400;
	    }
	    if ($line =~ /\s(\d+)\s+hr/) {
		$uptime_seconds += $1 * 3600;
	    }
	    if ($line =~ /\s(\d+)\s+min/) {
		$uptime_seconds += $1 * 60;
	    }
	    if ($line =~ /\s(\d+)\s+sec/) {
		$uptime_seconds += $1;
	    }
	    if ($line =~ /\s(\d+):(\d+),/) {
		$uptime_seconds += $1 * 3600 + $2 * 60;
	    }

	    # If uptime_seconds is 0, there was no uptime information found
	    if (!$uptime_seconds) { next }

	    # Total uptime is the number of seconds of reported uptime plus the
	    #  adjustment variable if running on a logset
	    $NODE_INFO{$lnn}{'uptime'} = $uptime_seconds + $adjustment;
	    debug("NODE_INFO->${lnn}->uptime->$NODE_INFO{$lnn}{'uptime'}");
	}
    }
} # End gatherUptime
###
# UPG_F011018EE: Upgrade plan generator for FCO F011018EE
sub UPG_F011018EE {
    my ($aref_plan, $type) = @_;
    if (!$aref_plan or !$type) { return }
    my %activity = (
	'Type' => 'Isilon Hourly',
	'Current Code' => UPG_getCurrentCode($aref_plan),
	'Standalone' => 1,
	'Count' => 1,
	);
    if ($type eq 'F011018EE1') {
	$activity{'Description'} = 'FCO F011018EE Part 1 of 3';
	@{$activity{'Additional Comments'}} = ('Start IntegrityScan job');
    } elsif ($type eq 'F011018EE2') {
	$activity{'Description'} = 'FCO F011018EE Part 2 of 3';
	@{$activity{'Additional Comments'}} =
	    ('Review IntegrityScan job results, modify sysctl setting,'.
	     ' and start Collect job');
    } elsif ($type eq 'F011018EE3') {
	$activity{'Description'} = 'FCO F011018EE Part 3 of 3';
	@{$activity{'Additional Comments'}} =
	    ('Review Collect job results and restore sysctl setting');
    }
    $activity{'Duration'} = getActivityDuration(\%activity);
    push(@$aref_plan, \%activity);
} # End UPG_F011018EE
###
# UPG_F042415EE: The upgrade plan generator for FCO F042415EE
sub UPG_F042415EE {
    my ($aref_plan, $type) = @_;
    if (!$aref_plan or !$type) { return }
    my $href_details = $RPS{$type};
    my @nodes = @{$href_details->{'Nodes'}};
    my @nodes_with_l3 = ();
    foreach my $lnn (@nodes) {
	if ($NODE_INFO{$lnn}{'L3'}) {
	    push(@nodes_with_l3, $lnn);
	} else {
	    my %activity = (
		'Reference' => $RPS{$type},
		'Type' => $type,
		'Current Code' => UPG_getCurrentCode($aref_plan),
		'Target' => 'Drive Firmware Package 1.10',
		'Standalone' => 1,
		'Count' => 1,
		'Additional Comments' => ["Node $lnn"],
		);
	    push(@$aref_plan, \%activity);
	}
    }
    if (@nodes_with_l3) {
	my $count = scalar(@nodes_with_l3);
	my %activity = (
	    'Reference' => $RPS{$type},
	    'Type' => $type,
	    'Current Code' => UPG_getCurrentCode($aref_plan),
	    'Target' => 'Drive Firmware Package 1.10',
	    'Standalone' => 1,
	    'Count' => $count,
	    );
	if (scalar(@nodes) != $count or
	    scalar(@nodes) != $CLUSTER_INFO{'Node Count'}) {
	    $activity{'Additional Comments'} =
		[ 'Nodes '.compressRange(@nodes_with_l3) ];
	}
	push(@$aref_plan, \%activity);
    }
    addRequiredFiles('Isilon Drive Firmware Package 1.10');
} # End UPG_F042415EE
###
# UPG_OneFS: Generate OneFS upgrade activities and adds them to the upgrade plan
sub UPG_OneFS {
    my ($aref_upgrade_plan, $upgrade_step) = @_;
    if (!$aref_upgrade_plan or !$upgrade_step) { return }
	checkNodeFirmware();

    $current_code = UPG_getCurrentCode($aref_plan);
    my @destinations = @{$RPS{'Upgrade Path'}}[1..$#{$RPS{'Upgrade Path'}}];
	
    foreach my $destination (@destinations) {
	my %activity = (
	    'Reference' => $RPS{'OneFS'},
	    'Current Code' => $current_code,
	    'Target' => $destination,
	    'Count' => $CLUSTER_INFO{'Node Count'},
	    'Nodes' => \@LNNS,
	    'Type' => 'OneFS Upgrade',
	    'Description' => 'OneFS Upgrade',
	    'Reboot' => 1,
	    );

	# Include node firmware
	if (!$CLUSTER_INFO{'000350946'} and $RPS{'Node Firmware'}{'Position'} and
	    $RPS{'Node Firmware'}{'Position'} == 'OneFS' and isDestinationOneFS($destination) and
	    compareVersions($current_code, '9.2') >= 0 && !$RPS{'Single Neighborhood'}) {
	    $activity{'Devices'} = $RPS{'Node Firmware'}{'Devices'};
	    push(@{$activity{'Additional Comments'}},
		 'Include node firmware updates during the OneFS upgrade by using'.
		 ' --fw-pkg=<node-firmware-package-full-path-filename>');
#		$activity{'Target'}.=' + '.$RPS{'Node Firmware'}{'Target'};
		$RPS{'Node Firmware'}{'Include_Vers'} = $RPS{'Node Firmware'}{'Target'};
	    $RPS{'Node Firmware'}{'Position'} = 'Included';
	    $activity{'Description'} .= ' with Node Firmware Updates';
	    addRequiredFiles(@{$RPS{'Node Firmware'}{'Files'}});
	}

	if (isUpgradeSimultaneous($current_code, $destination) or
	    ($RPS{'OneFS'}{'Method'} and
	     $RPS{'OneFS'}{'Method'} eq 'simultaneous')) {
	    $activity{'Description'} .= ' (Reboot)(Simultaneous)';
	    $activity{'Method'} = 'simultaneous';
	    if (compareVersions($current_code, '8') >= 0) {
		push(@{$activity{'Additional Comments'}},
		     'Perform OneFS upgrade with --simultaneous');
	    }
	} elsif (!$CLUSTER_INFO{'000350946'} and $RPS{'OneFS'}{'Method'} and
		 $RPS{'OneFS'}{'Method'} eq 'parallel' and 
		 !$RPS{'Single Neighborhood'} and
		 compareVersions($current_code, '8.2.2') >= 0) {
	    $activity{'Description'} .= ' (Reboot)(Parallel)';
	    $activity{'Method'} = 'parallel';
	    push(@{$activity{'Additional Comments'}},
		 'Perform OneFS upgrade with --parallel');
	} elsif (!$CLUSTER_INFO{'000350946'} and !$RPS{'OneFS'}{'Method'} and
		  !$RPS{'Single Neighborhood'} and
		 !$RPS{'Disjoint Networks'} and
		 compareVersions($current_code, '8.2.2') >= 0) {
	    $activity{'Description'} .= ' (Reboot)(Parallel)';
	    $activity{'Method'} = 'parallel';
	    push(@{$activity{'Additional Comments'}},
		 'Perform OneFS upgrade with --parallel');
	} else {
	    $activity{'Description'} .= ' (Reboot)(Rolling)';
	    $activity{'Method'} = 'rolling';
		push(@{$activity{'Additional Comments'}}, 'Nodes '.compressRange(@{$CLUSTER_INFO{'000350946'}}).' contain P5316 SED drives and require upgrade to be done node by node.') if $CLUSTER_INFO{'000350946'};
        push(@{$activity{'Additional Comments'}}, 'Please ensure that the above nodes are ugraded individually using steps outlined on https://www.dell.com/support/kbdoc/en-us/000350946') if $CLUSTER_INFO{'000350946'};

	    if (compareVersions($current_code, '8') < 0 or
		compareVersions($current_code, '8.2.2') >=0) {
		push(@{$activity{'Additional Comments'}},
		     'Perform OneFS upgrade with --rolling');
	    }
	}

	$activity{'Duration'} = getActivityDuration(\%activity);

	if ($activity{'Reboot'}) {
		push(@{$activity{'Additional Comments'}}, 'Please ensure you proactively flush nodes prior to this activity using: \'screen -S proactive_flush -d -m '.${SUDO}.'isi_for_array -X isi_flush\'');
		if($activity{'Duration'} > 60){
			push(@{$activity{'Additional Comments'}}, 'As this reboot activity is greater than 1 hour, hourly proactive flushing must be performed.');
		}
	}

	if ($Bundles{"Isilon OneFS $destination installation bundle"} and
	    (!$Patched_OneFS{$destination} or !$RPS{'Disable Pre-Patched'})) {
	    addRequiredFiles("Isilon OneFS $destination installation bundle");
	} else {
	    addRequiredFiles("Isilon OneFS $destination installation file");
	}

	if (isDestinationOneFS($destination) and
	    $RPS{'Post Upgrade Patches'} or
	    !isDestinationOneFS($destination)) {
	    if ($Patched_OneFS{$destination} and !$RPS{'Disable Pre-Patched'}) {
		# Upgrading to pre-patched OneFS image

		# Compare post upgrade patch list to those included in the
		# pre-patched image and then delete from the post upgrade
		# patches list the included patches and any PATCH-INFRA patches
		my @included_patches = @{$Patched_OneFS{$destination}};
		my @post_upgrade_patches = keys %{$RPS{'Post Upgrade Patches'}};
		foreach my $patch (@post_upgrade_patches) {
		    my ($included) = grep {$_ eq $patch} @included_patches;
		    if ($included) {
			delete $RPS{'Post Upgrade Patches'}{$patch};
		    }
		    # Remove deprecated PATCH-INFRA patches
		    if ($patch eq '8.1.2.0_UGA-PATCH-INFRA_2019-09_255624' or
			$patch eq '8.2.0_UGA-PATCH-INFRA_2019-08_251917') {
			delete $RPS{'Post Upgrade Patches'}{$patch};
		    }
		}

		# Set the patches list to those included in the patched image
		@{$activity{'Patches'}} = @{$Patched_OneFS{$destination}};
		push(@{$activity{'Additional Comments'}},
		     'The OneFS install file includes the listed patches');

	    } elsif (isDestinationOneFS($destination)) {
		# Standard upgrade with patches applied post upgrade
		push(@{$activity{'Additional Comments'}}, 'Subsequent patch installs should be scheduled in the same maintenance window as the OneFS upgrade');
		addCustomerInformation('Upgrade Plan', 'Patch installs are recommended to be performed in the same maintenance window as the OneFS upgrade');
	    }
	}

	# Add the OneFS upgrade to the plan
	push(@$aref_upgrade_plan, \%activity);

	if (isDestinationOneFS($destination) and
	    isDestinationOneFS('>=9.1')) {
	    if (!$RPS{'Node Firmware'}{'Position'} or
		$RPS{'Node Firmware'}{'Position'} ne 'Included') {
			my $comment_forNFP = "Please use the following when updating Node Firmware so nodes with CX6 NIC\'s are excluded: isi upgrade cluster firmware start --fw-pkg=\"full-path-filename\" --nodes-to-upgrade=".compressRange(@{$RPS{'Node Firmware'}{'Nodes'}});
		push(@$aref_upgrade_plan,
		     {
			 'Reference' => $RPS{'OneFS'},
			 'Type' => 'Patch',
			 'Description' => 'Node Firmware Assessment',
			 'Current Code' => $destination,
			 'Duration' => 15,
		     }) unless @{$RPS{'KB000226297'}};
		push(@$aref_upgrade_plan,
		     {
			 'Reference' => $RPS{'OneFS'},
			 'Type' => 'Patch',
			 'Description' => 'Node Firmware Assessment',
			 'Current Code' => $destination,
			 'Duration' => 15,
			 'Additional Comments' => $comment_forNFP,
		     }) if (@{$RPS{'KB000226297'}} and $RPS{'IsiFw'} =~ /^12\.[012]$/);
	    }
	    push(@$aref_upgrade_plan,
		 {
		     'Reference' => $RPS{'OneFS'},
		     'Type' => 'Patch',
		     'Description' => 'Drive Support Package Install',
		     'Current Code' => $destination,
		     'Duration' => 15,
		 });
	}

	# Additional steps required after the OneFS upgrade will prevent
	#  back-to-back OneFS upgrades from being performed
	# Upgrade restriping job that runs post upgrade
	if (!isDestinationOneFS($destination) and
	    isUpgradeRestripe($current_code, $destination)) {
	    my %activity = (
		'Reference' => $RPS{'OneFS'},
		'Type' => 'None',
		'Description' => 'One week delay to allow "Upgrade" data restriping job to complete.',
		'Current Code' => $destination,
		'Standalone' => 1,
		);
	    push(@$aref_upgrade_plan, \%activity);
	}
	# Additional steps required after the OneFS upgrade will prevent
	#  back-to-back OneFS upgrades from being performed
	# CloudPools smart-link Job (stub file conversion) that runs post upgrade
	if ($CLUSTER_INFO{'hasCloudPools'} and $CLUSTER_INFO{'hasCloudPools'} == 1 and compareVersions($current_code, '8.2') < 0 and
	    compareVersions($destination, '8.2') >= 0 and !isDestinationOneFS($destination) and isOneFSUpgrade()) {
	    my %activity = (
		'Reference' => $RPS{'OneFS'},
		'Type' => 'None',
		'Description' => 'One week delay to allow "CloudPools" smartlink-upgrade job to complete.',
		'Current Code' => $destination,
		'Standalone' => 1,
		);
	    push(@$aref_upgrade_plan, \%activity);
	}
	# License conversion required after 8.1 upgrade prior to next OneFS jump
	if (!isDestinationOneFS($destination) and
	    compareVersions($current_code, '8.1') < 0 and
	    compareVersions($destination, '8.1') >= 0) {
	    my %activity = (
		'Reference' => $RPS{'OneFS'},
		'Type' => 'None',
		'Description' => 'License update must be completed prior to next OneFS upgrade',
		'Current Code' => $destination,
		'Standalone' => 1,
		'Duration' => 0,
		);
	    push(@$aref_upgrade_plan, \%activity);
	    addCustomerInformation('Upgrade Plan', "Please note, as you are upgrading from a version prior to OneFS 8.1, and multiple OneFS upgrades are required to reach the requested destination, the upgrade plan has been altered to allow time in between OneFS upgrades for you to update the OneFS license on the cluster. The OneFS upgrade from $destination cannot be performed until you have completed the necessary license update.");

	    if ($CLUSTER_INFO{'RemoteSupport'}{'Enabled'} and
		isDestinationOneFS('>=9.1')) {
		addCustomerInformation('Upgrade Plan', 'The SRSv2 method for remote connectivity via "isi remotesupport connectemc" commands is enabled, after completing the license update, this method must be disabled before upgrading to OneFS 9.1 or later. It is recommended to configure the SRSv3 method for remote connectivity via "isi esrs" commands or in the WebUI. See https://dl.dell.com/content/manual73126448 for more information on how to configure SRSv3.');
	    }
	}
	$current_code = $destination;
    }
} # End UPG_OneFS
###
# UPG_generic: Generate activities for most things
sub UPG_generic {
    my ($aref_plan, $type) = @_;
    if (!$aref_plan or !$type or !$RPS{$type}) { return }
    my $href_details = $RPS{$type};

    ###
    # Create the activity
    my %activity = ();
    $activity{'Reference'} = $RPS{$type};

    # Activity current code
    $activity{'Current Code'} = UPG_getCurrentCode($aref_plan);

    # Activity type, description, and method
    $activity{'Type'} = $type;
    if ($type eq 'Drive Firmware Reboot' or
	$type eq 'Proactive Reboot') {
	$activity{'Reboot'} = 1;
	$activity{'Type'} = 'Reboot';
	if ($RPS{'Simultaneous'}) {
	    $activity{'Method'} = 'simultaneous';
	} else {
	    $activity{'Method'} = 'rolling';
	}
    } elsif ($type eq 'BMC Firmware' or
	     $type eq 'Node Firmware') {
	$activity{'Type'} = 'Node Firmware Update';
	$activity{'Reboot'} = 1;
	push(@{$activity{'Additional Comments'}}, 'Nodes '.compressRange(@{$CLUSTER_INFO{'000350946'}}).' contain P5316 SED drives and require Node Firmware to be done node by node.') if $CLUSTER_INFO{'000350946'};
    push(@{$activity{'Additional Comments'}}, 'Please ensure that the above nodes are updated individually using steps outlined on https://www.dell.com/support/kbdoc/en-us/000350946') if $CLUSTER_INFO{'000350946'};

	if ($href_details->{'Method'} and
	    $href_details->{'Method'} eq 'parallel' and
		!$RPS{'Single Neighborhood'} and
	    compareVersions($activity{'Current Code'}, '8.2') >= 0) {
	    $activity{'Method'} = 'parallel';
	    if (compareVersions($activity{'Current Code'}, '9.2') >= 0) {
		$activity{'Description'} =
		    'Node Firmware Update (Reboot)(Parallel)';
		push(@{$activity{'Additional Comments'}},
		     'Use the additional argument --parallel');
	    } elsif (compareVersions($activity{'Current Code'}, '9') >= 0) {
		$activity{'Description'} =
		    'Node Firmware Update (Reboot)(Balanced)';
		push(@{$activity{'Additional Comments'}},
		     'Perform a "Balanced" node firmware update');
	    } elsif (compareVersions($activity{'Current Code'}, '8.2') >= 0) {
		$activity{'Description'} =
		    'Node Firmware Update (Reboot)(Balanced)';
		push(@{$activity{'Additional Comments'}},
		     'Use isi_upgrade_helper to perform a "Balanced" node'.
		     ' firmware update');
	    }
	} elsif ($href_details->{'Method'} and
		 $href_details->{'Method'} eq 'simultaneous' and
		 compareVersions($activity{'Current Code'}, '8.2') >= 0) {
	    $activity{'Method'} = 'simultaneous';
	    if (compareVersions($activity{'Current Code'}, '9.2') >= 0) {
		$activity{'Description'} =
		    'Node Firmware Update (Reboot)(Simultaneous)';
		push(@{$activity{'Additional Comments'}},
		     'Use the additional argument --simultaneous');
	    } elsif (compareVersions($activity{'Current Code'}, '9') >= 0) {
		$activity{'Description'} =
		    'Node Firmware Update (Reboot)(Fastest)';
		push(@{$activity{'Additional Comments'}},
		     'Perform a "Fastest" node firmware update');
	    } elsif (compareVersions($activity{'Current Code'}, '8.2') >= 0) {
		$activity{'Description'} =
		    'Node Firmware Update (Reboot)(Fastest)';
		push(@{$activity{'Additional Comments'}},
		     'Use isi_upgrade_helper to perform a "Fastest" node'.
		     ' firmware update');
	    }
	} 
	elsif (!$CLUSTER_INFO{'000350946'} and !exists $href_details->{'Method'} and
		 !$RPS{'Single Neighborhood'} and
		 !$RPS{'Disjoint Networks'} and
		 compareVersions($activity{'Current Code'}, '9.2') >= 0) {
	    $activity{'Method'} = 'parallel';
	    $activity{'Description'} =
	      'Node Firmware Update (Reboot)(Parallel)';
	    push(@{$activity{'Additional Comments'}},
		 'Use the additional argument --parallel');
	} 
	elsif (!$CLUSTER_INFO{'000350946'} and !exists $href_details->{'Method'} and
		 !$RPS{'Disjoint Networks'} and
		 !$RPS{'Single Neighborhood'} and
		 compareVersions($activity{'Current Code'}, '8.2') >= 0) {
	    $activity{'Method'} = 'parallel';
	    if (compareVersions($activity{'Current Code'}, '9.2') >= 0) {
		$activity{'Description'} =
		  'Node Firmware Update (Reboot)(Parallel)';
		push(@{$activity{'Additional Comments'}},
		     'Use the additional argument --parallel');
	    } elsif (compareVersions($activity{'Current Code'}, '9') >= 0) {
		$activity{'Description'} =
		  'Node Firmware Update (Reboot)(Balanced)';
		push(@{$activity{'Additional Comments'}},
		     'Perform a "Balanced" node firmware update');
	    } elsif (compareVersions($activity{'Current Code'}, '8.2') >= 0) {
		$activity{'Description'} =
		  'Node Firmware Update (Reboot)(Balanced)';
		push(@{$activity{'Additional Comments'}},
		     'Use isi_upgrade_helper to perform a "Balanced" node'.
		     ' firmware update');
	    }
	} else {
	    $activity{'Method'} = 'rolling';
	    $activity{'Description'} = 'Node Firmware Update (Reboot)(Rolling)';
	    if ($type eq 'Node Firmware' and $href_details->{'Newer'} and
		compareVersions($activity{'Current Code'}, '8') >= 0 and
		compareVersions($activity{'Current Code'}, '9') < 0) {
		push(@{$activity{'Additional Comments'}},
		     'To avoid applying firmware updates to nodes that are'.
		     ' already at newer firmware levels, use the following'.
		     ' additional argument: --nodes-to-upgrade='.
		     compressRange(@{$href_details->{'Nodes'}}));
	    }
	}
    } elsif ($type =~ /Drive Support Package/) {
		$activity{'Description'} = 'Non-Disruptive Drive Firmware Update';
		if($CLUSTER_INFO{'000350946'} and 
		(($RPS{'Drive Support Package'}{'Position'} != '3' and (isCurrentOneFS('<9.5.1.4') or (isCurrentOneFS('>=9.7') and isCurrentOneFS('<9.7.1.8')) or (isCurrentOneFS('>=9.8') and isCurrentOneFS('<9.10.1.3')) or (isCurrentOneFS('>=9.11') and isCurrentOneFS('<9.12'))))
        or
        ($RPS{'Drive Support Package'}{'Position'} == '3' and (isDestinationOneFS('<9.5.1.4') or (isDestinationOneFS('>=9.7') and isDestinationOneFS('<9.7.1.8')) or (isDestinationOneFS('>=9.8') and isDestinationOneFS('<9.10.1.3')) or (isDestinationOneFS('>=9.11') and isDestinationOneFS('<9.12')))))
		){
			push(@{$activity{'Additional Comments'}}, 'Nodes '.compressRange(@{$CLUSTER_INFO{'000350946'}}).' contain P5316 SED drives and require manual updates bay by bay.');
			push(@{$activity{'Additional Comments'}}, 'Please ensure that the drive health is verified after each drive is updated.');
		}
    }

    # Activity destination code
    if ($href_details->{'Target'}) {
	$activity{'Target'} = $href_details->{'Target'};
    }

    # Activity patches
    if ($href_details->{'Patch'}) {
	push(@{$activity{'Patches'}}, keys %{$href_details->{'Patch'}});
    }

    # Activity nodes
    if ($href_details->{'Nodes'}) {
	$activity{'Nodes'} = $href_details->{'Nodes'};
    } else {
	$activity{'Nodes'} = \@LNNS;
    }

    # Activity count
    $activity{'Count'} = scalar(@{$activity{'Nodes'}});

    # Activity comments
    if ($href_details->{'Additional Comments'}) {
	push(@{$activity{'Additional Comments'}},
	     @{$href_details->{'Additional Comments'}});
    }

    # Activity duration
    $activity{'Duration'} = getActivityDuration(\%activity);

	if ($activity{'Reboot'}) {
		push(@{$activity{'Additional Comments'}}, 'Please ensure you proactively flush nodes prior to this activity using: \'screen -S proactive_flush -d -m '.${SUDO}.'isi_for_array -X isi_flush\'');
		if($activity{'Duration'} > 60){
			push(@{$activity{'Additional Comments'}}, 'As this reboot activity is greater than 1 hour, hourly proactive flushing must be performed.');
		}
	}

    # End creating activity
    ###

    # Add to plan
    push(@$aref_plan, \%activity);

    # For any priority updates, provide the customer details
    if ($href_details->{'Priority'}) {
	addCustomerInformation('Upgrade Plan',
			       @{$href_details->{'Priority'}});
    }

    # Add required files and patch impacts
    if ($href_details->{'Patch'}) {
	foreach my $patch (keys %{$href_details->{'Patch'}}) {
	    if ($patch !~ /uninstall/) { addRequiredFiles($patch) }
	    my $href_patch = $href_details->{'Patch'}{$patch};
	    if ($href_patch->{'Impacts'}) {
		addCustomerInformation('Upgrade Plan',
				       $href_patch->{'Impacts'});
	    }
	    if ($href_patch->{'Important'}) {
		addCustomerInformation('Upgrade Plan',
				       $href_patch->{'Important'});
	    }
	}
    }

    # Add required files for the activity
    if ($href_details->{'Files'}) {
	addRequiredFiles(@{$href_details->{'Files'}});
    }
} # UPG_generic
sub UPG_getCurrentCode {
    my ($aref_plan) = @_;
    if (!$aref_plan or !@$aref_plan) {
	return $CLUSTER_INFO{'OneFS'};
    }
    if ($aref_plan->[-1]{'Target'} and
	$aref_plan->[-1]{'Target'} =~ /\d+\.\d+\.\d+\.\d+/) {
	return $aref_plan->[-1]{'Target'};
    }
    return $aref_plan->[-1]{'Current Code'};
}
###
# UPG_patches: The upgrade plan generator for patches
sub UPG_patches {
    my ($aref_plan, $type) = @_;
    if (!$aref_plan or !$type) { return }
    my $href_details = $RPS{$type};
    foreach my $patch_name (patchSort(keys %$href_details)) {
	if (!$patch_name or !defined $href_details->{$patch_name}) { next }
	my %patch = %{$href_details->{$patch_name}};

	# Add required files
	if ($patch_name !~ /uninstall/i) { addRequiredFiles($patch_name) }

	# Add important information
	if ($patch{'Important'}) { addCustomerInformation($patch{'Important'}) }

	# Add impacts if simultaneous or no service restart script
	if ($patch{'Impacts'} and
	    (($RPS{'Patch'}{'Method'} and
	      $RPS{'Patch'}{'Method'} eq 'simultaneous') or
	     !$patch{'Service Restart'})) {
	    addCustomerInformation($patch{'Impacts'});
	}

	###
	# Create the activity
	my %activity = (
	    'Reference' => $RPS{$type},
	    'Patches' => [ $patch_name ],
	    'Type' => 'Patch',
	    'Current Code' => UPG_getCurrentCode($aref_plan),
	    'Nodes' => \@LNNS,
	    );

	# Patches that change the OneFS version
	if ($patch{'Target'}) { $activity{'Target'} = $patch{'Target'} }

	# Patches that require node reboots
	if ($patch{'Reboot'}) {
	    $activity{'Reboot'} = 1;
	    $activity{'Nodes'} = $patch{'Reboot'};
	}

	# Set the activity method and some additional values
	if ($RPS{'Patch'}{'Method'} and
	    $RPS{'Patch'}{'Method'} eq 'simultaneous') {
	    $activity{'Method'} = 'simultaneous';
	} elsif ($patch{'Reboot'} and
		 compareVersions($activity{'Current Code'}, '9.1') >= 0 and
		 ((exists $RPS{'Patch'}{'Method'} and $RPS{'Patch'}{'Method'} eq 'parallel') or
		  (!exists $RPS{'Patch'}{'Method'} and !$RPS{'Disjoint Networks'} 
		  and !$RPS{'Single Neighborhood'}
		  ))) {
	    $activity{'Method'} = 'parallel';
	} elsif ($patch{'Reboot'}) {
	    $activity{'Method'} = 'rolling';
		} elsif ($patch{'Service Restart'}) {
	    $activity{'Method'} = 'rolling';
	    if ($patch{'Service Restart'} == 1) {
		$activity{'Reboot'} = 1;
	    }
	} else {
	    $activity{'Method'} = 'simultaneous';
	}

	# Set the activity description
	$activity{'Description'} =
	    'Patch'.
	    (($patch_name =~ /uninstall/i)?' Uninstall':' Install').
	    ($activity{'Reboot'}?' (Reboot)':' (Rebootless)').
	    '('.ucfirst($activity{'Method'}).')';

	# Add to additional comments on how the patch will be applied
	if (compareVersions($activity{'Current Code'}, '9.1') >= 0) {
	    $activity{'Additional Comments'} =
		[ 'Use the additional argument --'.$activity{'Method'} ];
	} elsif ($activity{'Method'} eq 'simultaneous') {
	    if (compareVersions($activity{'Current Code'}, '8.2') >= 0) {
		$activity{'Additional Comments'} =
		    [ 'Use the additional argument --simultaneous' ];
	    } elsif (compareVersions($activity{'Current Code'}, '8') >= 0) {
		$activity{'Additional Comments'} =
		    [ 'Use the additional argument --rolling=false' ];
	    }
	}

	# Add that only some nodes need to be rebooted
	if ($patch{'Reboot'} and $patch{'Nodes'} and
	    scalar(@{$patch{'Nodes'}}) < $CLUSTER_INFO{'Node Count'}) {
	    push(@{$activity{'Additional Comments'}},
		 'Only the following nodes require rebooting: '.
		 compressRange(@{$patch{'Nodes'}}));
	}

	if ($patch{'Additional Comments'}) {
	    push(@{$activity{'Additional Comments'}},
		 @{$patch{'Additional Comments'}});
	}

	$activity{'Duration'} = getActivityDuration(\%activity);
	if ($activity{'Reboot'}) {
		push(@{$activity{'Additional Comments'}}, 'Please ensure you proactively flush nodes prior to this activity using: \'screen -S proactive_flush -d -m '.${SUDO}.'isi_for_array -X isi_flush\'');
		if($activity{'Duration'} > 60){
			push(@{$activity{'Additional Comments'}}, 'As this reboot activity is greater than 1 hour, hourly proactive flushing must be performed.');
		}
	}
	push(@$aref_plan, \%activity);
    }
}
###
# addCustomerInformation: Adds information to be provided to the customer
#  while attempting to ensure information is unique.
sub addCustomerInformation {
    my ($section, @info) = @_;
    if (!$section) { return }
    if (@info) {
	push(@{$RPS{'Customer Information'}{$section}}, @info);
    } else {
	push(@{$RPS{'Customer Information'}{'Generic'}}, $section);
    }
} # End addCustomerInformation
sub addReleaseNotes {

	my $UPG_SECTION = 'Customer Preparation Guide';
    my @cupg = ();
	my $MSG_PG = 'Review the Upgrade Preparation Guide (UPG) prior to your upgrade tasks.';
	push(@cupg, "Customer Upgrade Preparation Guide: https://www.dell.com/support/kbdoc/000200890");
	addCustomerInformation($UPG_SECTION, $MSG_PG, @cupg);

	# OneFS Release Notes
    my $MSG_RNS = 'Review the OneFS release notes for each major OneFS version'.
	' between the current and destination OneFS versions prior to'.
	' upgrading.';
    my $MSG_DSP = "Drive Support Package Release Notes: $ReleaseNotes{'DF'}";
    my $SECTION = 'Release Notes';
    my @rns = ();
    foreach my $version (sort {compareVersions($b, $a)} keys %{$ReleaseNotes{'OneFS'}}) {
	if (isCurrentOneFS("<$version") and isDestinationOneFS(">=$version")) {
	    push(@rns, "OneFS $version Release Notes: ".
		 "$ReleaseNotes{'OneFS'}{$version}");
	}
    }
    if (@rns) {	addCustomerInformation($SECTION, $MSG_RNS, @rns) }

    # Patch README
    if ($RPS{'Post Upgrade Patches'} or $RPS{'Pre Upgrade Patches'}) {
	my $version = isDestinationOneFS() || isCurrentOneFS();
	my $major = '';
	if ($version =~ /^(^\d+\.\d+\.\d+)\./) { $major = $1 }
	if ($ReleaseNotes{'RUP'}{$major}) {
	    addCustomerInformation($SECTION,
				   "OneFS $major RUP README:".
				   " $ReleaseNotes{'RUP'}{$major}");
	}
    }

    # Drive Support Package Release Notes
    if ($RPS{'Drive Support Package'}{'Target'}) {
	addCustomerInformation($SECTION, $MSG_DSP);
    }

    # Node Firmware Package Release Notes
    if ($RPS{'Node Firmware'}{'Target'}) {
	my $major = '';
	if ($RPS{'IsiFw'} =~ /^(\d+)\./) { $major = $1 }
	foreach my $version (keys %{$ReleaseNotes{'NF'}}) {
	    if (compareVersions($version, $major) == 0) {
		addCustomerInformation($SECTION,
				       "Node Firmware Package $version Release".
				       " Notes: $ReleaseNotes{'NF'}{$version}");
	    }
	}
    }
	my $AR_SECTION = 'Additional Resources';
    my @add_res = ();
	my $MSG_AD = 'Additional documentation related to cluster administration can be found on the following InfoHubs.';
	push(@add_res, "PowerScale OneFS InfoHub: https://www.dell.com/support/kbdoc/000152189");
	push(@add_res, "PowerScale Firmware InfoHub: https://www.dell.com/support/kbdoc/000198854");
	push(@add_res, "PowerScale Current Patches: https://www.dell.com/support/kbdoc/000184794");
	addCustomerInformation($AR_SECTION, $MSG_AD, @add_res);
}
###
# addRequiredFiles: Adds files to the list of required files, making
#  sure to merge with existing files to avoid any duplicates
sub addRequiredFiles {
    my @files = @_;
    if (!@files) { return }
    if (!$RPS{'Files'}) { @{$RPS{'Files'}} = () }
    @{$RPS{'Files'}} = merge(@{$RPS{'Files'}}, @files);
} # End addRequiredFiles
###
# getActivitiesName: Returns an array of activity names that
#  corresponds to activities in the RCM Scheduler based on the
#  activity type ( Drive Firmware/ Node Firmware/ Rolling
#  Upgrade/ Rolling Reboot/ FCO F042415EE ) and a number of
#  nodes or for Isilon Hourly activities the duration.
sub getActivitiesName {
    my ($self) = @_;
    if (!$self) { return }
    our %Activities;
    my $type = $self->{'Type'};
    if ($type eq 'Node Firmware Update' or
	$type eq 'OneFS Upgrade' or
	$type eq 'Drive Support Package' or
	$type eq 'Patch' or
	$type eq 'Reboot') {
	$type = 'Isilon Hourly';
    } elsif ($type eq 'None') {
	$type = $self->{'Description'};
    }
    if (!$Activities{$type}) {
	return ($type);
    }
    my $count = $self->{'Count'};
    if ($type eq 'Isilon Hourly' and $self->{'Duration'}) {
	$count = $self->{'Duration'} / 60;
    }
    if (!$count) { return }
    my $last_index = 0;
    my @activities = ();
    foreach my $index (sort {$b <=> $a} keys %{$Activities{$type}}) {
	if ($count <= 0) { last }
	if ($count > $index) {
	    if ($last_index) { $index = $last_index }
	    push(@activities, $Activities{$type}{$index});
	    $count = $count - $index;
	    $last_index = 0;
	    redo;
	}
	if ($count == $index) {
	    push(@activities, $Activities{$type}{$index});
	    $last_index = 0;
	    last;
	}
	$last_index = $index;
    }
    if ($last_index) {
	push(@activities, $Activities{$type}{$last_index});
    }
    return @activities;
} # End getActivitiesName
# getActivityDuration: Return the estimated duration for this activity
sub getActivityDuration {
    my ($self) = @_;
    if (!$self) { return 0 }
    if (!$self->{'Type'}) { return 0 }
    our %Durations;

    ###
# Parallel upgrade estimates
if ($self->{'Nodes'} and
	$self->{'Reboot'} and
	(
		$self->{'Method'} and
		$self->{'Method'} eq 'parallel'
	) and
	!$RPS{'Single Neighborhood'} and
	(
		$self->{'Type'} eq 'OneFS Upgrade' or
		$self->{'Type'} eq 'Node Firmware Update' or
	($self->{'Type'} eq 'Patch' and 
	 (isCurrentOneFS('>=9.5.0.4') or ((isCurrentOneFS('=9.4') and isCurrentOneFS('>=9.4.0.15')))))
	)
	) {	
		gatherNeighborhoods();
		if ($CLUSTER_INFO{'Neighborhoods'}) {
			my @nodes = @{$self->{'Nodes'}};
			delete $self->{'Nodes'};
			$self->{'Method'} = 'rolling';
			my $max_duration = 0;
			foreach my $n (values %{$CLUSTER_INFO{'Neighborhoods'}}) {
			
			@{$self->{'Nodes'}} = array_duplicates(@$n, @nodes);
			my $duration = getActivityDuration($self);
			#print "Node @$n | @{$self->{'Nodes'}} | DURATION FOR $self->{'Type'} | $self->{'Method'} = $duration\n";
			if ($duration > $max_duration) {
				$max_duration = $duration;
			}
			}
			$self->{'Method'} = 'parallel';
			@{$self->{'Nodes'}} = @nodes;
			#print "MAX DURATION = $max_duration\n";
			return $max_duration;
		}
}

    # Simultaneous Node Firmware estimate, 1 hour, doubles for Gen6 hardware
    if ($self->{'Method'} and
	$self->{'Method'} eq 'simultaneous' and
	$self->{'Nodes'} and
	$self->{'Type'} eq 'Node Firmware Update') {
	if ($CLUSTER_INFO{'Gen6'}) {
	    my @nodes = array_duplicates(@{$CLUSTER_INFO{'Gen6'}},
					   @{$self->{'Nodes'}});
	    if (@nodes) { return 120 }
	}
	return 60;
    }

    # Activities defined as 'Isilon Hourly' have a preset count which is the
    # number of hours associated with the activity
    if ($self->{'Type'} eq 'Isilon Hourly') {
	return $self->{'Count'} * 60;
    }

    # Start calculating the duration
    my $duration = 0;

    # Add time for the base duration
    if ($Durations{$self->{'Type'}}{'Base'}) {
	#print "Base Duration is $duration, adding in $Durations{$self->{'Type'}}{'Base'} \n";
	$duration += $Durations{$self->{'Type'}}{'Base'};
	}

    # Add time for the node duration
    if ($self->{'Method'} and
	$self->{'Nodes'} and
	$self->{'Reboot'} and
	$Durations{$self->{'Type'}}{'Nodes'}) {
	my $dur = $Durations{$self->{'Type'}}{'Nodes'};
	if ($self->{'Method'} eq 'simultaneous') {
	    #print "$duration + ($dur * 1.5) \n";
		$duration += ($dur * 1.5);
	} else {
	#print "Node $duration + ($dur * scalar(@{$self->{'Nodes'}}) \n";    
		$duration += ($dur * scalar(@{$self->{'Nodes'}}));
	}
    }

    # Add time based on hardware type for reboots
    if ($self->{'Reboot'} and
	$self->{'Nodes'} and
	$self->{'Method'}) {
	my $href_dur = $Durations{'Reboot'};
	my $max_duration = 0;
	my $total_duration = 0;
	foreach my $lnn (@{$self->{'Nodes'}}) {
	    if (!$NODE_INFO{$lnn}{'Product'}) { next }
	    my $prod = $NODE_INFO{$lnn}{'Product'};
	    my ($match) = grep {$prod =~ /$_/} keys %{$href_dur};
	    if (!$match) { next }
		#print "$prod | $total_duration += $href_dur->{$match} + $href_dur->{'Nodes'}\n";
	    #$total_duration += ($href_dur->{$match} + $href_dur->{'Nodes'});
	    $total_duration += $href_dur->{$match};
	    if ($href_dur->{$match} > $max_duration) {
		#$max_duration = ($href_dur->{$match} + $href_dur->{'Nodes'});
		$max_duration = $href_dur->{$match};
		#print "($href_dur->{$match} + $href_dur->{'Nodes'}) | MAX = $max_duration \n";
	    }
	}
	if ($self->{'Method'} eq 'simultaneous') {
	    $duration += $max_duration;
	} else {
	    $duration += $total_duration;
	}
    }

    # Add time per drive for Drive Support Package
    if ($self->{'Reference'} and
	$self->{'Reference'}{'Drives'}) {
	my $dur;
	if(compareVersions($self->{'Current Code'},'9.12') >= 0){
		$dur = 0.25;
	}
	else{
		$dur = $Durations{'Drive Support Package'}{'Drives'};
	}
	my $drives = $self->{'Reference'}{'Drives'};
	$duration += ($drives * $dur);
    }

    # Add time for service restarts on rebootless rolling patch installs
    if ($self->{'Type'} eq 'Patch' and
	!$self->{'Reboot'}) {
	my $dur = $Durations{'Patch'}{'Service Restart'};
	if ($self->{'Method'} and
	    $self->{'Method'} eq 'simultaneous') {
	    $duration += ($dur * 1.5);
	} else {
		#print "Duration is currently $duration";
	    $duration += ($dur * scalar(@{$self->{'Nodes'}}));
		#print "Adding ($dur * scalar(@{$self->{'Nodes'}}) Duration is now $duration";
	}
    }

    # Add time based on the device types for Node Firmware Updates
    if ($self->{'Reference'} and $self->{'Reference'}{'Devices'}) {
	$self->{'Devices'} = $self->{'Reference'}{'Devices'};
    }
    if ($self->{'Nodes'} and $self->{'Devices'}) {
	my $href_dur = $Durations{'Node Firmware Update'};
	foreach my $dev (keys %{$self->{'Devices'}}) {
	    my ($match) = grep {$dev =~ /$_/} keys %{$href_dur};
	    if (!$match) { next }
		foreach my $node (keys %{$self->{'Devices'}{$dev}}) {
		    my @nodes = array_duplicates(@{$self->{'Nodes'}},
					 @{$self->{'Devices'}{$dev}{$node}});
		#print "Duration is $duration, adding time for $match | $duration += ($href_dur->{$match} * scalar(@nodes)) \n";
	    $duration += ($href_dur->{$match} * scalar(@nodes));
		}
	}
    }
	#print "Final Duration is $duration \n";
    return $duration;
} # End getActivityDuration
###
# isUpgradeRestripe: Provided a source and destination version, returns 1 if
#  an upgrade job will run after the upgrade
sub isUpgradeRestripe {
    my ($source, $destination) = @_;
    if (!$source or !$destination) {
	debug('isUpgradeRestripe: Not enough arguments provided.');
	return 0;
    }
    debug("isUpgradeRestripe: Source: $source, Destination: $destination");

    # List of versions where upgrade jobs run after upgrade
    my @upgrade_restripe = ( '6', '7', '8', '9.2' );

    # Return 1 if the source to destination upgrade starts an upgrade job
    foreach my $version (@upgrade_restripe) {
	if (compareVersions($source, $version) < 0 and
	    compareVersions($destination, $version) >= 0) {
	    return 1;   
	}
    }

    # Return 0 if it will not
    return 0;
} # End isUpgradeRestripe
###
# isUpgradeSimultaneous: Provided a source and target version, returns 1 if upgrade must be simultaneous
sub isUpgradeSimultaneous {
    if (!(defined $_[0] and defined $_[1])) {
	debug("isUpgradeSimultaneous: Not enough arguments provided.");
	return 0;
    }
    debug("isUpgradeSimultaneous: $_[0] $_[1]");
    return 1 if (compareVersions($_[0], "5") == 0 and compareVersions($_[1], "6") >= 0);
    return 1 if (compareVersions($_[0], "6.0") == 0 and compareVersions($_[1], "6.5") >= 0);
    return 1 if (compareVersions($_[0], "6.5") == 0 and compareVersions($_[1], "7") >= 0);
    return 1 if (compareVersions($_[0], "7.0") == 0 and compareVersions($_[1], "7.1") >= 0);
    return 1 if (compareVersions($_[0], "7") == 0 and compareVersions($_[1], "8") >= 0);
    return 1 if ($_[0] =~ /^7\.1\.1\.[89]$/ and $_[1] =~ /^7\.2\.1\.3$/);
    return 0;
} # End isUpgradeSimultaneous
###
# patchSort: Sorting routine to allow patch ordering when a patch has a priority
sub patchSort {
    my @unsorted = @_;
    # Sort first by priority
    foreach my $a (0..$#unsorted) {
	foreach my $b (0..$#unsorted) {
	    next unless $a < $b;
	    if (($RPS{$_}{$unsorted[$a]}{'Priority'} and $RPS{$_}{$unsorted[$b]}{'Priority'} and
		 $RPS{$_}{$unsorted[$a]}{'Priority'} < $RPS{$_}{$unsorted[$b]}{'Priority'}) or
		(!$RPS{$_}{$unsorted[$a]}{'Priority'} and $RPS{$_}{$unsorted[$b]}{'Priority'} and
		 0 < $RPS{$_}{$unsorted[$b]}{'Priority'}) or
		($RPS{$_}{$unsorted[$a]}{'Priority'} and !$RPS{$_}{$unsorted[$b]}{'Priority'} and
		 $RPS{$_}{$unsorted[$a]}{'Priority'} < 0)) {
		my $tmp = $unsorted[$a];
		$unsorted[$a] = $unsorted[$b];
		$unsorted[$b] = $tmp;
	    }
	}
    }

    # Adjust for any pre-requisites
    foreach my $a (0..$#unsorted) {
	foreach my $b (0..$#unsorted) {
	    if ($RPS{$_}{$unsorted[$b]}{'Requires'}) {
		foreach my $i ($b..$#unsorted) {
		    next unless $i > $b;
		    foreach my $r (@{$RPS{$_}{$unsorted[$b]}{'Requires'}}) {
			next unless $unsorted[$i] eq $r;
			my $tmp = splice(@unsorted, $i, 1);
			splice(@unsorted, $b, 0, $tmp);
		    }
		}
	    }
	}
    }
    return @unsorted
} # End patchSort
###
# printCustomerInformation: Prints gathered customer information
sub printCustomerInformation {
    if (!$RPS{'Customer Information'}) { return }
    my @info = ();
    push(@info, '');
    push(@info, '');
    push(@info, colored('The following information should be included to the customer:', 'bold underline'));
    push(@info, '');
    if ($RPS{'Customer Information'}{'Generic'}) {
	push(@info, @{$RPS{'Customer Information'}{'Generic'}});
    }
    foreach my $header (keys %{$RPS{'Customer Information'}}) {
	if ($header eq 'Generic') { next }
	push(@info, '-'x20);
	push(@info, $header);
	push(@info, '-'x20);
	push(@info, @{$RPS{'Customer Information'}{$header}});
    #push(@info, @{$CLUSTER_INFO{'Protection Levels'}});
    }
    push(@info, '');

    # Print the customer information
    local $, = "\n";
    print @info unless $RPS{'JSON'};
} # End printCustomerInformation
###
# printRequiredFiles: Prints the list of files required
sub printRequiredFiles {
    if (!$RPS{'Files'}) { return }
    our %CodeLinks;
    our %Bundles;
    my @files = @{$RPS{'Files'}};

    # Remove files that are included in a bundle
    my @bundles = grep {$_ =~ /bundle/i} @files;
    foreach my $bundle (@bundles) {
	foreach my $file (@{$Bundles{$bundle}}) {
	    @files = grep {$_ ne $file} @files;
	}
    }

    # Build the output to print
    my @output = ();
    push(@output, '');
    push(@output, '');
    push(@output, colored('The following files will need to be downloaded to /ifs/data/:', 'bold underline'));
    push(@output, '');
    foreach my $file (sort @files) {
	push(@output, $file);
	if ($CodeLinks{$file}{'Code'}){
	    push(@output, "Code: $CodeLinks{$file}{'Code'}");
	}
	if ($CodeLinks{$file}{'sha256'}) {
	    push(@output, "sha256: $CodeLinks{$file}{'sha256'}");
	}
	push(@output, '');
    }

    # Print the files
    local $, = "\n";
    print @output unless $RPS{'JSON'};
} # End printRequiredFiles
###
# printUpgradePlan: Prints out the upgrade plan
sub printUpgradePlan {
    my ($aref_plan) = @_;
    if (!$aref_plan or !@$aref_plan) { return }

    # Determine the OneFS destination
    my $destination = '0.0.0.0';
	my $parse_target;
	my $target_parse;
    foreach my $href_act (@$aref_plan) {

	if (!$href_act->{'Target'} or $href_act->{'Target'} !~ /^(\d+\.\d+\.\d+\.\d+).*$/) { next }

	$parse_target = $1 if $href_act->{'Target'} =~ /^(\d+\.\d+\.\d+\.\d+).*$/;
	if (compareVersions($destination, $parse_target) < 0) {
	    $destination = $parse_target;
	}
    }

    my @output = ();
    push(@output, '');
    push(@output, '');
    push(@output, colored('The following upgrade plan is for scheduling activities to be performed by Remote Proactive:', 'bold underline'));
    push(@output, '');
    push(@output, 'System Details:');
	$COMPLETE_RESULT{'Plan'}{'Cluster Name'} = $CLUSTER_INFO{'Name'};
    push(@output, "Cluster Name: $CLUSTER_INFO{'Name'}");
	$COMPLETE_RESULT{'Plan'}{'Software ID'} = $CLUSTER_INFO{'OneFS License ID'};
    push(@output, "Software ID: $CLUSTER_INFO{'OneFS License ID'}");
	$COMPLETE_RESULT{'Plan'}{'Number of Nodes'} = $CLUSTER_INFO{'Node Count'};
    push(@output, "Number of Nodes: $CLUSTER_INFO{'Node Count'}");
    if ($CLUSTER_INFO{'SyncIQ'}{'Source'} || $CLUSTER_INFO{'SyncIQ'}{'Target'}) {
	$COMPLETE_RESULT{'Plan'}{'Replication Status (SyncIQ)'} = 'Yes';
	push(@output, 'Replication Status (SyncIQ): Yes');
    } else {
	$COMPLETE_RESULT{'Plan'}{'Replication Status (SyncIQ)'} = 'No';
	push(@output, 'Replication Status (SyncIQ): No');
    }
    if (isCompliance()) {
	$COMPLETE_RESULT{'Plan'}{'Compliance Mode'} = 'Yes';
	push(@output, 'Compliance Mode: Yes');
    } else {
	$COMPLETE_RESULT{'Plan'}{'Compliance Mode'} = 'No' ;
	push(@output, 'Compliance Mode: No');
    }
	if (isHardened()) {
	$COMPLETE_RESULT{'Plan'}{'Hardening Enabled'} = 'Yes';
	push(@output, 'Hardening Enabled: Yes');
    } else {
	$COMPLETE_RESULT{'Plan'}{'Hardening Enabled'} = 'No' ;
	push(@output, 'Hardening Enabled: No');
    }
	if(hasCloudPools){
	$COMPLETE_RESULT{'Plan'}{'CloudPools Configured'} = 'Yes';
	push(@output, 'CloudPools Configured: Yes');
    } else {
	$COMPLETE_RESULT{'Plan'}{'CloudPools Configured'} = 'No' ;
	push(@output, 'CloudPools Configured: No');
	}
    $COMPLETE_RESULT{'Plan'}{'Current OneFS'} = $CLUSTER_INFO{'OneFS'};
	push(@output, "Current OneFS: $CLUSTER_INFO{'OneFS'}");
    if ($destination ne '0.0.0.0') {
	$COMPLETE_RESULT{'Plan'}{'Destination OneFS'} = $destination;
	push(@output, "Destination OneFS: $destination");
    } else {
	$COMPLETE_RESULT{'Plan'}{'Destination OneFS'} = 'No OneFS Change';
	push(@output, 'Destination OneFS: No OneFS Change');
    }
    push(@output, '');
    push(@output, 'Schedule the following activities:');
    my $activity_number = 0;
    if ($RPS{'AutoTC'}) {
	if ($RPS{'Requires TC'}) {
	    $activity_number++;
		$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Activity Name'} = 'Technical Consultation';
	    push(@output, "$activity_number: Technical Consultation");
	} elsif ($RPS{'Issues'}) {
	    $activity_number++;
		$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Activity Name'} = 'Triage Event';
	    push(@output, "$activity_number: Triage Event");
	}
    }
    foreach my $href_act (@$aref_plan) {
		next unless $href_act->{'Type'};
		my @activities = getActivitiesName($href_act);
		$target_parse = $1 if $href_act->{'Target'} =~ /^(\d+\.\d+\.\d+\.\d+).*$/;
		my $trgt_vers;
		foreach my $activity_name (@activities) {
			$activity_number++;
			if ($activity_number > 1) { push(@output, '') }
			$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Activity Name'} = $activity_name;
			push(@output, "$activity_number: $activity_name");
			if($activity_name =~ /.*Hours$/ and $href_act->{'Description'} != ''){ push(@output, "Activity Type: $href_act->{'Description'}"); }
			if ($href_act->{'Type'} eq 'None') { next }
			$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Current Code'} = $href_act->{'Current Code'};
			push(@output, "Current Code: $href_act->{'Current Code'}");
			if ($href_act->{'Target'}) {
				if($RPS{'Node Firmware'}{'Include_Vers'} and $href_act->{'Target'} eq $destination and $href_act->{'Target'} !~ /Drive Support/) {
					$trgt_vers = $href_act->{'Target'}.' + '.$RPS{'Node Firmware'}{'Include_Vers'};
				} else {
					$trgt_vers = $href_act->{'Target'};
				};
			$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Upgrading To'} = $trgt_vers;
			push(@output, "Upgrading To: $trgt_vers");
			} else {
			$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Upgrading To'} = 'No OneFS Change';
			push(@output, 'Upgrading To: No OneFS Change');
			}
			if ($href_act->{'Patches'}) {
			local $" = ', ';
			$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Patches'} = \@{$href_act->{'Patches'}};
			push(@output, "Patch: @{$href_act->{'Patches'}}");
			}
			if ($href_act->{'Additional Comments'}) {
				if (scalar(@{$href_act->{'Additional Comments'}}) == 1) {
					$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Additional Comments'}{'1'} = $href_act->{'Additional Comments'}[0] if $RPS{'JSON'};
					push(@output, "Additional Comments: $href_act->{'Additional Comments'}[0]");
				} else {
					local $" = "\n# ";
					my $c_count = 0;
					my $d_count = 0;
					if($RPS{'JSON'}){
						foreach $t (@{$href_act->{'Additional Comments'}}){ 
							if($t =~ /^(.*):(.*)$/ && $t !~ /^.*flush.*$/){
								my $k = $1;
								my $v = $2;
								$c_count++ if $k eq 'Activity Type';
								$d_count = 1;
								$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Additional Comments'}{$c_count}{$k} = "$v";
							}
							elsif(defined($COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Additional Comments'}{$c_count}{'Activity Type'}) && $t ne ''){
								$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Additional Comments'}{$c_count}{'Additional Details'}{$d_count} = "$t";
								$d_count++;
							}
							elsif($t ne '' && $t !~ /^\s*$/){
								#if(!defined($COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Additional Comments'}{$c_count}{'Activity Type'}) && $c_count == 0){ $c_count = 1; }
								$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Additional Comments'}{$c_count} = "$t";
								$c_count++;						
							}
							else{
								next;
							}
								#$COMPLETE_RESULT{'Plan'}{'Activities'}{$activity_number}{'Additional Comments'}{'Comment '.$c_count} = "$t";	
						}
					}
					push(@output, "Additional Comments: \n# @{$href_act->{'Additional Comments'}}");
				}
			}
		}
    }
    push(@output, '');
	if($RPS{'UpgradePlan'} && $RPS{'JSON'}){
		$RPS{'UPG_PLAN_DETAIL'} = %{ $COMPLETE_RESULT{'Plan'} };
	}
    # Print the upgrade plan
    local $, = "\n";
    print @output unless $RPS{'JSON'};
	
} # End printUpgradePlan
###
# removeRequiredFiles: Remove files from the list of required files
sub removeRequiredFiles {
    my @files = @_;
    if (!@files or !$RPS{'Files'}) { return }
    foreach my $file (@files) {
	@{$RPS{'Files'}} = grep {$_ ne $file} @{$RPS{'Files'}};
    }
} # End removeRequiredFiles
###
# upgradePlan: Steps used to generate and print an upgrade plan
sub upgradePlan {
    if (wasCalled()) { return }

    # Run a few checks, the bare minimum for an upgrade plan
    checkUptime();
    checkNodeFirmware();
    checkDriveFirmware();
    checkUpgradePath();
    checkPatches();

    # Generate the upgrade plan
    my $order = upgradePlanGenerateOrder();
    my $plan = upgradePlanGeneratePlan($order);
    my $combined_plan = upgradePlanConvertToCombined($plan);
    my $similar_plan =  upgradePlanCombinedSimilar($plan);
    my $overview = upgradePlanGenerateOverview($plan);
    if (@$overview) {
	addCustomerInformation('Overview of Upgrade Steps', @$overview);
    }

    # Print the upgrade plan
    addReleaseNotes();
    printCustomerInformation();
    printRequiredFiles();
    if ($RPS{'F011018EE'} or
	$RPS{'F121917EE'} or
	(scalar(@$combined_plan) == 1 and
	 $combined_plan->[0]{'Count'} <= 12)) {
    	printUpgradePlan($combined_plan);
    } else {
	printUpgradePlan($similar_plan);
    }
    #printUpgradePlan($plan);
    #printUpgradePlan($combined_plan);
    #printUpgradePlan($similar_plan);
} # End upgradePlan
sub upgradePlanCombinedSimilar {
    my ($aref_upgrade_plan) = @_;
    if (!$aref_upgrade_plan) { return }
    my @new_plan = ();
    my @tmp_plan = ();
    my $href_previous = undef;
    foreach my $href_current (@$aref_upgrade_plan) {
	if (!$href_previous) {
	    $href_previous = $href_current;
	    @tmp_plan = ($href_previous);
	    next;
	}
	if ($href_current->{'Type'} ne 'None' and
	    ($href_previous->{'Type'} eq $href_current->{'Type'} or
	     ($href_previous->{'Type'} eq 'OneFS Upgrade' and
	      $href_current->{'Type'} eq 'Patch'))) {
	    my $test_plan = upgradePlanConvertToCombined
		([@tmp_plan, $href_current]);
	    if (scalar(@$test_plan) > 1) {
		if (scalar(@tmp_plan) > 1) {
		    push(@new_plan, shift(@$test_plan));
		} else {
		    push(@new_plan, $href_previous);
		}
		$href_previous = undef;
		redo;
	    } else {
		push(@tmp_plan, $href_current);
		$href_previous = $href_current;
	    }
	} else {
	    if (scalar(@tmp_plan) > 1) {
		my $test_plan = upgradePlanConvertToCombined(\@tmp_plan);
		push(@new_plan, shift(@$test_plan));
	    } else {
		push(@new_plan, $href_previous);
	    }
	    $href_previous = undef;
	    redo;
	}
    }
    if (@tmp_plan) {
	if (scalar(@tmp_plan) > 1) {
	    my $test_plan = upgradePlanConvertToCombined(\@tmp_plan);
	    push(@new_plan, shift(@$test_plan));
	} else {
	    push(@new_plan, $href_previous);
	}
    }
    return \@new_plan;
}
###
# upgradePlanConvertToCombined: Given an upgrade plan will convert it
# into a plan that is hourly activities and combined
sub upgradePlanConvertToCombined {
    my ($aref_upgrade_plan) = @_;
    if (!$aref_upgrade_plan) { return }
    my @c_plan = ();
    my $c_href = undef;
    foreach my $a_href (@$aref_upgrade_plan) {
	if ($a_href->{'Standalone'} or
	    $a_href->{'Duration'} + ($c_href?$c_href->{'Duration'}:0) >= 720) {
	    push(@c_plan, $c_href);
	    $c_href = undef;
	}
	if ($a_href->{'Standalone'}) {
	    push(@c_plan, $a_href);
	    next;
	}
	if (!$c_href) {
	    $c_href->{'Type'} = 'Isilon Hourly';
	    if ($a_href->{'Current Code'}) {
		$c_href->{'Current Code'} = $a_href->{'Current Code'};
	    }
	} else {
	    push(@{$c_href->{'Additional Comments'}}, '');
	}
	$c_href->{'Duration'} += $a_href->{'Duration'};
	$c_href->{'Count'} = $c_href->{'Duration'} / 60;
	if ($a_href->{'Type'}) {
	    my ($type) = getActivitiesName($a_href);
	    if ($a_href->{'Description'}) {
		$type = $a_href->{'Description'};
	    }
	    push(@{$c_href->{'Additional Comments'}},
		 sprintf('Activity Type: %s (%.1f hours)',
			 $type, $a_href->{'Duration'} / 60));
	}
	push(@{$c_href->{'Additional Comments'}},
	     "Current Code: $a_href->{'Current Code'}");
	if ($a_href->{'Target'}) {
	    push(@{$c_href->{'Additional Comments'}},
		 sprintf('Upgrading To: %s',
			 $a_href->{'Target'}));
	    if ($a_href->{'Target'} =~ /^\d+\.\d+\.\d+\.\d+$/ and
		(!$c_href->{'Target'} or
		 compareVersions($c_href->{'Target'}, $a_href->{'Target'}) < 0)) {
		$c_href->{'Target'} = $a_href->{'Target'};
	    }
	}
	if ($a_href->{'Patches'}) {
	    push(@{$c_href->{'Additional Comments'}},
		 sprintf('Patch: %s',
			 join(', ', @{$a_href->{'Patches'}})));
	}
	if ($a_href->{'Additional Comments'}) {
	    if (scalar(@{$a_href->{'Additional Comments'}} == 1)) {
		push(@{$c_href->{'Additional Comments'}},
		     sprintf('Additional Details: %s',
			     $a_href->{'Additional Comments'}[0]));
	    } else {
		push(@{$c_href->{'Additional Comments'}},
		     join("\n# ", 'Additional Details:',
			  @{$a_href->{'Additional Comments'}}));
	    }
	}
    }
    if ($c_href) { push(@c_plan, $c_href) }
    return \@c_plan;
} # End upgradePlanConvertToCombined
###
# upgradePlanGenerateOrder: Returns the order of activites to be performed
sub upgradePlanGenerateOrder {

    # Just incase node firmware was flagged to be performed post OneFS, do BMC firmware updates post node firmware
    # Unless updating to OneFS 9.4 as there is no signed BMC firmware package
    if ($RPS{'BMC Firmware'}{'Position'}
        and $RPS{'BMC Firmware'}{'Position'} == 1
        and $RPS{'CMC Firmware'}{'Nodes'}
        and $RPS{'Node Firmware'}{'Position'}
        and $RPS{'Node Firmware'}{'Position'} == 2
	and isDestinationOneFS('<9.4')) {
        $RPS{'BMC Firmware'}{'Position'} = 2;
    }

    #CHANGE

    # Adjust for combined OneFS + NF
    if (
        !$RPS{'OneFS'}{'Exclude Node Firmware'} and
	    (
            (
                !$RPS{'OneFS'}{'Method'} and !$RPS{'Disjoint Networks'} and !$RPS{'Single Neighborhood'}
            ) or
         $RPS{'OneFS'}{'Method'} eq 'parallel'
        ) and
	    $RPS{'Node Firmware'}{'Position'} and
	    $RPS{'Upgrade Path'} and
	    scalar(@{$RPS{'Upgrade Path'}}) > 1 and 
        #$RPS{'Node Firmware'}{'Position'} ne 'OneFS' and
        !$RPS{'Node Firmware Additional'}{'Devices'}{'CX NIC POST OE'}
        and !$CLUSTER_INFO{'000350946'}
    ) {
	foreach my $version (@{$RPS{'Upgrade Path'}}) {
	    if (isDestinationOneFS($version)) { last }
	    if (compareVersions($version, '9.2') >= 0 && !$RPS{'Single Neighborhood'}) {
		$RPS{'Node Firmware'}{'Position'} = 'OneFS';
		last;
	    }
	}
    }

    # Flag Node firmware updates as optional unless they are at FCO F010517EE levels
    if ($RPS{'F010517EE'} and !$RPS{'FCO'}{'F010517EE'}{'Nodes'}) {
	addCustomerInformation('Upgrade Plan', 'Node firmware updates available but already at required levels for FCO F010517EE and are considered optional.');
    }

    # Flag Node Firmware updates as optional unless they are a priority
    if ($RPS{'F033018EE'} and $RPS{'Node Firmware'}{'Position'} and !$RPS{'Node Firmware'}{'Priority'}) {
	addCustomerInformation('Upgrade Plan', 'Node firmware updates available and included but considered optional for FCO F033018EE.');
    }
    if ($RPS{'F082318EE'} and $RPS{'Drive Support Package'}{'Position'}) {
	addCustomerInformation('Upgrade Plan', 'Drive support package updates available and included but considered optional for FCO F082318EE.');
    }

    # Define/reset the upgrade order
    my @upgrade_order = ();
    push(@upgrade_order, 'Drive Support Package') if ($RPS{'Drive Support Package'}{'Position'} and $RPS{'Drive Support Package'}{'Position'} == '1' and !$CLUSTER_INFO{'000314255'});
    push(@upgrade_order, 'Drive Firmware Reboot') if ($RPS{'Drive Firmware Reboot'}{'Position'} and $RPS{'Drive Firmware Reboot'}{'Position'} == '1');
    push(@upgrade_order, 'Proactive Reboot') if ($RPS{'Proactive Reboot'});
    push(@upgrade_order, 'Pre Upgrade Patches') if ($RPS{'Pre Upgrade Patches'});
    push(@upgrade_order, 'Pre NFW Patches') if ($RPS{'IsiFw'} && $RPS{'Pre NFW Patches'} and $RPS{'Node Firmware'}{'Position'} && $RPS{'Node Firmware'}{'Position'} == '1');
    push(@upgrade_order, 'Node Firmware') if ($RPS{'Node Firmware'}{'Position'} and $RPS{'Node Firmware'}{'Position'} == '1');
    push(@upgrade_order, 'BMC Firmware') if ($RPS{'BMC Firmware'}{'Position'} and $RPS{'BMC Firmware'}{'Position'} == '1');
    push(@upgrade_order, 'Drive Support Package') if ($RPS{'Drive Support Package'}{'Position'} and $RPS{'Drive Support Package'}{'Position'} == '2');
    push(@upgrade_order, 'Drive Firmware Reboot') if ($RPS{'Drive Firmware Reboot'}{'Position'} and $RPS{'Drive Firmware Reboot'}{'Position'} == '2');
    push(@upgrade_order, 'Pre NFW Patches') if ($RPS{'IsiFw'} && $RPS{'Node Firmware'}{'Position'} && $RPS{'Node Firmware'}{'Position'} eq 'OneFS');
    push(@upgrade_order, 'OneFS') if ($RPS{'Upgrade Path'} and scalar(@{$RPS{'Upgrade Path'}}) > 1);
    push(@upgrade_order, 'Post Upgrade Patches') if ($RPS{'Post Upgrade Patches'});
    push(@upgrade_order, 'Pre NFW Patches') if (
        $RPS{'IsiFw'} and $RPS{'Pre NFW Patches'} and $RPS{'Node Firmware'}{'Position'} and 
        $RPS{'Node Firmware'}{'Position'} == '2' and 
        !hasPatch('HealthCheck_9\.[0-9]\.[0-9]_[3-9][4-9]\.0\.0$') and 
        (
            !$RPS{'Post Upgrade Patches'} && !$RPS{'Pre Upgrade Patches'}
        ));
    #####
    push(@upgrade_order, 'Node Firmware') if (($RPS{'Node Firmware'}{'Position'} and $RPS{'Node Firmware'}{'Position'} == '2') or (isCurrentOneFS('>=9.0') and isCurrentOneFS('<9.2') and $RPS{'Node Firmware'}{'Position'} ne 'OneFS'));
    push(@upgrade_order, 'BMC Firmware') if ($RPS{'BMC Firmware'}{'Position'} and $RPS{'BMC Firmware'}{'Position'} == '2');
    push(@upgrade_order, 'Drive Support Package') if ($RPS{'Drive Support Package'}{'Position'} and $RPS{'Drive Support Package'}{'Position'} == '3');
    push(@upgrade_order, 'Drive Firmware Reboot') if ($RPS{'Drive Firmware Reboot'}{'Position'} and $RPS{'Drive Firmware Reboot'}{'Position'} == '3');
    push(@upgrade_order, 'FCO F042415EE') if ($RPS{'FCO F042415EE'}{'Nodes'});

    # Only Node Firmware updates for FCO F121917EE
    if ($RPS{'F121917EE'}) {
	@upgrade_order = ();
	@upgrade_order = ('Node Firmware') if $RPS{'FCO'}{'F121917EE'}{'Nodes'} and $RPS{'Node Firmware'}{'Position'};
    }

    # Special plan for FCO F011018EE
    if ($RPS{'F011018EE'}) {
	if ($RPS{'FCO'}{'F011018EE'}{'Nodes'} and $RPS{'Node Firmware'}{'Position'}) {
	    @upgrade_order = ('Node Firmware', 'F011018EE2', 'F011018EE3');
	} else {
	    @upgrade_order = ('F011018EE1', 'F011018EE2', 'F011018EE3');
	}
    }

    # If requirements for FCO F033018EE are met already, no upgrade plan is provided.
    if ($RPS{'F033018EE'}) {
	my @results = grep {$_ =~ /Drive Support|OneFS/} @upgrade_order;
	if (!@results and isCurrentOneFS('>=8.1.2.0')) {
	    @upgrade_order = ();
	}
    }

    # If requirements for FCO F082318EE are met already, no upgrade plan is provided.
    if ($RPS{'F082318EE'}) {
	my @results = grep {$_ =~ /Node Firmware|OneFS/} @upgrade_order;
	if (!@results and isCurrentOneFS('>=8.1.0.4')) {
	    @upgrade_order = ();
	}
    }

    return \@upgrade_order;
} # End upgradePlanOrder
###
# upgradePlanGenerateOverview: Generates an overview of the upgrade plan for customer consumption.
sub upgradePlanGenerateOverview {
    my ($aref_plan) = @_;
    if (!$aref_plan or !@$aref_plan) { return }
    my $total_duration = 0;
    my $activity_number = 0;
    my @upgrade_overview = ();
    foreach my $href_activity (@$aref_plan) {
	my %activity = %{$href_activity};
	my $description = $activity{'Description'} || $activity{'Type'};
	my $custom_trgt = '';
	if (!$description) { next }
	$activity_number++;
	if (@upgrade_overview) { push(@upgrade_overview, '') }
	if ($activity{'Duration'}) {
	    $total_duration += $activity{'Duration'};
	    push(@upgrade_overview,
		 sprintf('%s: %s (%.1f hours)',
			 $activity_number,
			 $description,
			 $activity{'Duration'} / 60));
	} else {
	    push(@upgrade_overview, "$activity_number: $description");
	}
	if ($activity{'Type'} eq 'None') { next }
	push(@upgrade_overview, "Current Code: $activity{'Current Code'}");
	if ($activity{'Target'}) {
		if($description eq 'OneFS Upgrade with Node Firmware Updates (Reboot)(Parallel)'){
			$custom_trgt = $activity{'Target'}.' + '.$RPS{'Node Firmware'}{'Include_Vers'};
		}
		else{ 
			$custom_trgt = $activity{'Target'}
		}
	    push(@upgrade_overview,
		 'Upgrading To: '.$custom_trgt);
	}
	if ($activity{'Patches'}) {
	    push(@upgrade_overview,
		 'Patch: '.join(', ', @{$activity{'Patches'}}));
	}
	if ($activity{'Nodes'}) {
	    push(@upgrade_overview,
		 sprintf('Nodes: %s (%s nodes)',
			 compressRange(@{$activity{'Nodes'}}),
			 scalar(@{$activity{'Nodes'}})));
	}
	if ($activity{'Additional Comments'}) {
	    if (scalar(@{$activity{'Additional Comments'}}) == 1) {
		push(@upgrade_overview,
		     'Additional Comments: '.$activity{'Additional Comments'}[0]);
	    } else {
		my @comments = map { '# '.$_ } @{$activity{'Additional Comments'}};
		push(@upgrade_overview,
		     'Additional Comments: ',
		     @comments);
	    }
	}
    }
    if ($total_duration) {
	push(@upgrade_overview, '',
	     sprintf('Estimated total duration: %.1f hours',
		     $total_duration/60));
    }
    return \@upgrade_overview;
}
###
# upgradePlanGeneratePlan: Generates an upgrade plan provided the upgrade order
sub upgradePlanGeneratePlan {
    my ($aref_upgrade_order) = @_;
    if (!$aref_upgrade_order) { return }
    my @upgrade_plan = ();
    foreach (@$aref_upgrade_order) {
	if (/Support|Firmware|Reboot/) {
	    UPG_generic(\@upgrade_plan, $_);
	} elsif (/^OneFS/) {
	    UPG_OneFS(\@upgrade_plan, $_);
	} elsif (/Patches/) {
	    UPG_patches(\@upgrade_plan, $_);
	} elsif (/F042415EE/) {
	    UPG_F042415EE(\@upgrade_plan, $_);
	} elsif (/F011018EE/) {
	    UPG_F011018EE(\@upgrade_upan, $_);
	}
    }
    return \@upgrade_plan;
} # End upgradePlanGeneratePlan
package Converters;
use strict;
use warnings;
use Exporter qw( import );
eval { require Logger };
import Logger qw( debug );

our $VERSION = v1.0.0.0;
BEGIN {
    our @EXPORT_OK = qw
	( convertDurationToSeconds convertFromNumeric convertToNumeric
	  convertIPToNumeric );
    our %EXPORT_TAGS = ( ALL => \@EXPORT_OK );
}

###
# convertDurationToSeconds: Convert from a duration, i.e. 1Y1W1D1H1m1s to
#   123153424 seconds
sub convertDurationToSeconds {
    my ($duration) = @_;
    my $seconds = 0;
    if (!defined $duration) { $duration = 0 }
    debug("convertDurationToSeconds: Argument: $duration");
    if ($duration =~ /(\d+)Y/) { $seconds += $1 * 31536000 }
    if ($duration =~ /(\d+)M/) { $seconds += $1 * 2592000 }
    if ($duration =~ /(\d+)W/) { $seconds += $1 * 604800 }
    if ($duration =~ /(\d+)D/) { $seconds += $1 * 86400 }
    if ($duration =~ /(\d+)H/) { $seconds += $1 * 3600 }
    if ($duration =~ /(\d+)m/) { $seconds += $1 * 60 }
    if ($duration =~ /(\d+)s/) { $seconds += $1 }
    debug("convertDurationToSeconds: Return: $seconds");
    return $seconds;
} # End convertDurationToSeconds

###
# convertFromNumeric: Convert from a number of bytes to a count of a prefix of
#   bytes.  I.e. 1024, K => 1KB
sub convertFromNumeric {
    my ($bytes, $prefix) = @_;
    my $num = 0;
    my $count = 0;
    if (!defined $bytes) { $bytes = 0 }
    if (!defined $prefix) { $prefix = '' }
    debug("convertFromNumeric: Arguments: $bytes, $prefix");
    if ($bytes !~ /^\d+$/) { $bytes = 0 }
    if ($prefix !~ /^[KMGTP]$/) { $prefix = '' }
    if ($prefix eq 'K') { $count = $bytes / 2**10 }
    elsif ($prefix eq 'M') { $count = $bytes / 2**20 }
    elsif ($prefix eq 'G') { $count = $bytes / 2**30 }
    elsif ($prefix eq 'T') { $count = $bytes / 2**40 }
    elsif ($prefix eq 'P') { $count = $bytes / 2**50 }
    if ($count) { $count = int($count) + 1 }
    if ($prefix) { $num = "$count${prefix}B" }
    else { $num = $count }
    debug("convertFromNumeric: Return: $num");
    return $num;
} # End convertFromNumeric

###
# convertToNumeric: Convert from a count and prefix into just a number
sub convertToNumeric {
    my ($num) = @_;
    my $bytes = 0;
    my $count = 0;
    my $prefix = '';
    if (!defined $num) { $num = 0 }
    debug("convertToNumeric: Arguments: $num");
    if ($num =~ /^\s*(\d*\.?\d*)\s*([KMGTP])\D*\s*$/) {
	($count, $prefix) = ($1, $2);
    }
    if ($prefix eq 'K') { $bytes = $count * 2**10 }
    elsif ($prefix eq 'M') { $bytes = $count * 2**20 }
    elsif ($prefix eq 'G') { $bytes = $count * 2**30 }
    elsif ($prefix eq 'T') { $bytes = $count * 2**40 }
    elsif ($prefix eq 'P') { $bytes = $count * 2**50 }
    debug("convertToNumeric: Return: $bytes");
    return $bytes;
} # End convertToNumeric

###
# convertIPToNumeric: Converts a string IP into it's bitwise equivalent
sub convertIPToNumeric {
    my ($str_ip) = @_;
    my $int_ip = 0b0;
    my @str_octets = ();
    if (!defined $str_ip) { $str_ip = '' }
    debug("convertIPToNumeric: Arguments: $str_ip");
    @str_octets = split(/\./, $str_ip);
    if ($#str_octets == 3) { # IPv4 IP
	for (my $i = 3; $i >= 0; $i--) {
	    $int_ip += $str_octets[$i] * (2 ** ((3 - $i) * 8));
	}
    }
    debug('convertIPToNumeric: Return: '.sprintf('%032b', $int_ip));
    return $int_ip;
} # End convertIPToNumeric

package Helper;
use strict;
use warnings;
use Exporter qw( import );
eval { require Logger };
import Logger qw( debug fail );

our $VERSION = v1.1.0.1;
BEGIN {
    our @EXPORT_OK = qw
	( compressRange expandRange compareVersions merge
	  generateUnpackTemplate upgradeDigit toJSON findLowestCommonPath
	  gatherCluster gatherLocal gatherCustom array_duplicates );
    our %EXPORT_TAGS = ( ALL => \@EXPORT_OK );
}
our $ALRM_TMOUT = 600;
our $ALRM_FAILURE = 'TMOUT';
our $ALRM_MSG = 'Gathering command timed out';
our $FRK_FAILURE = 'FRKFAIL';
our $FRK_MSG = 'Gathering failed to create a fork to run command';

###
# expandRange: Takes a node list such as 1,3,5-6,11-132,2 and generates an
#  array of all nodes in the list
sub expandRange {
    my ($range) = @_;
    if (!defined $range) { $range = '' }
    debug("Argument: $range");

    # Remove white space
    $range =~ s/\s+//g;

    # Validate input format
    if ($range !~ /^(?:(?:\d+-\d+)|,|\d+)*$/) {
	debug('Invalid list format');
	$range = '';
    }

    # Expand the ranges
    my @values = ();
    my @groups = split(',', $range);
    foreach my $group (@groups) {
	if ($group =~ /^\d+-\d+$/) {
	    my ($start, $end) = split('-', $group);
	    if ($start > $end) {
		debug("Invalid range, $group, $start is greater than $end");
		@values = ();
		last;
	    } else {
		push(@values, ($start .. $end));
	    }
	} elsif ($group =~ /^\d+$/) {
	    push(@values, $group);
	}
    }

    # Remove duplicates and sort the array
    @values = sort {$a <=> $b} merge(@values);

    # Return the expanded range array
    debug("Return: [ @values ]");
    return @values;
} # End expandRange

###
# compressRange: Takes an array of nodes and returns a compressed range.
sub compressRange {
    my @values = @_;
    debug("Arguments: [ @values ]");

    # Validate all values are integers
    for (my $i = 0; $i <= $#values; $i++) {
	if (!defined $values[$i]) {
	    debug('Undefined value detected');
	    @values = ();
	    last;
	}
	$values[$i] =~ s/^\s+|\s+$//g;
	if ($values[$i] !~ /^\d+$/) {
	    debug('Non-integer value detected');
	    @values = ();
	    last;
	}
    }

    # Merge duplicates and then sort the values
    @values = sort {$a <=> $b} merge(@values);

    # Create the compressed range
    my $range = @values?$values[0]:'';
    my $start = 0;
    for (my $i = 1; $i <= $#values; $i++) {
	if ($values[$i - 1] + 1 != $values[$i]) {
	    if ($i - 1 != $start) {
		$range .= "-$values[$i - 1]";
	    }
	    $range .= ",$values[$i]";
	    $start = $i;
	} elsif ($#values == $i) {
	    if ($i - 1 == $start) {
		$range .= ",$values[$i]";
	    } else {
		$range .= "-$values[$i]";
	    }
	}
    }

    # Return the compressed range
    debug("Return: $range");
    return $range;
} # End compressRange

###
# compareVersions: Compares two OneFS versions.
# $1 > $2 => Positive
# $1 = $2 => 0
# $1 < $2 => Negative
# If only a partial version is provided, it will match up to given criteria.
# IE: 7.0 and 7.0.2 would be equivalent.
sub compareVersions {
    my @left = split(/\.|-|\+/, $_[0]);
    my @right = split(/\.|-|\+/, $_[1]);
    my $last = ($#left > $#right)?$#right:$#left;
    my $comparison = 0;
    for (my $i = 0; $i <= $last; $i++) {
	if ($left[$i] =~ /^\d*$/ and $right[$i] =~ /^\d*$/) {
	    $comparison = $left[$i] - $right[$i];
	} else {
	    $comparison = $left[$i] cmp $right[$i];
	}
	last if ($comparison);
    }
    debug("Arguments: $_[0], $_[1] Results: $comparison");
    return $comparison;
} # End compareVersions

###
# Given two arrays, combine them removing duplicates and return the merged array
sub merge {
    my %hash = ();
    $hash{$_} = 1 foreach(@_);
    return keys %hash;
}

###
# generateUnpackTemplate: Generates an unpack template based on provided header line and header labels
sub generateUnpackTemplate {
    debug("generateUnpackTemplate: ".join(",",@_));
    if (scalar(@_) <= 1) {
	debug("Not enough arguments provided");
	return "";
    }
    my $unpack_template = "";
    my $header_line = shift;
    my @indexes = ();
    foreach my $label (@_) {
	push(@indexes, index($header_line, $label));
    }
    my $previous_index = 0;
    foreach my $index (sort {$a <=> $b} @indexes) {
	next if ($index == -1 or $index == 0);
	my $unpack_diff = $index - $previous_index - 1;
	$unpack_template .= "A${unpack_diff}x";
	$previous_index = $index;
    }
    $unpack_template .= "A*";
    debug($unpack_template);
    return $unpack_template;
} # End generateUnpackTemplate

###
# upgradeDigit: Returns the digit the upgrade being performed is.
#
# Example: 8.1.0.4, 8.1.2.0 = 3rd digit difference
sub upgradeDigit {
    return 4 unless @_ and scalar(@_) == 2;
    my @left = split(/\./, $_[0]);
    my @right = split(/\./, $_[1]);
    return 4 unless scalar(@left) == 4 and scalar(@right) == 4;
    for (my $i = 1; $i <= 4; $i++) {
	return $i if $left[$i - 1] ne $right[$i - 1];
    }
    return 4;
} # End upgradeDigit
###
# toJSON: Converts the first argument to JSON and returns it as a string
sub toJSON {
    my ($obj) = @_;
    if (!defined $obj) { return '""' }
    my $type = ref($obj);

    # The object is not a reference
    if ($type eq "") {

	# The object is a boolean or number
	if ($obj eq "false" or $obj eq "true" or
	    $obj =~ /^-?[0-9]+(\.[0-9]+)?$/) {
	    return $obj;
	}

	# The object is a string and should be quoted
	else {
	    my %esc = ("\n" => '\n', "\r" => '\r', "\t" => '\t',
		       "\f" => '\f', "\b" => '\b', "\"" => '\"',
		       "\\" => '\\\\', "\'" => '\\\'');
	    $obj =~ s/([\x22\x5c\n\r\t\f\b])/$esc{$1}/g;
	    $obj =~ s/([\x00-\x08\x0b\x0e-\x1f])/'\\u00' . unpack('H2', $1)/eg;
	    return sprintf('"%s"', $obj);
	}
    }

    # The object is a reference to an ARRAY
    if ($type eq "ARRAY") {
        return sprintf('[%s]', join(', ', map { toJSON($_) } @{$obj}));
    }

    # The object is a reference to a HASH
    if ($type eq "HASH") {
	my @kvs = ();
	foreach my $key (sort keys %{$obj}) {
	    my $k = toJSON($key);
	    my $v = toJSON($obj->{$key});
	    if ($k !~ /^\".*\"$/) { $k = sprintf('"%s"', $k) }
	    push(@kvs, sprintf('%s: %s', $k, $v));
	}
        return sprintf('{%s}', join(', ', @kvs));
    }

    # The object is unknown
    return '""';
} # End toJSON
###
# stopProcessGroup: Try to safely stop all processes in a group, and
# then force kill after a  period of time.
sub stopProcessGroup {
    my ($pgid) = @_;
    if (!$pgid) { return }
    $pgid = "-$pgid";
    if (kill 'ZERO', $pgid) { kill 'TERM', $pgid }
    foreach (0..7) {
	if (kill 'ZERO', $pgid) { sleep 1 }
	else { return }
    }
    if (kill 'ZERO', $pgid) { kill 'INT', $pgid }
    foreach (0..7) {
	if (kill 'ZERO', $pgid) { sleep 1 }
	else { return }
    }
    if (kill 'ZERO', $pgid) { kill 'KILL', $pgid }
} # End stopProcessGroup

###
# gatherCustom: Runs command and parses based on it being a cluster wide command.
sub gatherCustom {
    my ($cmd) = @_;
    if (!$cmd) { return }
    debug("Command: $cmd");
	my $trgflg;
	my %gather = ();
	
	eval {

	# Use open to fork a sub process
	my $child_pid = open(my $output, "-|") // die $FRK_FAILURE;
	if ($child_pid == 0) {
	    # As the child, set the process group to allow for easy
	    # killing and then execute the command and never return.
	    setpgrp;
	    exec($cmd);
	}
	local $SIG{ALRM} = sub { stopProcessGroup($child_pid); die $ALRM_FAILURE };
	alarm $ALRM_TMOUT;
	while (<$output>) {
	    if (/No such file or directory|command not found:/) { next }
	    s/\x1b\[[0-9;]*m//g;
	    if (/^\s*\S+-(\d+):\s*\->\s*:(.*)$/) {
			$trgflg = 1;
			$gather{$1} .= $2."\n"; 
	}
else{
		#if (/^\s*Cap\s*deg\s*(?:uAh)?:\s*(\d+)\s*:\s*\d+\s*[a-zA-Z0-9_\-]+-(\d+)|(\d+)\s?\S?\s?\d?\s+?\S+-(\d+)\S*?$/){ push(@{$gather{$2}}, $1) }
		if (/\S+-(\d+)\S*?:\s*Cap\s*deg\s*u?A?h?:\s*(\d+).*$/) 
		{ push(@{$gather{$1}}, $2) }
		elsif (/.*Cap\s*deg\s*u?A?h?:\s*(\d+).*\S+-(\d+)$/) { push(@{$gather{$2}}, $1) }
		#elsif() {  }
		elsif (/^\S+-(\d+).*[:\-]\s*(.*)$/) { push(@{$gather{$1}}, $2) }
	    if (/^\S+-(\d+)\/\S*?:(.*?)\s*$/) { push(@{$gather{$1}}, $2) }
		if (/^C (.*?)\s*$/) { push(@{$gather{$1}}, $2) }
		if (/^.*\/([0-9]+)\/.*:\s*(.*)$/) { push(@{$gather{$1}}, $2) }
		#if (/(\d+)\s?\S?\s?\d?\s+?\S+-(\d+)\S*?$/) { push(@{$gather{$2}}, $1) debug("REGEX FOUND2");}
		if (/(\d+)\/?.*:.*\"status\":\s\"([A-Z]+)\".*(At least one Infiniband socket is not armed).*$/) { push(@{$gather{$1}}, $2." - ".$3) }
		if (/^[a-zA-Z0-9_\-]+-(\d+):\s*.*\/boot\/kernel\.amd64\/kernel:.*(SDP: alternate path record lookup failed: -22)$/) { push(@{$gather{$1}}, $2) }
		if (/^[a-z]+\/\S+-(\d+)-[a-z]+:.*(SDP:\salternate path record lookup failed:\s\-22)$/) { push(@{$gather{$1}}, $2) }
}
	}
	close $output;
	alarm 0;
    };
    if ($@ =~ $ALRM_FAILURE) { fail($ALRM_MSG) }
    if ($@ =~ $FRK_FAILURE) { fail($FRK_MSG) }
    if ($Logger::LEVEL <= &Logger::DEBUG) {
		foreach my $lnn (keys %gather) {
			if (!$gather{$lnn}) { next }
			debug("--------------------", " Node $lnn", "--------------------");
			if ($trgflg){
				foreach my $ne ($gather{$lnn}){
					debug($ne); 
				}	
			}
			else{
				debug(@{$gather{$lnn}});
			}
		}
	}
    return wantarray?%gather:\%gather;
} # End gatherCustom

###
# gatherCluster: Runs command and parses based on it being a cluster wide command.
sub gatherCluster {
    my ($cmd) = @_;
    if (!$cmd) { return }
    debug("Command: $cmd");
    my %gather = ();

    eval {
	# Use open to fork a sub process
	my $child_pid = open(my $output, "-|") // die $FRK_FAILURE;
	if ($child_pid == 0) {
	    # As the child, set the process group to allow for easy
	    # killing and then execute the command and never return.
	    setpgrp;
	    exec($cmd);
	}
	local $SIG{ALRM} = sub { stopProcessGroup($child_pid); die $ALRM_FAILURE };
	alarm $ALRM_TMOUT;
	while (<$output>) {
	    if (/No such file or directory|command not found:/) { next }
	    s/\x1b\[[0-9;]*m//g;
		if (/^\S+-(\d+)\/\S*?[:\-](.*?)\s*$/) { push(@{$gather{$1}}, $2); }
	    if (/^\S+-(\d+)[:\-] (.*?)\s*$/) { push(@{$gather{$1}}, $2); }
		#if (/^\S+-(\d+) (.*)$/) { push(@{$gather{$1}}, $2); debug("triggered regex"); }

	}
	close $output;
	alarm 0;
    };
    if ($@ =~ $ALRM_FAILURE) { fail($ALRM_MSG) }
    if ($@ =~ $FRK_FAILURE) { fail($FRK_MSG) }
    if ($Logger::LEVEL <= &Logger::DEBUG) {
	foreach my $lnn (keys %gather) {
	    if (!$gather{$lnn}) { next }
	    debug("--------------------", " Node $lnn", "--------------------");
	    debug(@{$gather{$lnn}});
	}
    }
    return wantarray?%gather:\%gather;
} # End gatherCluster

###
# gatherLocal: Runs command and parses based on it being a local only command.
sub gatherLocal {
    my ($cmd) = @_;
    if (!$cmd) { return }
    debug("Command: $cmd");
    my @gather = ();
    eval {
	# Use open to fork a sub process
	my $child_pid = open(my $output, "-|") // die $FRK_FAILURE;
	if ($child_pid == 0) {
	    # As the child, set the process group to allow for easy
	    # killing and then execute the command and never return.
	    setpgrp;
	    exec($cmd);
	}
	local $SIG{ALRM} = sub { stopProcessGroup($child_pid); die $ALRM_FAILURE };
	alarm $ALRM_TMOUT;
	while (<$output>) {
	    if (/No such file or directory|command not found:/) { next }
	    s/\x1b\[[0-9;]*m//g;
	    s/\s+$//;
	    push(@gather, $_);
	}
	close $output;
	alarm 0;
    };
    if ($@ =~ $ALRM_FAILURE) { fail($ALRM_MSG) }
    if ($@ =~ $FRK_FAILURE) { fail($FRK_MSG) }
    debug(@gather);
    return wantarray?@gather:\@gather;
} # End gatherLocal
###
# findLowestCommonPath: Finds the 'lowest' common path given an array of paths.
#  Used to give a guess at what the base directory should be for each access zone.
sub findLowestCommonPath {
    debug("Arguments [ ".join(", ", @_)." ]");
    my @lowest_common_path_array = ();
    foreach my $share_path (@_) {
	my @share_path_array = split("/", $share_path);
	if (!@lowest_common_path_array) {
	    @lowest_common_path_array = @share_path_array;
	    next;
	}
	for (my $i = 0; $i <= $#lowest_common_path_array; $i++) {
	    if ($i > $#share_path_array or $share_path_array[$i] ne $lowest_common_path_array[$i]) {
		splice(@lowest_common_path_array, $i);
		last;
	    }
	}
    }
    my $lowest_common_path = join("/", @lowest_common_path_array);
    debug("Results [ $lowest_common_path ]");
    return $lowest_common_path;
} # End findLowestCommonPath

###
# array_duplicates: Returns an array of items that appear more than once
sub array_duplicates {
    my @array = @_;
    my %count;
    foreach (@array) { $count{$_}++ }
    return grep { $count{$_} > 1 } keys %count;
} # End array_duplicates

1;
package Logger;
use strict;
use warnings;
use Term::ANSIColor;
use Exporter qw( import );
eval { require Helper };
import Helper qw( compressRange );

our $VERSION = v1.0.0.0;
BEGIN {
    our @EXPORT_OK = qw
	( ALL DEBUG VERBOSE DETAILS INFO WARN CRITICAL FAIL
	  printUrgent printResults printCheck printSummary
	  missing info warning fail critical details verbose debug
	  unsupported verboseHeader disableColors isResults );
    our %EXPORT_TAGS = ( ALL => \@EXPORT_OK );
}

use constant {
    ALL => 0,
    DEBUG => 1,
    VERBOSE => 2,
    DETAILS => 3,
    INFO => 4,
    WARN => 5,
    CRITICAL => 6,
    FAIL => 7,
};

our $LEVEL = &INFO;
our $JSON = 0;
our @DEBUG = ();
our @DETAILS = ();
our @INFO = ();
our @WARN = ();
our @FAIL = ();
our @CRITICAL = ();
our %MISSING = ();
our @RESULTS = ();
our $" = $, = ", ";

###
# enableJSON: Enable JSON mode
sub enableJSON { $JSON = 1 }

###
# debug: Record debug messages, but only if enabled for debug
sub debug {
    my @messages = @_;
    if ($LEVEL > &DEBUG) { return @messages }
    my $time = time();
    my $caller = (caller(1))[3];
    push(@DEBUG, "$time : $caller : $_") foreach @messages;
    return @messages;
} # End debug

###
# verbose: Record verbose messages, but only if enabled for verbose
sub verbose {
    if ($LEVEL <= &VERBOSE) { push(@DEBUG, @_); }
    return @_;
} # End verbose

###
# details: Record details messages
sub details {
    push(@DETAILS, @_);
    return @_;
} # End details

###
# info: Record info messages
sub info {
    push(@INFO, @_);
    return @_;
} # End info

###
# warning: Record warn messages
sub warning {
    push(@WARN, @_);
    return @_;
} # End warning

###
# fail: Record fail messages
sub fail {
    push(@FAIL, @_);
    return @_;
} # End fail

###
# critical: Record critical messages
sub critical {
    push(@CRITICAL, @_);
    return @_;
} # End critical

###
# missing: Store a list of nodes with missing information
sub missing {
    my ($lnn) = @_;
    if (!$lnn) { return; }
    $MISSING{$lnn} = 1;
    return;
} # End missing

###
# unsupported: Used for HealthCheck Framework to report unsupported checks
sub unsupported {
    my ($msg, @args) = @_;
    if (!$msg) { return }
    if (exists $IOCA::MSG{$msg}) { $msg = $IOCA::MSG{$msg} }
    if (@args) { $msg = sprintf($msg, @args) }
    debug($msg);
    my $subroutine = (caller(1))[3];
    $subroutine =~ s/^.*:://;
    my %result = (
	'name' => "ioca_$subroutine",
	'node' => "cluster",
	'status' => "UNSUPPORTED",
	'value' => -1,
	'parameters' => {},
	'details' => [ $msg ],
	);
    push(@RESULTS, \%result);
    return;
} # End unsupported

###
# verboseHeader: A verbose header
sub verboseHeader {
    my ($header) = @_;
    if (!$header) { return; }
    verbose('',
	    '==============================',
	    $header,
	    '==============================');
    return;
} # End verboseHeader

###
# isResults: Return 1 if there are results to print
sub isResults {
    my ($check_level) = @_;
    if ($check_level) {
	if ($check_level eq &ALL) { return 1; }
	if ($check_level <= &VERBOSE and @DEBUG) { return 1; }
	if ($check_level <= &INFO and (@DETAILS or @INFO)) { return 1; }
	if ($check_level <= &WARN and @WARN) { return 1; }
	if ($check_level <= &FAIL and (@CRITICAL or @FAIL or %MISSING)) { return 1; }
    } elsif (@FAIL or @CRITICAL or @WARN or @INFO or @DETAILS or @DEBUG or %MISSING) { return 1; }
    return 0;
} # End isResults

###
# disableColors: Disables terminal coloring
sub disableColors {
    no warnings 'redefine';
    *colored = sub { if ($_[0]) { return $_[0]; } };
    return;
} # End disableColors

###
# printUrgent: Prints a bold red urgent message
sub printUrgent {
    return unless $_[0];
    print colored(sprintf("## %-70s ##\n", $_[0]), "bold red");
    return;
} # End printUrgent

###
# printCheck:  Prints header for starting checks and, if a second
#  argument is provided, prints that as the result of the check.
sub printCheck {
    printf "%-50s", $_[0] if ($_[0]);
    printf "%s\n", $_[1] if ($_[1]);
    return;
} # End printCheck

###
# printResults: If provided an argument, prints it followed by an end
#  line, otherwise evaluates variables used in checks and prints results.
sub printResults {
    my ($msg) = @_;

    # If there was any nodes missing information
    if (%MISSING) {
	fail(sprintf('Missing information for the following nodes: %s',
		     compressRange(keys %MISSING)));
    }

    # Add to count of issues
    if (@FAIL) { $IOCA::RPS{'Issues'}++ }


    if (!$JSON) {
	# Colors for each level of failure
	my $FAIL = colored(($msg)?"FAIL ($msg)":"FAIL", 'red')."\n";
	my $WARN = colored(($msg)?"WARN ($msg)":"WARN", 'yellow')."\n";
	my $PASS = colored(($msg)?"PASS ($msg)":"PASS", 'green')."\n";
	my $INFO = colored(($msg)?"INFO ($msg)":"INFO", 'blue')."\n";

	if (@FAIL) {
	    print $FAIL;
	} elsif (@CRITICAL) {
	    print $FAIL;
	} elsif (@WARN) {
	    print $WARN;
	} elsif (@INFO) {
	    print $INFO;
	} else {
	    print $PASS;
	}
	print colored("  FAIL: ", 'red')."$_\n" foreach (@FAIL);
	print colored("  CRITICAL: ", 'red')."$_\n" foreach (@CRITICAL);
	print colored("  WARN: ", 'yellow')."$_\n" foreach (@WARN);
	print "  DETAILS: $_\n" foreach (@DETAILS);
	print colored("  INFO: ", 'blue')."$_\n" foreach (@INFO);
	print "  $_\n" foreach (@DEBUG);
    }

    my %result = ();
    my $subroutine = (caller(1))[3];
    $subroutine =~ s/^IOCA:://;
    $result{'name'} = "$subroutine";
    $COMPLETE_RESULT{'checks'}{$subroutine}{'name'} = "$subroutine";
    $result{'name'} = "ioca_$subroutine" if $JSON;
    $COMPLETE_RESULT{'checks'}{$subroutine}{'name'} = "ioca_$subroutine" if $JSON;
    $result{'node'} = "cluster";
    $COMPLETE_RESULT{'checks'}{$subroutine}{'node'} = "cluster";
    #$COMPLETE_RESULT{'checks'}{$subroutine}{'onefs_target'} = $RPS{'Target'};
    $result{'fw_target'} = "$RPS{'DSP'}" if $subroutine =~ /checkDriveSupportPackage/ and $JSON;
    $COMPLETE_RESULT{'checks'}{$subroutine}{'firmware_version'} = "$RPS{'DSP'}" if $subroutine =~ /checkDriveSupportPackage/ and $JSON;
    $result{'fw_target'} = "$RPS{'IsiFw'}" if $subroutine =~ /checkNodeFirmware/ and $JSON;
    $COMPLETE_RESULT{'checks'}{$subroutine}{'firmware_version'} = "$RPS{'IsiFw'}" if $subroutine =~ /checkNodeFirmware/ and $JSON;
    $COMPLETE_RESULT{'checks'}{$subroutine}{'node'} = "cluster";
    %{$result{'parameters'}} = ();
    %{$COMPLETE_RESULT{'checks'}{$subroutine}{'parameters'}} = ();
    $result{'status'} = "OK";
    $COMPLETE_RESULT{'checks'}{$subroutine}{'status'} = "OK";
    $result{'value'} = 100;
    $COMPLETE_RESULT{'checks'}{$subroutine}{'value'} = 100;
    $result{'status'} = "WARNING" if @WARN;
    $COMPLETE_RESULT{'checks'}{$subroutine}{'status'} = "WARNING" if @WARN;
    $result{'value'} = 75 if @WARN;
    $COMPLETE_RESULT{'checks'}{$subroutine}{'value'} = 75 if @WARN;
    $result{'status'} = "CRITICAL" if @FAIL or @CRITICAL;
    $COMPLETE_RESULT{'checks'}{$subroutine}{'status'} = "CRITICAL" if @FAIL or @CRITICAL;
    $result{'value'} = 50 if @FAIL or @CRITICAL;
    $COMPLETE_RESULT{'checks'}{$subroutine}{'value'} = 50 if @FAIL or @CRITICAL;
    @{$result{'details'}} = ();
    $COMPLETE_RESULT{'checks'}{$subroutine}{'details'} = ();
    
    push(@{$COMPLETE_RESULT{'checks'}{$subroutine}{'details'}}, "FAIL: $_") foreach (@FAIL);
    push(@{ $COMPLETE_RESULT{'checks'}{$subroutine}{'details'}}, "CRITICAL: $_") foreach (@CRITICAL);
    push(@{$COMPLETE_RESULT{'checks'}{$subroutine}{'details'}}, "WARN: $_") foreach (@WARN);
    push(@{$COMPLETE_RESULT{'checks'}{$subroutine}{'details'}}, "DETAILS: $_") foreach (@DETAILS);
    push(@{$COMPLETE_RESULT{'checks'}{$subroutine}{'details'}}, "INFO: $_") foreach (@INFO);
    push(@{$COMPLETE_RESULT{'checks'}{$subroutine}{'details'}}, "$_") foreach (@DEBUG);

    push(@{$result{'details'}}, "FAIL: $_") foreach (@FAIL);
    push(@{$result{'details'}}, "CRITICAL: $_") foreach (@CRITICAL);
    push(@{$result{'details'}}, "WARN: $_") foreach (@WARN);
    push(@{$result{'details'}}, "DETAILS: $_") foreach (@DETAILS);
    push(@{$result{'details'}}, "INFO: $_") foreach (@INFO);
    push(@{$result{'details'}}, "$_") foreach (@DEBUG);
    #push plan
    push(@{$result{'upgrade_plan'}},$RPS{'UPG_PLAN_DETAIL'}) if $JSON and !exists($result{'upgrade_plan'});
    push(@RESULTS, \%result);
    @FAIL = ();
    @CRITICAL = ();
    @WARN = ();
    @INFO = ();
    @DETAILS = ();
    @DEBUG = ();
    %MISSING = ();
    return;
} # End printResults

sub printSummary {
    local $"="\n";
    my @output = ();
    foreach my $r_href (@RESULTS) {
	my %result = %$r_href;
	if ($result{'value'} <= 75) {
	    push(@output, "=========================");
	    push(@output, "$result{'name'}");
	    push(@output, "=========================");
	    push(@output, "@{$result{'details'}}");
	}
    }
    if (@output) {
	print colored("\nSummary of checks with warnings or failures:", 'bold underline');
	print "\n\n@output\n";
    } else {
	print colored("\nAll checks passed\n\n", 'bold underline');
    }
    return;
}

1;
package Parsers;
use strict;
use warnings;
use Exporter qw( import );
use Time::Local;
eval { require Logger };
import Logger qw( debug );

our $VERSION = v1.1.0.0;
BEGIN {
    our @EXPORT_OK = qw
	( parseOneFSVersion parseRules parseCoreCount
	  parseGconfigItem parseGconfigList parseTime );
    our %EXPORT_TAGS = ( ALL => \@EXPORT_OK );
}

sub parseOneFSVersion {
    return () unless @_ and $_[0] and $_[0] =~ /^0x([0-9a-f]+)$/i;
    my $h = $1;
    my %version = (
	'major' => hex(substr $h, -16, 2),
	'minor' => hex(substr $h, -14, 2),
	'sub'   => hex(substr $h, -12, 2),
	'style' => hex(substr $h, -10, 1),
	'dot'   => hex(substr $h, -7, 2),
	'build' => hex(substr $h, -5),
	);
    return \%version;
}

sub parseRules {
    # Operators: Append -> ++, Insert -> +=, Replace -> =>, Remove Groups -> --, Join -> &=
    # Options: []
    my @rules = ();
    return @rules unless $_[0];
    while ($_[0] =~ /\\?"?(\S+)\s+(\+\+|\+=|=>|--|&=)\s+(\S+)\s*(\[\S+\])?\\?"?/g) {
	my $rule = {};
	$rule->{'left'} = $1;
	$rule->{'operator'} = $2;
	$rule->{'right'} = $3;
	$rule->{'options'} = $4 if $4;
	push(@rules, $rule);
	debug("parseRules: Left: $rule->{'left'} Operator: $rule->{'operator'} Right: $rule->{'right'}".
	     ($rule->{'options'}?" Options: $rule->{'options'}":"")."\n");
    }
    return @rules;
}

###
# parseCoreCount: Parses the PROC line from isi_hw_status and return the number of cores (int).
sub parseCoreCount {
    my ($line) = @_;
    return 0 unless $line;
    $_ = $line;
    s/-HT//;
    s/Single/1/;
    s/Dual/2/;
    s/Quad/4/;
    s/Hexa/6/;
    s/Octa/8/;
    my ($proc, $core) = (1, 1);
    $proc = $1 if /(\d+)-proc/;
    $core = $1 if /(\d+)-core/;
    return int($proc * $core);
} # End parseCoreCount

sub parseGconfigItem {
    my ($gconfig) = @_;
    my ( $key, $value ) = (q{}) x 2;
    if ( !defined $gconfig ) { $gconfig = q{} }
    debug("parseGconfigItem: Argument: $gconfig");
    if ( $gconfig =~ /\s=/xms ) {
        ( $key, $value ) = split /=/xms, $gconfig, 2;
        $key   =~ s/^(\S+).*$/$1/xms;
        $value =~ s/^\s*"?(\S.*?)"?$/$1/xms;
    }
    debug("parseGconfigItem: Return: [ $key, $value ]");
    return $key, $value;
}

sub parseGconfigList {
    my ($gconfig) = @_;
    my ( $key, $value ) = (q{}) x 2;
    my @values = ();
    if ( !defined $gconfig ) { $gconfig = q{} }
    debug("parseGconfigList: Argument: $gconfig");
    ( $key, $value ) = parseGconfigItem($gconfig);
    $value =~ s/^\s*\[\s*|\s*\]\s*$|\\"//gxms;
    if ( $value !~ /^\[\]$/xms ) { @values = split /,\s/xms, $value }
    foreach (@values) { s/^"|"$//gxms }
    debug("parseGconfigList: Return: [ $key, @values ]");
    return $key, @values;
}

sub parseTime {
    my ($str) = @_;
    if (!$str) { return }
    my ($year, $mon, $day, $hour, $min, $sec, $tz);
    debug("Parsing $str");

    # Trim whitespace
    $str =~ s/^\s*|\s*$//g;

    # Parse date in yyyy-mm-dd format
    if ($str =~ /(\d{4})-(\d{2})-(\d{2})/) {
	($year, $mon, $day) = ($1, $2, $3);
    }

    # Parse time in hh:mm:ss format
    if ($str =~ /(\d{2}):(\d{2}):(\d{2})/) {
	($hour, $min, $sec) = ($1, $2, $3);
    }

    # Parse time zone in either Z, +hh:mm, or -hh:mm format
    if ($str =~ /Z$/) {
	$tz = 0;
    } elsif ($str =~ /-(\d{2}):(\d{2})$/) {
	$tz = $1 * 60 * 60 + $2 * 60;
    } elsif ($str =~ /\+(\d{2}):(\d{2})$/) {
	$tz = -1 * $1 * 60 * 60 + $2 * 60;
    }

    # Convert the time into an epoch value
    my $epoch = 0;
    eval { $epoch = timegm($sec, $min, $hour, $day, $mon - 1, $year) + $tz };
    if ($@) { debug($@) }

    # Return the epoch value
    debug("Returning $epoch");
    return $epoch;
}

1;
package main;
exit IOCA::main();
