source: trunk/klask @ 107

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