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

#  $Name:  $ 
#  $Id: Report.pm,v 1.49 2002/10/28 23:19:54 ccadieux Exp $

use Carp;
use Util::Http;
use ReportContent;
use strict;

use vars qw($ERROR_LOG $WARNING_LOG $NOTICE_LOG  $DEVICE_ERROR $HOST_CONFIG $HOST_DISK_INQ $HOST_DATA_PING $SYSTEM_ERROR $ERR $REMLIST %ISSWITCH);

#
#  $rep = Report->new( { $type, $id, $value, $log_value , $status });
#  Interface:
#   $rep->name
#   $rep->type
#   $rep->category
#   $rep->content
#   $rep->id
#   $rep->created
#   $rep->key


sub STATUS_CANNOT_CONNECT  {'CC'};
sub STATUS_OK              {'OK'};

sub CAT_A3500FC          {'a3500fc'};
sub CAT_A1000            {'a1000'};
sub CAT_A2000            {'a2000'};
sub CAT_A3000            {'a3000'};
sub CAT_INTERNAL         {'internal'};
sub CAT_T3               {'t3'};
sub CAT_SE               {'se'};
sub CAT_A5K              {'a5k'};
sub CAT_SWITCH           {'switch'};
sub CAT_SWITCH2          {'switch2'};
sub CAT_V880DISK         {'v880disk'};
sub CAT_BROCADE          {'brocade'};
sub CAT_HOST             {'host'};
sub CAT_BACKUP           {'backup'};
sub CAT_ST4              {'st4'};
sub CAT_MESSAGE          {'message'};
sub CAT_T3MESSAGE        {'t3message'};
sub CAT_SAN              {'san'};
sub CAT_TOPO             {'topo'};
sub CAT_TAPE             {'tape'};
sub CAT_SP               {'sp'};
sub CAT_WHAT             {'what'};
sub CAT_D2               {'d2'};
sub CAT_VE               {'ve'};
sub CAT_SVE              {'sve'};     # SUN VE
sub CAT_MINNOW           {'minnow'};
sub CAT_MCDATA           {'mcdata'};
sub CAT_HITACHI          {'hitachi'};


%ISSWITCH = (
    switch    => 1,
    switch2   => 1,
    brocade   => 1,
    ve        => 1,
    );


$ERROR_LOG       = 'L1';
$WARNING_LOG     = 'L2';
$NOTICE_LOG      = 'L3';
$DEVICE_ERROR    = 'x';
$HOST_CONFIG     = 'y';
$HOST_DISK_INQ   = 'h';
$HOST_DATA_PING  = 'p';
$SYSTEM_ERROR    = 'e';

#$id = { 
#        deviceName => $wwn,
#        name        => $device->{name},
#        category    => Report::CAT_T300,
#        ip          => $device->{ip},
#      };



sub new {
  my($class, $id, $report, $warn_log, $error, $err_log) = @_;

  my(%id2);
  $id2{_id}         = $id;
  croak("Id need deviceName") if (!exists($id->{deviceName}));
  $id2{_created}    = Util->get_today;
  $id2{_value}      = $report || {};
  $id2{_log}        = $warn_log;
  $id2{_logErr}     = $err_log;
  $id2{_status}     = $error || 'OK';

  my($r) = \%id2;
  bless($r, "Report");
  return $r;
  
}



# 
# PUSH ReportsList to Master
#
sub pushReportsList {
  my($class, $master, $force) = @_;
  my($url, $err, $ans, $list);
  $master = Util->findMaster();
  my($renv) = System->get_renv;

  if ($master) { # you are a slave
    my($hostname) = $renv->{hostname};
    $list = Report->makeLocalReportList();
    ($err, $ans)  = Util::Http->saveFile($master, "REMOTE_LISTS/$hostname", $list, 20);
    if ($err) {
      Debug->print2("Cannot push reportList: $err");
    }
  } else { # save reportList locally
    $list = Report->makeLocalReportList();
    open(O, ">" . System->get_home() . "/DATA/REMOTE_LISTS/$renv->{hostname}");
    print O $list;
    close(O);
  }
}


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

  my($VAR1);
  my($D) = System->get_home() . "/DATA/OLD_REPORTS";
  opendir(O, $D);
  my(@dirs) = readdir(O); closedir(O);
  my(@out);

  foreach my $d (@dirs) {
     my @bb = split(/:/, $d);
     next if ($#bb != 1 || (index("host,san,topo", $bb[0]) >= 0));
     open(O, "$D/$d");
     my(@a) = <O>; close(O);
     eval "@a";
     if ($VAR1) {
       my($name)   = $VAR1->{_id}{name};
       my($created) = $VAR1->{_created};
       my($type, $key) = split(/:/, $d);
       push(@out, [$type, $key,$created, "AccessName", $name]);
     }
  }
  return \@out;
}

#  filename=date=XX=name

sub makeLocalReportList {
  my($report) = @_;
  my($VAR1);
  my($D) = System->get_home() . "/DATA/OLD_REPORTS";
  opendir(O, $D);
  my(@dirs) = readdir(O); closedir(O);
  my($out);

  foreach my $d (@dirs) {
     my @bb = split(/:/, $d);
     next if ($#bb != 1);
     open(O, "$D/$d");
     my(@a) = <O>; close(O);
     eval "@a";
     if ($VAR1) {
       my($name)   = $VAR1->{_id}{name};
       my($created) = $VAR1->{_created};
       $out .= "$d=$created=AccessName=$name\n";
     }
  }
  return $out;
}


#
# looks in OLD_REPORTS and REMOTE_LISTS for the host of a report.
#
sub findReportHost {
  my($class, $key) = @_;
  my($l);
  if (-f System->get_home() . "/DATA/OLD_REPORTS/$key") {
     return ""; # LOCAL
  }
 
  my $D =  System->get_home() . "/DATA/REMOTE_LISTS";

  opendir(O, $D); my @files = readdir(O); closedir(O);
  foreach my $f (@files) {
    next if (substr($f,0,1) eq ".");
    open(O, "$D/$f");
    while ($l = <O>) {
      chop($l);
      my @a = split(/=/, $l);
      if ($a[0] eq $key) {
         close(O);
         return $f;
      }
    }
    close(O);
  }
  return undef;
}

#  read report from a file
#  $rep1 = Report->readLocal("a5k:12321321");

sub readLocal {
  my($class, $file) = @_;
  my($VAR1);

  my($D) = System->get_home() . "/DATA/OLD_REPORTS";

  if (open(O, "$D/$file")) {
    my(@a) = <O>; close(O);
    eval "@a";
    return $VAR1;
  } else {
    return undef;
  }
}
$REMLIST = undef;
#   ALL REPORTS (local and remote by key)
#   $rm = Report->reportList(); 
#   $rm->{"11232132"} = [host, type, date, name, name2] # no type in the key
#
sub reportList {
  my($class) = @_;
  my (@R, %RM);
 # return $REMLIST if ($REMLIST);

  my $RM = System->get_home() . "/DATA/REMOTE_LISTS";
  opendir(O, $RM);
  my @L = readdir(O); closedir(O);
  foreach my $l (@L) {
     next if (substr($l,0,1) eq ".");
     open(O, "$RM/$l");
     my $r;
     while ($r = <O>) {
        chop($r);
        my($key, $date, $name, $name2) = split(/\=/, $r);
        next if (!$key);
        my($type, $k) = split(/\:/, $key);
        $RM{$key} =  [$l, $type, $date, $name, $name2];
     }
     close(O);
  }
  $REMLIST = \%RM;
  return $REMLIST;
}

#
#  host = "*" means find the host
#  ($err, $report) = Report->readReport("121212", "*"); # no type required if "*" is used
#  ($err, $report) = Report->readReport("121212" ); # * automatic if type is missing
#  ($err, $report) = Report->readReport("ve:1231232131", [ host ]);

sub readReport {
  my($class, $key, $host, $to) = @_;
  my($type, $VAR1, $data);
  my($renv) = System->get_renv();
  $to = 10 if (!$to);
  $ERR = undef;
  $host = "*" if (index($key, ":") < 0);

  if (!$host || $host eq $renv->{hostname} ) { # local
    if (-f System->get_home() . "/DATA/OLD_REPORTS/$key") {
      $data = &get_instr_report({file  => $key});
    } else {
      $data = "ERR No report available!";
    }
  } elsif ($host eq "*") { # find the host
    my $rm = Report->reportList();
    if (!$rm->{$key}) {
       $data = "ERR No report available!";
    } else {
      my $dev = $rm->{$key};
      $data = Util::Http->readFile($dev->[0], "OLD_REPORTS/$key", $to);
    }
  } else {
    $data = Util::Http->readFile($host, "OLD_REPORTS/$key", $to);
  }

  if (substr($data,0,3) eq "ERR") {
     $ERR = $data;
     return ($ERR, undef);
  } elsif (!$data) {
     $ERR = "No Report available!";
     return ($ERR, undef);
  } else {
     if ($data =~ /\s*\$VAR1 =/) {
       eval $data;
       return (undef, $VAR1, $host);
     } else {
       return ("ERR: $data", undef, $host);
     }
  }
}
        


# static
# use host to call remote machine
# use fileName, wwn or enc-name in this order

#  Report->readNet({ host     => 'ccdev',  
#                          timeout => 20,
#                          file     => 'asdsad',
#                          wwn      => '123213',
#                          name     => 'diag189'}
#                        );

sub readSpecial {
  my($report, $arg) = @_;
  my($host) = $arg->{host};
  my($data, $q);
  my($to) = $arg->{timeout} || $main::TO;
  my($CD) = System->get_home() . "/DATA/remote";
  my($url);

  if (!$arg->{name} && !$arg->{file} && !$arg->{wwn}) {
     return "ERR No argument (name, wwn, filename) ";
  }
  if ($host) {
      my $command ;
      if ($arg->{file}) {
        $command = "FILER&file=OLD_REPORTS/$arg->{file}";
      } else {
        $command = "Report::instr_report&file=$arg->{file}&name=$arg->{name}&wwn=$arg->{wwn}&HTTP=1";
      }
      $data = Util::Http->getCommand($host, $command, $to);
      if ($arg->{file}) {
        my($cache) = $CD . "/$arg->{file}";
        mkdir $CD, 0777 if (!-d $CD);
        open(O, ">$cache");
        print O $data; close(O);
      }
  } else {
      $q->{HTTP} = undef;
      $q->{file} = $arg->{file};
      $q->{wwn} = $arg->{wwn};
      $q->{name} = $arg->{name};
      $data = &get_instr_report($q);
  }

  if (!$data) {
    return ("Cannot retrieve $arg->{name} $arg->{file} from $host ($Util::Http::ERROR)<br>",undef)
  }
  if (substr($data,0,3) =~ "ER") {
    return ($data, undef);
  }
  my($VAR1);
  eval $data;

  if (!$VAR1) {
    return ("$arg->{name} $arg->{file} not found on $host!<br>", undef);
  }
  if ($host && !$arg->{file}) {
    my($id) = $VAR1->{_id};
    my($cache) = "$CD/" . $id->{category} . ":" . $id->{deviceName};
    mkdir $CD, 0777 if (!-d $CD);
    open(O, ">$cache");
    print O $data; close(O);

  }
  return (undef, $VAR1);

}

# called locally or remotely 

sub get_instr_report {
  my($q) = @_;
  my($l);
  my($P) = System->get_home() . "/DATA/OLD_REPORTS";
  my($out);

  if ($q->{file}) {   #got a file name
    if (open(O, "$P/$q->{file}")) {
      while ($l = <O>) {
        $out .= $l;
      }
      close(O);
    } else {
      $out = "ERR File $q->{file} does not exist!";
    }
  } elsif ($q->{name} || $q->{wwn}) { # got an enclosure name
     my($wwn);
     if (!$q->{wwn}) {
        $wwn = PDM->name2wwn($q->{name});
     } else {
        $wwn = $q->{wwn};
     }
     $out = "ERR $q->{name}/$q->{wwn} not found";
     opendir(O, $P);
     my(@dirs) = readdir(O); closedir(O);
     foreach my $d (@dirs) {
        if ($d =~ /$wwn/) {
           $out = Util->readf("$P/$d");
           last;
        }
     }
  }

  if ($q->{HTTP}) {
    print "\n$out";
  } else {
    return $out;
  }
}
  

sub writeReport {
  my($report, $r) = @_;
require Data::Dumper;

  $Data::Dumper::Indent = 1;
  my($key) = $report->fileKey();
  open(O, ">" . System->get_home() . "/DATA/OLD_REPORTS/$key");
  print O Data::Dumper::Dumper($r);
  close(O);
}

# will return a new report or the old one if communication was lost

sub readNew {
  my($class, $key) = @_;
  return Util->deserialize("/NEW_REPORTS/$key");
}
  
# uses deviceName as Key

sub saveNew {
  my($report) = @_;
  my $renv = System->get_renv();
  my $key = $report->fileKey();
  Debug->print2("NEW_REPORTS missing") 
         if (!-d System->get_home() . "/DATA/NEW_REPORTS");
  if ($key) {
     Util->serialize("NEW_REPORTS/$key", $report);
     if ($renv->{report_trace_dir} && -d $renv->{report_trace_dir} ) {
        my $ts = Util->get_today();
        $ts =~ s/[ \:]//g; 
        require Data::Dumper;
        $Data::Dumper::Indent = 1;
        my $out = Data::Dumper::Dumper($report);
        if (open(OO, ">$renv->{report_trace_dir}/$key.$ts")) {
           print OO $out;
           close(OO);
        }
     }
  }
}


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

  my $D = System->get_home() . "/DATA/NEW_REPORTS";
  opendir(O, $D);
  my @L = readdir(O);
  closedir(O);
  foreach my $f (@L) {
    next if (substr($f,0,1) eq ".");
    unlink "$D/$f";
  }
}

sub new2old {
  my($class) = @_;
  
  my $N = System->get_home() . "/DATA/NEW_REPORTS";
  my $O = System->get_home() . "/DATA/OLD_REPORTS";
  my $cp = System->find_command("cp");
  system("$cp $N/* $O");
}

# used when communication is lost and no instr-report was generated
# will merge information from rasagent with last good instr-report.

sub updateStatus {
  my($report) = @_;
  
  my($old_report) = Report->readLocal($report->fileKey() );
  if ($old_report) {
    $old_report->{_status} = $report->{_status};
    my($val) = $old_report->{_value};
    my($new)  = $report->{_value};
    foreach my $v (keys %$new) {    # update all information from the new report
       $val->{$v} = $new->{$v};
    }
    $old_report->saveNew();
    # $report->writeReport($r);
  } else {
    my $display = $report->displayName();
    Debug->errNoRepeat(CANNOT_READ_OLD => $report->fileKey(), 24, 
    "Device $display need to be deleted from the configuration (Maintain Devices)");
  }
}


sub status {
  my($report) = @_;
  return $report->{_status};
}

#  remove this once patch for brocade is out
sub accessName {
  my($report) = @_;
  return $report->{_id}{accessName};
}

  
sub deviceName {
  my($r) = @_;
  return  $r->{_id}{deviceName};
}

sub displayName {
  my($r) = @_;
  my $name;
  $name = $r->{_id}{category};
  $name .= ":" . $r->{_id}{name};
  $name .= "($r->{_id}{ip})" if ($r->{_id}{name});
  return $name;
}


#
# used to store this report in the file-system

sub fileKey {
  my($r) = @_;
  return  lc($r->{_id}->{category}) . ":" . $r->{_id}{deviceName};
}

sub makeFileKey {
  my($r, $cat, $deviceName) = @_;
  return lc($cat) . ":$deviceName";
}
  

sub name {
  my($report) = @_;
  return $report->{_id}{name};
}


sub category {
  my($report) = @_;

  return $report->{_id}->{category};
}

sub value {
  my($report, $id, $value) = @_;
  if (defined($value) && $id) {
    $report->{_value}{$id} = $value;
    return $value;
  } elsif ($id) {
    return $report->{_value}{$id};
  } else {
    return $report->{_value};
  }
}

sub subset {
  my($report, $val) = @_;
  $report = $report->content();
  return $report->subset($val);
}

sub content {
  my($report) = @_;
  
  return ReportContent->new($report->{_value});
}

sub logError {
  my($report) = @_;

  return $report->{_logErr};
}

sub log {
  my($report) = @_;

  return $report->{_log};
}

sub toTab {
  my($r, $arg) = @_;
  my $summary = $arg->{summary};
  my $exclude = $arg->{exclude};
  my $include = $arg->{include};
  my $id  = $r->id();
  my $rep = $r->content();
  my $out = "header.created\t$r->{_created}\n";

  foreach  my $v (sort keys %$id) {
     $out .= "header.$v\t$id->{$v}\n";
  }
  my $last;
  foreach my $v (sort keys %$rep) {
      my($tag, $rest) = split(/\./, $v, 2);
      next if ($summary && $rest =~ /\.stats\./);
      next if ($exclude && substr($v,0,length($exclude)) eq $exclude);
      next if ($include && substr($v,0,length($include)) ne $include);
      $out .= "$v\t$rep->{$v}\n";
  }
  return $out;
}

sub toString {
  my($r, $arg) = @_;
  my $summary = $arg->{summary};
  my $exclude = $arg->{exclude};
  my $include = $arg->{include};
  my $id  = $r->id();
  my $rep = $r->content();
  my $out .= "HEADER\n";
  $out .= sprintf("  %-30s: %s\n", "created", $r->{_created});

  foreach  my $v (sort keys %$id) {
     $out .= sprintf("  %-30s: %s\n", $v, $id->{$v});
  }
  my $last;
  foreach my $v (sort keys %$rep) {
      my($tag, $rest) = split(/\./, $v, 2);
      if ($tag ne $last) {
         $out .= "SECTION_$tag" . "\n";
         $last = $tag;
      }
      next if ($summary && $rest =~ /\.stats\./);
      next if ($exclude && substr($v,0,length($exclude)) eq $exclude);
      next if ($include && substr($v,0,length($include)) ne $include);
      $out .= sprintf("  %-30s: %s\n", $rest, $rep->{$v});
  }
  return $out;
}

# by default, toXML will not include the <REPORT> ... </REPORT> tags
#
sub toXML {
  my($r, $arg) = @_;
  my $out;
  my $header  = $arg->{header};
  my $summary = $arg->{summary};

  $out = "<REPORT>\n" if ($header);
  my $id  = $r->id();
  my $rep = $r->content();

  $out .= "  <HEADER>\n";
  $out .= "    <INFO VALUE=\"created\">$r->{_created}</INFO>\n";
  foreach my $v (sort keys %$id) {
      $out .= "    <INFO VALUE=\"$v\">$id->{$v}</INFO>\n";
  }
  $out .= "  </HEADER>\n";
  my $last;
  foreach my $v (sort keys %$rep) {
      my($tag, $rest) = split(/\./, $v, 2);
      if ($tag ne $last) {
         $out .= "  </SECTION_$last>\n" if ($last);
         $out .= "  <SECTION_$tag>\n";
         $last = $tag;
      }
      next if ($summary && $rest =~ /\.stats\./);
      $out .= "    <INFO VALUE=\"$rest\">$rep->{$v}</INFO>\n";
  }
  return $out;
}
  


sub deviceInfo {
  my($r, $key) = @_;

  if (exists($r->{_value})) {
    return $r->{_value}{"id.device_$key"};

  } elsif (exists($r->{"id.device_$key"})) {
    return $r->{"id.device_$key"};
  } 
  return undef;
}

sub id {
  my($r, $key) = @_;
  if ($key) {
     return $r->{_id}{$key};
  }
  return $r->{_id};
}

sub created {
  my($r) = @_;
  return $r->{_created};
}




                          
1;



# 1;
#


__END__

=head1 NAME

Report.pm - Class that contains instrumentation reports


=head3 SYNOPSIS

 use Report;

 $rep = Report->new({ ... });


=head3 DESCRIPTION

This module create reports from instrumention or from saved files.



=head3 CONSTRUCTOR

=over 4

=item new( <type>, <device_id>, <report_hash> );

 Example:
   $id = {
           acceName     => $dev_name,
           deviceName   => $dev_name,
           category     => Report::CAT_A3500FC,
           ip           => 'NOIP',
           key_type     => "ctrl_id",
           key_value    => $internal_name,
         };

   $report = Report->new($id, $sys_report);


=back 4


=head3 METHODS

=over 4

=item status();

return the status of the Report.

=item name();

returns the name of the device report.

=item category();

returns the category of the report.

=item content();

returns a ReportContent object.

 Example:
  print $content->get('disk.u1d1.state');

=item log();

returns loglines from the message files.


=item created();

returns creation date of the report.

=item deviceKey();

returns category . ":" . name ;

=item key();

returns type . ":" . category . ":" . name ;


=back 4

=head3 SEE ALSO




=head3 AUTHOR

 Christian Cadieux (ccadieux@central.sun)



=head3 COPYRIGHT

Copyright (c) 2000 Sun Microsystems

=cut
