package Discover;
use strict;
use System;
use Lease;
use Timer;
use Modules;
use Util::Http;
use Snmp;
use DiscoveryObject;
use Discover::HBAApi;
use TO;


#
#  AUTO-ON Providers
#  executed at the beginning of the netconnect provider.
#

sub provider {
 my($class, $renv) = @_;

 my $renv = System->get_renv();

 if (Util->isDataHost() || $renv->{solution} eq "N" ) {

   $renv->{'pro.netconnect.active'} = 'Y';
   $renv->{'pro.netconnect.version'} = 31;

  # if (netconnect_works) {
  #   $renv->{'pro.nscc_email.active'} = 'N';
  #   $renv->{'pro.netconnect.active'} = 'Y';
  #   $renv->{'pro.netconnect.version'} = 31;
  #
  # } else {
  #   $renv->{'pro.netconnect.active'}  = 'N';
  #   $renv->{'pro.nscc_email.active'}  = 'Y';
  #   $renv->{'pro.nscc_email.encrypt'} = 'Y';
  #
  #
  #
 }
}


use vars qw ($TOPO);

#
# AUTO-DISCOVERY
# called by rasagent, may not be needed anymore.
# RUNS hba_api daily by default
# auto_discovery: add new devices to the topo from HBAApi automatically .
# manual_topology=0 : run HBAApi regularly and fix the topology.
# return 1: if auto-discovery ran

sub auto {
  my($class) = @_;
  my $renv = System->get_renv();

  my $auto_disco = $renv->{auto_discovery};
  my($rc);

  if ($auto_disco) {
    my $freq = $renv->{'auto_discovery.freq'} || 60*24; 

    my($out);
    if (Timer->getTimer("discover_hba") > $freq) {
       my $status = Process->status(undef, "inband");
       if ($status !~ /Running/ && Process->startIf("inband")) {
          Timer->resetTimer("discover_hba");
          Debug->print2("Discover::auto: running HBAApi->discoverInBand()");

          Discover->logit("#START", "inband", 1);
          my $disco_list = Discover::HBAApi->discoverInBand();
          foreach my $disco (@$disco_list) {
             $disco->{monitor_on} = "Y";
             Discover->logit($disco->serial(), "inband");
          }
          Discover->logit("#END", "inband");

          Discover->processQueue("inband");
          Process->done("inband");
          $rc= 1;
       }
     }
  } 
  return $rc;

}

sub beginTopo {
  my($class) = @_;
  my $renv = System->get_renv();
  my $reset = $renv->{reset_topology};

  if (Util->doOnce("last_reset_topology", $renv->{reset_topology})) {
     Debug->print2("reset_topology request: clearing topology and hba devices!");
     my $D = System->get_home() . "/DATA/OLD_REPORTS";
     opendir(W, $D);
     my @list = readdir(W); closedir(W);
     foreach my $f (@list) {
        if ($f =~ /^hbas/) {
          unlink "$D/$f";
        }
     }
     my $D = System->get_home() . "/DATA/topo";
     opendir(W, $D);
     my @list = readdir(W); closedir(W);
     foreach my $f (@list) {
        if (substr($f ,0,1) ne ".") {
          unlink "$D/$f";
        }
     }
  }

  TO->initTopo();  # create an empty topo if needed
}

# SEND TOPO TO MASTER;
sub endTopo {
  my $master  = Util->findMaster;
  my $renv = System->get_renv();
  my $topo = TO->readExistingTopo();
  if ($topo) {
     $topo->removeDuplicates();
     $topo->syncWithConfig();
     $topo->connectAll();
     $topo->saveTopo($renv->{hostname});
     # SEND TOPO TO MASTER IF IT IS DIFFERENT
     if ($master) {
        my $f1 = "/tmp/last_ras_topo";
        my $f2 = "/tmp/last_ras_topo2";
        $topo->toXML($f2);
        if (!-f $f1 || `/usr/bin/diff $f1 $f2`) {
           $topo->push($master, 1);
           #Util->touch("new_topo_flag");
        }
        rename $f2, $f1;
     }
  }
  TO->mergeTopos("MERGE-MASTER");
}


# if you find a hba_device node in the topo,
# change it to the type of the $dev device.
# $dev = { key => "KEY", type => "t3", wwn => "2012312..", wwn2 => "12312321" }
#
sub mergeWithHBADevice {
  my($class, $to, $dev) = @_;

  my(@L);
  push(@L, $dev->{key});
  push(@L, $dev->{wwn}) if ($dev->{wwn});
  push(@L, $dev->{wwn2}) if ($dev->{wwn2});
  if ($dev->{wwns}) {
    my @W = split(/[,\|]/, $dev->{wwns});
    push(@L, @W);
  }
  foreach my $wwn0 (@L) {
     next if (!$wwn0);
     my $node_list = $to->nodesByWWN($wwn0);
     foreach my $node (@$node_list) {
       my ($nodes);
       if ($node->{info}{type} eq "hbastorage") {
         $nodes =  $to->[2];
       } elsif ($node->{info}{type} eq "hbaswitch") {
         $nodes =  $to->[1];
       } else {
         next;
       }
       my $oldkey = $node->{info}{name};
       my ($type, $key) = split(/\:/, $oldkey);

       my $newkey = "$dev->{type}:$dev->{key}";
       my $newNode = $to->nodeByName($newkey);

       if (!$newNode) { # brand new
         Debug->print25("mergeWithHBADevice: copying $oldkey to $newkey");
         $node->{info}{type} = $dev->{type};
         my @CL = split(/\./, $node->{info}{class});
         $CL[$#CL] = $dev->{type};
         $node->{info}{class} = join(".", @CL);
         $node->{info}{name} = $newkey;
         bless($node, "TO::" . uc($dev->{type}));
         $nodes->{$newkey} = $node;
         delete $nodes->{$oldkey};

       } else {         # already there
         $to->mergeDevices($newNode, $node);
         delete $nodes->{$oldkey};
       }
     }
  }
}

#
# add/ update a device in the topology
# called by the Discover modules and Agents
# may have extra information already in 
# $arg->{report}
#
sub updateTopoDevice {
  my($class, $disco_dev, $arg) = @_;
  my($lease);
  my $renv = System->get_renv();
  my $Config  = System->get_Config();
  my $dev_okay = 1;
  return($dev_okay)  if (!$Config);

  $Config->addDevice0($disco_dev);
  my $devices = $Config->devices();
  return ($dev_okay) if ($renv->{manual_topology}); # can turn off auto-topo
  return ($dev_okay) if (!$disco_dev->{key});

  if (($lease = Lease->get("topo", 4, 8)) ) {
     my $topo = TO->readExistingTopo();
     if ($topo) {
       my $type = uc($disco_dev->{type});
       my $mod = "TO::$type";
       if ($mod->can("newFromDev")) {
          $class->mergeWithHBADevice($topo, $disco_dev);
          $dev_okay = $mod->newFromDev($topo, $disco_dev, $arg);
       }
       $topo->saveTopo($renv->{hostname});  # Save topo here JRK
     } else {
       Debug->print2("Discover::updateTopo: no topology");
     }

     $lease->release();
  } else {
     Debug->print2("lease topo already taken");
  }
  return $dev_okay;
  
}



#
# CALLED BY Agent::HBASWITCH AND Discover->HBAApi
#
sub updateTopoInterconnects {
  my($class, $switch_list) = @_;
  my $renv = System->get_renv();
  my($lease);
  return if ($renv->{manual_topology}); # can turn off auto-topo

  if (($lease = Lease->get("topo", 4, 8)) ) {
     my $topo = TO->readExistingTopo();
     if ($topo) {
       foreach my $sw (@$switch_list) {
          my $dev = {  type => "hbaswitch", 
                      name => $sw->{info}{InterLogicalName},
                       key => "IB" . $sw->{info}{InterNodeWWN},
                  };
          $class->mergeWithHBADevice($topo, $dev);
          TO::HBASWITCH->newFromDev($topo, $dev, {switch => $sw});
       }
       $topo->saveTopo($renv->{hostname});
     }
     $lease->release();
  }  else {
     Debug->print2("lease topo already taken");
  }
}


# CALLED BY Agent::HBASWITCH
#
sub updateTopoHost {
  my($class, $hba_list) = @_;
  my $renv = System->get_renv();
# ADD HOST TO TOPOLOGY
  my $dev = {
        type => "host", 
        name => $renv->{hostname}, 
         key => $renv->{hostname},
            };
  Discover->updateTopoDevice($dev, {hba_list => $hba_list});

#
# ADD CONNECTIONS/volInfo/portInfo TO TOPO
#
  require TO::HOST;
  TO::HOST->addHBAs($hba_list);
}



# select: hostname or '' for all hosts.
# ID: deviceIP | subnet | 
# sel_host=local | slave_name | Blank(all hosts)
use vars qw($CODE);

sub ras_admin {
  my($class, $sel_host, $command, $renv, $hosts, $ID, $args) = @_;
  my $L = Labels->read("GUIAdmin::default")->section("discover");

  $command .= " -P $args->{prefix}" if ($args->{prefix});
  $command .= " -M" if ($args->{monitor_on} eq "N");

  my $DIR = System->get_home() . "/DATA/Discover";
  mkdir $DIR,0777 if (!-d $DIR);
  my $errors;

  if ($sel_host eq "local" || !$sel_host) {
     #open(O, ">$DIR/$ID.local"); print O "#START\n"; close(O);
     print $L->expand(getting2 => $renv->{hostname}) . "<br>(ras_admin $command)<br>";
     my $com = System->get_home() . "/bin/ras_admin $command";
     my $pid = fork();
     if (!$pid) {
        close(STDIN); close(STDOUT); close(STDERR);
        exec "$com";
     }
  }
  foreach my $h (@$hosts) {
     next if($h->{dh} eq "Y");
     if ($h->{hostname} eq $sel_host || !$sel_host) {
       print $L->expand(getting2 => $h->{hostname}) . "\n(ras_admin $command)\n";
       my($err) = Util::Http->remoteCommand($h->{ipno}, "ras_admin $command", 15);
       $errors .= "Error calling $h->{ipno}; $err\n" if ($err);
     }
  }
  return $errors;
}

sub read {
  my($class, $file) = @_;
  my $D1 = System->get_home() . "/DATA/Discover";
  open(O2, "$D1/$file");
  my($l, @LIST);
  while ($l = <O2>) {
     my $obj;
     next if (substr($l,0,1) eq "#");
     if (index($l, "VAR1 =") >= 0) {  # new format, create object
        $obj = DiscoveryObject->deserial($l);
     } elsif (index($l, "|") >= 0) { # for backward compatibility
        $obj = DiscoveryObject->newFromString($l);
     }
     push(@LIST, $obj) if ($obj);
  }
  close(O2);
  return \@LIST;
}


sub stats {
  my($class, $detail_flag) = @_;

  my(%TOT, $l);
  my $D1 = System->get_home() . "/DATA/Discover";
  opendir(O, $D1);
  my @F = readdir(O); closedir(O);
  foreach my $f (@F) {
     next if (-d "$D1/$f");
     next if (substr($f,0,1) eq ".");
     my ($a, $b);
     if ($detail_flag) {
       $a = $f;
     } else {
       ($a, $b) = split(/\./, $f);
     }
     $TOT{$a}{cnt}++;
     open(O2, "$D1/$f");
     my($start, $stop, $cnt, $processed);
     my $status = "Running";
     my ($start_date, $end_date);
     while ($l = <O2>) {
        if ($l =~ /^#START\s*(.*)/) {
           $start = 1; $start_date = $1;

        } elsif ($l =~ /^#END\s*(.*)/) {
           $stop = 1;  $end_date = $1;
           $status = "Done";

        } elsif (substr($l,0,10)  eq "#PROCESSED") {
           $processed = 1;
        } elsif (index($l, "VAR1") >= 0 || index($l, "|") >= 0 ) {
           $cnt++;
        }
     }
     $start = 1 if ($start < $stop);
     close(O2);
     $TOT{$a}{start} += $start;
     $TOT{$a}{stop}  += $stop;
     $TOT{$a}{status} = $status;
     $TOT{$a}{start_date} = $start_date;
     $TOT{$a}{end_date}   = $end_date;
     $TOT{$a}{lines} += $cnt;
     $TOT{$a}{processed} += $processed;
  }
  return \%TOT;
}

# run process_discovery on master or slave
#

sub processQueue {
  my($class, $method) = @_;
  my $master = Util->findMaster();
  my $renv   = System->get_renv();
  my $h      = $renv->{hostname};
  $method = $master ? "$method.$h" : "$method.local";

  if (!$master) {
     Discover->process_discovery({mode => 'cli', method => $method});
  } else {
     Util::Http->getCommand($master, "Discover::process_discovery&method=$method", 30);
  }
}

sub get_test {
  my($q) = @_;
  open(O, ">>/tmp/TEST"); print O "Discovery->get_test\n"; close(O);
  print Html->text_header();
  print "OK\n";

}


use Logic::SWITCH;

#
# called by ras_admin to send to the DATA/Discover/* logfiles.
#
use vars qw (@SAVE);
sub logit {
  my($class, $line, $TAG, $new) = @_;

  my $master = Util->findMaster();
  my $renv   = System->get_renv();
  my $h = $renv->{hostname};

  if (!$master) {
     open(O, ($new? ">":">>") . System->get_home() . "/DATA/Discover/$TAG.local");
     print O "$line\n";
     close(O);
     if($new){
        chmod 0664, System->get_home() . "/DATA/Discover/$TAG.local" ;
     }
  } elsif ($new) {
     Util::Http->saveFile($master, "Discover/$TAG.$h", $line, 10);

  } else {
     push(@SAVE, "$line\n");
     if ($#SAVE >= 5 || $line =~ /#END/) {
        my $out = join("", @SAVE);
        @SAVE = ();
        Util::Http->appendFile($master, "Discover/$TAG.$h", $out);
     }
  }
}

sub get_process_discovery {
  my($q) = @_;
  my ($error, $running, $info, $added) =  Discover->process_discovery($q);
  print Html->text_header();
  my $var = { error => $error, 
            running => $running, 
               info => $info, 
              added => $added };
  require Data::Dumper;
  print Data::Dumper::Dumper($var);
}

sub process_discovery {
  my($class, $q) = @_;
  my($l, %TOT, $c1, %F);
  my ($lease, $all_running, $info);
  my $tag = "<br>" if (!$q->{mode});
  my $method = $q->{method};  # deviceIP.local | subnet.ccadieux.central.sun.com
     
  if (!($lease = Lease->get("rasagent.conf", 6, 5)) ) {
    return ("CANNOT TAKE process_discovery lease", 0);
  }
  my $D1 = System->get_home() . "/DATA/Discover";

  my($renv, $devs, $hosts,$notifs, $Config) = PDM::ConfigFile->read();
  foreach my $d (@$devs) {
     $F{$d->{key}} = $d;
  }
  opendir(O, $D1);
  my @F = readdir(O); closedir(O);
  my($adding);

  foreach my $f (@F) {
     next if (substr($f,0,1) eq "." || -d "$D1/$f");
     #next if ($method && $f ne $method);
     my($method0, $host0) = split(/\./, $f, 2);
     if (open(O, "$D1/$f")) {
       my $running = 1;
       my $processed;
       while ($l = <O>) {
         chop($l);
         if ($l =~ /#START/) {
           $running = 1;
         } elsif ($l =~ /#END/) {
           $running = 0;
         } elsif ($l =~ /#PROCESSED/) {
           $processed = 1;
         }
       }
       $all_running += $running;
       close(O);
       if (!$running && !$processed) {
         Debug->print2("Discover::process_discovery: reading $f ");
         open(W, ">>$D1/$f"); print W "#PROCESSED\n"; close(W);
         print "\nProcessing queue $f:\n" if ($q->{TRACE});

         my $traceF = System->get_home() . "/DATA/tmp/$method0${host0}_trace"; # local or slave
         System->set_traceFile($traceF);
         Debug->trace("", 1);
         Debug->print1("Adding devices from $f to configuration:");

         open(O, "$D1/$f");
         while ($l = <O>) {
           chop($l);
           next if (substr($l,0,1) eq "#" || length($l) < 3);
           my ($VAR1, $obj);
           if (substr($l,0,3) eq "ERR") {
             $obj = DiscoveryObject->error("IP", $l);
             $info .= "$l<br>\n";
  
           } elsif (index($l, "VAR1 =") >= 0) {  # new format, create object
             $obj = DiscoveryObject->deserial($l);
  
           } elsif (index($l, "|") >= 0) { # for backward compatibility
             $obj = DiscoveryObject->newFromString($l);
           }
           next if (!$obj);
           $obj->{host} = $host0 if ($host0 ne "local");
           my $err_text;
           Debug->print2("  Discovery->process: $obj->{type}, $obj->{ipno} ");
           $CODE="adding";
           my ($nodes, $trace) = $class->addDevice( $Config, {}, $obj, \%F, \$err_text);

           Debug->print1($trace);

           if ($q->{TRACE}) {
             print "  $CODE $obj->{type} $obj->{name}/$obj->{ip}, $obj->{key} \n";
           }
           $adding += $#$nodes+1;
         }
         close(O);
       }
     }
  }
  if ($adding > 0) {
     PDM::ConfigFile->write($renv, $devs, $hosts, $notifs);
  }
  $lease->release();
  return (undef, $all_running, $info, $adding);
}



#####################################################
#  GENERALIZED IP-BASED DISCOVERY
#  will return a DiscoveryObject for supported devices
#  will skip sun servers.
#####################################################

use vars qw($AGENTS $TYPES);


sub inband {
  my($class, $args) = @_;
  my(@LIST, %ORDER);
  $class->register() if (!$AGENTS);
  my $renv = System->get_renv();
  my $disco_level = defined $args->{disco_level} ? $args->{disco_level} :
                                                  $renv->{disco_level} ;

  foreach my $disco (keys %$AGENTS) {
     my $reg = $AGENTS->{$disco};
     if ($reg->{inBand}) {
        if ($reg->{disco_level} <= $disco_level) {
          my $o = sprintf("%3.3d,%s", $reg->{inBand}, $disco);
          $ORDER{$o} = 1;
        }
     }
  }
  foreach my $o (sort keys %ORDER) {
     my($order, $disco) = split(/\,/, $o);
     Debug->print1("Discover::inband: trying $disco");
     my $disco_list = $disco->discoverInBand({ disco_level => $disco_level} );
     if ($#$disco_list >= 0) {
       Debug->print1("  - found " . ($#$disco_list+1) . " device(s) using $disco");
       push(@LIST, @$disco_list) ;
     }
  }
  return \@LIST;  # list of disco object
}
  


# Discover->fromIP($IP, { 
#      type     => "3310",
#      password => "diags",
#      sysDesc  => "sysDescription"  OPTIONAL
#      timeout  => 5,
#        });

sub fromIP {
  my($class, $ip, $args) = @_;

  my $TO   = $args->{timeout} || 5;
  my $type = $args->{type};
  my $sysDesc = $args->{sysDesc};
  if (!Util->ping($ip, 5)) {
     Debug->print2("  Cannot ping $ip");
     return  [DiscoveryObject->error($ip, "Cannot ping")];
  }

  $class->register() if (!$AGENTS);
#
# IF THERE IS A HINT ABOUT THE DEVICE-TYPE
#
  Debug->print1(" Start Discover::fromIP on $ip");
  my $valid_types;

  if ($type) {
     foreach my $disco (keys %$AGENTS) {
        my $reg = $AGENTS->{$disco};
        my @types = split(/,/, $reg->{types});
        foreach my $t (@types) {
	   if($disco->can("discoverFromIP")){
	      $valid_types = $valid_types . " $t";
              if ($t eq $type) {
	         my $realIP = Util->name2ip($ip);
                 my $disco_list = $disco->discoverFromIP($realIP, $args);
                 if ($#$disco_list >= 0) {
                    Debug->print1("  - found " . ($#$disco_list+1) . " device(s)");
                    return $disco_list ;
                 }
	      }
           }
        }
     }
     Debug->print1("  - No Device found for $ip using $type type, please check for a valid ip/type.");
     Debug->print1("  - Valid types are$valid_types");

     return undef;
  }

#
# FIND SYSDESC IF IT WAS NOT PROVIDED
#
  if (!exists $args->{sysDesc}) {
     $sysDesc = Snmp->sysDesc($ip, $TO);
  }
  Debug->print1(" - snmp sysDesc is $sysDesc") if ($sysDesc);
#
# TRY AGENTS THAT SUPPORT SNMP PATTERNS: sorted name order
#
  if ($sysDesc) {
     foreach my $disco (sort keys %$AGENTS) {
        my $reg = $AGENTS->{$disco};
        if ($reg->{sysDesc}) {
           Debug->print2(" - trying snmp agent: $disco");
	   my $realIP = Util->name2ip($ip);
           my $disco_list = $disco->discoverFromSysDesc($realIP, $sysDesc, $args);
           if ($#$disco_list >= 0) {
             Debug->print1("   - found " . ($#$disco_list+1) . " device(s) on $disco");
             return $disco_list ;
           }
        }
     }
  }
#
# TRY ALL OTHER Discovery Agents , in $reg->{fromIP} order
#
  my (%ORDER, $disco_object);
  foreach my $disco (keys %$AGENTS) {
     my $reg = $AGENTS->{$disco};
     next if ($sysDesc && $reg->{no_snmp});
     if ($reg->{fromIP}) {
        my $o = sprintf("%3.3d,%s", $reg->{fromIP}, $disco);
        $ORDER{$o} = 1;
     }
  }
  foreach my $o (sort keys %ORDER) {
     my($order, $disco) = split(/\,/, $o);
     Debug->print2(" - trying fromIP agent: $disco");
     my $disco_list = $disco->discoverFromIP($ip, $args);
     if ($#$disco_list >= 0) {
       Debug->print1("   - found " . ($#$disco_list+1) . " device(s) using $disco");
       return $disco_list;
     }
  }

  return undef;
}


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

  my $agents = Modules->load("Discover");
  foreach my $agent (@$agents) {
     my $fun = "Discover::$agent";

     if ($fun->can("REGISTER")) {
        my $reg = $fun->REGISTER();

        # {    types     => "list of device types",
        #      sysDesc   => ORDER_NO,  # can discover from snmp sysDesc.
        #      fromIP    => 1..10      # can be discovered from IP, orderNo
        # }
        $reg->{function} = $fun;
        $AGENTS->{$fun} = $reg;

        my @types = split(/,/, $reg->{types});
        foreach my $t (@types) {
          $TYPES->{$t} = $fun;
        }
        
     }
  }
}

#=================================================
# FUNCTION : addDevice
#  return ( $node_added, $trace) 
#  will add to the configfile in memory but does not call writeConfig()
#=================================================
#
sub addDevice {
  my($class, $Config, $q, $disco_object, $DevHash, $err_text) = @_;

  my($adding, @nodes);
  my $renv  = System->get_renv();
  my $br    = "<br>" if (!$q->{TEXT});
  my $silent= $q->{silent};
  my $short = Util->shortHostname($q->{host} || $renv->{hostname});

   my $L   = $disco_object;
   my $key = $L->{key};
#
#  VALIDATE OBJECT
#
   if ($L->{error}) {
      my($info);
      foreach my $el (keys %$L) {
          $info .= "$el=$L->{$el}, " if ($L->{$el});
      }
      Debug->print1($info);
      $$err_text .= "\nERR: $L->{error}, ";
      return([]);

   } elsif (!$L->{class} && !$L->{wwn}) {
      # The first line from a slave is all blank except
      # for type so don't print out anything
      return([]);

   } elsif (!$L->{key} || !$L->{type} || !$L->{class} || !$L->{wwn}) {
      my $e1;
      $e1 = "key"     if (!$L->{key});
      $e1 = ",type"   if (!$L->{type});
      $e1 .= ",class" if (!$L->{class});
      $e1 .= ",wwn"   if (!$L->{wwn});
      Debug->print2("skipping $L->{ip}/$L->{username} because $e1 is blank");

      $$err_text .= "\nERR: Skipping $L->{ip} because $e1 is blank, ";
      return([]);
   }

# 
# FIX Disco_object
#
   my($path);
   if (index($L->{ip}, "/") >= 0) {
      $L->{path} = $L->{ip};
      $L->{ip} = "";
   }

   my $ip  = $L->{ip};
   $L->{active}    = ($L->{monitor_on} eq "Y" || $q->{monitor_on} eq "Y") ? "Y" : "N";
   delete $L->{monitor_on};
   $L->{mgmtLevel} = "D" if (!$L->{mgmtLevel});
   $L->{ipno}      = $L->{ip} if (!$L->{ipno});

   if ($q->{prefix} || $L->{prefix}) {
      my $p = $q->{prefix} || $L->{prefix};
      $L->{name} = "$p-$L->{name}";
   }

   if ($L->{type} =~ /^se\d*/) { # system
      $L->{model}  = $L->{userLabel};
      $L->{ipName} = $ip;
   }

# CHECK IF IT SHOULD BE ADDED / MERGED

   $class->register() if (!$AGENTS);

   my ($merge, $merge_fun);
   foreach my $disco (keys %$AGENTS) {
     my $reg = $AGENTS->{$disco};
     if (index(",$reg->{types},", ",$L->{type},") >= 0) {
        $merge = $reg->{merge_devices};
        $merge_fun = $disco;
        last;
     }
   }
   if (!$DevHash->{$key}) {  # ADD 
     $adding           = 1;
     $DevHash->{$key}  = PDM::ConfigFile->addDevice($Config->{devices}, $L);
     push(@nodes,  $L);
     $CODE="adding device ";

   } else {                  # UPDATE / MERGE
     my $dev = $DevHash->{$key};
     $dev->{wwn}  = $L->{wwn}  if ($L->{wwn});
     $dev->{wwn2} = $L->{wwn2} if ($L->{wwn2});
     $dev->{wwns} = $L->{wwns} if ($L->{wwns});

     if ($merge && $dev && $merge_fun->can("MERGE")) {
        $adding = $merge_fun->MERGE($dev, $L, $Config, $DevHash); # call disco. agent 
        push(@nodes,  $L) if ($adding);
        $CODE="merging device ";

     } else {
        $CODE="device already exist  ";
     }
   }
   my $trace = "$CODE: $L->{name}/$L->{wwn}/ip=$L->{ipno}";


   return (\@nodes, $trace); # list of new nodes
}



sub error {
 my($class, $L, $dev1) = @_;

 my $show  = $dev1->{ip} || $dev1->{name};
 my $show1 = $L->{ip} || $L->{devname} || $L->{username} ;
 return  "The unique identifier($L->{key}) for $L->{type}:$show1 conflicts with device at $show\n<br>";
}

  

1;
