source: trunk/klask @ 110

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