source: trunk/klask @ 118

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