source: trunk/klask @ 114

Last change on this file since 114 was 114, checked in by g7moreau, 12 years ago
  • Add command bad-vlan-config
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 64.2 KB
Line 
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005-2012 Gabriel Moreau.
4#
5# $Id: klask 114 2012-10-01 15:36:13Z g7moreau $
6
7use strict;
8use warnings;
9use version; our $VERSION = qv('0.5.5');
10
11use Readonly;
12use FileHandle;
13use Net::SNMP;
14#use YAML;
15use YAML::Syck;
16use Net::Netmask;
17use Net::CIDR::Lite;
18use NetAddr::IP;
19use Getopt::Long qw(GetOptions);
20use Socket;
21use List::Util 'shuffle';
22
23# apt-get install snmp fping libnet-cidr-lite-perl libnet-netmask-perl libnet-snmp-perl libnetaddr-ip-perl libyaml-perl
24# libcrypt-des-perl libcrypt-hcesha-perl libdigest-hmac-perl
25# arping net-tools fping bind9-host arpwatch
26
27my $KLASK_VAR      = '/var/lib/klask';
28my $KLASK_CFG_FILE = '/etc/klask/klask.conf';
29my $KLASK_DB_FILE  = "$KLASK_VAR/klaskdb";
30my $KLASK_SW_FILE  = "$KLASK_VAR/switchdb";
31
32test_running_environnement();
33
34my $KLASK_CFG = YAML::Syck::LoadFile("$KLASK_CFG_FILE");
35
36my %DEFAULT = %{ $KLASK_CFG->{default} };
37my @SWITCH  = @{ $KLASK_CFG->{switch}  };
38
39my %switch_level = ();
40my %SWITCH_DB    = ();
41LEVEL_OF_EACH_SWITCH:
42for my $sw (@SWITCH){
43   $switch_level{$sw->{hostname}} = $sw->{level} || $DEFAULT{switch_level}  || 2;
44   $SWITCH_DB{$sw->{hostname}} = $sw;
45   }
46@SWITCH = reverse sort { $switch_level{$a->{hostname}} <=> $switch_level{$b->{hostname}} } @{$KLASK_CFG->{switch}};
47
48my %SWITCH_PORT_COUNT = ();
49
50my %CMD_DB = (
51   help       => \&cmd_help,
52   version    => \&cmd_version,
53   exportdb   => \&cmd_exportdb,
54   updatedb   => \&cmd_updatedb,
55   searchdb   => \&cmd_searchdb,
56   removedb   => \&cmd_removedb,
57   cleandb    => \&cmd_cleandb,
58   search     => \&cmd_search,
59   enable     => \&cmd_enable,
60   disable    => \&cmd_disable,
61   status     => \&cmd_status,
62   updatesw   => \&cmd_updatesw,
63   exportsw   => \&cmd_exportsw,
64   iplocation => \&cmd_ip_location,
65   'ip-free'  => \&cmd_ip_free,
66   'search-mac-on-switch' => \&cmd_search_mac_on_switch,
67   'bad-vlan-config'  => \&cmd_bad_vlan_config,
68   );
69
70Readonly my %INTERNAL_PORT_MAP => (
71   0 => 'A',
72   1 => 'B',
73   2 => 'C',
74   3 => 'D',
75   4 => 'E',
76   5 => 'F',
77   6 => 'G',
78   7 => 'H',
79   );
80Readonly my %INTERNAL_PORT_MAP_REV => reverse %INTERNAL_PORT_MAP;
81
82Readonly my %SWITCH_KIND => (
83   J3299A => { model => 'HP224M',     match => 'HP J3299A ProCurve Switch 224M'  },
84   J4120A => { model => 'HP1600M',    match => 'HP J4120A ProCurve Switch 1600M' },
85   J9029A => { model => 'HP1800-8G',  match => 'PROCURVE J9029A'                 },
86   J9449A => { model => 'HP1810-8G',  match => 'HP ProCurve 1810G - 8 GE'        },
87   J4093A => { model => 'HP2424M',    match => 'HP J4093A ProCurve Switch 2424M' },
88   J9279A => { model => 'HP2510G-24', match => 'ProCurve J9279A Switch 2510G-24' },
89   J9280A => { model => 'HP2510G-48', match => 'ProCurve J9280A Switch 2510G-48' },
90   J4813A => { model => 'HP2524',     match => 'HP J4813A ProCurve Switch 2524'  },
91   J4900A => { model => 'HP2626A',    match => 'HP J4900A ProCurve Switch 2626'  },
92   J4900B => { model => 'HP2626B',    match => 'J4900B.+?Switch 2626'            },# ProCurve J4900B Switch 2626 # HP J4900B ProCurve Switch 2626
93   J4899B => { model => 'HP2650',     match => 'ProCurve J4899B Switch 2650'     },
94   J9021A => { model => 'HP2810-24G', match => 'ProCurve J9021A Switch 2810-24G' },
95   J9022A => { model => 'HP2810-48G', match => 'ProCurve J9022A Switch 2810-48G' },
96   J4903A => { model => 'HP2824',     match => 'J4903A.+?Switch 2824,'           },
97   J4110A => { model => 'HP8000M',    match => 'HP J4110A ProCurve Switch 8000M' },
98   BS350T => { model => 'BS350T',     match => 'BayStack 350T HW'                },
99   );
100
101Readonly my %OID_NUMBER => (
102   sysDescription  => '1.3.6.1.2.1.1.1.0',
103   sysName         => '1.3.6.1.2.1.1.5.0',
104   sysContact      => '1.3.6.1.2.1.1.4.0',
105   sysLocation     => '1.3.6.1.2.1.1.6.0',
106   searchPort      => '1.3.6.1.2.1.17.4.3.1.2',
107   );
108
109Readonly my $RE_MAC_ADDRESS  => qr{ [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} }xms;
110Readonly my $RE_IPv4_ADDRESS => qr{ [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} }xms;
111
112
113################
114# principal
115################
116
117my $cmd = shift @ARGV || 'help';
118if (defined $CMD_DB{$cmd}) {
119   $CMD_DB{$cmd}->(@ARGV);
120   }
121else {
122   print {*STDERR} "klask: command $cmd not found\n\n";
123   $CMD_DB{help}->();
124   exit 1;
125   }
126
127exit;
128
129sub test_running_environnement {
130   die "Configuration file $KLASK_CFG_FILE does not exists. Klask need it !\n" if not -e "$KLASK_CFG_FILE";
131   die "Var folder $KLASK_VAR does not exists. Klask need it !\n"              if not -d "$KLASK_VAR";
132   return;
133   }
134
135sub test_switchdb_environnement {
136   die "Switch database $KLASK_SW_FILE does not exists. Launch updatesw before this command !\n" if not -e "$KLASK_SW_FILE";
137   return;
138   }
139
140sub test_maindb_environnement {
141   die "Main database $KLASK_DB_FILE does not exists. Launch updatedb before this command !\n" if not -e "$KLASK_DB_FILE";
142   return;
143   }
144
145###
146# fast ping dont l'objectif est de remplir la table arp de la machine
147sub fast_ping {
148   # Launch this command without waiting...
149   system "fping -c 1 @_ >/dev/null 2>&1 &";
150   return;
151   }
152
153sub shell_command {
154   my $cmd = shift;
155
156   my $fh     = new FileHandle;
157   my $result = '';
158   open $fh, q{-|}, "LANG=C $cmd" or die "Can't exec $cmd\n";
159   $result .= <$fh>;
160   close $fh;
161   chomp $result;
162   return $result;
163   }
164
165###
166# donne l'@ ip, dns, arp en fonction du dns OU de l'ip
167sub resolve_ip_arp_host {
168   my $param_ip_or_host = shift;
169   my $interface = shift || q{*};
170   my $type      = shift || q{fast};
171
172   my %ret = (
173      hostname_fq  => 'unknow',
174      ipv4_address => '0.0.0.0',
175      mac_address  => 'unknow',
176      );
177
178   # perl -MSocket -E 'say inet_ntoa(scalar gethostbyname("tech7meylan.hmg.inpg.fr"))'
179   my $packed_ip = scalar gethostbyname($param_ip_or_host);
180   return %ret if not defined $packed_ip;
181   $ret{ipv4_address} = inet_ntoa($packed_ip);
182
183   # perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("194.254.66.240"), AF_INET)'
184   my $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET);
185   $ret{hostname_fq} = $hostname_fq if defined $hostname_fq;
186
187   # my $cmd = q{grep  -he '\b} . $param_ip_or_host . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
188   my $cmd = q{grep  -he '\b} . $ret{ipv4_address} . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
189   my $cmd_arpwatch = shell_command $cmd;
190   my ($arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch;
191
192   $ret{mac_address}  = $arp       if $arp;
193   $ret{timestamp}    = $timestamp if $timestamp;
194
195   my $nowtimestamp = time;
196
197   if ( $type eq 'fast' and ( not defined $timestamp or $timestamp < ( $nowtimestamp - 45 * 60 ) ) ) { # 45 min
198      $ret{mac_address} = 'unknow';
199      return %ret;
200      }
201
202   # resultat de la commande arp
203   # tech7meylan.hmg.inpg.fr (194.254.66.240) at 00:14:22:45:28:A9 [ether] on eth0
204   # sw2-batF0-legi.hmg.priv (192.168.22.112) at 00:30:c1:76:9c:01 [ether] on eth0.37
205   my $cmd_arp  = shell_command "arp -a $param_ip_or_host";
206   if ( $cmd_arp =~ m{ (\S*) \s \( ( $RE_IPv4_ADDRESS ) \) \s at \s ( $RE_MAC_ADDRESS ) }xms ) {
207      ( $ret{hostname_fq}, $ret{ipv4_address}, $ret{mac_address} )  = ($1, $2, $3);
208      }
209
210   # Normalize MAC Address
211   if ($ret{mac_address} ne 'unknow') {
212      my @paquets = ();
213      foreach ( split m/ : /xms, $ret{mac_address} ) {
214         my @chars = split m//xms, uc "00$_";
215         push @paquets, "$chars[-2]$chars[-1]";
216         }
217      $ret{mac_address} = join q{:}, @paquets;
218      }
219
220   return %ret;
221   }
222
223# Find Surname of a switch
224sub get_switch_model {
225   my $sw_snmp_description = shift || 'unknow';
226
227   for my $sw_kind (keys %SWITCH_KIND) {
228      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{match}/ms; # option xms break search, why ?
229
230      return $SWITCH_KIND{$sw_kind}->{model};
231      }
232
233   return $sw_snmp_description;
234   }
235
236###
237# va rechercher le nom des switchs pour savoir qui est qui
238sub init_switch_names {
239   my $verbose = shift;
240
241   printf "%-26s                %-25s %s\n",'Switch','Description','Type';
242   print "------------------------------------------------------------------------------\n" if $verbose;
243
244   INIT_EACH_SWITCH:
245   for my $sw (@SWITCH) {
246      my %session = ( -hostname   => $sw->{hostname} );
247         $session{-version} = $sw->{version}   || 1;
248         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
249         if (exists $sw->{version} and $sw->{version} eq '3') {
250            $session{-username} = $sw->{username} || 'snmpadmin';
251            }
252         else {
253            $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
254            }
255
256      $sw->{local_session} = \%session;
257
258      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
259      print "$error \n" if $error;
260
261      my $result = $session->get_request(
262         -varbindlist => [
263            $OID_NUMBER{sysDescription},
264            $OID_NUMBER{sysName},
265            $OID_NUMBER{sysContact},
266            $OID_NUMBER{sysLocation},
267            ]
268         );
269      $sw->{description} = $result->{$OID_NUMBER{sysName}} || $sw->{hostname};
270      $sw->{model} = get_switch_model( $result->{$OID_NUMBER{sysDescription}});
271      #$sw->{location} = $result->{"1.3.6.1.2.1.1.6.0"} || $sw->{hostname};
272      #$sw->{contact} = $result->{"1.3.6.1.2.1.1.4.0"} || $sw->{hostname};
273      $session->close;
274
275      # Ligne à virer car on récupère maintenant le modèle du switch
276      my ($desc, $type) = split m/ : /xms, $sw->{description}, 2;
277      printf "%-26s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, $sw->{model} if $verbose;
278      }
279
280   print "\n" if $verbose;
281   return;
282   }
283
284###
285# convertit l'hexa (uniquement 2 chiffres) en decimal
286sub digit_hex_to_dec {
287   #00:0F:1F:43:E4:2B
288   my $car = '00' . uc shift;
289
290   return '00' if $car eq '00UNKNOW';
291   my %table = (
292      '0'=>'0',  '1'=>'1',  '2'=>'2',  '3'=>'3',  '4'=>'4',
293      '5'=>'5',  '6'=>'6',  '7'=>'7',  '8'=>'8',  '9'=>'9',
294      'A'=>'10', 'B'=>'11', 'C'=>'12', 'D'=>'13', 'E'=>'14', 'F'=>'15',
295      );
296   my @chars = split m//xms, $car;
297   return $table{$chars[-2]}*16 + $table{$chars[-1]};
298   }
299
300###
301# convertit l'@ mac en decimal
302sub mac_address_hex_to_dec {
303   #00:0F:1F:43:E4:2B
304   my $mac_address = shift;
305
306   my @paquets = split m/ : /xms, $mac_address;
307   my $return = q{};
308   foreach(@paquets) {
309      $return .= q{.} . digit_hex_to_dec($_);
310      }
311   return $return;
312   }
313
314###
315# va rechercher le port et le switch sur lequel est la machine
316sub find_switch_port {
317   my $mac_address     = shift;
318   my $switch_proposal = shift || q{};
319
320   my %ret;
321   $ret{switch_description} = 'unknow';
322   $ret{switch_port} = '0';
323
324   return %ret if $mac_address eq 'unknow';;
325
326   my @switch_search = @SWITCH;
327   if ($switch_proposal ne q{}) {
328      for my $sw (@SWITCH) {
329         next if $sw->{hostname} ne $switch_proposal;
330         unshift @switch_search, $sw;
331         last;
332         }
333      }
334
335   my $research = $OID_NUMBER{searchPort} . mac_address_hex_to_dec($mac_address);
336
337   LOOP_ON_SWITCH:
338   for my $sw (@switch_search) {
339      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
340      print "$error \n" if $error;
341
342      my $result = $session->get_request(
343         -varbindlist => [$research]
344         );
345      if (not defined $result or $result->{$research} eq 'noSuchInstance') {
346         $session->close;
347         next LOOP_ON_SWITCH;
348         }
349
350         my $swport = $result->{$research};
351         $session->close;
352
353         # IMPORTANT !!
354         # ceci empeche la detection sur certains port ...
355         # en effet les switch sont relies entre eux par un cable reseau et du coup
356         # tous les arp de toutes les machines sont presentes sur ces ports (ceux choisis ici sont les miens)
357         # cette partie est a ameliore, voir a configurer dans l'entete
358         # 21->24 45->48
359#         my $flag = 0;
360         SWITCH_PORT_IGNORE:
361         foreach my $p (@{$sw->{portignore}}) {
362            next SWITCH_PORT_IGNORE if $swport ne get_numerical_port($sw->{model},$p);
363#            $flag = 1;
364            next LOOP_ON_SWITCH;
365            }
366#         if ($flag == 0) {
367            $ret{switch_hostname}    = $sw->{hostname};
368            $ret{switch_description} = $sw->{description};
369            $ret{switch_port}        = get_human_readable_port($sw->{model}, $swport); # $swport;
370
371            last LOOP_ON_SWITCH;
372#            }
373#         }
374#      $session->close;
375      }
376   return %ret;
377   }
378
379###
380# va rechercher les port et les switch sur lequel est la machine
381sub find_all_switch_port {
382   my $mac_address = shift;
383
384   my $ret = {};
385
386   return $ret if $mac_address eq 'unknow';
387
388#   for my $sw (@SWITCH) {
389#      next if exists $SWITCH_PORT_COUNT{$sw->{hostname}};
390#
391#      $SWITCH_PORT_COUNT{$sw->{hostname}} = {};
392#      print "DEBUG: SWITCH_PORT_COUNT defined for $sw->{hostname}\n" if $DEBUG xor 2;
393#      }
394
395   my $research = $OID_NUMBER{searchPort} . mac_address_hex_to_dec($mac_address);
396   LOOP_ON_ALL_SWITCH:
397   for my $sw (@SWITCH) {
398      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
399      print "$error \n" if $error;
400
401      my $result = $session->get_request(
402         -varbindlist => [$research]
403         );
404
405      if(defined $result and $result->{$research} ne 'noSuchInstance'){
406         my $swport = $result->{$research};
407
408         $ret->{$sw->{hostname}} = {};
409         $ret->{$sw->{hostname}}{hostname}    = $sw->{hostname};
410         $ret->{$sw->{hostname}}{description} = $sw->{description};
411         $ret->{$sw->{hostname}}{port}        = get_human_readable_port($sw->{model}, $swport);
412
413#         $SWITCH_PORT_COUNT{$sw->{hostname}}->{$swport}++;
414         }
415
416      $session->close;
417      }
418   return $ret;
419   }
420
421sub get_list_network {
422
423   return keys %{$KLASK_CFG->{network}};
424   }
425
426sub get_current_interface {
427   my $vlan_name = shift;
428
429   return $KLASK_CFG->{network}{$vlan_name}{interface};
430   }
431
432###
433# liste l'ensemble des adresses ip d'un réseau
434sub get_list_ip {
435   my @vlan_name = @_;
436
437   my $cidrlist = Net::CIDR::Lite->new;
438
439   for my $net (@vlan_name) {
440      my @line  = @{$KLASK_CFG->{network}{$net}{'ip-subnet'}};
441      for my $cmd (@line) {
442         for my $method (keys %{$cmd}){
443            $cidrlist->add_any($cmd->{$method}) if $method eq 'add';
444            }
445         }
446      }
447
448   my @res = ();
449
450   for my $cidr ($cidrlist->list()) {
451      my $net = new NetAddr::IP $cidr;
452      for my $ip (@{$net}) {
453         $ip =~ s{ /32 }{}xms;
454         push @res,  $ip;
455         }
456      }
457
458   return @res;
459   }
460
461# liste l'ensemble des routeurs du réseau
462sub get_list_main_router {
463   my @vlan_name = @_;
464
465   my @res = ();
466
467   for my $net (@vlan_name) {
468      push @res, $KLASK_CFG->{network}{$net}{'main-router'};
469      }
470
471   return @res;
472   }
473
474sub get_human_readable_port {
475   my $sw_model = shift;
476   my $sw_port  = shift;
477
478   if ($sw_model eq 'HP8000M') {
479
480      my $reste = (($sw_port - 1) % 8) + 1;
481      my $major = int (($sw_port - 1) / 8);
482      return "$INTERNAL_PORT_MAP{$major}$reste";
483      }
484
485   if ($sw_model eq 'HP2424M') {
486      if ($sw_port > 24) {
487         
488         my $reste = $sw_port - 24;
489         return "A$reste";
490         }
491      }
492
493   if ($sw_model eq 'HP1600M') {
494      if ($sw_port > 16) {
495         
496         my $reste = $sw_port - 16;
497         return "A$reste";
498         }
499      }
500
501   return $sw_port;
502   }
503
504sub get_numerical_port {
505   my $sw_model = shift;
506   my $sw_port  = shift;
507
508   if ($sw_model eq 'HP8000M') {
509
510      my $letter = substr $sw_port, 0, 1;
511      my $reste =  substr $sw_port, 1;
512
513      return $INTERNAL_PORT_MAP_REV{$letter} * 8 + $reste;
514      }
515
516   if ($sw_model eq 'HP2424M') {
517      if ($sw_port =~ m/^A/xms ) {
518
519         my $reste =  substr $sw_port, 1;
520
521         return 24 + $reste;
522         }
523      }
524
525   if ($sw_model eq 'HP1600M') {
526      if ($sw_port =~ m/^A/xms ) {
527
528         my $reste =  substr $sw_port, 1;
529
530         return 16 + $reste;
531         }
532      }
533
534   return $sw_port;
535   }
536
537################
538# Les commandes
539################
540
541sub cmd_help {
542
543print <<'END';
544klask - ports manager and finder for switch
545
546 klask updatedb
547 klask exportdb --format [txt|html]
548 klask removedb computer*
549 klask cleandb  --day number_of_day --verbose
550
551 klask updatesw
552 klask exportsw --format [txt|dot]
553
554 klask searchdb computer
555 klask search   computer
556 klask search-mac-on-switch switch mac_addr
557
558 klask ip-free --day number_of_day --format [txt|html] [vlan_name]
559
560 klask enable  switch port
561 klask disable switch port
562 klask status  switch port
563END
564   return;
565   }
566
567sub cmd_version {
568
569print <<'END';
570Klask - ports manager and finder for switch
571Copyright (C) 2005-2012 Gabriel Moreau
572
573END
574   print ' $Rev: 114 $'."\n";
575   print ' $Date: 2012-10-01 15:36:13 +0000 (Mon, 01 Oct 2012) $'."\n";
576   print ' $Id: klask 114 2012-10-01 15:36:13Z g7moreau $'."\n";
577   return;
578   }
579
580sub cmd_search {
581   my @computer = @_;
582
583   init_switch_names();    #nomme les switchs
584   fast_ping(@computer);
585   for my $clientname (@computer) {
586      my %resol_arp = resolve_ip_arp_host($clientname);          #resolution arp
587      my %where     = find_switch_port($resol_arp{mac_address}); #retrouve l'emplacement
588      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"
589         unless $where{switch_description} eq 'unknow' and $resol_arp{hostname_fq} eq 'unknow' and $resol_arp{mac_address} eq 'unknow';
590      }
591   return;
592   }
593
594sub cmd_searchdb {
595   my @computer = @_;
596
597   fast_ping(@computer);
598   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
599
600   LOOP_ON_COMPUTER:
601   for my $clientname (@computer) {
602      my %resol_arp = resolve_ip_arp_host($clientname);      #resolution arp
603      my $ip = $resol_arp{ipv4_address};
604
605      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
606
607      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
608      $year += 1900;
609      $mon++;
610      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
611
612      printf "%-22s %2s %-30s %-15s %-18s %s\n",
613         $computerdb->{$ip}{switch_name},
614         $computerdb->{$ip}{switch_port},
615         $computerdb->{$ip}{hostname_fq},
616         $ip,
617         $computerdb->{$ip}{mac_address},
618         $date;
619      }
620   return;
621   }
622
623sub cmd_updatedb {
624   my @network = @_;
625      @network = get_list_network() if not @network;
626
627   test_switchdb_environnement();
628
629   my $computerdb = {};
630      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
631   my $timestamp = time;
632
633   my %computer_not_detected = ();
634   my $timestamp_last_week = $timestamp - (3600 * 24 * 7);
635
636   my $number_of_computer = get_list_ip(@network); # + 1;
637   my $size_of_database   = keys %{$computerdb};
638      $size_of_database   = 1 if $size_of_database == 0;
639   my $i = 0;
640   my $detected_computer = 0;
641
642   init_switch_names('yes');    #nomme les switchs
643
644   { # Remplis le champs portignore des ports d'inter-connection pour chaque switch
645   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
646   my %db_switch_output_port       = %{$switch_connection->{output_port}};
647   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
648   my %db_switch_chained_port = ();
649   for my $swport (keys %db_switch_connected_on_port) {
650      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
651      $db_switch_chained_port{$sw_connect} .= "$port_connect:";
652      }
653   for my $sw (@SWITCH){
654      push @{$sw->{portignore}}, $db_switch_output_port{$sw->{hostname}}  if exists $db_switch_output_port{$sw->{hostname}};
655      if ( exists $db_switch_chained_port{$sw->{hostname}} ) {
656         chop $db_switch_chained_port{$sw->{hostname}};
657         push @{$sw->{portignore}}, split m/ : /xms, $db_switch_chained_port{$sw->{hostname}};
658         }
659#      print "$sw->{hostname} ++ @{$sw->{portignore}}\n";
660      }
661   }
662
663   my %router_mac_ip = ();
664   DETECT_ALL_ROUTER:
665#   for my $one_router ('194.254.66.254') {
666   for my $one_router ( get_list_main_router(@network) ) {
667      my %resol_arp = resolve_ip_arp_host($one_router);
668      $router_mac_ip{ $resol_arp{mac_address} } = $resol_arp{ipv4_address};
669      }
670
671   ALL_NETWORK:
672   for my $net (@network) {
673
674      my @computer = get_list_ip($net);
675      my $current_interface = get_current_interface($net);
676
677      #fast_ping(@computer);
678
679      LOOP_ON_COMPUTER:
680      for my $one_computer (@computer) {
681         $i++;
682
683         my $total_percent = int (($i*100)/$number_of_computer);
684
685         my $localtime = time - $timestamp;
686         my ($sec,$min) = localtime $localtime;
687
688         my $time_elapse = 0;
689            $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0;
690         my ($sec_elapse,$min_elapse) = localtime $time_elapse;
691
692         printf "\rComputer scanned: %4i/%i (%2i%%)",  $i,                 $number_of_computer, $total_percent;
693         printf ', detected: %4i/%i (%2i%%)', $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
694         printf ' [Time: %02i:%02i / %02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
695         printf ' %-8s %-14s', $current_interface, $one_computer;
696
697         my %resol_arp = resolve_ip_arp_host($one_computer, $current_interface);
698
699         # do not search on router connection (why ?)
700         if ( exists $router_mac_ip{$resol_arp{mac_address}}) {
701            $computer_not_detected{$one_computer} = $current_interface;
702            next LOOP_ON_COMPUTER;
703            }
704
705         # do not search on switch inter-connection
706         if (exists $switch_level{$resol_arp{hostname_fq}}) {
707            $computer_not_detected{$one_computer} = $current_interface;
708            next LOOP_ON_COMPUTER;
709            }
710
711         my $switch_proposal = q{};
712         if (exists $computerdb->{$resol_arp{ipv4_address}} and exists $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}) {
713            $switch_proposal = $computerdb->{$resol_arp{ipv4_address}}{switch_hostname};
714            }
715
716         # do not have a mac address
717         if ($resol_arp{mac_address} eq 'unknow' or (exists $resol_arp{timestamps} and $resol_arp{timestamps} < ($timestamp - 3 * 3600))) {
718            $computer_not_detected{$one_computer} = $current_interface;
719            next LOOP_ON_COMPUTER;
720            }
721
722         my %where = find_switch_port($resol_arp{mac_address},$switch_proposal);
723
724         #192.168.24.156:
725         #  arp: 00:0B:DB:D5:F6:65
726         #  hostname: pcroyon.hmg.priv
727         #  port: 5
728         #  switch: sw-batH-legi:hp2524
729         #  timestamp: 1164355525
730
731         # do not have a mac address
732#         if ($resol_arp{mac_address} eq 'unknow') {
733#            $computer_not_detected{$one_computer} = $current_interface;
734#            next LOOP_ON_COMPUTER;
735#            }
736
737         # detected on a switch
738         if ($where{switch_description} ne 'unknow') {
739            $detected_computer++;
740            $computerdb->{$resol_arp{ipv4_address}} = {
741               hostname_fq        => $resol_arp{hostname_fq},
742               mac_address        => $resol_arp{mac_address},
743               switch_hostname    => $where{switch_hostname},
744               switch_description => $where{switch_description},
745               switch_port        => $where{switch_port},
746               timestamp          => $timestamp,
747               network            => $net,
748               };
749            next LOOP_ON_COMPUTER;
750            }
751
752         # new in the database but where it is ?
753         if (not exists $computerdb->{$resol_arp{ipv4_address}}) {
754            $detected_computer++;
755            $computerdb->{$resol_arp{ipv4_address}} = {
756               hostname_fq        => $resol_arp{hostname_fq},
757               mac_address        => $resol_arp{mac_address},
758               switch_hostname    => $where{switch_hostname},
759               switch_description => $where{switch_description},
760               switch_port        => $where{switch_port},
761               timestamp          => $resol_arp{timestamp},
762               network            => $net,
763               };
764            }
765
766         # mise a jour du nom de la machine si modification dans le dns
767         $computerdb->{$resol_arp{ipv4_address}}{hostname_fq} = $resol_arp{hostname_fq};
768
769         # mise à jour de la date de détection si détection plus récente par arpwatch
770         $computerdb->{$resol_arp{ipv4_address}}{timestamp}   = $resol_arp{timestamp} if exists $resol_arp{timestamp} and $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $resol_arp{timestamp};
771
772         # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine
773#         push @computer_not_detected, $resol_arp{ipv4_address} if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
774         $computer_not_detected{$resol_arp{ipv4_address}} = $current_interface if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
775
776         }
777      }
778
779   # final end of line at the end of the loop
780   printf "\n";
781
782   my $dirdb = $KLASK_DB_FILE;
783      $dirdb =~ s{ / [^/]* $}{}xms;
784   mkdir "$dirdb", 0755 unless -d "$dirdb";
785   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
786
787   for my $one_computer (keys %computer_not_detected) {
788      my $interface = $computer_not_detected{$one_computer};
789      system "arping -c 1 -w 1 -rR -i $interface $one_computer &>/dev/null";
790#      print  "arping -c 1 -w 1 -rR -i $interface $one_computer 2>/dev/null\n";
791      }
792   return;
793   }
794
795sub cmd_removedb {
796   my @computer = @_;
797
798   test_maindb_environnement();
799
800   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
801
802   LOOP_ON_COMPUTER:
803   for my $one_computer (@computer) {
804
805      if ( $one_computer =~ m/^ $RE_IPv4_ADDRESS $/xms
806            and exists $computerdb->{$one_computer} ) {
807         delete $computerdb->{$one_computer};
808         next;
809         }
810
811      my %resol_arp = resolve_ip_arp_host($one_computer);
812
813      delete $computerdb->{$resol_arp{ipv4_address}} if exists $computerdb->{$resol_arp{ipv4_address}};
814      }
815
816   my $dirdb = $KLASK_DB_FILE;
817      $dirdb =~ s{ / [^/]* $}{}xms;
818   mkdir "$dirdb", 0755 unless -d "$dirdb";
819   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
820   return;
821   }
822
823sub cmd_cleandb {
824   my @ARGV  = @_;
825
826   my $days_to_clean = 15;
827   my $verbose;
828   my $database_has_changed;
829
830   my $ret = GetOptions(
831      'day|d=i'   => \$days_to_clean,
832      'verbose|v' => \$verbose,
833      );
834
835   my @vlan_name = get_list_network();
836
837   my $computerdb = {};
838      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
839   my $timestamp = time;
840
841   my $timestamp_barrier = 3600 * 24 * $days_to_clean;
842   my $timestamp_3month  = 3600 * 24 * 90;
843
844   my %mactimedb = ();
845   ALL_VLAN:
846   for my $vlan (shuffle @vlan_name) {
847
848      my @ip_list   = shuffle get_list_ip($vlan);
849     
850      LOOP_ON_IP_ADDRESS:
851      for my $ip (@ip_list) {
852
853         next LOOP_ON_IP_ADDRESS if
854            not exists $computerdb->{$ip};
855           
856            #&& $computerdb->{$ip}{timestamp} > $timestamp_barrier;
857         my $ip_timestamp   = $computerdb->{$ip}{timestamp};
858         my $ip_mac         = $computerdb->{$ip}{mac_address};
859         my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
860
861         $mactimedb{$ip_mac} ||= {
862            ip          => $ip,
863            timestamp   => $ip_timestamp,
864            vlan        => $vlan,
865            hostname_fq => $ip_hostname_fq,
866            };
867         
868         if (
869            ( $mactimedb{$ip_mac}->{timestamp} - $ip_timestamp > $timestamp_barrier
870               or (
871                  $mactimedb{$ip_mac}->{timestamp} > $ip_timestamp
872                  and $timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_3month
873                  )
874            )
875            and (
876               not $mactimedb{$ip_mac}->{hostname_fq} =~ m/^float/
877               or $ip_hostname_fq =~ m/^float/
878               )) {
879            print "remove ip $ip\n" if $verbose;
880            delete $computerdb->{$ip};
881            $database_has_changed++;
882            }
883
884         elsif (
885            ( $ip_timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_barrier
886               or (
887                  $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}
888                  and $timestamp - $ip_timestamp > $timestamp_3month
889                  )
890            )
891            and (
892               not $ip_hostname_fq =~ m/^float/
893               or $mactimedb{$ip_mac}->{hostname_fq} =~ m/^float/
894               )) {
895            print "remove ip ".$mactimedb{$ip_mac}->{ip}."\n" if $verbose;
896            delete $computerdb->{$mactimedb{$ip_mac}->{ip}};
897            $database_has_changed++;
898            }
899
900         if ( $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}) {
901            $mactimedb{$ip_mac} = {
902               ip          => $ip,
903               timestamp   => $ip_timestamp,
904               vlan        => $vlan,
905               hostname_fq => $ip_hostname_fq,
906               };
907            }
908         }
909      }
910
911   if ( $database_has_changed ) {
912      my $dirdb = $KLASK_DB_FILE;
913         $dirdb =~ s{ / [^/]* $}{}xms;
914      mkdir "$dirdb", 0755 unless -d "$dirdb";
915      YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
916      }
917   return;
918   }
919
920sub cmd_exportdb {
921   @ARGV = @_;
922
923   my $format = 'txt';
924
925   my $ret = GetOptions(
926      'format|f=s'  => \$format,
927      );
928
929   my %possible_format = (
930      txt  => \&cmd_exportdb_txt,
931      html => \&cmd_exportdb_html,
932      );
933
934   $format = 'txt' if not defined $possible_format{$format};
935
936   $possible_format{$format}->(@ARGV);
937   return;
938   }
939
940sub cmd_exportdb_txt {
941   test_maindb_environnement();
942
943   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
944
945   printf "%-27s %-4s            %-40s %-15s %-18s %-16s %s\n", qw(Switch Port Hostname-FQ IPv4-Address MAC-Address Date VLAN);
946   print "--------------------------------------------------------------------------------------------------------------------------------------------\n";
947
948   LOOP_ON_IP_ADDRESS:
949   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
950
951#      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow';
952
953      # to be improve in the future
954      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
955
956# dans le futur
957#      next if $computerdb->{$ip}{hostname_fq} eq 'unknow';
958
959      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
960      $year += 1900;
961      $mon++;
962      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
963
964      printf "%-28s  %2s  <-------  %-40s %-15s %-18s %-16s %s\n",
965         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
966         $computerdb->{$ip}{switch_port},
967         $computerdb->{$ip}{hostname_fq},
968         $ip,
969         $computerdb->{$ip}{mac_address},
970         $date,
971         $computerdb->{$ip}{network} || '';
972      }
973   return;
974   }
975
976sub cmd_exportdb_html {
977   test_maindb_environnement();
978
979   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
980
981#<link rel="stylesheet" type="text/css" href="style-klask.css" />
982#<script src="sorttable-klask.js"></script>
983
984   print <<'END_HTML';
985<table class="sortable" summary="Klask Host Database">
986 <caption>Klask Host Database</caption>
987 <thead>
988  <tr>
989   <th scope="col" class="klask-header-left">Switch</th>
990   <th scope="col" class="sorttable_nosort">Port</th>
991   <th scope="col" class="sorttable_nosort">Link</th>
992   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
993   <th scope="col" class="hklask-ipv4">IPv4-Address</th>
994   <th scope="col" class="sorttable_alpha">MAC-Address</th>
995   <th scope="col" class="sorttable_alpha">VLAN</th>
996   <th scope="col" class="klask-header-right">Date</th>
997  </tr>
998 </thead>
999 <tfoot>
1000  <tr>
1001   <th scope="col" class="klask-footer-left">Switch</th>
1002   <th scope="col" class="fklask-port">Port</th>
1003   <th scope="col" class="fklask-link">Link</th>
1004   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1005   <th scope="col" class="fklask-ipv4">IPv4-Address</th>
1006   <th scope="col" class="fklask-mac">MAC-Address</th>
1007   <th scope="col" class="fklask-vlan">VLAN</th>
1008   <th scope="col" class="klask-footer-right">Date</th>
1009  </tr>
1010 </tfoot>
1011 <tbody>
1012END_HTML
1013
1014   my %mac_count = ();
1015   LOOP_ON_IP_ADDRESS:
1016   foreach my $ip (keys %{$computerdb}) {
1017
1018      # to be improve in the future
1019      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1020
1021      $mac_count{$computerdb->{$ip}{mac_address}}++;
1022      }
1023
1024   my $typerow = 'even';
1025
1026   LOOP_ON_IP_ADDRESS:
1027   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1028
1029      # to be improve in the future
1030      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1031
1032      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1033      $year += 1900;
1034      $mon++;
1035      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1036
1037#      $odd_or_even++;
1038#      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
1039      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1040
1041      my $switch_hostname = $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description} || 'unkown';
1042      chomp $switch_hostname;
1043      my $switch_hostname_sort = sprintf '%s %3s' ,$switch_hostname, $computerdb->{$ip}{switch_port};
1044
1045      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1046
1047      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{mac_address}}, $computerdb->{$ip}{mac_address};
1048
1049      $computerdb->{$ip}{hostname_fq} = 'unknow' if $computerdb->{$ip}{hostname_fq} =~ m/^ \d+ \. \d+ \. \d+ \. \d+ $/xms;
1050      my ( $host_short ) = split m/ \. /xms, $computerdb->{$ip}{hostname_fq};
1051
1052      my $vlan = $computerdb->{$ip}{network} || '';
1053
1054      print <<"END_HTML";
1055  <tr class="$typerow">
1056   <td sorttable_customkey="$switch_hostname_sort">$switch_hostname</td>
1057   <td class="bklask-port">$computerdb->{$ip}{switch_port}</td>
1058   <td><-------</td>
1059   <td sorttable_customkey="$host_short">$computerdb->{$ip}{hostname_fq}</td>
1060   <td sorttable_customkey="$ip_sort">$ip</td>
1061   <td sorttable_customkey="$mac_sort">$computerdb->{$ip}{mac_address}</td>
1062   <td>$vlan</td>
1063   <td>$date</td>
1064  </tr>
1065END_HTML
1066      }
1067
1068   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1069
1070   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1071   my %db_switch_parent            = %{$switch_connection->{parent}};
1072   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1073   my %db_switch                   = %{$switch_connection->{switch_db}};
1074
1075   for my $sw (sort keys %db_switch_output_port) {
1076
1077      my $switch_hostname_sort = sprintf '%s %3s' ,$sw, $db_switch_output_port{$sw};
1078
1079      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1080
1081      if (exists $db_switch_parent{$sw}) {
1082
1083      my $mac_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{mac_address};
1084      my $ipv4_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{ipv4_address};
1085      my $timestamp = $db_switch{$db_switch_parent{$sw}->{switch}}->{timestamp};
1086
1087      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1088      $year += 1900;
1089      $mon++;
1090      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1091
1092      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1093
1094      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1095
1096      my ( $host_short ) = sprintf '%s %3s' , split(m/ \. /xms, $db_switch_parent{$sw}->{switch}, 1), $db_switch_parent{$sw}->{port};
1097
1098      print <<"END_HTML";
1099  <tr class="$typerow">
1100   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1101   <td class="bklask-port">$db_switch_output_port{$sw}</>
1102   <td>+--> $db_switch_parent{$sw}->{port}</td>
1103   <td sorttable_customkey="$host_short">$db_switch_parent{$sw}->{switch}</>
1104   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1105   <td sorttable_customkey="$mac_sort">$mac_address</td>
1106   <td></td>
1107   <td>$date</td>
1108  </tr>
1109END_HTML
1110         }
1111      else {
1112         print <<"END_HTML";
1113  <tr class="$typerow">
1114   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1115   <td class="bklask-port">$db_switch_output_port{$sw}</>
1116   <td>+--></td>
1117   <td sorttable_customkey="router">router</>
1118   <td sorttable_customkey="999999999999"></td>
1119   <td sorttable_customkey="99999"></td>
1120   <td></td>
1121   <td></td>
1122  </tr>
1123END_HTML
1124         }
1125      }
1126
1127   for my $swport (sort keys %db_switch_connected_on_port) {
1128      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1129      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1130
1131         my $switch_hostname_sort = sprintf '%s %3s' ,$sw_connect, $port_connect;
1132
1133      my $mac_address = $db_switch{$sw}->{mac_address};
1134      my $ipv4_address = $db_switch{$sw}->{ipv4_address};
1135      my $timestamp = $db_switch{$sw}->{timestamp};
1136
1137      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1138      $year += 1900;
1139      $mon++;
1140      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year,$mon,$mday,$hour,$min;
1141
1142      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1143
1144      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1145
1146      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1147
1148         if (exists $db_switch_output_port{$sw}) {
1149
1150            my ( $host_short ) = sprintf '%s %3s' , split( m/\./xms, $sw, 1), $db_switch_output_port{$sw};
1151
1152            print <<"END_HTML";
1153  <tr class="$typerow">
1154   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1155   <td class="bklask-port">$port_connect</>
1156   <td>&lt;--+ $db_switch_output_port{$sw}</td>
1157   <td sorttable_customkey="$host_short">$sw</>
1158   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1159   <td sorttable_customkey="$mac_sort">$mac_address</td>
1160   <td></td>
1161   <td>$date</td>
1162  </tr>
1163END_HTML
1164            }
1165         else {
1166            print <<"END_HTML";
1167  <tr class="$typerow">
1168   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1169   <td class="bklask-port">$port_connect</>
1170   <td>&lt;--+</td>
1171   <td sorttable_customkey="$sw">$sw</>
1172   <td sorttable_customkey="">$ipv4_address</td>
1173   <td sorttable_customkey="">$mac_address</td>
1174   <td></td>
1175   <td>$date</td>
1176  </tr>
1177END_HTML
1178            }
1179         }
1180      }
1181
1182   print <<'END_HTML';
1183 </tbody>
1184</table>
1185END_HTML
1186   return;
1187   }
1188
1189sub cmd_bad_vlan_config {
1190   test_maindb_environnement();
1191
1192   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1193
1194   my %swithportdb = ();
1195   LOOP_ON_IP_ADDRESS:
1196   foreach my $ip (keys %{$computerdb}) {
1197      # to be improve in the future
1198      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1199
1200      my $ip_timestamp   = $computerdb->{$ip}{timestamp};
1201      my $ip_mac         = $computerdb->{$ip}{mac_address};
1202      my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
1203
1204      my $swpt = sprintf "%-28s  %2s", $computerdb->{$ip}{switch_hostname}, $computerdb->{$ip}{switch_port};
1205      $swithportdb{$swpt} ||= {
1206         ip          => $ip,
1207         timestamp   => $ip_timestamp,
1208         vlan        => $computerdb->{$ip}{network},
1209         hostname_fq => $ip_hostname_fq,
1210         mac_address => $ip_mac,
1211         };
1212     
1213      if (
1214         ($ip_timestamp > $swithportdb{$swpt}->{timestamp})
1215         and (
1216            $ip_hostname_fq !~ m/^float/
1217            or $ip_timestamp > ( $swithportdb{$swpt}->{timestamp} + (15 * 24 * 3600) )
1218            )
1219         ) {
1220         $swithportdb{$swpt} = {
1221            ip          => $ip,
1222            timestamp   => $ip_timestamp,
1223            vlan        => $computerdb->{$ip}{network},
1224            hostname_fq => $ip_hostname_fq,
1225            mac_address => $ip_mac,
1226            };
1227         }
1228      }
1229
1230   foreach my $swpt (keys %swithportdb) {
1231      next if $swpt =~ m/^\s*0$/;
1232      next if $swithportdb{$swpt}->{hostname_fq} !~ m/^float/;
1233     
1234      my $src_ip = $swithportdb{$swpt}->{ip};
1235      my $src_timestamp = 0;
1236      foreach my $ip (keys %{$computerdb}) {
1237         next if $computerdb->{$ip}{mac_address} ne  $swithportdb{$swpt}->{mac_address};
1238         next if $computerdb->{$ip}{hostname_fq} =~ m/^float/;
1239         next if $computerdb->{$ip}{timestamp} < $src_timestamp;
1240         
1241         $src_ip = $ip;
1242         $src_timestamp = $computerdb->{$ip}{timestamp};
1243         }
1244     
1245      next if $src_timestamp == 0;
1246     
1247      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $swithportdb{$swpt}->{timestamp};
1248      $year += 1900;
1249      $mon++;
1250      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1251
1252      ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$src_ip}{timestamp};
1253      $year += 1900;
1254      $mon++;
1255      my $src_date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1256
1257      printf "%s / %-10s +-> %-10s  %s / %s # %s\n",
1258         $swpt, $swithportdb{$swpt}->{vlan}, $computerdb->{$src_ip}{network},
1259         $date,
1260         $src_date,
1261         $computerdb->{$src_ip}{hostname_fq};
1262      }
1263   }
1264
1265sub cmd_ip_location {
1266   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1267
1268   LOOP_ON_IP_ADDRESS:
1269   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1270
1271      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1272
1273      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || q{};
1274      next if $sw_hostname eq 'unknow';
1275
1276      my $sw_location = q{};
1277      for my $sw (@SWITCH) {
1278         next if $sw_hostname ne $sw->{hostname};
1279         $sw_location = $sw->{location};
1280         last;
1281         }
1282
1283      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
1284      }
1285   return;
1286   }
1287
1288sub cmd_ip_free {
1289   @ARGV = @_; # VLAN name with option
1290
1291   my $days_to_dead = 365 * 2;
1292   my $format = 'txt';
1293   my $verbose;
1294
1295   my $ret = GetOptions(
1296      'day|d=i'      => \$days_to_dead,
1297      'format|f=s'   => \$format,
1298      'verbose|v'    => \$verbose,
1299      );
1300
1301   my %possible_format = (
1302      txt  => \&cmd_ip_free_txt,
1303      html => \&cmd_ip_free_html,
1304      none => sub {},
1305      );
1306   $format = 'txt' if not defined $possible_format{$format};
1307
1308   my @vlan_name = @ARGV;
1309   @vlan_name = get_list_network() if not @vlan_name;
1310
1311   my $computerdb = {};
1312      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1313   my $timestamp = time;
1314
1315   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_dead );
1316
1317   my %result_ip = ();
1318
1319   ALL_NETWORK:
1320   for my $vlan (@vlan_name) {
1321
1322      my @ip_list = get_list_ip($vlan);
1323
1324      LOOP_ON_IP_ADDRESS:
1325      for my $ip (@ip_list) {
1326
1327         next LOOP_ON_IP_ADDRESS if
1328            exists $computerdb->{$ip}
1329            && $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1330
1331         my $ip_date_last_detection = '';
1332         if (exists $computerdb->{$ip}) {
1333            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1334            $year += 1900;
1335            $mon++;
1336            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1337            }
1338
1339         $result_ip{$ip} ||= {};
1340         $result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
1341
1342         my $packed_ip = scalar gethostbyname($ip);
1343         my $hostname_fq = 'unknown';
1344            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip;
1345         $result_ip{$ip}->{hostname_fq} = $hostname_fq;
1346
1347         $result_ip{$ip}->{vlan} = $vlan;
1348
1349         printf "VERBOSE_1: %-15s %-12s %s\n", $ip, $vlan, $hostname_fq if $verbose;
1350         }
1351      }
1352
1353   $possible_format{$format}->(%result_ip);
1354   }
1355
1356sub cmd_ip_free_txt {
1357   my %result_ip = @_;
1358   
1359   printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
1360   print "-------------------------------------------------------------------------------\n";
1361   LOOP_ON_IP_ADDRESS:
1362   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1363         printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $result_ip{$ip}->{vlan};
1364      }
1365   }
1366
1367sub cmd_ip_free_html {
1368   my %result_ip = @_;
1369
1370   print <<'END_HTML';
1371<table class="sortable" summary="Klask Free IP Database">
1372 <caption>Klask Free IP Database</caption>
1373 <thead>
1374  <tr>
1375   <th scope="col" class="klask-header-left">IPv4-Address</th>
1376   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1377   <th scope="col" class="sorttable_alpha">VLAN</th>
1378   <th scope="col" class="klask-header-right">Date</th>
1379  </tr>
1380 </thead>
1381 <tfoot>
1382  <tr>
1383   <th scope="col" class="klask-footer-left">IPv4-Address</th>
1384   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1385   <th scope="col" class="fklask-vlan">VLAN</th>
1386   <th scope="col" class="klask-footer-right">Date</th>
1387  </tr>
1388 </tfoot>
1389 <tbody>
1390END_HTML
1391
1392   my $typerow = 'even';
1393
1394   LOOP_ON_IP_ADDRESS:
1395   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1396
1397      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1398
1399      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1400      my ( $host_short ) = split m/ \. /xms, $result_ip{$ip}->{hostname_fq};
1401
1402      print <<"END_HTML";
1403  <tr class="$typerow">
1404   <td sorttable_customkey="$ip_sort">$ip</td>
1405   <td sorttable_customkey="$host_short">$result_ip{$ip}->{hostname_fq}</td>
1406   <td>$result_ip{$ip}->{vlan}</td>
1407   <td>$result_ip{$ip}->{date_last_detection}</td>
1408  </tr>
1409END_HTML
1410      }
1411   print <<'END_HTML';
1412 </tbody>
1413</table>
1414END_HTML
1415   }
1416
1417sub cmd_enable {
1418   my $switch = shift;
1419   my $port   = shift;
1420
1421   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
1422   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
1423   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
1424   return;
1425   }
1426
1427sub cmd_disable {
1428   my $switch = shift;
1429   my $port   = shift;
1430
1431   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
1432   return;
1433   }
1434
1435sub cmd_status {
1436   my $switch = shift;
1437   my $port   = shift;
1438
1439   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
1440   return;
1441   }
1442
1443sub cmd_search_mac_on_switch {
1444   my $switch_name = shift || q{};
1445   my $mac_address = shift || q{};
1446
1447   if ($switch_name eq q{} or $mac_address eq q{}) {
1448      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
1449      }
1450
1451   $switch_name = join(',', map {$_->{hostname}} @SWITCH ) if $switch_name eq q{*};
1452
1453   for my $sw_name (split /,/, $switch_name) {
1454      if (not defined $SWITCH_DB{$sw_name}) {
1455         die "Switch $sw_name must be defined in klask configuration file\n";
1456         }
1457
1458      my $sw = $SWITCH_DB{$sw_name};
1459      my %session = ( -hostname => $sw->{hostname} );
1460         $session{-version} = $sw->{version}   || 1;
1461         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1462      if (exists $sw->{version} and $sw->{version} eq '3') {
1463         $session{-username} = $sw->{username} || 'snmpadmin';
1464         }
1465      else {
1466         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1467         }
1468
1469      my $research = $OID_NUMBER{searchPort} . mac_address_hex_to_dec($mac_address);
1470      print "Klask search OID $research on switch $sw_name\n";
1471
1472      my ($session, $error) = Net::SNMP->session( %session );
1473      print "$error \n" if $error;
1474
1475      my $result = $session->get_request(
1476         -varbindlist => [$research]
1477         );
1478
1479      if (not defined $result or $result->{$research} eq 'noSuchInstance') {
1480         print "Klask do not find MAC $mac_address on switch $sw_name\n";
1481         $session->close;
1482         }
1483
1484      my $swport = $result->{$research};
1485      $session->close;
1486
1487      print "Klask find MAC $mac_address on switch $sw_name port $swport\n";
1488      }
1489   return;
1490   }
1491
1492sub cmd_updatesw {
1493   @ARGV = @_;
1494
1495   my $verbose;
1496
1497   my $ret = GetOptions(
1498      'verbose|v' => \$verbose,
1499      );
1500
1501   init_switch_names('yes');    #nomme les switchs
1502   print "\n";
1503
1504   my %where = ();
1505   my %db_switch_output_port = ();
1506   my %db_switch_ip_hostnamefq = ();
1507
1508   DETECT_ALL_ROUTER:
1509#   for my $one_computer ('194.254.66.254') {
1510   for my $one_router ( get_list_main_router(get_list_network()) ) {
1511      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
1512
1513      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
1514      print "VERBOSE_1: Router detected $resol_arp{ipv4_address} - $resol_arp{mac_address}\n" if $verbose;
1515
1516      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # retrouve les emplacements des routeurs
1517      }
1518
1519   ALL_ROUTER_IP_ADDRESS:
1520   for my $ip_router (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
1521
1522      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip_router}; # /a priori/ idiot car ne sers à rien...
1523
1524      ALL_SWITCH_CONNECTED:
1525      for my $switch_detected ( keys %{$where{$ip_router}} ) {
1526
1527         my $switch = $where{$ip_router}->{$switch_detected};
1528
1529         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
1530
1531         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
1532         print "VERBOSE_2: output port $switch->{hostname} : $switch->{port}\n" if $verbose;
1533         }
1534      }
1535
1536   my %db_switch_link_with = ();
1537
1538   my @list_all_switch = ();
1539   my @list_switch_ipv4 = ();
1540   for my $sw (@SWITCH){
1541      push @list_all_switch, $sw->{hostname};
1542      }
1543
1544   my $timestamp = time;
1545
1546   ALL_SWITCH:
1547   for my $one_computer (@list_all_switch) {
1548      my %resol_arp = resolve_ip_arp_host($one_computer, q{*}, q{low}); # arp resolution
1549      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
1550
1551      push @list_switch_ipv4, $resol_arp{ipv4_address};
1552
1553      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # find port on all switch
1554
1555      if ($verbose) {
1556         print "VERBOSE_3: $one_computer $resol_arp{ipv4_address} $resol_arp{mac_address}\n";
1557         print "VERBOSE_3: $one_computer --- ",
1558            join(' + ', keys %{$where{$resol_arp{ipv4_address}}}),
1559            "\n";
1560         }
1561
1562      $db_switch_ip_hostnamefq{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
1563      print "VERBOSE_4: db_switch_ip_hostnamefq $resol_arp{ipv4_address} -> $resol_arp{hostname_fq}\n" if $verbose;
1564
1565      $SWITCH_DB{$one_computer}->{ipv4_address} = $resol_arp{ipv4_address};
1566      $SWITCH_DB{$one_computer}->{mac_address}  = $resol_arp{mac_address};
1567      $SWITCH_DB{$one_computer}->{timestamp}    = $timestamp;
1568      }
1569
1570   ALL_SWITCH_IP_ADDRESS:
1571   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
1572
1573      print "VERBOSE_5: loop on $db_switch_ip_hostnamefq{$ip}\n" if $verbose;
1574
1575      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
1576#      next ALL_SWITCH_IP_ADDRESS if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostnamefq{$ip} };
1577
1578      DETECTED_SWITCH:
1579      for my $switch_detected ( keys %{$where{$ip}} ) {
1580
1581         my $switch = $where{$ip}->{$switch_detected};
1582         print "VERBOSE_6: $db_switch_ip_hostnamefq{$ip} -> $switch->{hostname} : $switch->{port}\n" if $verbose;
1583
1584         next if $switch->{port}     eq '0';
1585         next if $switch->{port}     eq $db_switch_output_port{$switch->{hostname}};
1586         next if $switch->{hostname} eq $db_switch_ip_hostnamefq{$ip}; # $computerdb->{$ip}{hostname};
1587
1588         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} } ||= {};
1589         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} }->{ $switch->{hostname} } = $switch->{port};
1590         print "VERBOSE_7: +++++\n" if $verbose;
1591         }
1592
1593      }
1594
1595   my %db_switch_connected_on_port = ();
1596   my $maybe_more_than_one_switch_connected = 'yes';
1597
1598   while ($maybe_more_than_one_switch_connected eq 'yes') {
1599      for my $sw (keys %db_switch_link_with) {
1600         for my $connect (keys %{$db_switch_link_with{$sw}}) {
1601
1602            my $port = $db_switch_link_with{$sw}->{$connect};
1603
1604            $db_switch_connected_on_port{"$connect:$port"} ||= {};
1605            $db_switch_connected_on_port{"$connect:$port"}->{$sw}++; # Just to define the key
1606            }
1607         }
1608
1609      $maybe_more_than_one_switch_connected  = 'no';
1610
1611      SWITCH_AND_PORT:
1612      for my $swport (keys %db_switch_connected_on_port) {
1613
1614         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
1615
1616         $maybe_more_than_one_switch_connected = 'yes';
1617
1618         my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1619         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
1620
1621         CONNECTED:
1622         for my $sw_connected (@sw_on_same_port) {
1623
1624            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
1625
1626            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
1627
1628            for my $other_sw (@sw_on_same_port) {
1629               next if $other_sw eq $sw_connected;
1630
1631               delete $db_switch_link_with{$other_sw}->{$sw_connect};
1632               }
1633
1634            # We can not do better for this switch for this loop
1635            next SWITCH_AND_PORT;
1636            }
1637         }
1638      }
1639
1640   my %db_switch_parent =();
1641
1642   for my $sw (keys %db_switch_link_with) {
1643      for my $connect (keys %{$db_switch_link_with{$sw}}) {
1644
1645         my $port = $db_switch_link_with{$sw}->{$connect};
1646
1647         $db_switch_connected_on_port{"$connect:$port"} ||= {};
1648         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
1649
1650         $db_switch_parent{$sw} = {switch => $connect, port => $port};
1651         }
1652      }
1653
1654   print "Switch output port and parent port connection\n";
1655   print "---------------------------------------------\n";
1656   for my $sw (sort keys %db_switch_output_port) {
1657      if (exists $db_switch_parent{$sw}) {
1658         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1659         }
1660      else {
1661         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1662         }
1663      }
1664   print "\n";
1665
1666   print "Switch parent and children port inter-connection\n";
1667   print "------------------------------------------------\n";
1668   for my $swport (sort keys %db_switch_connected_on_port) {
1669      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1670      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1671         if (exists $db_switch_output_port{$sw}) {
1672            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1673            }
1674         else {
1675            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1676            }
1677         }
1678      }
1679
1680   my $switch_connection = {
1681      output_port       => \%db_switch_output_port,
1682      parent            => \%db_switch_parent,
1683      connected_on_port => \%db_switch_connected_on_port,
1684      link_with         => \%db_switch_link_with,
1685      switch_db         => \%SWITCH_DB,
1686      };
1687
1688   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
1689   return;
1690   }
1691
1692sub cmd_exportsw {
1693   @ARGV = @_;
1694
1695   test_switchdb_environnement();
1696
1697   my $format = 'txt';
1698
1699   my $ret = GetOptions(
1700      'format|f=s'  => \$format,
1701      );
1702
1703   my %possible_format = (
1704      txt => \&cmd_exportsw_txt,
1705      dot => \&cmd_exportsw_dot,
1706      );
1707
1708   $format = 'txt' if not defined $possible_format{$format};
1709
1710   $possible_format{$format}->(@ARGV);
1711   return;
1712   }
1713
1714sub cmd_exportsw_txt {
1715
1716   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1717
1718   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1719   my %db_switch_parent            = %{$switch_connection->{parent}};
1720   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1721
1722   print "Switch output port and parent port connection\n";
1723   print "---------------------------------------------\n";
1724   for my $sw (sort keys %db_switch_output_port) {
1725      if (exists $db_switch_parent{$sw}) {
1726         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1727         }
1728      else {
1729         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1730         }
1731      }
1732   print "\n";
1733
1734   print "Switch parent and children port inter-connection\n";
1735   print "------------------------------------------------\n";
1736   for my $swport (sort keys %db_switch_connected_on_port) {
1737      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1738      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1739         if (exists $db_switch_output_port{$sw}) {
1740            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1741            }
1742         else {
1743            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1744            }
1745         }
1746      }
1747   return;
1748   }
1749
1750sub cmd_exportsw_dot {
1751
1752   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1753
1754   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1755   my %db_switch_parent            = %{$switch_connection->{parent}};
1756   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1757   my %db_switch_link_with         = %{$switch_connection->{link_with}};
1758   my %db_switch_global            = %{$switch_connection->{switch_db}};
1759
1760   my %db_building= ();
1761   for my $sw (@SWITCH) {
1762      my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
1763      $db_building{$building} ||= {};
1764      $db_building{$building}->{$location} ||= {};
1765      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
1766      }
1767
1768
1769   print "digraph G {\n";
1770
1771   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
1772   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
1773
1774   my $b=0;
1775   for my $building (keys %db_building) {
1776      $b++;
1777
1778      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
1779      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
1780
1781      my $l = 0;
1782      for my $loc (keys %{$db_building{$building}}) {
1783         $l++;
1784
1785         print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
1786#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
1787         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
1788
1789         for my $sw (keys %{$db_building{$building}->{$loc}}) {
1790
1791            print "\"$sw:$db_switch_output_port{$sw}\" [label = $db_switch_output_port{$sw}, color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
1792
1793            my $swname  = $sw;
1794               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
1795            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
1796            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
1797            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
1798
1799
1800            for my $swport (keys %db_switch_connected_on_port) {
1801               my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1802               next if not $sw_connect eq $sw;
1803               next if $port_connect eq $db_switch_output_port{$sw};
1804               print "\"$sw:$port_connect\" [label = $port_connect, color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
1805               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
1806              }
1807            }
1808         }
1809      }
1810
1811#   print "Switch output port and parent port connection\n";
1812#   print "---------------------------------------------\n";
1813   for my $sw (sort keys %db_switch_output_port) {
1814      if (exists $db_switch_parent{$sw}) {
1815#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
1816         }
1817      else {
1818         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
1819         }
1820      }
1821   print "\n";
1822
1823#   print "Switch parent and children port inter-connection\n";
1824#   print "------------------------------------------------\n";
1825   for my $swport (sort keys %db_switch_connected_on_port) {
1826      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1827      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1828         if (exists $db_switch_output_port{$sw}) {
1829            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
1830            }
1831         else {
1832            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
1833            }
1834         }
1835      }
1836
1837print "}\n";
1838   return;
1839   }
1840
1841
1842__END__
1843
1844=head1 NAME
1845
1846klask - ports manager and finder for switch
1847
1848
1849=head1 USAGE
1850
1851 klask updatedb
1852 klask exportdb --format [txt|html]
1853 klask removedb computer*
1854 klask cleandb  --day number_of_day --verbose
1855
1856 klask updatesw
1857 klask exportsw --format [txt|dot]
1858
1859 klask searchdb computer
1860 klask search   computer
1861 klask search-mac-on-switch switch mac_addr
1862
1863 klask ip-free --day number_of_day --format [txt|html] [vlan_name]
1864
1865 klask enable  switch port
1866 klask disable swith port
1867 klask status  swith port
1868
1869
1870=head1 DESCRIPTION
1871
1872klask is a small tool to find where is a host in a big network. klask mean search in brittany.
1873
1874Klask has now a web site dedicated for it !
1875
1876 http://servforge.legi.grenoble-inp.fr/projects/klask
1877
1878
1879=head1 COMMANDS
1880
1881
1882=head2 search
1883
1884This 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.
1885
1886
1887=head2 enable
1888
1889This command activate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1890
1891
1892=head2 disable
1893
1894This command deactivate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1895
1896
1897=head2 status
1898
1899This 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.
1900
1901
1902=head2 updatedb
1903
1904This command will scan networks and update a database. To know which are the cmputer scan, you have to configure the file /etc/klask/klask.conf This file is easy to read and write because klask use YAML format and not XML.
1905
1906
1907=head2 exportdb
1908
1909This 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...
1910
1911
1912=head2 updatesw
1913
1914This command build a map of your manageable switch on your network. The list of the switch must be given in the file /etc/klask/klask.conf.
1915
1916
1917=head2 exportsw --format [txt|dot]
1918
1919This 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.
1920
1921 klask exportsw --format dot > /tmp/map.dot
1922 dot -Tpng /tmp/map.dot > /tmp/map.png
1923
1924
1925
1926=head1 CONFIGURATION
1927
1928Because klask need many parameters, it's not possible actually to use command line parameters. The configuration is done in a /etc/klask/klask.conf YAML file. This format have many advantage over XML, it's easier to read and to write !
1929
1930Here an example, be aware with indent, it's important in YAML, do not use tabulation !
1931
1932 default:
1933   community: public
1934   snmpport: 161
1935
1936 network:
1937   labnet:
1938     ip-subnet:
1939       - add: 192.168.1.0/24
1940       - add: 192.168.2.0/24
1941     interface: eth0
1942     main-router: gw1.labnet.local
1943
1944   schoolnet:
1945     ip-subnet:
1946       - add: 192.168.6.0/24
1947       - add: 192.168.7.0/24
1948     interface: eth0.38
1949     main-router: gw2.schoolnet.local
1950
1951 switch:
1952   - hostname: sw1.klask.local
1953     portignore:
1954       - 1
1955       - 2
1956
1957   - hostname: sw2.klask.local
1958     location: BatK / 2 / K203
1959     type: HP2424
1960     portignore:
1961       - 1
1962       - 2
1963
1964I 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.
1965
1966
1967=head1 FILES
1968
1969 /etc/klask/klask.conf
1970 /var/lib/klask/klaskdb
1971 /var/lib/klask/switchdb
1972
1973=head1 SEE ALSO
1974
1975Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
1976
1977
1978=head1 VERSION
1979
1980$Id: klask 114 2012-10-01 15:36:13Z g7moreau $
1981
1982
1983=head1 AUTHOR
1984
1985Written by Gabriel Moreau, Grenoble - France
1986
1987
1988=head1 LICENSE AND COPYRIGHT
1989
1990GPL version 2 or later and Perl equivalent
1991
1992Copyright (C) 2005-2012 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.