source: trunk/klask @ 44

Last change on this file since 44 was 44, checked in by g7moreau, 15 years ago
  • Add HP3500-24G switch model
  • Change to YAML::Syck perl module in replacement to YAML. 10 times faster.
  • Correct bug in resolve_ip_arp_host function
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 45.3 KB
Line 
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005-2008 Gabriel Moreau.
4#
5# $Id: klask 44 2009-04-20 13:11:17Z g7moreau $
6
7use strict;
8use warnings;
9
10use Net::SNMP;
11#use YAML;
12use YAML::Syck;
13use Net::Netmask;
14use Net::CIDR::Lite;
15use NetAddr::IP;
16use Getopt::Long;
17
18# apt-get install snmp fping libnet-cidr-lite-perl libnet-netmask-perl libnet-snmp-perl libnetaddr-ip-perl libyaml-perl
19# libcrypt-des-perl libcrypt-hcesha-perl libdigest-hmac-perl
20# arping fping bind9-host arpwatch
21
22my $KLASK_VAR      = '/var/cache/klask';
23my $KLASK_CFG_FILE = '/etc/klask.conf';
24my $KLASK_DB_FILE  = "$KLASK_VAR/klaskdb";
25my $KLASK_SW_FILE  = "$KLASK_VAR/switchdb";
26
27test_running_environnement();
28
29my $KLASK_CFG = YAML::Syck::LoadFile("$KLASK_CFG_FILE");
30
31my %DEFAULT = %{$KLASK_CFG->{default}};
32my @SWITCH  = @{$KLASK_CFG->{switch}};
33
34my %switch_level = ();
35my %SWITCH_DB = ();
36LEVEL_OF_EACH_SWITCH:
37for my $sw (@SWITCH){
38   $switch_level{$sw->{hostname}} = $sw->{level} || $DEFAULT{switch_level}  || 2;
39   $SWITCH_DB{$sw->{hostname}} = $sw;
40   }
41@SWITCH = sort { $switch_level{$b->{hostname}} <=> $switch_level{$a->{hostname}} } @{$KLASK_CFG->{switch}}; 
42
43my %SWITCH_PORT_COUNT = ();
44
45my %CMD_DB = (
46   help       => \&cmd_help,
47   version    => \&cmd_version,
48   exportdb   => \&cmd_exportdb,
49   updatedb   => \&cmd_updatedb,
50   searchdb   => \&cmd_searchdb,
51   removedb   => \&cmd_removedb,
52   search     => \&cmd_search,
53   enable     => \&cmd_enable,
54   disable    => \&cmd_disable,
55   status     => \&cmd_status,
56   updatesw   => \&cmd_updatesw,
57   exportsw   => \&cmd_exportsw,
58   dotsw      => \&cmd_exportsw_dot,
59   iplocation => \&cmd_iplocation,
60   'search-mac-on-switch' => \&cmd_search_mac_on_switch,
61   );
62
63my %INTERNAL_PORT_MAP = (
64   0 => 'A',
65   1 => 'B',
66   2 => 'C',
67   3 => 'D',
68   4 => 'E',
69   5 => 'F',
70   6 => 'G',
71   7 => 'H',
72   );
73my %INTERNAL_PORT_MAP_REV = reverse %INTERNAL_PORT_MAP;
74
75my %SWITCH_KIND = (
76   J3299A => { model => 'HP224M',     match => 'HP J3299A ProCurve Switch 224M'  },
77   J4120A => { model => 'HP1600M',    match => 'HP J4120A ProCurve Switch 1600M' },
78   J9029A => { model => 'HP1800-8G',  match => 'PROCURVE J9029A'                 },
79   J4093A => { model => 'HP2424M',    match => 'HP J4093A ProCurve Switch 2424M' },
80   J4813A => { model => 'HP2524',     match => 'HP J4813A ProCurve Switch 2524'  },
81   J4900A => { model => 'HP2626A',    match => 'HP J4900A ProCurve Switch 2626'  },
82   J4900B => { model => 'HP2626B',    match => 'J4900B.+?Switch 2626'            },# ProCurve J4900B Switch 2626 # HP J4900B ProCurve Switch 2626
83   J4899B => { model => 'HP2650',     match => 'ProCurve J4899B Switch 2650'     },
84   J9021A => { model => 'HP2810-24G', match => 'ProCurve J9021A Switch 2810-24G' },
85   J9022A => { model => 'HP2810-48G', match => 'ProCurve J9022A Switch 2810-48G' },
86   J4903A => { model => 'HP2824',     match => 'J4903A.+?Switch 2824,'           },
87   J4110A => { model => 'HP8000M',    match => 'HP J4110A ProCurve Switch 8000M' },
88   BS350T => { model => 'BS350T',     match => 'BayStack 350T HW'                },
89   );
90 
91my %OID_NUMBER = (
92   sysDescr    => '1.3.6.1.2.1.1.1.0',
93   sysName     => '1.3.6.1.2.1.1.5.0',
94   sysContact  => '1.3.6.1.2.1.1.4.0',
95   sysLocation => '1.3.6.1.2.1.1.6.0',
96   );
97
98################
99# principal
100################
101
102my $cmd = shift @ARGV || 'help';
103if (defined $CMD_DB{$cmd}) {
104   $CMD_DB{$cmd}->(@ARGV);
105   }
106else {
107   print STDERR "klask: command $cmd not found\n\n";
108   $CMD_DB{help}->();
109   exit 1;
110   }
111
112exit;
113
114sub test_running_environnement {
115   die "Configuration file $KLASK_CFG_FILE does not exists. Klask need it !\n" if not -e "$KLASK_CFG_FILE";
116   die "Var folder $KLASK_VAR does not exists. Klask need it !\n"              if not -d "$KLASK_VAR";
117   }
118
119sub test_switchdb_environnement {
120   die "Switch database $KLASK_SW_FILE does not exists. Launch updatesw before this command !\n" if not -e "$KLASK_SW_FILE";
121   }
122
123sub test_maindb_environnement {
124   die "Main database $KLASK_DB_FILE does not exists. Launch updatedb before this command !\n" if not -e "$KLASK_DB_FILE";
125   }
126
127###
128# fast ping dont l'objectif est de remplir la table arp de la machine
129sub fastping {
130   system "fping -c 1 @_ >/dev/null 2>&1";
131   }
132
133###
134# donne l'@ ip, dns, arp en fonction du dns OU de l'ip
135sub resolve_ip_arp_host {
136   my $param_ip_or_host = shift;
137   my $interface = shift || '*';
138   my $type      = shift || 'fast';
139
140   my %ret = (
141      hostname_fq  => 'unknow',
142      ipv4_address => '0.0.0.0',
143      mac_address  => 'unknow',
144      );
145
146#   my $cmdarping  = `arping -c 1 -w 1 -rR $param 2>/dev/null`;
147   if (not $param_ip_or_host =~ m/^\d+\.\d+\.\d+\.\d+$/) {
148      $param_ip_or_host =~ s/\..*//;
149      }
150
151   # controler que arpwatch tourne !
152   # resultat de la commande arpwatch
153   # /var/lib/arpwatch/arp.dat
154   # 0:13:d3:e1:92:d0        192.168.24.109  1163681980      theo8sv109
155   # my $cmd = "grep  -e '".'\b'."$param_ip_or_host".'\b'."' /var/lib/arpwatch/arp.dat | sort +2rn | head -1";
156   # my $cmd = "grep  -he '".'\b'."$param_ip_or_host".'\b'."' /var/lib/arpwatch/*.dat | sort +2rn | head -1";
157   my $cmd = "grep  -he '".'\b'."$param_ip_or_host".'\b'."' /var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
158   my $cmd_arpwatch = `$cmd`;
159   chomp $cmd_arpwatch;
160   my ($arp, $ip, $timestamp, $host) = split /\s+/, $cmd_arpwatch;
161
162#print "OOO $cmd\n";
163#print "TTT arp $arp -> $ip pour host $host\n";
164
165   $ret{ipv4_address} = $ip        if $ip;
166   $ret{mac_address}  = $arp       if $arp;
167   $ret{timestamp}    = $timestamp if $timestamp;
168
169   my $nowtimestamp = time();
170
171   if ( $type eq 'fast' and ( not defined $timestamp or $timestamp < ( $nowtimestamp - 3 * 3600 ) ) ) {
172      $ret{mac_address} = 'unknow';
173      return %ret;
174      }
175
176  # resultat de la commande arp
177   # tech7meylan.hmg.inpg.fr (194.254.66.240) at 00:14:22:45:28:A9 [ether] on eth0
178   # sw2-batF0-legi.hmg.priv (192.168.22.112) at 00:30:c1:76:9c:01 [ether] on eth0.37
179   my $cmd_arp  = `arp -a $param_ip_or_host 2>/dev/null`;
180   chomp $cmd_arp;
181   $cmd_arp =~ /(\S*)\s\(([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\)\sat\s([0-9,A-Z]{2}:[0-9,A-Z]{2}:[0-9,A-Z]{2}:[0-9,A-Z]{2}:[0-9,A-Z]{2}:[0-9,A-Z]{2})/;
182   $ret{hostname_fq}  = $1 if(defined($1));
183   $ret{ipv4_address} = $2 if(defined($2));
184   $ret{mac_address}  = $3 if(defined($3));
185#print "RET1 $ret{ipv4_address} -> $ret{mac_address} : $ret{hostname_fq}\n";
186
187#   if ($ret{ipv4_address} eq '0.0.0.0' and $ret{mac_address} eq 'unknow'and $ret{hostname_fq} eq 'unknow') {
188      # resultat de la commande host si le parametre est ip
189      # 250.66.254.194.in-addr.arpa domain name pointer legihp2100.hmg.inpg.fr.
190      my $cmd_host = `host $param_ip_or_host 2>/dev/null`;
191      chomp $cmd_host;
192      $cmd_host =~ m/domain\sname\spointer\s(\S+)\.$/;
193      $ret{hostname_fq} = $1 if defined $1;
194
195      # resultat de la commande host si parametre est hostname
196      # tech7meylan.hmg.inpg.fr has address 194.254.66.240
197      $cmd_host =~ m/(\S*)\shas\saddress\s([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/;
198      $ret{hostname_fq}  = $1 if defined $1;
199      $ret{ipv4_address} = $2 if defined $2;
200
201      $cmd_host =~ m/\b([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.in-addr\.arpa\s/;
202      $ret{ipv4_address} = "$4.$3.$2.$1"     if defined $1 and  defined $2 and  defined $3 and  defined $4;
203      $ret{hostname_fq}  = $param_ip_or_host if not defined $1 and $ret{hostname_fq} eq 'unknow';
204#      }
205
206#print "RET2 $ret{ipv4_address} -> $ret{mac_address} : $ret{hostname_fq}\n";
207   unless ($ret{mac_address} eq 'unknow') {
208      my @paquets = ();
209      foreach ( split(/:/, $ret{mac_address}) ) {
210         my @chars = split //, uc("00$_");
211         push @paquets, "$chars[-2]$chars[-1]";
212         }
213      $ret{mac_address} = join ':', @paquets;
214      }
215
216#print "RET3 $ret{ipv4_address} -> $ret{mac_address} : $ret{hostname_fq}\n";
217   return %ret;
218   }
219
220# Find Surname of a switch
221sub get_switch_model {
222   my $sw_snmp_description = shift || 'unknow';
223   
224   for my $sw_kind (keys %SWITCH_KIND) {
225      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{match}/;
226     
227      return $SWITCH_KIND{$sw_kind}->{model};
228      }
229     
230   return $sw_snmp_description;
231   }
232
233###
234# va rechercher le nom des switchs pour savoir qui est qui
235sub init_switch_names {
236   my $verbose = shift;
237   
238   printf "%-25s                %-25s %s\n",'Switch','Description','Type';
239#   print "Switch description\n" if $verbose;
240   print "-------------------------------------------------------------------------\n" if $verbose;
241
242   INIT_EACH_SWITCH:
243   for my $sw (@SWITCH) {
244      my %session = ( -hostname   => $sw->{hostname} );
245         $session{-version} = $sw->{version}   || 1;
246         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
247         if (exists $sw->{version} and $sw->{version} eq 3) {
248            $session{-username} = $sw->{username} || 'snmpadmin';
249            }
250         else {
251            $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
252            }
253
254      $sw->{local_session} = \%session;
255
256      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
257      print "$error \n" if $error;
258
259      my $result = $session->get_request(
260         -varbindlist => [
261            $OID_NUMBER{sysDescr},
262            $OID_NUMBER{sysName},
263            $OID_NUMBER{sysContact},
264            $OID_NUMBER{sysLocation},
265            ]
266         );
267      $sw->{description} = $result->{$OID_NUMBER{sysName}} || $sw->{hostname};
268      $sw->{model} = get_switch_model( $result->{$OID_NUMBER{sysDescr}});
269      #$sw->{location} = $result->{"1.3.6.1.2.1.1.6.0"} || $sw->{hostname};
270      #$sw->{contact} = $result->{"1.3.6.1.2.1.1.4.0"} || $sw->{hostname};
271      $session->close;
272
273      # Ligne à virer car on récupère maintenant le modèle du switch
274      my ($desc, $type) = split ':', $sw->{description}, 2;
275#      printf "%-25s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, uc($type)."**" if $verbose;
276      printf "%-25s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, $sw->{model} if $verbose;
277      }
278
279   print "\n" if $verbose;
280   }
281
282###
283# convertit l'hexa (uniquement 2 chiffres) en decimal
284sub hex_to_dec {
285   #00:0F:1F:43:E4:2B
286   my $car = '00' . uc(shift);
287
288   return '00' if $car eq '00UNKNOW';
289   my %table = (
290      "0"=>"0",  "1"=>"1",  "2"=>"2",  "3"=>"3",  "4"=>"4",  "5"=>"5", "6"=>"6", "7"=>"7", "8"=>"8", "9"=>"9",
291      "A"=>"10", "B"=>"11", "C"=>"12", "D"=>"13", "E"=>"14", "F"=>"15"
292      );
293   my @chars = split(//, $car);
294   return $table{$chars[-2]}*16 + $table{$chars[-1]};
295   }
296
297###
298# convertit l'@ arp en decimal
299sub arp_hex_to_dec {
300   #00:0F:1F:43:E4:2B
301   my $arp = shift;
302
303   my @paquets = split /:/, $arp;
304   my $return = '';
305   foreach(@paquets) {
306      $return .= ".".hex_to_dec($_);
307      }
308   return $return;
309   }
310
311###
312# va rechercher le port et le switch sur lequel est la machine
313sub find_switch_port {
314   my $arp = shift;
315   my $switch_proposal = shift || '';
316   
317   my %ret;
318   $ret{switch_description} = "unknow";
319   $ret{switch_port} = "0";
320
321   return %ret if $arp eq 'unknow';;
322
323   my @switch_search = @SWITCH;
324   if ($switch_proposal ne '') {
325      for my $sw (@SWITCH) {
326         next if $sw->{hostname} ne $switch_proposal;
327         unshift @switch_search, $sw;
328         last;
329         }
330      }
331
332   my $research = "1.3.6.1.2.1.17.4.3.1.2".arp_hex_to_dec($arp);
333   
334   LOOP_ON_SWITCH:
335   for my $sw (@switch_search) {
336      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
337      print "$error \n" if $error;
338
339      my $result = $session->get_request(
340         -varbindlist => [$research]
341         );
342      if (not defined($result) or $result->{$research} eq 'noSuchInstance') {
343         $session->close;
344         next LOOP_ON_SWITCH;
345         }
346
347         my $swport = $result->{$research};
348         $session->close;
349
350         # IMPORTANT !!
351         # ceci empeche la detection sur certains port ...
352         # en effet les switch sont relies entre eux par un cable reseau et du coup
353         # tous les arp de toutes les machines sont presentes sur ces ports (ceux choisis ici sont les miens)
354         # cette partie est a ameliore, voir a configurer dans l'entete
355         # 21->24 45->48
356#         my $flag = 0;
357         SWITCH_PORT_IGNORE:
358         foreach my $p (@{$sw->{portignore}}) {
359            next SWITCH_PORT_IGNORE if $swport ne get_numerical_port($sw->{model},$p);
360#            $flag = 1;
361            next LOOP_ON_SWITCH;
362            }
363#         if ($flag == 0) {
364            $ret{switch_hostname}    = $sw->{hostname};
365            $ret{switch_description} = $sw->{description};
366            $ret{switch_port}        = get_human_readable_port($sw->{model}, $swport); # $swport;
367           
368            last LOOP_ON_SWITCH;
369#            }
370#         }
371#      $session->close;
372      }
373   return %ret;
374   }
375
376###
377# va rechercher les port et les switch sur lequel est la machine
378sub find_all_switch_port {
379   my $arp = shift;
380
381   my $ret = {};
382
383   return $ret if $arp eq 'unknow';
384
385   for my $sw (@SWITCH) {
386      $SWITCH_PORT_COUNT{$sw->{hostname}} = {} if not exists $SWITCH_PORT_COUNT{$sw->{hostname}};
387      }
388
389   my $research = "1.3.6.1.2.1.17.4.3.1.2".arp_hex_to_dec($arp);
390   LOOP_ON_ALL_SWITCH:
391   for my $sw (@SWITCH) {
392      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
393      print "$error \n" if $error;
394
395      my $result = $session->get_request(
396         -varbindlist => [$research]
397         );
398
399      if(defined($result) and $result->{$research} ne 'noSuchInstance'){
400         my $swport = $result->{$research};
401
402         $ret->{$sw->{hostname}} = {};
403         $ret->{$sw->{hostname}}{hostname}    = $sw->{hostname};
404         $ret->{$sw->{hostname}}{description} = $sw->{description};
405         $ret->{$sw->{hostname}}{port}        = get_human_readable_port($sw->{model}, $swport);
406
407         $SWITCH_PORT_COUNT{$sw->{hostname}}->{$swport}++;
408         }
409
410      $session->close;
411      }
412   return $ret;
413   }
414
415sub get_list_network {
416
417   return keys %{$KLASK_CFG->{network}};
418   }
419
420sub get_current_interface {
421   my $network = shift;
422
423   return $KLASK_CFG->{network}{$network}{interface};
424   }
425 
426###
427# liste l'ensemble des adresses ip d'un réseau
428sub get_list_ip {
429   my @network = @_;
430
431   my $cidrlist = Net::CIDR::Lite->new;
432
433   for my $net (@network) {
434      my @line  = @{$KLASK_CFG->{network}{$net}{'ip-subnet'}};
435      for my $cmd (@line) {
436         for my $method (keys %$cmd){
437            $cidrlist->add_any($cmd->{$method}) if $method eq 'add';
438            }
439         }
440      }
441
442   my @res = ();
443
444   for my $cidr ($cidrlist->list()) {
445      my $net = new NetAddr::IP $cidr;
446      for my $ip (@$net) {
447         $ip =~ s#/32##;
448         push @res,  $ip;
449         }
450      }
451
452   return @res;
453   }
454
455# liste l'ensemble des routeurs du réseau
456sub get_list_main_router {
457   my @network = @_;
458
459   my @res = ();
460
461   for my $net (@network) {
462      push @res, $KLASK_CFG->{network}{$net}{'main-router'};
463      }
464
465   return @res;
466   }
467
468sub get_human_readable_port {
469   my $sw_model = shift;
470   my $sw_port  = shift;
471   
472   return $sw_port if not $sw_model eq 'HP8000M';
473   
474   my $reste = (($sw_port - 1) % 8) + 1;
475   my $major = int( ($sw_port - 1) / 8 );
476
477   return "$INTERNAL_PORT_MAP{$major}$reste";
478   }
479
480sub get_numerical_port {
481   my $sw_model = shift;
482   my $sw_port  = shift;
483   
484   return $sw_port if not $sw_model eq 'HP8000';
485
486   my $letter = substr($sw_port, 0, 1);
487   
488#   return $port if $letter =~ m/\d/;
489   
490   my $reste =  substr($sw_port, 1);
491   
492   return $INTERNAL_PORT_MAP_REV{$letter} * 8 + $reste;
493   }
494
495################
496# Les commandes
497################
498
499sub cmd_help {
500
501print <<END;
502klask - ports manager and finder for switch
503
504 klask updatedb
505 klask exportdf
506
507 klask searchdb computer
508 klask search   computer
509
510 klask enable  switch port
511 klask disable switch port
512 klask status  switch port
513END
514   }
515
516sub cmd_version {
517
518print <<END;
519Klask - ports manager and finder for switch
520Copyright (C) 2005-2008 Gabriel Moreau
521
522END
523   print ' $Rev: 44 $'."\n";
524   print ' $Date: 2009-04-20 13:11:17 +0000 (Mon, 20 Apr 2009) $'."\n";
525   print ' $Id: klask 44 2009-04-20 13:11:17Z g7moreau $'."\n";
526   }
527
528sub cmd_search {
529   my @computer = @_;
530   
531   init_switch_names();    #nomme les switchs
532   fastping(@computer);
533   for my $clientname (@computer) {
534      my %resol_arp = resolve_ip_arp_host($clientname);          #resolution arp
535      my %where     = find_switch_port($resol_arp{mac_address}); #retrouve l'emplacement
536      printf "%-22s %2i %-30s %-15s %18s", $where{switch_description}, $where{switch_port}, $resol_arp{hostname_fq}, $resol_arp{ipv4_address}, $resol_arp{mac_address}."\n"
537         unless $where{switch_description} eq 'unknow' and $resol_arp{hostname_fq} eq 'unknow' and $resol_arp{mac_address} eq 'unknow';
538      }
539   }
540
541sub cmd_searchdb {
542   my @computer = @_;
543
544   fastping(@computer);
545   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
546   
547   LOOP_ON_COMPUTER:
548   for my $clientname (@computer) {
549      my %resol_arp = resolve_ip_arp_host($clientname);      #resolution arp
550      my $ip = $resol_arp{ipv4_address};
551     
552      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
553     
554      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($computerdb->{$ip}{timestamp});
555      $year += 1900;
556      $mon++;
557      my $date = sprintf "%04i-%02i-%02i %02i:%02i", $year,$mon,$mday,$hour,$min;
558
559      printf "%-22s %2s %-30s %-15s %-18s %s\n",
560         $computerdb->{$ip}{switch_name},
561         $computerdb->{$ip}{switch_port},
562         $computerdb->{$ip}{hostname_fq},
563         $ip,
564         $computerdb->{$ip}{mac_address},
565         $date;
566      }
567   }
568
569sub cmd_updatedb {
570   my @network = @_;
571      @network = get_list_network() if not @network;
572
573   test_switchdb_environnement();
574
575   my $computerdb = {};
576      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
577   my $timestamp = time;
578
579   my %computer_not_detected = ();
580   my $timestamp_last_week = $timestamp - (3600 * 24 * 7);
581
582   my $number_of_computer = get_list_ip(@network); # + 1;
583   my $size_of_database   = keys %$computerdb;
584      $size_of_database   = 1 if $size_of_database == 0;
585   my $i = 0;
586   my $detected_computer = 0;
587
588   init_switch_names('yes');    #nomme les switchs
589
590   { # Remplis le champs portignore des ports d'inter-connection pour chaque switch
591   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
592   my %db_switch_output_port       = %{$switch_connection->{output_port}};
593   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
594   my %db_switch_chained_port = ();
595   for my $swport (keys %db_switch_connected_on_port) {       
596      my ($sw_connect,$port_connect) = split ':', $swport;
597      $db_switch_chained_port{$sw_connect} .= "$port_connect:";
598      }
599   for my $sw (@SWITCH){
600      push @{$sw->{portignore}}, $db_switch_output_port{$sw->{hostname}}  if exists $db_switch_output_port{$sw->{hostname}};
601      if ( exists $db_switch_chained_port{$sw->{hostname}} ) {
602         chop $db_switch_chained_port{$sw->{hostname}};
603         push @{$sw->{portignore}}, split(':',$db_switch_chained_port{$sw->{hostname}});
604         }
605#      print "$sw->{hostname} ++ @{$sw->{portignore}}\n";
606      }
607   }
608
609   my %router_mac_ip = ();
610   DETECT_ALL_ROUTER:
611#   for my $one_router ('194.254.66.254') {
612   for my $one_router ( get_list_main_router(@network) ) {
613      my %resol_arp = resolve_ip_arp_host($one_router);
614      $router_mac_ip{ $resol_arp{mac_address} } = $resol_arp{ipv4_address};
615      }
616
617   ALL_NETWORK:
618   for my $net (@network) {
619
620      my @computer = get_list_ip($net);
621      my $current_interface = get_current_interface($net);
622
623      fastping(@computer);
624
625      LOOP_ON_COMPUTER:
626      for my $one_computer (@computer) {
627         $i++;
628         
629         my $total_percent = int(($i*100)/$number_of_computer);
630
631         my $localtime = time - $timestamp;
632         my ($sec,$min) = localtime($localtime);
633
634         my $time_elapse = 0;
635            $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0;
636         my ($sec_elapse,$min_elapse) = localtime($time_elapse);
637
638         printf "\rComputer scanned: %4i/%i (%2i%%)",  $i,                 $number_of_computer, $total_percent;
639#         printf ", Computer detected: %4i/%i (%2i%%)", $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
640         printf ", detected: %4i/%i (%2i%%)", $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
641         printf " [Time: %02i:%02i / %02i:%02i]", int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
642#         printf "  [%02i:%02i/%02i:%02i]", int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
643         printf " %-14s", $one_computer;
644
645         my %resol_arp = resolve_ip_arp_host($one_computer,$current_interface);
646         
647         # do not search on router connection (why ?)
648         if ( exists $router_mac_ip{$resol_arp{mac_address}}) {
649            $computer_not_detected{$one_computer} = $current_interface;
650            next LOOP_ON_COMPUTER;
651            }
652
653         # do not search on switch inter-connection
654         if (exists $switch_level{$resol_arp{hostname_fq}}) {
655            $computer_not_detected{$one_computer} = $current_interface;
656            next LOOP_ON_COMPUTER;
657            }
658
659         my $switch_proposal = '';
660         if (exists $computerdb->{$resol_arp{ipv4_address}} and exists $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}) {
661            $switch_proposal = $computerdb->{$resol_arp{ipv4_address}}{switch_hostname};
662            }
663
664         # do not have a mac address
665         if ($resol_arp{mac_address} eq 'unknow' or (exists $resol_arp{timestamps} and $resol_arp{timestamps} < ($timestamp - 3 * 3600))) {
666            $computer_not_detected{$one_computer} = $current_interface;
667            next LOOP_ON_COMPUTER;
668            }
669
670         my %where = find_switch_port($resol_arp{mac_address},$switch_proposal);
671
672         #192.168.24.156:
673         #  arp: 00:0B:DB:D5:F6:65
674         #  hostname: pcroyon.hmg.priv
675         #  port: 5
676         #  switch: sw-batH-legi:hp2524
677         #  timestamp: 1164355525
678
679         # do not have a mac address
680#         if ($resol_arp{mac_address} eq 'unknow') {
681#            $computer_not_detected{$one_computer} = $current_interface;
682#            next LOOP_ON_COMPUTER;
683#            }
684
685         # detected on a switch
686         if ($where{switch_description} ne 'unknow') {
687            $detected_computer++;
688            $computerdb->{$resol_arp{ipv4_address}} = {
689               hostname_fq        => $resol_arp{hostname_fq},
690               mac_address        => $resol_arp{mac_address},
691               switch_hostname    => $where{switch_hostname},
692               switch_description => $where{switch_description},
693               switch_port        => $where{switch_port},
694               timestamp          => $timestamp,
695               };
696            next LOOP_ON_COMPUTER;
697            }
698
699         # new in the database but where it is ?
700         if (not exists $computerdb->{$resol_arp{ipv4_address}}) {
701            $detected_computer++;
702            $computerdb->{$resol_arp{ipv4_address}} = {
703               hostname_fq        => $resol_arp{hostname_fq},
704               mac_address        => $resol_arp{mac_address},
705               switch_hostname    => $where{switch_hostname},
706               switch_description => $where{switch_description},
707               switch_port        => $where{switch_port},
708               timestamp          => $resol_arp{timestamp},
709               };
710            }
711
712         # mise a jour du nom de la machine si modification dans le dns
713         $computerdb->{$resol_arp{ipv4_address}}{hostname_fq} = $resol_arp{hostname_fq};
714       
715         # mise à jour de la date de détection si détection plus récente par arpwatch
716         $computerdb->{$resol_arp{ipv4_address}}{timestamp}   = $resol_arp{timestamp} if exists $resol_arp{timestamp} and $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $resol_arp{timestamp};
717
718         # provisoire car changement de nom des attributs
719#         $computerdb->{$resol_arp{ipv4_address}}{mac_address}        = $computerdb->{$resol_arp{ipv4_address}}{arp};
720#         $computerdb->{$resol_arp{ipv4_address}}{switch_description} = $computerdb->{$resol_arp{ipv4_address}}{switch};
721#         $computerdb->{$resol_arp{ipv4_address}}{switch_port}        = $computerdb->{$resol_arp{ipv4_address}}{port};
722       
723         # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine
724#         push @computer_not_detected, $resol_arp{ipv4_address} if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
725         $computer_not_detected{$resol_arp{ipv4_address}} = $current_interface if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
726       
727         }
728      }
729
730   # final end of line at the end of the loop
731   printf "\n";
732
733   my $dirdb = $KLASK_DB_FILE;
734      $dirdb =~ s#/[^/]*$##;
735   mkdir "$dirdb", 0755 unless -d "$dirdb";
736   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
737
738   for my $one_computer (keys %computer_not_detected) {
739      my $interface = $computer_not_detected{$one_computer};
740      system "arping -c 1 -w 1 -rR -i $interface $one_computer &>/dev/null";
741#      print  "arping -c 1 -w 1 -rR -i $interface $one_computer 2>/dev/null\n";
742      }
743   }
744
745sub cmd_removedb {
746   my @computer = @_;
747
748   test_maindb_environnement();
749
750   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
751
752   LOOP_ON_COMPUTER:
753   for my $one_computer (@computer) {
754
755      my %resol_arp = resolve_ip_arp_host($one_computer);
756
757      delete $computerdb->{$resol_arp{ipv4_address}} if exists $computerdb->{$resol_arp{ipv4_address}};
758      }
759
760   my $dirdb = $KLASK_DB_FILE;
761      $dirdb =~ s#/[^/]*$##;
762   mkdir "$dirdb", 0755 unless -d "$dirdb";
763   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
764   }
765
766sub cmd_exportdb {
767   test_maindb_environnement();
768
769   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
770
771   printf "%-24s %-4s            %-30s %-15s %-18s %-s\n", qw(Switch Port Hostname IPv4-Address MAC-Address Date);
772   print "---------------------------------------------------------------------------------------------------------------------------\n";
773
774   LOOP_ON_IP_ADDRESS:
775   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %$computerdb)) {
776   
777#      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow';
778
779      # to be improve in the future
780      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
781
782# dans le futur
783#      next if $computerdb->{$ip}{hostname_fq} eq 'unknow';
784     
785      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($computerdb->{$ip}{timestamp});
786      $year += 1900;
787      $mon++;
788      my $date = sprintf "%04i-%02i-%02i %02i:%02i", $year,$mon,$mday,$hour,$min;
789
790      printf "%-25s  %2s  <-------  %-30s %-15s %-18s %s\n",
791         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
792         $computerdb->{$ip}{switch_port},
793         $computerdb->{$ip}{hostname_fq},
794         $ip,
795         $computerdb->{$ip}{mac_address},
796         $date;
797      }
798   }
799
800sub cmd_iplocation {
801   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
802
803   LOOP_ON_IP_ADDRESS:
804   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %$computerdb)) {
805
806      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
807
808      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || '';
809      next if $sw_hostname eq 'unknow';
810 
811      my $sw_location = '';
812      for my $sw (@SWITCH) {
813         next if $sw_hostname ne $sw->{hostname};
814         $sw_location = $sw->{location};
815         last;
816         }
817
818      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq '';
819      }
820   }
821
822sub cmd_enable {
823   my $switch = shift;
824   my $port   = shift;
825   
826   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
827   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
828   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
829   }
830
831sub cmd_disable {
832   my $switch = shift;
833   my $port   = shift;
834   
835   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
836   }
837
838sub cmd_status {
839   my $switch = shift;
840   my $port   = shift;
841   
842   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
843   }
844
845sub cmd_search_mac_on_switch {
846   my $switch_name = shift || '';
847   my $mac_address = shift || '';
848   
849   if ($switch_name eq '' or $mac_address eq '') {
850      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
851      }
852
853   if (not defined $SWITCH_DB{$switch_name}) {
854      die "Switch $switch_name must be defined in klask configuration file\n"; 
855      }
856
857   my $sw = $SWITCH_DB{$switch_name};
858   my %session = ( -hostname => $sw->{hostname} );
859      $session{-version} = $sw->{version}   || 1;
860      $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
861   if (exists $sw->{version} and $sw->{version} eq 3) {
862      $session{-username} = $sw->{username} || 'snmpadmin';
863      }
864   else {
865      $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
866      }
867
868   my $research = "1.3.6.1.2.1.17.4.3.1.2".arp_hex_to_dec($mac_address);
869   print "Klask search OID $research on switch $switch_name\n";
870
871   my ($session, $error) = Net::SNMP->session( %session );
872   print "$error \n" if $error;
873
874   my $result = $session->get_request(
875      -varbindlist => [$research]
876      );
877   
878   if (not defined($result) or $result->{$research} eq 'noSuchInstance') {
879      print "Klask do not find MAC $mac_address on switch $switch_name\n";
880      $session->close;
881      }
882
883   my $swport = $result->{$research};
884   $session->close;
885
886   print "Klask find MAC $mac_address on switch $switch_name port $swport\n";
887   }
888
889sub cmd_updatesw {
890
891   init_switch_names('yes');    #nomme les switchs
892   print "\n";
893
894   my %where = ();
895   my %db_switch_output_port = ();
896   my %db_switch_ip_hostname = ();
897
898   DETECT_ALL_ROUTER:
899#   for my $one_computer ('194.254.66.254') {
900   for my $one_router ( get_list_main_router(get_list_network()) ) {
901print "TT$one_router\n";     
902      my %resol_arp = resolve_ip_arp_host($one_router,'*','low');            # resolution arp
903      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
904print "$one_router\n";     
905      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # retrouve les emplacements des routeurs
906      }
907
908   ALL_ROUTER_IP_ADDRESS:
909   for my $ip (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
910   
911      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip}; # /a priori/ idiot car ne sers à rien...
912
913      ALL_SWITCH_CONNECTED:
914      for my $switch_detected ( keys %{$where{$ip}} ) {
915
916         my $switch = $where{$ip}->{$switch_detected};
917
918         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
919         
920         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
921         }
922      }   
923
924#   print "Switch output port\n"; 
925#   print "------------------\n";
926#   for my $sw (sort keys %db_switch_output_port) {
927#      printf "%-25s %2s\n", $sw, $db_switch_output_port{$sw};
928#      }
929#   print "\n";
930
931
932   my %db_switch_link_with = ();
933
934   my @list_switch_ip = ();
935   my @list_switch_ipv4 = ();
936   for my $sw (@SWITCH){
937      push @list_switch_ip, $sw->{hostname};
938      }
939
940   ALL_SWITCH:
941   for my $one_computer (@list_switch_ip) {
942      my %resol_arp = resolve_ip_arp_host($one_computer,'*','low'); # arp resolution
943      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
944     
945      push @list_switch_ipv4,$resol_arp{ipv4_address};
946     
947      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # find port on all switch
948
949      $db_switch_ip_hostname{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
950      }
951     
952   ALL_SWITCH_IP_ADDRESS:
953   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
954   
955      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
956
957      DETECTED_SWITCH:
958      for my $switch_detected ( keys %{$where{$ip}} ) {
959
960         next DETECTED_SWITCH if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostname{$ip}};
961
962         my $switch = $where{$ip}->{$switch_detected};
963
964         next if $switch->{port}     eq '0';
965         next if $switch->{port}     eq $db_switch_output_port{$switch->{hostname}};
966         next if $switch->{hostname} eq $db_switch_ip_hostname{$ip}; # $computerdb->{$ip}{hostname};
967
968         $db_switch_link_with{ $db_switch_ip_hostname{$ip} } ||= {};
969         $db_switch_link_with{ $db_switch_ip_hostname{$ip} }->{ $switch->{hostname} } = $switch->{port};
970         }
971
972      }
973   
974   my %db_switch_connected_on_port = ();
975   my $maybe_more_than_one_switch_connected = 'yes';
976   
977   while ($maybe_more_than_one_switch_connected eq 'yes') {
978      for my $sw (keys %db_switch_link_with) {
979         for my $connect (keys %{$db_switch_link_with{$sw}}) {
980         
981            my $port = $db_switch_link_with{$sw}->{$connect};
982         
983            $db_switch_connected_on_port{"$connect:$port"} ||= {};
984            $db_switch_connected_on_port{"$connect:$port"}->{$sw}++; # Just to define the key
985            }
986         }
987
988      $maybe_more_than_one_switch_connected  = 'no';
989
990      SWITCH_AND_PORT:
991      for my $swport (keys %db_switch_connected_on_port) {
992         
993         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
994         
995         $maybe_more_than_one_switch_connected = 'yes';
996
997         my ($sw_connect,$port_connect) = split ':', $swport;
998         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
999
1000         CONNECTED:
1001         for my $sw_connected (@sw_on_same_port) {
1002           
1003            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
1004           
1005            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
1006           
1007            for my $other_sw (@sw_on_same_port) {
1008               next if $other_sw eq $sw_connected;
1009               
1010               delete $db_switch_link_with{$other_sw}->{$sw_connect};
1011               }
1012           
1013            # We can not do better for this switch for this loop
1014            next SWITCH_AND_PORT;
1015            }
1016         }
1017      }
1018
1019   my %db_switch_parent =();
1020
1021   for my $sw (keys %db_switch_link_with) {
1022      for my $connect (keys %{$db_switch_link_with{$sw}}) {
1023     
1024         my $port = $db_switch_link_with{$sw}->{$connect};
1025     
1026         $db_switch_connected_on_port{"$connect:$port"} ||= {};
1027         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
1028       
1029         $db_switch_parent{$sw} = {switch => $connect, port => $port};
1030         }
1031      }
1032
1033   print "Switch output port and parent port connection\n"; 
1034   print "---------------------------------------------\n";
1035   for my $sw (sort keys %db_switch_output_port) {
1036      if (exists $db_switch_parent{$sw}) {
1037         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1038         }
1039      else {
1040         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1041         }
1042      }
1043   print "\n";
1044
1045   print "Switch parent and children port inter-connection\n";
1046   print "------------------------------------------------\n";
1047   for my $swport (sort keys %db_switch_connected_on_port) {       
1048      my ($sw_connect,$port_connect) = split ':', $swport;
1049      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1050         if (exists $db_switch_output_port{$sw}) {
1051            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1052            }
1053         else {
1054            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1055            }
1056         }
1057      }
1058
1059   my $switch_connection = {
1060      output_port       => \%db_switch_output_port,
1061      parent            => \%db_switch_parent,
1062      connected_on_port => \%db_switch_connected_on_port,
1063      link_with         => \%db_switch_link_with,
1064      switch_db         => \%SWITCH_DB,
1065      };
1066     
1067   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
1068   }
1069
1070sub cmd_exportsw {
1071   my @ARGV   = @_;
1072
1073   test_switchdb_environnement();
1074
1075   my $format = 'txt';
1076
1077   my $ret = GetOptions(
1078      'format|f=s'  => \$format,
1079      );
1080
1081   my %possible_format = (
1082      txt => \&cmd_exportsw_txt,
1083      dot => \&cmd_exportsw_dot,
1084      );
1085
1086   $format = 'txt' if not defined $possible_format{$format};
1087   
1088   $possible_format{$format}->(@ARGV);
1089   }
1090
1091sub cmd_exportsw_txt {
1092
1093   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1094
1095   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1096   my %db_switch_parent            = %{$switch_connection->{parent}};
1097   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1098
1099   print "Switch output port and parent port connection\n"; 
1100   print "---------------------------------------------\n";
1101   for my $sw (sort keys %db_switch_output_port) {
1102      if (exists $db_switch_parent{$sw}) {
1103         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1104         }
1105      else {
1106         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1107         }
1108      }
1109   print "\n";
1110
1111   print "Switch parent and children port inter-connection\n";
1112   print "------------------------------------------------\n";
1113   for my $swport (sort keys %db_switch_connected_on_port) {       
1114      my ($sw_connect,$port_connect) = split ':', $swport;
1115      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1116         if (exists $db_switch_output_port{$sw}) {
1117            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1118            }
1119         else {
1120            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1121            }
1122         }
1123      }
1124   }
1125
1126sub cmd_exportsw_dot {
1127
1128   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1129   
1130   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1131   my %db_switch_parent            = %{$switch_connection->{parent}};
1132   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1133   my %db_switch_link_with         = %{$switch_connection->{link_with}};
1134   my %db_switch_global            = %{$switch_connection->{switch_db}};
1135
1136   my %db_building= ();
1137   for my $sw (@SWITCH) {
1138      my ($building, $location) = split /\//, $sw->{location}, 2;
1139      $db_building{$building} ||= {};
1140      $db_building{$building}->{$location} ||= {};
1141      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
1142      }
1143 
1144 
1145   print "digraph G {\n";
1146
1147   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
1148   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
1149
1150   my $b=0;
1151   for my $building (keys %db_building) {
1152      $b++;
1153     
1154      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
1155      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
1156
1157      my $l = 0;
1158      for my $loc (keys %{$db_building{$building}}) {
1159         $l++;
1160 
1161         print "\"location$b-$l\" [label = \"$building".'/'.join('\n',split("/",$loc))."\", color = black, fillcolor = orange, style = filled];\n";
1162#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
1163         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
1164
1165         for my $sw (keys %{$db_building{$building}->{$loc}}) {
1166
1167            print "\"$sw:$db_switch_output_port{$sw}\" [label = $db_switch_output_port{$sw}, color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
1168
1169            my $swname  = $sw;
1170               $swname .= '\n-\n'."$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
1171            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
1172            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
1173            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
1174
1175
1176            for my $swport (keys %db_switch_connected_on_port) {
1177               my ($sw_connect,$port_connect) = split ':', $swport;
1178               next if not $sw_connect eq $sw;
1179               next if $port_connect eq $db_switch_output_port{$sw};
1180               print "\"$sw:$port_connect\" [label = $port_connect, color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
1181               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
1182              }
1183            }
1184         }
1185      }
1186
1187#   print "Switch output port and parent port connection\n"; 
1188#   print "---------------------------------------------\n";
1189   for my $sw (sort keys %db_switch_output_port) {
1190      if (exists $db_switch_parent{$sw}) {
1191#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
1192         }
1193      else {
1194         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
1195         }
1196      }
1197   print "\n";
1198
1199#   print "Switch parent and children port inter-connection\n";
1200#   print "------------------------------------------------\n";
1201   for my $swport (sort keys %db_switch_connected_on_port) {       
1202      my ($sw_connect,$port_connect) = split ':', $swport;
1203      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1204         if (exists $db_switch_output_port{$sw}) {
1205            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
1206            }
1207         else {
1208            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
1209            }
1210         }
1211      }
1212
1213print "}\n";
1214   }
1215
1216
1217__END__
1218
1219
1220=head1 NAME
1221
1222klask - ports manager and finder for switch
1223
1224
1225=head1 SYNOPSIS
1226
1227 klask updatedb
1228 klask exportdb
1229
1230 klask updatesw
1231 klask exportsw --format [txt|dot]
1232
1233 klask searchdb computer
1234 klask search   computer
1235
1236 klask enable  switch port
1237 klask disable swith port
1238 klask status  swith port
1239
1240
1241=head1 DESCRIPTION
1242
1243klask is a small tool to find where is a host in a big network. klask mean search in brittany.
1244
1245Klask has now a web site dedicated for it !
1246
1247 http://servforge.legi.inpg.fr/projects/klask
1248
1249
1250=head1 COMMANDS
1251
1252
1253=head2 search
1254
1255This command takes one or more computer in argument. It search a computer on the network and give the port and the switch on which the computer is connected.
1256
1257
1258=head2 enable
1259
1260This command activate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1261
1262
1263=head2 disable
1264
1265This command deactivate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1266
1267
1268=head2 status
1269
1270This command return the status of a port number on a switch by snmp. So you need to give the switch name and the port number on the command line.
1271
1272
1273=head2 updatedb
1274
1275This command will scan networks and update a database. To know which are the cmputer scan, you have to configure the file /etc/klask.conf This file is easy to read and write because klask use YAML format and not XML.
1276
1277
1278=head2 exportdb
1279
1280This command print the content of the database. There is actually only one format. It's very easy to have more format, it's just need times...
1281
1282
1283=head2 updatesw
1284
1285This command build a map of your manageable switch on your network. The list of the switch must be given in the file /etc/klask.conf.
1286
1287
1288=head2 exportsw --format [txt|dot]
1289
1290This command print the content of the switch database. There is actually two format. One is just txt for terminal and the other is the dot format from the graphviz environnement.
1291
1292 klask exportsw --format dot > /tmp/map.dot
1293 dot -Tpng /tmp/map.dot > /tmp/map.png
1294
1295
1296
1297=head1 CONFIGURATION
1298
1299Because klask need many parameters, it's not possible actually to use command line parameters. The configuration is done in a /etc/klask.conf YAML file. This format have many advantage over XML, it's easier to read and to write !
1300
1301Here an example, be aware with indent, it's important in YAML, do not use tabulation !
1302
1303 default:
1304   community: public
1305   snmpport: 161
1306
1307 network:
1308   labnet:
1309     ip-subnet:
1310       - add: 192.168.1.0/24
1311       - add: 192.168.2.0/24
1312     interface: eth0
1313     main-router: gw1.labnet.local
1314
1315   schoolnet:
1316     ip-subnet:
1317       - add: 192.168.6.0/24
1318       - add: 192.168.7.0/24
1319     interface: eth0.38
1320     main-router: gw2.schoolnet.local
1321
1322 switch:
1323   - hostname: sw1.klask.local
1324     portignore:
1325       - 1
1326       - 2
1327
1328   - hostname: sw2.klask.local
1329     location: BatK / 2 / K203
1330     type: HP2424
1331     portignore:
1332       - 1
1333       - 2
1334
1335I think it's pretty easy to understand. The default section can be overide in any section, if parameter mean something in theses sections. Network to be scan are define in the network section. You must put a add by network. Maybe i will make a delete line to suppress specific computers. The switch section define your switch. You have to write the port number to ignore, this is important if your switchs are cascade. Juste put the ports numbers between switch.
1336
1337
1338=head1 FILES
1339
1340 /etc/klask.conf
1341 /var/cache/klask/klaskdb
1342 /var/cache/klask/switchdb
1343
1344=head1 SEE ALSO
1345
1346Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
1347
1348
1349=head1 VERSION
1350
1351$Id: klask 44 2009-04-20 13:11:17Z g7moreau $
1352
1353
1354=head1 AUTHOR
1355
1356Written by Gabriel Moreau, Grenoble - France
1357
1358
1359=head1 COPYRIGHT
1360       
1361Copyright (C) 2005-2008 Gabriel Moreau.
1362
1363
1364=head1 LICENCE
1365
1366GPL version 2 or later and Perl equivalent
Note: See TracBrowser for help on using the repository browser.