# $Id: Device.pm,v 1.38 2005/05/26 20:44:12 sbrooks Exp $
# Copyright 2003,2004  Sun Microsystems, Inc., All Rights Reserved.
#
package Client::Device;
use Catalog::Implementation;
use Debug;
use Client;
use Util;
use Matrix;
use Revision;
use Process;
use Socket;
use State;
use Report;
use Util::Http;
use strict;
use Scheduler;
use DiscoveryObject;
use Discover;
use Discover::HBAApi;
use TO;

#------------------------------------------
# FUNCTION: addDevice: trusted
# name     : name of device
# ip
# ipno
# key      : key of device as defined in device spec.
# wwn   
# --- OPTIONAL  ---
# userLabel
# proxys    : management ip | url
# altip     : alternate ip
# altipno   : alternate ip
#------------------------------------------
sub addDevice {
  my( $q)  = @_;
  $q->{class} = "storage.$q->{type}" if (!$q->{class});

  my $dev = DiscoveryObject->new
	 ({
		username => $q->{name},
		key  => $q->{key},
		wwn  => $q->{wwn},
		type => $q->{type},
		class => $q->{class},
		ip   => $q->{ip},
		ipno => $q->{ipno} || $q->{ip}
	  });

  if ($dev && $dev->{error}) {
	 print Client->error("xml", 102, "Device::addDevice: $dev->{error}");
	 return;
  }
  foreach my $el ('proxys', 'userLabel', 'altip', 'altipno') {
    $dev->{$el} = $q->{$el} if ($q->{$el});
  }

  my $lease = Client::Device::doLeaseConf();
  if ($lease){
	 if (($lease = Lease->get("rasagent.conf", 3, 4)) ) {
		print Client->http_OK();
		print Client->xmlHeader();
		
		my $Config = PDM::ConfigFile->read();
		my $devs = $Config->devices();
		my ($err_text, %DevHash);
		foreach my $d (@$devs) {
		  $DevHash{$d->{key}} = $d;
		}
		my $rc = Discover->addDevice($Config, {}, $dev, \%DevHash, $err_text);
		
		if (ref($rc->[0]) eq "DiscoveryObject") {
		  $Config->save();
		  print "<CLIENT_ADD>\n";
		  print $rc->[0]->toXML();
		  print "</CLIENT_ADD>\n";
		} else {
		  print "<CLIENT_ADD></CLIENT_ADD>\n";
		}
		$lease->release();
		
	 } else {
		print Client->error("xml", 109,
								  "Device::addDevice: Unable to lease rasagent.conf");
	 }
  }
}


# Newer version of Add using IP address and framework discovery.
# Should not be used with fast cgi.
sub DiscoverIp {
  my($q) = @_;
  my $ip       = $q->{ip};
  my $type     = $q->{type};
  my $sysDesc  = $q->{sysDesc};
  my $timeout  = $q->{timeout}  || "60";
  my $redisco  = $q->{redisco}  || "F";
  my $addToCfg = $q->{addToCfg} || "T";
  my $monitor  = $q->{monitor}  || "T";

  my $args = { timeout => $timeout };
  $args->{type}    = $type    if $type;
  $args->{sysDesc} = $sysDesc if $sysDesc;

  print Client->http_OK();
  print "<DISCOVER>\n";

  my($renv, $devs, $hosts,$notifs) = PDM::ConfigFile->read();

  if ($redisco eq "F"){ # Don't probe existing ip addresses.
    my $ipno   = Util->name2ip($ip);
    foreach my $d (@$devs) {
      if (($d->{ip} eq $ip) || ($d->{ipno} eq $ipno)){
		  Debug->logLine("Client::Device::DiscoverIp Already Added $ip");
		  print "<DEVICE exists='true'>\n";
		  for my $dval ( keys %{$d} ){
			 print Client->xmlVALUE($dval, $d->{$dval});
		  }
		  print Client->xmlEndTag("DEVICE");
		  print "</DISCOVER>\n";
		  return;
      }
    }
  }

  my $disco_list = Discover->fromIP($ip, $args);

  my @newdevs;

  foreach my $disco (@$disco_list) {
    my $devMatch = undef;
    foreach my $d (@$devs) {
      if (($disco->{type} eq $d->{type}) && 
			 ($disco->{key}  eq $d->{key})){
		  $devMatch = "Y";
      }
    }
	 my $error = $disco->{error};
    if ($devMatch){
      print "<DEVICE exists='true' rediscovered='true'>\n";
    }
	 elsif ($error){
		print "<DEVICE error='true'>\n";
	 }
    else {
      print Client->xmlTag("DEVICE");
		if ("$addToCfg" eq "T"){
		  $disco->{active}     = $monitor;
		  $disco->{mgmtLevel}  = "D";
		  push (@newdevs, $disco);
		}
    }
    for my $dval ( keys %{$disco} ){
      print Client->xmlVALUE($dval, $disco->{$dval});
    }
    print Client->xmlEndTag("DEVICE");
  }

  if ($#newdevs >= 0){
	 Client::Device::doAddToConf(\@newdevs);
  }
  print "</DISCOVER>\n";
} # DiscoverIp


# Adds a device to be monitored.
# Arguments ip - Ip address of device.
#           type - hint at type of device.
#
# Returns the discovered properties for the device that have been
# written to the rasagent.conf file.
# Depricated - Use DiscoverIp. Leave in place as contracted ESM interface.
# Should not be used with fast cgi.
sub Add {
  require Logic::Subnet;
  require Agent::3310;

  my($q) = @_;
  my $in_ip    = $q->{ip};
  my $intype   = $q->{type} || "ALL";
  my $timeout  = $q->{timeout} || "5";
  my $format   = $q->{format} || "tab";
  my $userName = $q->{name};
  my $disco    = undef;

  Debug->logLine("Client::Device::Add $in_ip $intype");

  my($renv, $devs, $hosts,$notifs) = PDM::ConfigFile->read();
  # Check for device already added.
  foreach my $d (@$devs) {
    if (($d->{ip} eq $in_ip)||($d->{ipno} eq $in_ip)){
      Debug->logLine("Client::Device::Add Already Added");
		print Client->http_OK();
		if ( $q->{format} eq "xml"){
		  Client::Device::doPrintDevice($d, "CLIENT_ADD");
		}
		else {
		  Client::Device::doPrintDevice($d);
		}
		return;
    }
  }

  if (!(($intype eq "snmp") ||
		  ($intype eq "3510") || ($intype eq "3511") ||
		  ($intype eq "3310") || ($intype eq "3320") || 
		  ($intype eq "ALL"))){
    print Client->error($format, 102, "Unsuported type = $intype");
    return;
  }

  if ( ($intype eq "snmp") || ($intype eq "ALL") ){
    my $stuff = Logic::Subnet->snmpget1($in_ip, undef, $timeout);
    $disco    = pop(@$stuff);
  }
  if (!$disco){
    if (($intype eq "3510") || ($intype eq "3310") ||
        ($intype eq "3511") || ($intype eq "3320") ||
         ($intype eq "ALL") ){
      my $rc = Agent::3310->getFastWWN($in_ip);
      if ($rc->{error}){
		  print Client->error($format, 103, 
									 "Device 3510 not found for $in_ip $rc->{error}");
		  return;
      }
      my $t = $rc->{type};
		#      $line = 
		#	"storage.$t|$t|$in_ip|$rc->{wwn}|$rc->{key}|$rc->{wwn2}||$in_ip|";
      $disco = DiscoveryObject->new();
      $disco->{class} = "storage.$t";
      $disco->{type}  = $t;
      $disco->{ip}    = $in_ip;
      $disco->{wwn}   = $rc->{wwn};
      $disco->{key}   = $rc->{key};
      $disco->{wwn2}  = $rc->{wwn2};
      $disco->{userLabel} = $userName;
      $disco->{name} = $in_ip;
    }
  }

  if ((!$disco) || (!$disco->{key})){
    print Client->error($format, 103, "Device not found for $in_ip");
    return;
  }

  if ($disco->{error}) {
    print Client->error($format, 101, $disco->{error});
    return;
  }

  my $key   = $disco->{key};
  my $label = $disco->{userLabel};
  my $wwn   = $disco->{wwn};
  my $type  = $disco->{type};
  my $class = $disco->{class};
  my $ip    = $disco->{ip};
  my $wwn2  = $disco->{wwn2};

  # Add device to Config file
  my $new_dev = $#$devs ;
  my $cnt=1;
  my %F;
  foreach my $d (@$devs) {
    $F{$d->{key}} = $cnt;
    $cnt++;
  }

  my $timeAdded = Util->get_today();

  # Setup device name and ipno
  my $ipno   = Util->name2ip($ip);
  my $iaddr  = inet_aton($ipno);
  my ($gh_name,$gh_aliases,$gh_addrtype,$gh_length,@gh_addrs) =
    gethostbyaddr($iaddr, AF_INET);
  my $devname = $disco->{name};
  if ( ! $devname ){
    $devname = $userName || $gh_name || $ip;
  }
  my $device = {};
  $device->{name}       = $devname;
  $device->{type}       = $type;
  $device->{class}      = $class;
  $device->{userLabel}  = $label;
  $device->{ip}         = $ip;
  $device->{ipno}       = $ipno;
  $device->{wwn}        = $wwn;
  $device->{wwn2}       = $wwn2 if ($wwn2);
  $device->{key}        = $key;
  $device->{host}       = ""; #$q->{host};
  $device->{active}     = "Y";
  $device->{mgmtLevel}  = "D";
  $device->{time_added} = $timeAdded;

  # Save conf and update if device is new.
  if (!$F{$key}) {
	 my @devs;
	 push(@devs, $device);
	 Client::Device::doAddToConf(\@devs, 1);
    my $ras_cmd  = System->get_home() . "/bin/rasagent";
    my $ras_args = "-l $key";
    my $command =  "$ras_cmd $ras_args >> /dev/null 2>&1 &";
    system($command);
  }

  print Client->http_OK();
  if ( $q->{format} eq "xml"){
	 Client::Device::doPrintDevice($device, "CLIENT_ADD");
  }
  else {
	 Client::Device::doPrintDevice($device);
  }
}


# Find the properties for a given device
sub Properties {
  my($q) = @_;
  my $format = $q->{format} || "tab";
  my($type, $key) = split(/\:/, $q->{key}) ;

  Debug->logLine("Client::Device::Properties $type $key");

  my($renv, $devs, $hosts,$notifs) = PDM::ConfigFile->read();
  my $numDevs = $#$devs ;
  my $index=0;
  my %F;
  foreach my $d (@$devs) {
    if ($type eq $d->{type}) {
      if ($key eq $d->{key}){

		  print Client->http_OK();
		  if ( $q->{format} eq "xml"){
			 print "<?xml version=\"1.0\"?>\n";
			 print "<DEVICE_PROPERTIES>\n";
			 for my $xprop ( keys %{$d} ) {
				print "<VALUE ID='$xprop'>$d->{$xprop}</VALUE>\n";
			 }
			 print "</DEVICE_PROPERTIES>\n";
		  }
		  else {
			 print "#<pre>\n";
			 for my $prop ( keys %{$d} ) {
				print "$prop\t$d->{$prop}\n";
			 }
			 print "#DONE";
		  }
		  return;
      }
    }
    $index++;
  }

  print Client->errno(222, [$type, $key], {format => $format});
}


#
# Remove a device from the monitor list.
# key="DEVICE_KEY"
# Should work with hbastorage/hbaswitch
# delete from configfile
# delete from DATA/OLD_REPORTS
# delete from topology.
# delete from ALARMS.db
sub Remove {
  my($q) = @_;
  my $format = $q->{format} || "tab";
  my $renv = System->get_renv();

  my $file;
  if ($q->{key} =~ /hbas(\w*)\:(hbas\w*\:.*)/){ # key' => 'hbastorage:hbastorage:H200000e08b063b67' 
     $file = $2;
  }elsif($q->{key} =~ /(\w*)\:(hbas\w*\:.*)/){ # key' => 'SUN-SE6920:hbastorage:240000015d203400'
     $file = $2;
  }else{
	 $file = $q->{key};
  }
  my($type, $key) = split(/\:/, $file);
  Debug->logLine("Client::Device::Remove $type $key");

  my $list = Report->reportList();
  my $report = $list->{$file};
  if ($report && $report->[0] ne $renv->{hostname}) {   # ON SLAVE
	 my $host = $report->[0];
	 Report->deleteFromList($host, $file);
	 Util::Http->deleteFile($host, "OLD_REPORTS/$file", 6);
  } else {                                              # ON MASTER
	 unlink System->get_home() . "/DATA/OLD_REPORTS/$file";
	 Report->pushReportsList();
  }

  # ALARMS
  State->clearComponent($file);

  # TOPOLOGY
  my $topo = TO->readExistingTopo("MERGE-MASTER");
  if ($topo) {
	 my $dev = { type => $type, key => $key };
	 $topo->syncWithConfig($dev);
	 $topo->saveTopo("MERGE-MASTER");
  }

  # CONFIGFILE
  my $lease = Client::Device::doLeaseConf();
  if ($lease){
	 my($renv, $devs, $hosts, $notifs) = PDM::ConfigFile->read();
	 my $index=0;
	 my (%F, $x, $save_wwns);
	 for ($x=0; $x <= $#$devs; $x++) {
		my $d = $devs->[$x];
		if ($key eq $d->{key}){
        $devs->[$x] = undef;
        #$save_wwns = ",$d->{key},$d->{wwn},$d->{wwn2},$d->{wwns},";
          $renv->{"clear_topo_timestamp"} = time;
		  PDM::ConfigFile->write( $renv, $devs, $hosts, $notifs);
        last;
		}
	 }
	 print Client->http_OK();
	 print "OK";
  }
  else {
	 print Client->error($format, 109,
								"Device::Remove: Unable to lease rasagent.conf");
  }
}


# Delete a property from a device config entry.
sub DeleteProperty {
  my($q)    = @_;
  my $key   = $q->{key};
  my $name  = $q->{name};
  my $value = $q->{value};
  my $token = $q->{token} || "|";

  if ((!$key) || (!$name)){
    print Client->error("xml", 103,
				"Device::DeleteProperty: Required field, (key,name) missing.");
    return;
  }

  if (($name eq "key") ||
      ($name eq "type") ||
      ($name eq "name") ||
      ($name eq "time_added") ||
      ($name eq "active")){
    print Client->error("xml", 102,
				"Device::DeleteProperty: Cannot delete required property $name");
    return;
  }

  my($lease);
  if (($lease = Lease->get("rasagent.conf", 3, 4)) ) {
	 my($renv, $devs, $hosts,$notifs) = PDM::ConfigFile->read();
	 foreach my $d (@$devs) {
		if ($key eq $d->{key}){
		  if ($value){ # Delete a list entry
			 my $oldVal = $d->{$name};
			 if ($oldVal eq $value){
				delete $d->{$name};
			 }
			 else {
				my @values = split(/\$token/, $oldVal);
				my $sep;
				my $newVal;
				my $x;
				for ($x=1; $x <= $#values; $x++) {
				  if ( ! ($value eq $values[$x])){
					 $newVal = $newVal . $sep . $values[$x];
					 $sep    = $token if (!$sep);
				  }
				}
				$d->{$name} = $newVal;
			 }
		  }
		  else {
			 delete $d->{$name};
		  }
		  PDM::ConfigFile->write($renv, $devs, $hosts, $notifs);
		  $lease->release();

		  print Client->http_OK();
		  print "<DEVICE_DELETE>\n";
		  Client::Device::doPrintDevice($d, "DEVICE");
		  print "</DEVICE_DELETE>\n";
		  return;
		}
		else {
		  print Client->error
			 ("xml", 109,
			  "Device::DeleteProperty: Unable to lease rasagent.conf");
		}
	 }
  }
  print Client->error("xml", 101, 
							 "Device::DeleteProperty: No device exists for $key");
} # Delete


# Modify device properties
sub Modify {
  my($q) = @_;
  my $type = $q->{type};
  my $key  = $q->{key};
  my $cmd  = $q->{_CMD} || "replace";

  delete $q->{GO};
  delete $q->{_CMD};

  if ($cmd eq "replace"){
	 if ((!$type) || (!$key) || (!$q->{class}) || (!$q->{name}) ||
		  (!$q->{userLabel})){
		print Client->error("xml", 101, "Missing arguments.");
		return;
	 }
  }

  my($lease);
  if (($lease = Lease->get("rasagent.conf", 3, 4)) ) {
	 my($renv, $devs, $hosts,$notifs) = PDM::ConfigFile->read();

	 foreach my $d (@$devs) {
		if ($key eq $d->{key}){
		  print Client->http_OK();
		  if ( $cmd eq "replace"){
			 # Remove values in device properties not in the given arguments.
			 for my $dval ( keys %{$d} ){
				if ((!$q->{$dval}) && 
					 (!"_name" eq $dval) &&
					 (!"time_added" eq $dval) &&
					 (!"active" eq $dval)){
				  delete $d->{$dval};
				}
			 }
			 # Add all defined properties.
			 for my $mval ( keys %{$q} ){
				$d->{$mval} = $q->{$mval};
			 }
		  }
		  elsif ( $cmd eq "delete"){  # delete defined props
			 for my $mval ( keys %{$q} ){
				my $devVal = $d->{$mval};
				my $curVal = $q->{$mval};
				if ($devVal eq $curVal){
				  delete $d->{$mval};
				}
				else {
				  if (($mval eq "proxys") ||
						($mval eq "wwns")   ){
					 if (rindex($d->{$mval}, $q->{$mval}) < 0){
						# Todo: remove entry
					 }
				  }
				}
			 }
		  }
		  elsif ( $cmd eq "merge"){  # Only add new properties.
			 for my $mval ( keys %{$q} ){
				if (($mval eq "proxys") ||
					 ($mval eq "wwns")   ){ # Handle lists
				  if ($d->{$mval}){
					 if (rindex($d->{$mval}, $q->{$mval}) < 0){
						$d->{$mval} = $d->{$mval} . "|" . $q->{$mval};
					 }
				  }
				  else {
					 $d->{$mval} = $q->{$mval};
				  }
				}
				else {
				  $d->{$mval} = $q->{$mval};
				}
			 }
		  }
		  PDM::ConfigFile->write( $renv, $devs,
										  $hosts,$notifs);
		  $lease->release();
		  Client::Device::doPrintDevice($d, "DEVICE_MODIFY");
		  return;
		  }
	 }
  }
  else {
	 Client->error("xml", 109,
						"Device::Modify: Unable to lease rasagent.conf");
  }
  print Client->error("xml", 102, "No device exists for $type $key");
}

# Insert a new device entry.
sub Insert {
  my($q) = @_;
  my $type = $q->{type};
  my $key  = $q->{key};
  my $wwn  = $q->{wwn};
  my $device = undef;
  my $error  = undef;

  delete $q->{GO};

  Debug->logLine("Client::Device::Insert $type $key");

  if (!$type){
	 print Client->error("xml", 102,
								"Invalid configuration, type required.");
	 return;
  }
  if (!$key){
	 print Client->error("xml", 102,
								"Invalid configuration, key required.");
	 return;
  }

  my($renv, $devs, $hosts,$notifs) = PDM::ConfigFile->read();

  foreach my $d (@$devs) {
    if ($type eq $d->{type}) {
      if ($key eq $d->{key}){
		  $device = $d;
		  last;
      }
    }
	 if ($wwn){
		if ($wwn eq $d->{wwn}){
		  $device = $d;
		  last;
		}
	 }
  }


  if (!$device) {
	 my $args = { type => "$type" };
	 my $ip   = $q->{proxys};

	 if (!$ip){
		$ip = $q->{ipno} || $q->{ip};
	 }
	 else {
		my $i = index $ip, "|";
		if ($i > 0){
		  $ip = substr $ip, 0, $i;
		}
	 }
	 if (!$ip){
		print Client->error("xml", 102,
								  "Invalid configuration, ipno or ip required.");
		return;
	 }
	 Debug->logLine("Validating $type $ip");
	 # Validate

	 my $discos = Discover->fromIP($ip, $args);
	 Debug->logLine("Called disco.");
	 foreach my $disco (@$discos) {
		if ($disco->{key} eq $key){
		  $device = $disco;
		}
		else {
		  $error = $disco->{error};
		}
	 }
	 if (!$device){
		print Client->error("xml", 103, "Unable to validate device. $error");
		return;
	 }
  }

  Debug->logLine("Merging...");

  # Merge
  for my $mval ( keys %{$q} ){
	 if (!$device->{$mval}){
		$device->{$mval} = $q->{$mval};
	 }
  }
  $device->{active}     = "Y";
  $device->{mgmtLevel}  = "D";

  my @devs;
  push(@devs, $device);
  $error = Client::Device::doAddToConf(\@devs, 1);
  if ($error){
	 print Client->error("xml", 104, $error);
	 return;
  }
  else {
	 print Client->http_OK();
	 Client::Device::doPrintDevice($device, "DEVICE_INSERT");
  }

} # Insert


# Update all devices or if a key defined a single device.
# Should not be used with fast cgi.
sub Update {
  my($q) = @_;
  my $format = $q->{format};
  my($type, $key) = split(/\:/, $q->{dev}) ;

  my @l = split(/\|/, $q->{devlist});
  foreach my $k (@l) {
    my($ktype, $kvalue) = split(/\:/, $k);
    if ($key){
      $key .= ":".$kvalue;
    }
    else {
      $key = $kvalue;
    }
  }

  my $ras_cmd = System->get_home() . "/bin/rasagent";
  my $ras_args;

  if ($key){
    $ras_args = "-l $key";
  }

  my $command =  "$ras_cmd $ras_args >> /dev/null 2>&1 &";
  system($command);

  Debug->logLine("Client::Device::Update $command");

  print Client->http_OK();
  print "OK $command";
}

#
# Run ras_revcheck on a single device
# Should not be used with fast cgi.
sub Revision {

  my($q) = @_;

  my $key     = $q->{key};
  my $format  = $q->{format} || "xml";
  my $MODULES = $q->{module} || "ALL";
  my $revpid  = $q->{pid};
  my $bg      = $q->{bg};
  my $cmd     = $q->{cmd} || "print";

  my @REPORTS;
  my $revDir = System->get_home() ."/DATA/sae_revcheck";

  Debug->logLine("Client::Device::Revision $MODULES $key $revpid");


  if ($revpid){
    if (( -f "$revDir/$revpid") && (-s "$revDir/$revpid")){
      if ($cmd eq "print"){
		  open (REVOUT, "$revDir/$revpid");
		  my @L = <REVOUT> ; close(REVOUT);
		  print Client->http_OK();
		  foreach my $l (@L) {
			 print "$l";
		  }
      }
      elsif ($cmd eq "delete"){
		  unlink "$revDir/$revpid";
      }
    }
    else {
      if ( -f "$revDir/$revpid"){
		  print Client->error($format, 101,
									 "Output for $revpid not ready.");
      } else {
		  print Client->error($format, 102,
									 "No revision process for $revpid.");
      }
    }
    return;
  }

  if ($key){
    print Client->error($format, 99,
			"Revision Check for single device not implemented.");
    return;
  }

  my $pid="0";

  if ($bg eq "Y"){
	 my $pid = fork();
  }

  if ($pid){ #parent
    if ($bg eq "Y"){
      print Client->http_OK();
      print "OK $pid";
    }
    return;
  }

  if (defined $pid) {  # child

    my $out;
    if ($bg eq "Y"){
		
      if (! -d $revDir){
		  mkdir $revDir, 0777;
      }
      if ( -f "$revDir/$$"){
		  unlink "$revDir/$$";
      }
      open(REVFILE, ">$revDir/$$");
      print REVFILE "";
      close (REVFILE);
		
      $out = Client->xmlTag("REVISION_REPORT");
      close(STDIN);
      close(STDERR);
      close(STDOUT) if (!$ENV{FCGI});
    }
    else {
      print Client->http_OK();
      print Client->xmlTag("REVISION_REPORT ");
    }

    my $include;
    my ($env)      = Revision->hbas_present();
    my $MATRIX     = Matrix->defaultFile();
    my $matrix     = Matrix->read($MATRIX, $env);
    my $installedp = Revision->readInstalledPatches();
    my $mods       = Modules->load("Revision");

    foreach my $x (@$mods) {
      if ($MODULES eq "ALL" || index(lc("|$MODULES|"), lc("|$x|")) >= 0) {
		  $include .= " $x|";
		  my $mod = "Revision::$x";
		  my $db;
		  eval {
			 Debug->logLine("Client::Device::Revision Running module $mod");
			 $db = $mod->RUN($matrix, $installedp);
		  };
		  if (!$db) {
			 #$db = Revision->warning2($x);
			 #push(@REPORTS, @$db);
		  } elsif ($@) {
			 #$errors .= "ERROR in $mod: $@ \n";
		  } else {
			 push(@REPORTS, @$db);
		  }
		  #open(O, ">>$LOG"); print O "module $x done\n"; close(O);
		  chop($include);
      }
    }
	 
    my($renv, $devs) = PDM::ConfigFile->read();
    my %KEY;
    foreach my $d (@$devs) {
      $KEY{$d->{name}} = $d->{key};
      $KEY{$d->{ipno}} = $d->{key};
    }

    foreach my $ele (@REPORTS){
      my $devKey = $ele->[8];
      if (!$devKey){
	$devKey = $KEY{$ele->[1]};
      }
      if (!$devKey){
	my @N = split(/\./, $ele->[1]);
	$devKey = $KEY{$N[0]};

	# Add smart ip parsing.
	if (!$devKey){
	  my $ipkey = $N[0];
	  my $index;
	  for ($index = 1; $index <= $#N && (!$devKey); $index++) {
	    $ipkey .= "." . $N[$index];
	    $devKey = $KEY{$ipkey};
	  }
	}

      }
      my $devType;
      foreach my $d (@$devs) {
	if ($d->{key} eq $devKey) {
	  $devType = $d->{type};
	}
      }
      my $fruKey = $ele->[9];

      $out .= Client->xmlTagOpen("ITEM");
      $out .= Client->xmlAttrib("Type", $ele->[0]);
      $out .= Client->xmlAttrib("DevType", $devType);
      $out .= Client->xmlAttrib("DevKey", $devKey);
      $out .= Client->xmlAttrib("Name", $ele->[1]);
      $out .= Client->xmlAttrib("Status", $ele->[2]);
      $out .= Client->xmlAttrib("CurrVers", $ele->[3]);
      $out .= Client->xmlAttrib("ExptVers", $ele->[4]);
      $out .= Client->xmlAttrib("CurrPatch", $ele->[5]);
      $out .= Client->xmlAttrib("ExptPatch", $ele->[6]);
      $out .= Client->xmlAttrib("Message", $ele->[10]);
      $out .= Client->xmlTagClose();

      $out .= Client->xmlEncode($ele->[7]) if $ele->[7];
      $out .= Client->xmlEndTag("ITEM");
    }
    $out .= Client->xmlEndTag("REVISION_REPORT");

    if ($bg eq "Y"){
      open(REVFILE, ">$revDir/$$");
      print REVFILE "$out";
      close (REVFILE);
      exit 0;
    }
    else {
      print "$out";
    }
  }
  else {
    print Client->error($format, 98,
			"Unable to run command.");
  }
}

sub classProperties {
  my($q) = @_;
  my $type = $q->{type};

  my $abbs = Util->readResource("System/abbreviations.txt", "type");
  print Client->http_OK();
  print "<REPORT><HEADER>\n";
  foreach my $a (keys %$abbs){
	 if ($a =~ /$type\.(.*)/){
		print Client->xmlVALUE($1, $abbs->{$a});
	 }
  }
  print "</HEADER></REPORT>\n";
}

sub getSummary {
  my($q) = @_;
  my $key = $q->{key};

  print Client->http_OK();

  my($renv, $devs, $hosts,$notifs) = PDM::ConfigFile->read();
  my $abbs = Util->readResource("System/abbreviations.txt", "type");
  require State;
  my $State = State->read();
  my $arms  = $State->hash();

  if ($key){
    print "<DEVICE_SUMMARY>\n";
    foreach my $d (@$devs) {
      if ($key eq $d->{key}){
	Client::Device::doPrintDeviceSummary($d, $abbs, $arms);
      }
    }
    print "</DEVICE_SUMMARY>\n";
    return;
  }

  print "<DEVICE_SUMMARY>\n";
  foreach my $d (@$devs) {
    Client::Device::doPrintDeviceSummary($d, $abbs, $arms);
  }
  print "</DEVICE_SUMMARY>\n";
}

sub doPrintDeviceSummary {
  my ($dev, $abb, $arms) = @_;
  my $type = $dev->{type};
  my $key = $dev->{key};

  my $sum =
	 {
	  "key" => $key,
	  "wwn" => $dev->{wwn},
	  "ip"  => $dev->{ip} || $dev->{ipno},
	  "monitored"       => $dev->{active},
	  "monitoringAgent" => $dev->{host},
	  "displayName"     => $dev->{displayName} || $dev->{name} ||  $dev->{ip},
	  "class"       => $dev->{class},
	  "vendor"      => $dev->{vendor},
	  "model"       => $dev->{model},
	  "description" => $dev->{description},
	  "caption"     => $dev->{caption}
	 };

  if ($abb->{$type . ".class"}){
	 $sum->{class}  = $dev->{class}  || $abb->{$type . ".class"};
	 $sum->{vendor} = $dev->{vendor} || $abb->{$type . ".vendor"} || "Sun";
	 $sum->{model}  = $dev->{model}  || $abb->{$type . ".model"}  || 
		$abb->{$type . ".short"};
	 $sum->{description} = $dev->{description} || $abb->{$type . ".long"};
	 $sum->{caption} = $dev->{caption} || $abb->{$type . ".medium"}
  } 
  else {
	 # Get info from catalog.
	 my $cat = new Catalog::Implementation($dev->{type});
	 my $info = $cat->getInfo();

	 $sum->{class} = $dev->{class} || $type . "." . $info->{class};
	 $sum->{vendor} = $dev->{vendor} || $info->{vendor} || "Sun";
	 $sum->{model} = $dev->{model} || $info->{model};
	 $sum->{caption} = $dev->{caption} || $info->{displayName};
	 $sum->{description} = $dev->{description} || $info->{description};
  }



  my $typekey = "$type:$key";
  my $AlarmSev;
  my $AlarmDesc;

  if ($arms){
    my $comp = $arms->{$typekey};
    foreach my $topic (keys %$comp) {
      my $sev    = int($comp->{$topic}[0] + 0.5);
      my $desc   = $comp->{$topic}[1];
      my $devKey = $comp->{$topic}[2];
      my $X      = $comp->{$topic}[3];
      my $etype  = $comp->{$topic}[4];
      my $numcor = $comp->{$topic}[5];
      my $corr   = $comp->{$topic}[6];
      my $ack    = $comp->{$topic}[7];
      next if ($ack);
      next if (!$sev || $numcor < 0);
      $AlarmSev  = $sev;
      $AlarmDesc = $desc;
    }
  }
  $sum->{alarmSeverity} = $AlarmSev;
  $sum->{alarmMessage}  = $AlarmDesc;

  print "<DEVICE>\n";
  foreach my $name (keys %$sum){
	 print Client->xmlVALUE($name, $sum->{$name});
  }
  print "</DEVICE>\n";
}


#
# Lease a device
# See Client::Lock::lock
sub LeaseRequest {
  my($q) = @_;

  my $format   = $q->{format};

  Debug->logLine("Client::Device::LeaseRequest $format");

  print Client->error($format, 99,
		      "Lease Request not implemented.");
}

#
# Clear a device from a lease
# See Client::Lock::unlock
sub LeaseRelease {
  my($q) = @_;

  my $format   = $q->{format};

  Debug->logLine("Client::Device::LeaseRelease $format");

  print Client->error($format, 99,
		      "Lease Release not implemented.");
}


sub doPrintDevice
{
  my($device, $tag) = @_;
  if ($tag){
	 print Client->xmlTag($tag);
	 for my $prop ( keys %{$device} ) {
		print Client->xmlVALUE($prop, $device->{$prop});
	 }
	 print Client->xmlEndTag($tag);
  }
  else {
	 print "#<pre>\n";
	 for my $prop ( keys %{$device} ) {
		print $prop . "\t" . $device->{$prop} . "\n";
	 }
	 print "#DONE";
  }
}

sub doLeaseConf {
  my $lease;
  if (($lease = Lease->get("rasagent.conf", 3, 4)) ) {
	 return $lease;
  }
  return undef;
}

sub doAddToConf {
  my($devices, $modify) = @_;

  my $lease = Client::Device::doLeaseConf();
  if ($lease){
	 my($renv, $devs, $hosts,$notifs) = PDM::ConfigFile->read();
	
	 my $new_dev = $#$devs ;
	 my $index;
	 for($index = 0; $index <= $#$devices; $index++) {
		my $device = $devices->[$index];
		
                # if there is no key then do not process the device
                next if (!$device->{key});
                
                # Check for device already added.
                my $modified;
		foreach my $d (@$devs) {
		  if ( $d->{key} eq $device->{key} ){
                  
                         $modified = 1;
                     
			 if ($modify){
				for my $prop ( keys %{$device} ) {
				  $d->{$prop} = $device->{$prop};
				}
			 }
			 else {
				$lease->release();
				return "Device already exists for " .
				  $device->{type} . ":" . $device->{key};
			 }
		  }
		}
                if (!$modified){
		   $new_dev++;
		   $devs->[$new_dev]{_name}      = "device" . $new_dev;
		   $devs->[$new_dev]{time_added} = Util->get_today();
		   for my $prop ( keys %{$device} ) {
		     $devs->[$new_dev]{$prop} = $device->{$prop};
		   }
                }
	 }

	 if (!PDM::ConfigFile->write( $renv, $devs, $hosts,$notifs)){
		$lease->release();
		return "Error writing to rasagent.conf";
	 }
	 $lease->release();
	 return undef;
  }
  else {
	 return "Unable to lease rasagent.conf";
  }
}

1;

