package Revision;
#<copyright>
# ----------------------------------------------------------
# Sun Proprietary/Confidential Code
# Copyright 2001, Sun Microsystems, Inc. All rights reserved.
# ----------------------------------------------------------
#</copyright>

use strict;
use System;
use Util;
use Matrix;
use PDM::ConfigFile;

#
#  returns: [type, rc, curr_version, expected_version, current_patch, expected_patch, desc]
#  rc = PASS , DOWNREV, UPREV, NI, (Not installed), ERR
use vars qw ($ERR);

use vars qw ($ERR);

sub baseRev {
  my($class, $patch) = @_;
  my($base, $req_rev);
  if ($patch =~ /-/) {
      ($base, $req_rev) = split (/-/, $patch);
   } else {
      $base = $patch;
      $req_rev = "";
   }
  return ($base, $req_rev);
}

#
# uses the matrix to verify if the right patch is present for a certain version of
# software, see SDS, SEVM in matrix.db for example
# 'SDS' => {
#   '4.2.1' => [ '108693-04', 'Solstice DiskSuite' ]
# },
#

sub checkSoftPatch {
  my($class, $category, $Matrix, $InstalledP, $curr_version) = @_;

  my $x = $Matrix->{$category};
  my (@R);
  foreach my $version (keys %$x) {
      next if ($version ne $curr_version);

      my $entry = $x->{$version};
      my $req_patch = $entry->[0];
      my $desc      = $entry->[1];
      my($req_base, $req_rev) = $class->baseRev($req_patch);

      if ($InstalledP->{patch}{$req_patch}) {
         push(@R, [$category, undef,'PASS',$curr_version, undef, $req_patch, $req_patch, $desc]);
         last;
      } elsif ($req_rev) {
         my $rev = $InstalledP->{base}{$req_base};
         if (!$rev) {
            push(@R, [$category, undef,'NI', $curr_version, undef, undef, $req_patch, $desc]);
            last;
         } elsif ($rev eq $req_rev) {
            push(@R, [$category, undef,'PASS', $curr_version, undef, "$req_base-$rev", $req_patch, $desc]);
         } elsif ($rev < $req_rev) {
            push(@R, [$category, undef,'DOWNREV', $curr_version, undef, "$req_base-$rev", $req_patch, $desc]);
            last;
         } else {
            push(@R, [$category, undef,'UPREV', $curr_version, undef, "$req_base-$rev", $req_patch, $desc] );
            last;
         }
     }
  }
  if ($#R < 0) {
    push(@R, [$category, undef,'ERR',$curr_version,undef, undef,undef,"No patches available for current version" ]);
  }
  return \@R;
}

#
# uses the matrix to verify if all required patches are present on the host
# see HOST in matrix.db
# 'PCI' => {
#      '109189-02' => [ 1, 'ifp' ],
#      '109399-03' => [ '0', 'PCI FC100 fcode' ]
#    },
#    'DEFAULT' => {
#      '108983-08' => [ 1, 'leadville fcip' ],
#      '108982-09' => [ 1, 'leadville fctl, fc & fp' ],
#      '109529-04' => [ 1, 'luxadm' ],
#

sub checkHostPatch {
  my($class, $section, $category, $Matrix, $InstalledP) = @_;

  my $x = $Matrix->{$section}{$category};
  my $cat = "Driver";
  my (@R);
  foreach my $req_patch (keys %$x) {  # read all req. patches

      my $entry = $x->{$req_patch};
      my $present = $entry->[0];
      my $desc    = $entry->[1];
      my($req_base, $req_rev) = $class->baseRev($req_patch);

      if ($present == 0) { # patch not present, probably t3 patch etc..
         push(@R, [$cat, undef, 'PASS', undef, undef, $req_patch, $req_patch, $desc]);

      } elsif ($InstalledP->{patch}{$req_patch}) {
         push(@R, [$cat, undef, 'PASS', undef, undef, $req_patch, $req_patch, $desc]);

      } elsif ($req_rev) {
         my $rev = $InstalledP->{base}{$req_base};
         if (!$rev) {
            push(@R, [$cat,undef, 'NI', undef,undef, undef, $req_patch, $desc]);
         } elsif ($rev eq $req_rev) {
            push(@R, [$cat, undef, 'PASS', undef, undef,"$req_base-$rev", $req_patch, $desc]);
         } elsif ($rev < $req_rev) {
            push(@R, [$cat, undef, 'DOWNREV', undef, undef,"$req_base-$rev", $req_patch, $desc]);
         } else {
            push(@R, [$cat, undef, 'UPREV', undef, undef,"$req_base-$rev", $req_patch, $desc]);
         }
      }
  }
  return \@R;
}

sub readInstalledPatches {
  my($class) = @_;
  my($l,%PATCHES);
  $ERR = undef;
  my($err, $sr) = Util->run_command("/bin/showrev -p", "installed_patches.text");
  if ($err) {
    $ERR = $err;
    return undef;
  }
  foreach $l (@$sr) {
     chop($l);
     my($head, $a,$b,$obs) = split(/\s+/, $l);
     if ($a =~ /\-/) {
       my($base, $rev) = split(/\-/, $a);
       $PATCHES{base}{$base} = $rev if ($rev >  $PATCHES{base}{$base});
     } else {
       $PATCHES{patch}{$a} = 1;
     }
  }
  return \%PATCHES;
}
my $A3500 = undef;

sub a3500Present {
    my($class) = @_;
    my($l);
    return $A3500 if defined($A3500);
    $A3500 = 0;
    
    $ERR = undef;
    my($err, $com) = Util->run_command("/usr/lib/osa/bin/lad","lad1.txt");

   if ("@$com" =~ /No RAID devices found/) {
        return undef;
   }
   elsif ($err){
        $ERR = $err;
        return undef;
   }
   foreach $l (@$com) {
         if ( $l =~ /LUNS:/) {
            $A3500 = 1;
            last;
         }
   }
   return $A3500; 
}


my $A5 = undef;

sub a5kPresent {
  my($class) = @_;
  my($l);
  return $A5 if (defined($A5));
  $A5= 0;

  $ERR = undef;
  my($err, $lp) = Util->run_command("/usr/sbin/luxadm probe", "lux_probe.txt");
  if ($err) {
     $ERR = $err;
     return undef;
  }
  foreach $l (@$lp) {
     if ($l =~ /SENA/) {
        $A5 = 1;
        last;
     }
  }
  return $A5;

}

sub checkAllDrivers {
  my($class) = @_;

  my @LD_ALL_PORTS = split(/\n/, `/usr/sbin/luxadm -e port | grep CONN`);

  my($text, %S);

  $ERR = undef;
  my($err, $mi) = Util->run_command("/usr/sbin/modinfo", "modinfo.text");
  if ($err) {
     $ERR = $err;
     return undef;
  }
  my $mod = "@$mi";

  foreach my $port (@LD_ALL_PORTS) {
     if ($port =~ /socal/) {
        $S{socal} = 1; $S{sf} = 1;

     } elsif ($port =~ /usoc/) {
        $S{usoc} = 1; $S{fp} = 1;

     } elsif ($port =~ /qlc/) {
        $S{qlc} = 1; $S{fp} = 1;

     } elsif ($port =~ /ifp/) {
        $S{ifp} = 1;
     }
  }
  foreach my $k (keys %S) {
    if ($mod !~ / $k /) {
       $err++;
       $text .= "  $k driver not configured\n";
    }
  }
  return ($err, $text);

}

my $DI  = undef;

sub runDiskInquiry {
  my($class, $test) = @_;
  my($l, @L);
  if ($test) {
     open(O,  System->get_home() . "/Test/di.txt");
     while ($l = <O>) {
       push(@L, $l);
     }
     close(O);
     return \@L;
  }
  return $DI if ($DI);

  my $F = System->get_home() . "/bin/disk_inquiry";
  open(O, "$F 2>&1|");
  while ($l = <O>) {
    push(@L, $l);
  }
  close(O);
  $DI = \@L;
  return $DI;
}


#0: c0t0d0      SEAGATE    ST34371W SUN4.2G     7462    9716D29086##    primary                /sbus@1f,0/SUNW,fas@e,8800000/sd@0,0
# 3: c1t1d1      SUN        T300                 0111    0000098111##    alternate

sub checkDiskFirmware {
  my($class, $Matrix, $InstalledP, $a5k_list) = @_;
  my($line, @L);
  
  my $di = $class->runDiskInquiry(0);

  my $disk_fw = $Matrix->{DISK_FW};
  my $category = "A5K_DISK_FW";
  my $cnt= 0;
  my $found=0;
  foreach $line (@$di) {
       next if ($line =~ /No such device/) ;
       next if ($cnt++ < 6) ;
       next if (($line =~ /T300/ ) || ($line =~ /T4/));
       my($dev, $vp,  $vp2) = split(/\t+/, Util->ltrim($line));
       if ( is_dev_a5k($dev,$a5k_list) ) {
          $found = 1;
          my($vendor, $prod) = split(/\s+/, $vp, 2);
          my($curr_fw, $serial) = split(/\s+/, $vp2, 2);
          $prod = Util->rtrim($prod);
          my $desc = $prod;

          next if ($prod eq "T3");
          next if ($prod eq "StorEdgeA3500FCd");
          next if ($prod eq "SESS01");

          my $key = "$vendor:$prod:A5000";
          if ($disk_fw->{$key}) {
            my $d = $disk_fw->{$key};
            my $req_fw = $d->[0];
            my $req_patch = $d->[2];
            my($req_base, $req_rev) = $class->baseRev($req_patch);
            my $comp;
            my $min;
            my $max; 
            if ( $req_fw eq "7F7."){ # special case
               $desc = $prod." ----   Note: version 7F7. matches range from 7F70 to 7F7F";
               $min= "7F70";
               $max= "7F7F";
               $comp = $class->compare_special_disk($curr_fw, $min,$max);
            
            } else {   # general case
               $comp = $class->compareFwLevels($curr_fw, $req_fw);
            }
            if ($comp == 0) {
             push(@L, [$category, $dev, 'PASS', $curr_fw, $req_fw, undef, $req_patch, $desc]);
	    } elsif ($comp > 0) {
             push(@L, [$category, $dev, 'UPREV', $curr_fw, $req_fw, undef, $req_patch, $desc]);
	    } elsif ($comp < 0) {
             push(@L, [$category, $dev, 'DOWNREV', $curr_fw, $req_fw, undef, $req_patch, $desc]);
   	    }
	    else {
             push(@L, [$category, $dev, 'ERR', $curr_fw, $req_fw, undef, $req_patch, "Unable to check device "]);
	    }
            
         }
         else {
            push(@L, [$category, $dev, 'ERR', $curr_fw,undef,undef,undef,"Unable to check the $vendor device, please see sunsolve online for potential patch information or contact your SUN representative"]);
         }

      }
   }
   if ($found ==0)
   {
          push(@L, [$category, 'N/A', 'ERR', undef, undef,undef,undef, "No A5k device are present, please check system"]);
   }
   return \@L;
}

sub compare_special_disk {
    my ($class, $fw, $fw_min, $fw_max) = @_;
    my @drive = split(//, $fw);
    my $m;
    foreach my $char (@drive) {
       if (($char ne ".") && ( $char ne "-") && ( $char ne "V")) {
           $m .=$char;
       }
    }
    if (hex($m) > hex($fw_max)){
          return 1;
    } elsif ( hex($m) < hex($fw_min)) {
          return -1;
    } else {
          return 0;
    } 
}

sub is_dev_a5k {
      my ($device, $a5k_ctr_arr) = @_;
      my $where1 = index($device,"c");
      my $where2 = index($device,"t");
      my $num= substr($device,$where1, $where2-$where1);

      foreach my $x (@$a5k_ctr_arr){
         if ($num eq $x){
            return 1; 
         }
      }       
      return 0;
}


 # Given two hex numbers (usually 4 digits) #
 # simply compare them. A dot is a wildcard #
 # and matches automatically.               #
 # if $drive is > $matrix then return 1    #
 # if $drive is < $matrix then return -1    #

sub compareFwLevels {
    my ($class, $drive, $matrix) = @_;
    my (@matrix, @drive);

    @matrix = split(//, $matrix); # separate characters
    @drive = split(//, $drive); # separate characters
    my ($m, $d);
    foreach my $char (@matrix) {
      if (( $char ne "b") && ( $char ne "v") && ( $char ne "V") && ( $char ne "R") && ( $char ne "E") && ( $char ne "e")){
            ###################################################################################
            #  1) 2G switch version is like V1.37-17-01, so need to remove "V"                #
            #     or "e43220", need remove "e"                                                #
            #  2) keep "." and "-" at this stage                                              #
            ###################################################################################
            $m .= $char;
        }
    }

    foreach my $char (@drive) {
       if (( $char ne "b") && ( $char ne "v") && ( $char ne "V") && ( $char ne "R") && ( $char ne "E") && ( $char ne "e")) {
            $d .= $char;
       }
    }
 
    my (@matrix2, @drive2);
    @matrix2 = split(/-/, $m);     
    @drive2  = split (/-/,$d);
 
    # find the smaller array size of matrix2 and drive2

    my $min;
    my $default_result=0;
    if ( $#matrix2 > $#drive2 ) {
        $min = $#drive2;
        $default_result = -1; 
    } elsif ($#matrix2 < $#drive2) {
        $min = $#matrix2;
        $default_result = 1;
    } else { # same, choose one
         $min = $#matrix2;
    }    

    my $i;
    my $ele1;
    my $ele2;
    my $result;

    for ( $i=0; $i<=$min; $i++) {
          $ele1 = $matrix2[$i];
          $ele2 = $drive2[$i];          
          $result = $class->compare($ele2, $ele1);

          if ($result !=0 ) {  # which one is bigger is known
              return $result;
          } elsif ($result == 0) {
              if ($i== $min) {
                  return $default_result;
              }
          }
    }
}

# input for e1 or e2 may be in format: 7.1.0 #
# so need further check                    #

sub compare {
    my ($class, $e1, $e2) = @_;
    my @arr1;
    my @arr2;
    my $min;
    my $i; 
    my $result;
    my $default_result=0;

    if ($e1 =~ /\./) {
       @arr1 = split(/\./,$e1);        
    } 

    if ($e2 =~ /\./) {
       @arr2 = split(/\./,$e2);
    } 

    if ( ($#arr1>0) && ( $#arr2>0) ) {
          if ( $#arr1 > $#arr2 ) {
               $min = $#arr2;
               $default_result = 1;
          } elsif ($#arr1 < $#arr2) {
               $min = $#arr1;
               $default_result = -1;
          } else { # same choose one 
               $min = $#arr1;
          }
             
          for ($i=0; $i<=$min; $i++) {
              $result = $class->compare($arr1[$i], $arr2[$i]); 
              if ( $result !=0 ) {
                    return $result;
              }

              if (($result ==0) && ($i==$min)) { 
                    # the last element in the arr are compared
                    # so the longer arr will be the bigger one
                    return $default_result;
              } 
          }
    } elsif (($#arr1==0) && ($#arr2==0) ) {
          # arr1 and arr2 each only has 1 element         
          # then we can directly compare $e1 and $e2
    } elsif ( ($#arr1>0) && ($#arr2==0)) {
          $e1 = $arr1[0];
          $default_result = -1;
    } elsif ( ($#arr1==0) && ($#arr2>0)) {
          $e2 = $arr2[0];
          $default_result = 1;
    }  
 
    if (hex($e1) > hex($e2)) {
          return 1;
    } elsif (hex($e1) < hex($e2)) {
          return -1;
    } else {
          return 0;
    }


}

sub compareDiskFwLevels {
    my ($class, $drive, $matrix) = @_;
    my (@matrix, @drive);

    @matrix = split(//, $matrix); # separate characters
    @drive = split(//, $drive); # separate characters
    my ($m, $d);
    foreach my $char (@matrix) {
      if (($char ne ".") && ( $char ne "-") && ( $char ne "b") && ( $char ne "v") && ( $char ne "V") && ( $char ne "R") ){
            $m .= $char;
        }
    }

    foreach my $char (@drive) {
     if (($char ne ".") && ( $char ne "-") && ( $char ne "b") && ( $char ne "v") && ( $char ne "V") && ( $char ne "R") ) {
            $d .= $char;
       }
    }
    if (hex($d) > hex($m)) {
        return 1;
    } elsif (hex($d) < hex($m)) {
        return -1;
    } else {
        return 0;
    }
}


sub devCompare {
   my($class, $key, $elem, $val, $dev, $mat, $type, $info) = @_;

   my $default_info = "no patch information available, please contact your sun representative";
   my $el  = $mat->{$type};

   my $rev   = $el->[0];
   my $patch = $el->[1];
   my $desc  = $el->[2];
   my $comp = ($dev->{name} || $dev->{ip});
   
   my $compType = "$comp.$type";
   if ($elem) {
      $compType = "$comp.$type.$elem"; 
   } 
   
   if (!$el) {
       return [$key, "$comp.$type", 'ERR' , undef, undef, undef , $patch, 
          "No entry in Matrix for $key.$type" ];
   }
   if (($val < 0) ||(!$val)) {
        return [$key, $compType, 'ERR' , undef, undef, undef , undef, "Device Revision is not available" ]; 
   }

   my $cp = $class->compareFwLevels($val, $rev);
   if ($val eq $rev) {
       if ( ($type eq "enc") || ($type eq "firmrev")){
         return [$key, "$comp.$type",'PASS' , $val, $rev, undef , undef, $info];
       }
       else {
         return [$key, $compType, 'PASS' , $val, $rev, undef , undef, $info];
       }
   } elsif ($cp > 0) {
       if ( ($type eq "enc") || ($type eq "firmrev")){
         if (( $patch =~ /xxxx/ ) && ( !$info ) ) {
            return [$key, "$comp.$type", 'UPREV' , $val, $rev, undef , "N/A", $default_info];
         } else {
            return [$key, $compType, 'UPREV' , $val, $rev, undef, $patch, $info];
         }
        
       } else {
         return [$key, $compType, 'UPREV' , $val, $rev, undef , $patch, $info];
       }
   } elsif ($cp < 0) {
       if (($type eq "enc") || ($type eq "firmrev")) {
          if (( $patch =~ /xxxx/ ) && ( !$info ) ){ 
             return [$key, "$comp.$type",'DOWNREV' , $val, $rev, undef , "N/A", $default_info];
          } else {
             return [$key, $compType, 'DOWNREV' , $val, $rev, undef, $patch, $info];
          } 
         
       } else{
           if (( $patch =~ /xxxx/ ) && ( !$info ) ){
               return [$key, $compType, 'DOWNREV' , $val, $rev, undef , "N/A", $default_info];
           } else {
              return [$key, $compType, 'DOWNREV' , $val, $rev, undef, $patch, $info];
           }

       }
   } else {
       if ($type eq "enc"){
         return [$key, "$comp.$type",'PASS' , $val, $rev, undef , undef, $info];
       } else {
         return [$key, $compType, 'PASS' , $val, $rev, undef , undef, $info];
       }
   }
}



sub version_ok {
    my ($class, $spec, $installed) = @_;

    my $return_value = 0;

    my ($spec_major_num, $spec_minor_num, $spec_increment) = $spec
        =~ /(\d+)\D*(\d*)\D*(\d*)/;

    my ($installed_major_num, $installed_minor_num, $installed_increment)
        = $installed =~ /(\d+)\D*(\d*)\D*(\d*)/;

    # Assign some default values if the minor number
    # or increment didn't match in the regular expression
    $spec_minor_num = 0 if (! $spec_minor_num);
    $spec_increment = 0 if (! $spec_increment);
    $installed_minor_num = 0 if (! $installed_minor_num);
    $installed_increment = 0 if (! $installed_increment);
    
    if ($installed_major_num > $spec_major_num) {
        return 2;
    }elsif ($installed_major_num < $spec_major_num) {
        return 0;
    } else { # Check minor number
        if ($installed_minor_num > $spec_minor_num) {
            return 2;
        } elsif ($installed_minor_num < $spec_minor_num) {
            return 0;
        } else { #Check increment
            if ($installed_increment > $spec_increment) {
                return 2;
            }  elsif ($installed_increment < $spec_increment) {
                return 0;
            } else {
                return 1;
            }
        }
    }
    Debug->err(TEXT => "version_ok: Logic error in version_ok subroutine!\n");
    return 0;
}

#
#  ($Present ...) = $class->hba_present();
#

sub hbas_present {
   my($class) = @_;
   my($l);
   my $PRESENT = {};
   my($err, $sr) = Util->run_command("/usr/sbin/luxadm -e port", "luxadme.text");
   $ERR = $err;
   return () if ($ERR);

   my(@luxdiag_on_ports, @luxdiag_off_ports);
   my(%LD_ORIG_PORTS, %socpp, %portnum, %phys_path, %TEST);
   my(@LD_OFF_PORTS);

   foreach my $cur_port (@$sr) {
      next if ($cur_port !~ /CONN/);
      my @cur_port_info = split(/\s+/, $cur_port);
      if ($cur_port =~ /^(.*socal.*):(\d+)(\s+.*)$/ ) {
          $cur_port = "$1/sf\@$2,0:devctl$3";
          $LD_ORIG_PORTS{"$1/sf\@$2,0:devctl"} =  $cur_port_info[0];
      } else {
          $LD_ORIG_PORTS{$cur_port_info[0]} = $cur_port_info[0];
      }
      if ($cur_port =~ /^(\S+)\s+NOT\s+CONNECTED/) {
          # allow offline ports to trigger storstat firmware checks #

          push @luxdiag_off_ports, $1;

          $PRESENT->{SOCAL} = 1 if ($cur_port =~ /^(.*socal.*)\/sf\@(\d+)/);
          $PRESENT->{USOC} = 1 if ($cur_port =~ /^(.*usoc.*)\/fp\@(\d+)/);
          $PRESENT->{QLC}  = 1 if ($cur_port =~ /^(.*qlc.*)\/fp\@(\d+)/);
          $PRESENT->{IFP}  = 1 if ($cur_port =~ /^(.*pci.*ifp\@)(\d+)/);

      } elsif ($cur_port =~ /^(\S+)\s+CONNECTED/) {
          push @luxdiag_on_ports, $1;
      }
    }
    @LD_OFF_PORTS = @luxdiag_off_ports;

    return () if (@luxdiag_on_ports == -1);


    # convert luxdiag output to match disklist and path-to-inst

    foreach my $ldprt (@luxdiag_on_ports) {
        chomp $ldprt;
        if ($ldprt =~ /^(.*socal.*)\/sf\@(\d+)/) {
            $socpp{$ldprt} = "$1";
            $portnum{$ldprt} = $2;
            $phys_path{$ldprt} = $ldprt;
            $phys_path{$ldprt} =~ s/\/devices//;
            $phys_path{$ldprt} =~ s/:devctl//;
            $TEST{$ldprt} = "lbf dex con";
            $PRESENT->{SOCAL} = 1;

        } elsif ($ldprt =~ /^(.*usoc.*)\/fp\@(\d+)/) {
            $socpp{$ldprt} = "$1";
            $portnum{$ldprt} = $2;
            $phys_path{$ldprt} = $ldprt;
            $phys_path{$ldprt} =~ s/\/devices//;
            $phys_path{$ldprt} =~ s/:devctl//;
            $TEST{$ldprt} = "lbf dex con";
            $PRESENT->{USOC} = 1;

        } elsif ($ldprt =~ /^(.*qlc.*)\/fp\@(\d+)/) {
            $socpp{$ldprt} = "$1";
            $portnum{$ldprt} = $2;
            $phys_path{$ldprt} = $ldprt;
            $phys_path{$ldprt} =~ s/\/devices//;
            $phys_path{$ldprt} =~ s/:devctl//;
            $TEST{$ldprt} = "lbf dex";
            $PRESENT->{QLC} = 1;

        } elsif ($ldprt =~ /^(.*pci.*ifp\@)(\d+)/) {
            $socpp{$ldprt} = "$1";
            #$portnum{$ldprt} = $2;
            $portnum{$ldprt} = 0;
            $phys_path{$ldprt} = $ldprt;
            $phys_path{$ldprt} =~ s/\/devices//;
            $phys_path{$ldprt} =~ s/:devctl//;
            $TEST{$ldprt} = "dex con";
            $PRESENT->{IFP}= 1;

        } elsif ($ldprt =~ /^(.*pci.*)\/scsi\@(\d+)/) {
            $socpp{$ldprt} = "$1";
            $portnum{$ldprt} = $2;
            $phys_path{$ldprt} = $ldprt;
            $phys_path{$ldprt} =~ s/\/devices//;
            $phys_path{$ldprt} =~ s/:devctl//;
            $TEST{$ldprt} = "lbf dex con";
            $PRESENT->{IFP} = 1;
        } else {
            print "Error: unknown Host Adapter port $ldprt\n";
        }
    }

    return ($PRESENT, \@luxdiag_on_ports, \@luxdiag_off_ports,
            \%LD_ORIG_PORTS, \%socpp, \%portnum, \%phys_path, \%TEST);
}


sub warning {
    my $class = @_;
    my @rc;
    push (@rc, [undef, undef, undef, undef, undef, undef, undef, "No known patch information about this operating system level at this time, once we get this information, the revision check can be done"]);
    return \@rc; 

} 


sub warning2 {
    my ($class, $mod) = @_;
    my @rc;
   push (@rc, [$mod, undef, 'ERR', undef, undef, undef, undef, "Error in this module, please check system setup"]);
    return \@rc;

}

sub switch_Compare {
   my($class, $key, $val, $dev, $mat, $type, $info) = @_;

   my $default_info = "no patch information available, please contact your sun representative";
   my $el  = $mat->{$type};

   my $rev   = $el->[0];
   my $patch = $el->[1];
   my $desc  = $el->[2];
   my $comp = ($dev->{name} || $dev->{ip});

   if (!$el) {
       return [$key, "$comp", 'ERR' , undef, undef, undef , $patch,
          "No entry in Matrix for $key.$type" ];
   }
   if ($val < 0) {
        return [$key, "$comp", 'ERR' , undef, undef, undef , undef, "Device Revision is not available" ];
   }

   my $cp = $class->compareFwLevels($val, $rev);
   if ($val eq $rev) {
         return [$key, "$comp",'PASS' , $val, $rev, undef , $patch, $desc];
   } elsif ($cp > 0) {
         if (( $patch =~ /xxxx/ ) && ( !$info ) ) {
            return [$key, "$comp", 'UPREV' , $val, $rev, undef , "N/A", $default_info];
         } else {
            return [$key, "$comp", 'UPREV' , $val, $rev, undef, $patch, $desc];
         }

   } elsif ($cp < 0) {
       if (($type eq "switch") || ($type eq "firmrev") || ($type eq "enc")) {
          if (( $patch =~ /xxxx/ ) && ( !$info ) ){
             return [$key, "$comp",'DOWNREV' , $val, $rev, undef , "N/A", $default_info];
          } else {
             return [$key, "$comp", 'DOWNREV' , $val, $rev, undef, $patch, $desc];
          }

       } else{
           if (( $patch =~ /xxxx/ ) && ( !$info ) ){
               return [$key, "$comp", 'DOWNREV' , $val, $rev, undef , "N/A", $default_info];
           } else {
              return [$key, "$comp", 'DOWNREV' , $val, $rev, undef, $patch, $desc];
           }

       }
   } else {
         return [$key, "$comp.$type",'PASS' , $val, $rev, undef , undef, $desc];
   }
}

sub get_s8_level {  # used by v880 patch check
   my $class = @_;
   my $file = "/etc/release";
   # Solaris 8 2/02 s28s_u7wos_08a SPARC
   open ( TEM, $file);
   while (my $l= <TEM>) {
     $l =~ s/^\s+//;  # strip the leading space
     my ($solaris, $os_level, $date, $update_level, $sparc ) = split(/\s+/,$l);
     if ($os_level == 9) {
        close (TEM);
        return (9);
     } elsif ($os_level == 8) {
        # need get update level
        if ( $update_level =~ /u(\d+)wos/ ) {
          close (TEM);
          my $tem_level = $1;
          return ($tem_level);
        }
     } 
   } 
   close (TEM); 
   return (-1);    # can not get os update level info
}


sub check_firmware {
   my($class, $key, $elem, $val, $dev, $mat, $type, $info, $pre) = @_;

   my $default_info = "no patch information available, please contact your sun representative";
   my $el  = $mat->{$type};

   my $rev   = $el->[0];
   my $patch = $el->[1];
   my $desc  = $el->[2];
   my $comp = ($dev->{name} || $dev->{ip});
   $comp = $comp.".$pre" if ($pre);

   if (!$el) {
       return [$key, "$comp.$type", 'ERR' , undef, undef, undef , $patch,
          "No entry in Matrix for $key.$type" ];
   }
   if ($val < 0) {
       return [$key, "$comp.$type.$elem", 'ERR' , undef, undef, undef , undef, "Ses Revision is not available" ];
   }

   my $cp = $class->compFwLevels($val, $rev);
   if ($cp > 0) {
         if (( $patch =~ /xxxx/ ) && ( !$info ) ) {
            return [$key, "$comp.$type", 'UPREV' , $val, $rev, undef , "N/A", $default_info];
         } else {
            return [$key, "$comp.$type", 'UPREV' , $val, $rev, undef, $patch, $info];
         }

   } elsif ($cp < 0) {
          if (( $patch =~ /xxxx/ ) && ( !$info ) ){
             return [$key, "$comp.$type",'DOWNREV' , $val, $rev, undef , "N/A", $default_info];
          } else {
             return [$key, "$comp.$type", 'DOWNREV' , $val, $rev, undef, $patch, $info];
          }

   } else {
       return [$key, "$comp.$type", 'PASS' , $val, $rev, undef , undef, $info];
   }
}

sub compFwLevels {
    my ($class, $drive, $matrix) = @_;
    my (@matrix, @drive);

    @matrix = split(//, $matrix); # separate characters
    @drive = split(//, $drive); # separate characters
    my ($m, $d);
    foreach my $char (@matrix) {
        if (($char ne ".") && ( $char ne "-")) {
            if ( $char =~ /[a-zA-Z]/ ) {
               $m .= ord($char);
            }
            else {
               $m .= $char;
            }
       }
    }

    foreach my $char (@drive) {
       if (($char ne ".") && ( $char ne "-") ) {
            if ( $char =~ /[a-zA-Z]/ ) {
               $d .= ord($char);
            }
            else {
               $d .= $char;
            }
       }
    }

    if (hex($d) > hex($m)) {
        return 1;
    } elsif (hex($d) < hex($m)) {
        return -1;
    } else {
        return 0;
    }
}

sub checkDiskFirmwareM {
     my($class,$Matrix, $InstalledP,$vendor, $model, $val, $dev,$elem ,$dev_type) = @_;
     my $disk_fw;
     $disk_fw = $Matrix->{DISK_FW};

     my $category = $dev_type."_DISK_FW";

     my $key = "$vendor:$model:$dev_type";    # default set up

     my $curr_fw = $val;
     my $cp = ($dev->{name} || $dev->{ip});

     if ($disk_fw->{$key}) {
            my $d = $disk_fw->{$key};
            my $req_fw = $d->[0];
            my $req_patch = $d->[2];
            my $desc =  "$vendor:$model";
            my $info =  " No patch available, please contact your SUN representative ";
            my($req_base, $req_rev) = $class->baseRev($req_patch);
            if ($curr_fw < 0){
               return [$category, "$cp.$elem", 'ERR', undef, $req_fw, undef, $req_patch, "Unable to get the device firmware level, please contact you SUN representative"];
            }
            my $comp = $class->compareDiskFwLevels($curr_fw, $req_fw);
            if ($comp == 0) {
               if ( ($req_patch =~ /xxxx/) || ( !$req_patch ) ) {
                      return [$category, "$cp.$elem", 'PASS', $curr_fw, $req_fw, undef, undef,undef];
               } else {
                      return [$category, "$cp.$elem", 'PASS', $curr_fw, $req_fw, undef, $req_patch, $desc];
               }
            } elsif ($comp > 0) {
               if ( ($req_patch =~ /xxxx/) || ( !$req_patch ) ) {
                     return [$category,"$cp.$elem", 'UPREV', $curr_fw, $req_fw, undef, undef, $desc];
               } else {
                     return [$category, "$cp.$elem", 'UPREV', $curr_fw, $req_fw, undef, $req_patch, $desc];
               }
            } elsif ($comp < 0) {
               if ( ($req_patch =~ /xxxx/) || ( !$req_patch ) ) {
                    return [$category, "$cp.$elem", 'DOWNREV', $curr_fw, $req_fw, undef, "N/A", $info];
               } else {
                    return [$category, "$cp.$elem", 'DOWNREV', $curr_fw, $req_fw, undef, $req_patch, $desc];
               }
            }
            else {
              return [$category, "$cp.$elem", 'ERR', $curr_fw, $req_fw, undef, $req_patch, "Unable to check $vendor device,please contact your SUN representative and update the device firmware"];
            }
     } else {
         return [$category, "$cp.$elem", 'ERR', undef, undef, undef, undef, "Unable to check the $vendor device, please see sunsolve online for potential patch information or contact your SUN representative"];
    }

}

# Check and return an error if device is locked, otherwise return undef.
use LockManager;
sub checkLock {
  my ($class, $device) = @_;
  my $lease = LockManager->new();
  $lease->cleanLocks();
  my $lockInfo = $lease->read($device->{key});
  if ($lockInfo){
	 my ($app, $key, $desc, $start, $mins, $ip, $name) =
		split(/\|/, $lockInfo->{info});
	 my $info = "Unable to check revision because device is locked by (".
		$app . ":" . $name .
		  ") for " . $lockInfo->{time_left} .
		  " more seconds.  Lock description: " . $desc;
	 return [$device->{type},  $device->{name}, 'ERR',
				undef, undef, undef, undef,
				$info];
  }
  return undef;
}


1;
