source: trunk/klask @ 77

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