# $Id: COMMON.pm,v 1.22 2006/05/19 05:09:51 sbrooks Exp $
# Copyright 2004 Sun Microsystems, Inc., All Rights Reserved.
# Sun Proprietary/Confidential: For Internal Use Only
package Agent::COMMON;

use strict;

use Agent;
use Catalog::Implementation;
use Data::Dumper;
use Debug;
use DeviceReport::COMMON;
use Health::Catalog;
use Java::Report;
use PDM::ConfigFile;

use base 'Agent';

sub revision { '$Revision: 1.22 $ '}

sub new {
  my($class, $type) = @_;

  my $this = { type => $type };
  bless($this, "Agent::COMMON");
  $this->load($type);

  return $this;
}

sub load {
  my($this, $type) = @_;

  my $impl = new Catalog::Implementation($type);
  $this->{impl} = $impl;
}

sub devType {
  my($this) = @_;
  return $this->{type};
}

sub category { 
  my($this) = @_;
  return $this->{category};
}

sub RUN {
  my($this, $static_list, $pass) = @_;

  my $locks = System->get_se_conflict();
  my $report;
  my $processed_list;
  my $dc;

  my @adevs = $this->deviceList($static_list, $this->{type});
  my $devs  = \@adevs;
  if ($this->{forceStatic}){
	 $devs = $static_list;
  }
  foreach my $dev ( @$devs ) {
    next if ($dev->{active} eq "N");
	 Debug->print2("Monitor " . $dev->{name} . ":" . $dev->{key} . " using " .
						$this->{type} . " plug-in agent.");
	 if ($locks && index(",$locks,", ",$dev->{name},") >= 0 ) {
       Debug->print2( "-> $dev->{type}: skipping $dev->{name}, " .
							 "detected device lockfile");
       next;
    }
	 $dc++;
	 my $sys_report = $this->INSTRUMENTATION($dev);
	 my $warn_lines = $this->MESSAGELOG($dev);

	 if (($warn_lines) && ($#$warn_lines >= 0)){
    	 my $id =
		  {
			deviceName  =>  $dev->{key},
			active      => "Y",
			display     => $dev->{name},
			name        => $dev->{name},
			class       => $dev->{class},
			category    => $dev->{type} . "message",
			ip          => $dev->{ip}
		  };
    	$report->{"id.name"} = $dev->{name};
    	my $new_rep = Report->new($id, $report , $warn_lines, Report::STATUS_OK);
    	require Health::Message;
    	Health::Message->all_logic($new_rep);
    	PDM->saveReport( $new_rep );
    }

    my $wwn = $dev->{key};
    my $connect_errs = $sys_report->{"rc.error"} ||
		       $sys_report->{"error._Description"};

    if (!$connect_errs) {
       if (!$sys_report->{"system._Key"}){
          $connect_errs = "_Key missing from system";
       }
    }

    # SAVE EVENT POINTER.
    my $problems = PDM->get_last_event();

    # CREATE A REPORT HEADER with identification for this report.
    my $agentClass = $this->category() . "." . $this->devType();
    my $report_header = {
			 deviceName => $wwn,
			 active     => $dev->{active},
			 class      => $agentClass,
			 name       => $dev->{name},
			 display    => $dev->{name},
			 category   => $this->devType(),
			 ip         => $dev->{ipno},
			};
    $sys_report = {} if (!$sys_report);
    $this->copyDev($dev, $sys_report);

    # Differentiate between a missing data error and all other
    # errors from the device agent.  No events should be 
    # created for missing data as we are assuming this is a
    # transient error.  Not a good assumption but there it is.
    my $skip;
    if ($connect_errs)
    {
       if ($connect_errs eq "Missing data")
       {
          $skip = 1;
       }
    }

    my ($broke, $abort);
    if (!$skip)
    {
       my $new_rep;
       if ($connect_errs) 
       {
         $new_rep = Report->new($report_header, $sys_report, undef,
       			     Report::STATUS_CANNOT_CONNECT );
       } 
       else 
       {
         Discover->updateTopoDevice($dev, {report => $sys_report});
         Agent->setIdSection($sys_report, $dev->{name},
       			  $dev->{mgmtLevel}, $dev->{ip}, $dev->{ipno}, 
       			  $dev->{key});
         $new_rep = Report->new($report_header, $sys_report);
       }
       
       # CALL HEALTH MODULE, generate EVENTS
       my $health_module = new Health::Catalog($this->{type});
       $health_module->LOGIC($new_rep, $dev);
       
       # SAVE INSTRUMENTATION REPORT
       $DB::single=1;
       PDM->saveReport($new_rep);
       
       # ABORT THE INSTRUMENTATION LOOP if a problem was found.
       ($broke, $abort) = $this->new_events($problems, $dev, 1);
    }

    push(@$processed_list, $dev);
    last if ($broke && $pass == 1 || $abort);
  }

  # ALWAYS END RUN by returning a list of processed devices.
  return $processed_list;

}


sub INSTRUMENTATION {
  my($this, $device) = @_;

  my $renv = System->get_renv();
  return Report->readTest($device) if (System->get_testMode());

  my $devType = $device->{type};
  my $devKey  = $device->{key};

  Debug->print2("Obtaining Report for $devType $devKey.\n");
  Timelapse->startDev( $device);
  my $data = Java::Report->instrumentDevice($device);
  my $R = $data->{report};
  my $modDevice = $data->{device};

  # ADD Site Identification data to the report
  $this->addIdentification($R);

  # ADD 'rc.key' to the report
  $R->{'rc.key'} = $R->{"id.wwn"};
  Timelapse->stopDev( $device);

  # Update rasagent.conf if device info has changed
  $this->checkForConfigChanges($device, $modDevice);
  Timelapse->stopDev( $device);

  # RETURN a pointer to the HASH
  return $R;
}


sub MESSAGELOG {
  my($this, $device) = @_;

  my $type   = $device->{type};
  my $key    = $device->{key};
  my $name   = $device->{name};
  my $ipno   = $device->{ipno};
  my $devKey = "$type:$key";
  my $renv   = System->get_renv();
  my (@err, $l);
  my ($index, @lines, $dev, $marker, $msgList);

  my $LOGName = "messages." . $this->devType();
  my $LOGFile = $renv->{"$LOGName"} || "/var/adm/$LOGName";
  my $msgs;

  if (System->get_testMode()) {
     # READ FROM messages.<type>
     open(O, $LOGFile);
     while ($l = <O>) {
       chop($l);
       push(@lines, { line => $l});
     }
     close(O);
	  $msgs = \@lines;
  } else {
    # READ LOGS FROM DEVICE, SAVE TO messages.<type>
    use DevDB;
    $dev = DevDB->read($devKey);
    $marker = $dev->{log_file_marker};
	 if (!$marker){
		
	 }
    $msgList = Java::Report->message($marker, $device);
	 $msgs = $msgList->{messages};
		
#	 if ($msgs){
#		open(O, ">>$LOGFile");
#		for($index = 0; $index <= $#$msgs; $index++) {
#		  my $msg = $msgs->[$index];
#		  my $L = "$msg->{line}";
#		  push(@lines, $L);
#		  print O "$L\n";
#		}
#		close(O);
#	 }
  }

  my $hc     = new Catalog::HealthConfig($type);
  my $errors = $hc->evaluateMessages($msgs, $device);

  if (!System->get_testMode()) {
    # SAVE MARKER IF NOT IN TEST-MODE
	 if ($msgList->{marker}){
		$dev->{log_file_marker} = $msgList->{marker};
		DevDB->write($devKey, $dev);
	 }
  }

  return $errors;
}

####################################################################
#
# sub FRUS
# this function returns a standard data-structure for any device
# INPUT : the Instrumentation Report (lib/Report.pm)
# the device name
# OUTPUT: a pointer to a table of frus with one run per fru.
# [
# [ "name", "device_type", "FruType", "FruId",
# "Vendor", "Model", "Serial", "Revision", "Status" ],
# [ "name", "device_type", "FruType", "FruId",
# ]
####################################################################
sub FRUS {
  my($this, $r, $name) = @_;

  my(@F);
  my $devtype = $r->category();
  my $pk      = new DeviceReport::COMMON($this->{type});
  my $R       = $pk->fruListMap($r);
  my $FRUS    = $R->{FRU};

  foreach my $id (keys %$FRUS) {
    my $fru = $FRUS->{$id};
    push(@F, [$name, $devtype, 
       $fru->{type},
		 $fru->{name},
		 $fru->{vendor},
		 $fru->{model},
		 $fru->{serial},
		 $fru->{revision},
              ]);
  }

  return \@F;
}

# This subroutine compares the rasagent.conf properties for a device
# with the properties as returned by the report generator.  If there
# are changes in an attribute, or an attribute is added, the 
# rasagent.conf file is updated.
sub checkForConfigChanges
{
   my ($this, $storedProps, $newProps) = @_;

   foreach my $propName (keys %$newProps) 
   {
      # If the property name already existed, and it has changed, write it out.

      if (($storedProps->{$propName}) && ($storedProps->{$propName} ne $newProps->{$propName}))
      {
	 Debug->print2("Updating propName $propName from stored value:$storedProps->{$propName} to new value: $newProps->{$propName}  ");

         PDM::ConfigFile->updateDevice($storedProps->{key}, { $propName => $newProps->{$propName} });   
      }
      if (!$storedProps->{$propName})
      {
         Debug->logLine ("Would have added $propName");
         Debug->logLine (Dumper($newProps));
      }
   }

}


####################################################################
# sub REPORT: returns html used in GUI to display device Report.
#
# Since the Java framework will use Standard Reports this is all that
# needs to be done.
####################################################################
use DeviceReport;
sub REPORT {
  my($class, $host, $r, $arg) = @_;
  return if (!$r);

  DeviceReport->standardReport($r, {
       skipSections => "diskExtent",
       skipColumns  => "unit.\$caption,disk.\$caption,ctrl.\$caption",
      }
  );
}

# Nasty code to essentially replicate the discovery code for the use
# of the system edition.
# Parameters: $ipno, $host, $TO, 1, $devType
# ipnumber	172.20.35.172
# host
# Timout in seconds (optional)
# 1
# device type	6140
sub getWWN 
{

  my($class, $ip, $host, $TO, $whazit, $devType) = @_;
  my $rc;
 
  if ($host) 
  {
  
      # Modify this line to use the target type of device.  Obtained generically
      # of course.
      $rc = Util::Http->getCommand($host, "Agent::$devType::WWN&ip=$ip&HTTP=1&TO=$TO", $TO+5);
      if (substr($rc,0,3) eq "ERR") 
      {
         #$ERR = $rc;
         return undef;
      } 
      else 
      {    
         eval substr($rc,3);
         #$rc = $VAR1;
      }
  } 
  else 
  {
      $rc = get_WWN({ip => $ip, TO => $TO, devType => $devType, frus => $whazit});
  }
}

# Nasty code to essentially replicate the discovery code for the use
# of the system edition.
sub get_WWN 
{
  my($q) = @_;
  my $ip = $q->{ip};
  my $TO = $q->{TO};
  my $devType = $q->{devType};
  my $frus = $q->{frus};
  
  my $rcs = Java::Discovery->deviceSearch({ ip => $q->{ip} }, $devType);
  
  my %H;
  my $found;
   
  foreach my $rc (@$rcs) 
  {
     my $foundIP = $rc->{ip};
    
     if ($ip eq $rc->{ipno})
     {
        $H{key}  = $rc->{key};
        $H{type} = $devType;
        $H{name} = $rc->{name};
        $H{ipno} = $rc->{ipno};
        $H{wwn}  = $rc->{wwn};
        $H{wwns} = $rc->{wwns};
        $H{said} = $rc->{said};
        $H{altip} = $rc->{altip};
        $H{altipno} = $rc->{altipno};
        $H{units} = $rc->{units};
        $H{boardID} = $rc->{boardID};


	my $sys_report = Agent::COMMON->INSTRUMENTATION($rc);

        # Search through the report...
        foreach my $entry (%$sys_report)
        {
           $entry = Util->ltrim($entry);

           # until you find a fru.x._Key entry.
           if ($entry =~ /^fru\.(\d+)\._Key/)
           {
              my $frudex = $1;
              my $key = $sys_report->{$entry} ;

              # Then search the entire report again...
              foreach my $line (%$sys_report)
              {
                 $line = Util->trim($line);

                 # until you find a _Realized field that...
                 if ($line =~ /(\w+)\.(\d+)\._Realized/)
                 {
                    my $realized = $sys_report->{$line};

                    # matches the _Key from above.
                    if ($realized eq $key)
                    {
		       # Extract the device id for identification purposes.
#                       my $deviceid = $sys_report->{"$1.$2._DeviceID"};
                       my $deviceid = $sys_report->{"$1.$2._Name"};

		       # temporarily change "." to "-" (changed back in Inventory.pm check_whitney
		       # (Tray.X.comp.Y)
		       $deviceid =~ tr/\./-/;

                       $H{"FRU.$deviceid.fruVendor"} =  Util->rtrim($sys_report->{"fru.$frudex._Vendor"});
                       $H{"FRU.$deviceid.fruModel"} =  Util->rtrim($sys_report->{"fru.$frudex._Model"});
#                       $H{"FRU.$deviceid.Version"} =  Util->rtrim($sys_report->{"fru.$frudex._Firmware"});
                       $H{"FRU.$deviceid.fruType"} =  Util->rtrim($sys_report->{"fru.$frudex._Type"});
                       $H{"FRU.$deviceid.fruSerialNo"} =  Util->rtrim($sys_report->{"fru.$frudex._SerialNumber"});
                       $H{"FRU.$deviceid.fruRevision"} =  Util->rtrim($sys_report->{"fru.$frudex._Firmware"});

                       last;
                    }
                 }
              }
           }
        }


        $found = 1; 
        last;
     }
  }
 
  if (!$found)
  {
     $H{warn} = "  No $devType type device found at $ip";
  }
    
  return \%H;      
  
}

1;
