source: trunk/klask @ 105

Last change on this file since 105 was 105, checked in by g7moreau, 12 years ago
  • Clean Float between VLAN
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 60.8 KB
Line 
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005-2008 Gabriel Moreau.
4#
5# $Id: klask 105 2011-12-12 13:19:48Z 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: 105 $'."\n";
578   print ' $Date: 2011-12-12 13:19:48 +0000 (Mon, 12 Dec 2011) $'."\n";
579   print ' $Id: klask 105 2011-12-12 13:19:48Z 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
846   my %mactimedb = ();
847   ALL_VLAN:
848   for my $vlan (@vlan_name) {
849
850      my @ip_list   = get_list_ip($vlan);
851     
852      LOOP_ON_IP_ADDRESS:
853      for my $ip (@ip_list) {
854
855         next LOOP_ON_IP_ADDRESS if
856            not exists $computerdb->{$ip};
857           
858            #&& $computerdb->{$ip}{timestamp} > $timestamp_barrier;
859         my $ip_timestamp   = $computerdb->{$ip}{timestamp};
860         my $ip_mac         = $computerdb->{$ip}{mac_address};
861         my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
862
863         $mactimedb{$ip_mac} ||= {
864            ip          => $ip,
865            timestamp   => $ip_timestamp,
866            vlan        => $vlan,
867            hostname_fq => $ip_hostname_fq,
868            };
869         
870         if ( $mactimedb{$ip_mac}->{timestamp} - $ip_timestamp > $timestamp_barrier
871            and (
872               not $mactimedb{$ip_mac}->{hostname_fq} =~ m/^float/
873               or $ip_hostname_fq =~ m/^float/
874               )) {
875            print "remove ip $ip\n" if $verbose;
876            delete $computerdb->{$ip};
877            $database_has_changed++;
878            }
879
880         if ( $ip_timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_barrier
881            and (
882               not $ip_hostname_fq =~ m/^float/
883               or $mactimedb{$ip_mac}->{hostname_fq} =~ m/^float/
884               )) {
885            print "remove ip ".$mactimedb{$ip_mac}->{ip}."\n" if $verbose;
886            delete $computerdb->{$mactimedb{$ip_mac}->{ip}};
887            $database_has_changed++;
888            }
889
890         if ( $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}) {
891            $mactimedb{$ip_mac} = {
892               ip          => $ip,
893               timestamp   => $ip_timestamp,
894               vlan        => $vlan,
895               hostname_fq => $ip_hostname_fq,
896               };
897            }
898         }
899      }
900
901   if ( $database_has_changed ) {
902      my $dirdb = $KLASK_DB_FILE;
903         $dirdb =~ s{ / [^/]* $}{}xms;
904      mkdir "$dirdb", 0755 unless -d "$dirdb";
905      YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
906      }
907   return;
908   }
909
910sub cmd_exportdb {
911   my @ARGV   = @_;
912
913   my $format = 'txt';
914
915   my $ret = GetOptions(
916      'format|f=s'  => \$format,
917      );
918
919   my %possible_format = (
920      txt  => \&cmd_exportdb_txt,
921      html => \&cmd_exportdb_html,
922      );
923
924   $format = 'txt' if not defined $possible_format{$format};
925
926   $possible_format{$format}->(@ARGV);
927   return;
928   }
929
930sub cmd_exportdb_txt {
931   test_maindb_environnement();
932
933   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
934
935   printf "%-27s %-4s            %-40s %-15s %-18s %-16s %s\n", qw(Switch Port Hostname-FQ IPv4-Address MAC-Address Date VLAN);
936   print "--------------------------------------------------------------------------------------------------------------------------------------------\n";
937
938   LOOP_ON_IP_ADDRESS:
939   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
940
941#      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow';
942
943      # to be improve in the future
944      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
945
946# dans le futur
947#      next if $computerdb->{$ip}{hostname_fq} eq 'unknow';
948
949      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
950      $year += 1900;
951      $mon++;
952      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
953
954      printf "%-28s  %2s  <-------  %-40s %-15s %-18s %-16s %s\n",
955         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
956         $computerdb->{$ip}{switch_port},
957         $computerdb->{$ip}{hostname_fq},
958         $ip,
959         $computerdb->{$ip}{mac_address},
960         $date,
961         $computerdb->{$ip}{network} || '';
962      }
963   return;
964   }
965
966sub cmd_exportdb_html {
967   test_maindb_environnement();
968
969   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
970
971#<link rel="stylesheet" type="text/css" href="style-klask.css" />
972#<script src="sorttable-klask.js"></script>
973
974   print <<'END_HTML';
975<table class="sortable" summary="Klask Host Database">
976 <caption>Klask Host Database</caption>
977 <thead>
978  <tr>
979   <th scope="col" class="klask-header-left">Switch</th>
980   <th scope="col" class="sorttable_nosort">Port</th>
981   <th scope="col" class="sorttable_nosort">Link</th>
982   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
983   <th scope="col" class="hklask-ipv4">IPv4-Address</th>
984   <th scope="col" class="sorttable_alpha">MAC-Address</th>
985   <th scope="col" class="sorttable_alpha">VLAN</th>
986   <th scope="col" class="klask-header-right">Date</th>
987  </tr>
988 </thead>
989 <tfoot>
990  <tr>
991   <th scope="col" class="klask-footer-left">Switch</th>
992   <th scope="col" class="fklask-port">Port</th>
993   <th scope="col" class="fklask-link">Link</th>
994   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
995   <th scope="col" class="fklask-ipv4">IPv4-Address</th>
996   <th scope="col" class="fklask-mac">MAC-Address</th>
997   <th scope="col" class="fklask-vlan">VLAN</th>
998   <th scope="col" class="klask-footer-right">Date</th>
999  </tr>
1000 </tfoot>
1001 <tbody>
1002END_HTML
1003
1004   my %mac_count = ();
1005   LOOP_ON_IP_ADDRESS:
1006   foreach my $ip (keys %{$computerdb}) {
1007
1008      # to be improve in the future
1009      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1010
1011      $mac_count{$computerdb->{$ip}{mac_address}}++;
1012      }
1013
1014   my $typerow = 'even';
1015
1016   LOOP_ON_IP_ADDRESS:
1017   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1018
1019      # to be improve in the future
1020      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1021
1022      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1023      $year += 1900;
1024      $mon++;
1025      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1026
1027#      $odd_or_even++;
1028#      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
1029      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1030
1031      my $switch_hostname = $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description} || 'unkown';
1032      chomp $switch_hostname;
1033      my $switch_hostname_sort = sprintf '%s %3s' ,$switch_hostname, $computerdb->{$ip}{switch_port};
1034
1035      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1036
1037      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{mac_address}}, $computerdb->{$ip}{mac_address};
1038
1039      $computerdb->{$ip}{hostname_fq} = 'unknow' if $computerdb->{$ip}{hostname_fq} =~ m/^ \d+ \. \d+ \. \d+ \. \d+ $/xms;
1040      my ( $host_short ) = split m/ \. /xms, $computerdb->{$ip}{hostname_fq};
1041
1042      my $vlan = $computerdb->{$ip}{network} || '';
1043
1044      print <<"END_HTML";
1045  <tr class="$typerow">
1046   <td sorttable_customkey="$switch_hostname_sort">$switch_hostname</td>
1047   <td class="bklask-port">$computerdb->{$ip}{switch_port}</td>
1048   <td><-------</td>
1049   <td sorttable_customkey="$host_short">$computerdb->{$ip}{hostname_fq}</td>
1050   <td sorttable_customkey="$ip_sort">$ip</td>
1051   <td sorttable_customkey="$mac_sort">$computerdb->{$ip}{mac_address}</td>
1052   <td>$vlan</td>
1053   <td>$date</td>
1054  </tr>
1055END_HTML
1056      }
1057
1058   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1059
1060   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1061   my %db_switch_parent            = %{$switch_connection->{parent}};
1062   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1063   my %db_switch                   = %{$switch_connection->{switch_db}};
1064
1065   for my $sw (sort keys %db_switch_output_port) {
1066
1067      my $switch_hostname_sort = sprintf '%s %3s' ,$sw, $db_switch_output_port{$sw};
1068
1069      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1070
1071      if (exists $db_switch_parent{$sw}) {
1072
1073      my $mac_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{mac_address};
1074      my $ipv4_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{ipv4_address};
1075      my $timestamp = $db_switch{$db_switch_parent{$sw}->{switch}}->{timestamp};
1076
1077      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1078      $year += 1900;
1079      $mon++;
1080      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1081
1082      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1083
1084      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1085
1086      my ( $host_short ) = sprintf '%s %3s' , split(m/ \. /xms, $db_switch_parent{$sw}->{switch}, 1), $db_switch_parent{$sw}->{port};
1087
1088      print <<"END_HTML";
1089  <tr class="$typerow">
1090   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1091   <td class="bklask-port">$db_switch_output_port{$sw}</>
1092   <td>+--> $db_switch_parent{$sw}->{port}</td>
1093   <td sorttable_customkey="$host_short">$db_switch_parent{$sw}->{switch}</>
1094   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1095   <td sorttable_customkey="$mac_sort">$mac_address</td>
1096   <td></td>
1097   <td>$date</td>
1098  </tr>
1099END_HTML
1100         }
1101      else {
1102         print <<"END_HTML";
1103  <tr class="$typerow">
1104   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1105   <td class="bklask-port">$db_switch_output_port{$sw}</>
1106   <td>+--></td>
1107   <td sorttable_customkey="router">router</>
1108   <td sorttable_customkey="999999999999"></td>
1109   <td sorttable_customkey="99999"></td>
1110   <td></td>
1111   <td></td>
1112  </tr>
1113END_HTML
1114         }
1115      }
1116
1117   for my $swport (sort keys %db_switch_connected_on_port) {
1118      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1119      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1120
1121         my $switch_hostname_sort = sprintf '%s %3s' ,$sw_connect, $port_connect;
1122
1123      my $mac_address = $db_switch{$sw}->{mac_address};
1124      my $ipv4_address = $db_switch{$sw}->{ipv4_address};
1125      my $timestamp = $db_switch{$sw}->{timestamp};
1126
1127      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1128      $year += 1900;
1129      $mon++;
1130      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year,$mon,$mday,$hour,$min;
1131
1132      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1133
1134      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1135
1136      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1137
1138         if (exists $db_switch_output_port{$sw}) {
1139
1140            my ( $host_short ) = sprintf '%s %3s' , split( m/\./xms, $sw, 1), $db_switch_output_port{$sw};
1141
1142            print <<"END_HTML";
1143  <tr class="$typerow">
1144   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1145   <td class="bklask-port">$port_connect</>
1146   <td>&lt;--+ $db_switch_output_port{$sw}</td>
1147   <td sorttable_customkey="$host_short">$sw</>
1148   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1149   <td sorttable_customkey="$mac_sort">$mac_address</td>
1150   <td></td>
1151   <td>$date</td>
1152  </tr>
1153END_HTML
1154            }
1155         else {
1156            print <<"END_HTML";
1157  <tr class="$typerow">
1158   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1159   <td class="bklask-port">$port_connect</>
1160   <td>&lt;--+</td>
1161   <td sorttable_customkey="$sw">$sw</>
1162   <td sorttable_customkey="">$ipv4_address</td>
1163   <td sorttable_customkey="">$mac_address</td>
1164   <td></td>
1165   <td>$date</td>
1166  </tr>
1167END_HTML
1168            }
1169         }
1170      }
1171
1172   print <<'END_HTML';
1173 </tbody>
1174</table>
1175END_HTML
1176   return;
1177   }
1178
1179sub cmd_iplocation {
1180   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1181
1182   LOOP_ON_IP_ADDRESS:
1183   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1184
1185      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1186
1187      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || q{};
1188      next if $sw_hostname eq 'unknow';
1189
1190      my $sw_location = q{};
1191      for my $sw (@SWITCH) {
1192         next if $sw_hostname ne $sw->{hostname};
1193         $sw_location = $sw->{location};
1194         last;
1195         }
1196
1197      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
1198      }
1199   return;
1200   }
1201
1202sub cmd_ip_free {
1203   my @vlan_name = @_;
1204
1205   my $days_to_dead = 365 * 2;
1206   my $format = 'txt';
1207   my $verbose;
1208
1209   my $ret = GetOptionsFromArray(\@vlan_name,
1210      'day|d=i'      => \$days_to_dead,
1211      'format|f=s'   => \$format,
1212      'verbose|v'    => \$verbose,
1213      );
1214
1215   my %possible_format = (
1216      txt  => \&cmd_ip_free_txt,
1217      html => \&cmd_ip_free_html,
1218      none => sub {},
1219      );
1220   $format = 'txt' if not defined $possible_format{$format};
1221
1222   @vlan_name = get_list_network() if not @vlan_name;
1223
1224   my $computerdb = {};
1225      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1226   my $timestamp = time;
1227
1228   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_dead );
1229
1230   my %result_ip = ();
1231
1232   ALL_NETWORK:
1233   for my $vlan (@vlan_name) {
1234
1235      my @ip_list = get_list_ip($vlan);
1236
1237      LOOP_ON_IP_ADDRESS:
1238      for my $ip (@ip_list) {
1239
1240         next LOOP_ON_IP_ADDRESS if
1241            exists $computerdb->{$ip}
1242            && $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1243
1244         my $ip_date_last_detection = '';
1245         if (exists $computerdb->{$ip}) {
1246            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1247            $year += 1900;
1248            $mon++;
1249            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1250            }
1251
1252         $result_ip{$ip} ||= {};
1253         $result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
1254
1255         my $packed_ip = scalar gethostbyname($ip);
1256         my $hostname_fq = 'unknown';
1257            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip;
1258         $result_ip{$ip}->{hostname_fq} = $hostname_fq;
1259
1260         $result_ip{$ip}->{vlan} = $vlan;
1261
1262         printf "VERBOSE_1: %-15s %-12s %s\n", $ip, $vlan, $hostname_fq if $verbose;
1263         }
1264      }
1265
1266   $possible_format{$format}->(%result_ip);
1267   }
1268
1269sub cmd_ip_free_txt {
1270   my %result_ip = @_;
1271   
1272   printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
1273   print "-------------------------------------------------------------------------------\n";
1274   LOOP_ON_IP_ADDRESS:
1275   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1276         printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $result_ip{$ip}->{vlan};
1277      }
1278   }
1279
1280sub cmd_ip_free_html {
1281   my %result_ip = @_;
1282
1283   print <<'END_HTML';
1284<table class="sortable" summary="Klask Free IP Database">
1285 <caption>Klask Free IP Database</caption>
1286 <thead>
1287  <tr>
1288   <th scope="col" class="klask-header-left">IPv4-Address</th>
1289   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1290   <th scope="col" class="sorttable_alpha">VLAN</th>
1291   <th scope="col" class="klask-header-right">Date</th>
1292  </tr>
1293 </thead>
1294 <tfoot>
1295  <tr>
1296   <th scope="col" class="klask-footer-left">IPv4-Address</th>
1297   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1298   <th scope="col" class="fklask-vlan">VLAN</th>
1299   <th scope="col" class="klask-footer-right">Date</th>
1300  </tr>
1301 </tfoot>
1302 <tbody>
1303END_HTML
1304
1305   my $typerow = 'even';
1306
1307   LOOP_ON_IP_ADDRESS:
1308   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1309
1310      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1311
1312      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1313      my ( $host_short ) = split m/ \. /xms, $result_ip{$ip}->{hostname_fq};
1314
1315      print <<"END_HTML";
1316  <tr class="$typerow">
1317   <td sorttable_customkey="$ip_sort">$ip</td>
1318   <td sorttable_customkey="$host_short">$result_ip{$ip}->{hostname_fq}</td>
1319   <td>$result_ip{$ip}->{vlan}</td>
1320   <td>$result_ip{$ip}->{date_last_detection}</td>
1321  </tr>
1322END_HTML
1323      }
1324   print <<'END_HTML';
1325 </tbody>
1326</table>
1327END_HTML
1328   }
1329
1330sub cmd_enable {
1331   my $switch = shift;
1332   my $port   = shift;
1333
1334   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
1335   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
1336   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
1337   return;
1338   }
1339
1340sub cmd_disable {
1341   my $switch = shift;
1342   my $port   = shift;
1343
1344   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
1345   return;
1346   }
1347
1348sub cmd_status {
1349   my $switch = shift;
1350   my $port   = shift;
1351
1352   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
1353   return;
1354   }
1355
1356sub cmd_search_mac_on_switch {
1357   my $switch_name = shift || q{};
1358   my $mac_address = shift || q{};
1359
1360   if ($switch_name eq q{} or $mac_address eq q{}) {
1361      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
1362      }
1363
1364   if (not defined $SWITCH_DB{$switch_name}) {
1365      die "Switch $switch_name must be defined in klask configuration file\n";
1366      }
1367
1368   my $sw = $SWITCH_DB{$switch_name};
1369   my %session = ( -hostname => $sw->{hostname} );
1370      $session{-version} = $sw->{version}   || 1;
1371      $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1372   if (exists $sw->{version} and $sw->{version} eq '3') {
1373      $session{-username} = $sw->{username} || 'snmpadmin';
1374      }
1375   else {
1376      $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1377      }
1378
1379   my $research = $OID_NUMBER{searchPort} . mac_address_hex_to_dec($mac_address);
1380   print "Klask search OID $research on switch $switch_name\n";
1381
1382   my ($session, $error) = Net::SNMP->session( %session );
1383   print "$error \n" if $error;
1384
1385   my $result = $session->get_request(
1386      -varbindlist => [$research]
1387      );
1388
1389   if (not defined $result or $result->{$research} eq 'noSuchInstance') {
1390      print "Klask do not find MAC $mac_address on switch $switch_name\n";
1391      $session->close;
1392      }
1393
1394   my $swport = $result->{$research};
1395   $session->close;
1396
1397   print "Klask find MAC $mac_address on switch $switch_name port $swport\n";
1398   return;
1399   }
1400
1401sub cmd_updatesw {
1402   my @options = @_;
1403
1404   my $verbose;
1405
1406   my $ret = GetOptionsFromArray(\@options,
1407      'verbose|v' => \$verbose,
1408      );
1409
1410   init_switch_names('yes');    #nomme les switchs
1411   print "\n";
1412
1413   my %where = ();
1414   my %db_switch_output_port = ();
1415   my %db_switch_ip_hostnamefq = ();
1416
1417   DETECT_ALL_ROUTER:
1418#   for my $one_computer ('194.254.66.254') {
1419   for my $one_router ( get_list_main_router(get_list_network()) ) {
1420      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
1421
1422      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
1423      print "VERBOSE_1: Router detected $resol_arp{ipv4_address} - $resol_arp{mac_address}\n" if $verbose;
1424
1425      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # retrouve les emplacements des routeurs
1426      }
1427
1428   ALL_ROUTER_IP_ADDRESS:
1429   for my $ip_router (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
1430
1431      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip_router}; # /a priori/ idiot car ne sers à rien...
1432
1433      ALL_SWITCH_CONNECTED:
1434      for my $switch_detected ( keys %{$where{$ip_router}} ) {
1435
1436         my $switch = $where{$ip_router}->{$switch_detected};
1437
1438         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
1439
1440         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
1441         print "VERBOSE_2: output port $switch->{hostname} : $switch->{port}\n" if $verbose;
1442         }
1443      }
1444
1445   my %db_switch_link_with = ();
1446
1447   my @list_all_switch = ();
1448   my @list_switch_ipv4 = ();
1449   for my $sw (@SWITCH){
1450      push @list_all_switch, $sw->{hostname};
1451      }
1452
1453   my $timestamp = time;
1454
1455   ALL_SWITCH:
1456   for my $one_computer (@list_all_switch) {
1457      my %resol_arp = resolve_ip_arp_host($one_computer, q{*}, q{low}); # arp resolution
1458      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
1459
1460      push @list_switch_ipv4, $resol_arp{ipv4_address};
1461
1462      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # find port on all switch
1463
1464      if ($verbose) {
1465         print "VERBOSE_3: $one_computer $resol_arp{ipv4_address} $resol_arp{mac_address}\n";
1466         print "VERBOSE_3: $one_computer --- ",
1467            join(' + ', keys %{$where{$resol_arp{ipv4_address}}}),
1468            "\n";
1469         }
1470
1471      $db_switch_ip_hostnamefq{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
1472      print "VERBOSE_4: db_switch_ip_hostnamefq $resol_arp{ipv4_address} -> $resol_arp{hostname_fq}\n" if $verbose;
1473
1474      $SWITCH_DB{$one_computer}->{ipv4_address} = $resol_arp{ipv4_address};
1475      $SWITCH_DB{$one_computer}->{mac_address}  = $resol_arp{mac_address};
1476      $SWITCH_DB{$one_computer}->{timestamp}    = $timestamp;
1477      }
1478
1479   ALL_SWITCH_IP_ADDRESS:
1480   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
1481
1482      print "VERBOSE_5: loop on $db_switch_ip_hostnamefq{$ip}\n" if $verbose;
1483
1484      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
1485#      next ALL_SWITCH_IP_ADDRESS if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostnamefq{$ip} };
1486
1487      DETECTED_SWITCH:
1488      for my $switch_detected ( keys %{$where{$ip}} ) {
1489
1490         my $switch = $where{$ip}->{$switch_detected};
1491         print "VERBOSE_6: $db_switch_ip_hostnamefq{$ip} -> $switch->{hostname} : $switch->{port}\n" if $verbose;
1492
1493         next if $switch->{port}     eq '0';
1494         next if $switch->{port}     eq $db_switch_output_port{$switch->{hostname}};
1495         next if $switch->{hostname} eq $db_switch_ip_hostnamefq{$ip}; # $computerdb->{$ip}{hostname};
1496
1497         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} } ||= {};
1498         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} }->{ $switch->{hostname} } = $switch->{port};
1499         print "VERBOSE_7: +++++\n" if $verbose;
1500         }
1501
1502      }
1503
1504   my %db_switch_connected_on_port = ();
1505   my $maybe_more_than_one_switch_connected = 'yes';
1506
1507   while ($maybe_more_than_one_switch_connected eq 'yes') {
1508      for my $sw (keys %db_switch_link_with) {
1509         for my $connect (keys %{$db_switch_link_with{$sw}}) {
1510
1511            my $port = $db_switch_link_with{$sw}->{$connect};
1512
1513            $db_switch_connected_on_port{"$connect:$port"} ||= {};
1514            $db_switch_connected_on_port{"$connect:$port"}->{$sw}++; # Just to define the key
1515            }
1516         }
1517
1518      $maybe_more_than_one_switch_connected  = 'no';
1519
1520      SWITCH_AND_PORT:
1521      for my $swport (keys %db_switch_connected_on_port) {
1522
1523         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
1524
1525         $maybe_more_than_one_switch_connected = 'yes';
1526
1527         my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1528         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
1529
1530         CONNECTED:
1531         for my $sw_connected (@sw_on_same_port) {
1532
1533            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
1534
1535            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
1536
1537            for my $other_sw (@sw_on_same_port) {
1538               next if $other_sw eq $sw_connected;
1539
1540               delete $db_switch_link_with{$other_sw}->{$sw_connect};
1541               }
1542
1543            # We can not do better for this switch for this loop
1544            next SWITCH_AND_PORT;
1545            }
1546         }
1547      }
1548
1549   my %db_switch_parent =();
1550
1551   for my $sw (keys %db_switch_link_with) {
1552      for my $connect (keys %{$db_switch_link_with{$sw}}) {
1553
1554         my $port = $db_switch_link_with{$sw}->{$connect};
1555
1556         $db_switch_connected_on_port{"$connect:$port"} ||= {};
1557         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
1558
1559         $db_switch_parent{$sw} = {switch => $connect, port => $port};
1560         }
1561      }
1562
1563   print "Switch output port and parent port connection\n";
1564   print "---------------------------------------------\n";
1565   for my $sw (sort keys %db_switch_output_port) {
1566      if (exists $db_switch_parent{$sw}) {
1567         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1568         }
1569      else {
1570         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1571         }
1572      }
1573   print "\n";
1574
1575   print "Switch parent and children port inter-connection\n";
1576   print "------------------------------------------------\n";
1577   for my $swport (sort keys %db_switch_connected_on_port) {
1578      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1579      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1580         if (exists $db_switch_output_port{$sw}) {
1581            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1582            }
1583         else {
1584            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1585            }
1586         }
1587      }
1588
1589   my $switch_connection = {
1590      output_port       => \%db_switch_output_port,
1591      parent            => \%db_switch_parent,
1592      connected_on_port => \%db_switch_connected_on_port,
1593      link_with         => \%db_switch_link_with,
1594      switch_db         => \%SWITCH_DB,
1595      };
1596
1597   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
1598   return;
1599   }
1600
1601sub cmd_exportsw {
1602   my @ARGV   = @_;
1603
1604   test_switchdb_environnement();
1605
1606   my $format = 'txt';
1607
1608   my $ret = GetOptions(
1609      'format|f=s'  => \$format,
1610      );
1611
1612   my %possible_format = (
1613      txt => \&cmd_exportsw_txt,
1614      dot => \&cmd_exportsw_dot,
1615      );
1616
1617   $format = 'txt' if not defined $possible_format{$format};
1618
1619   $possible_format{$format}->(@ARGV);
1620   return;
1621   }
1622
1623sub cmd_exportsw_txt {
1624
1625   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1626
1627   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1628   my %db_switch_parent            = %{$switch_connection->{parent}};
1629   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1630
1631   print "Switch output port and parent port connection\n";
1632   print "---------------------------------------------\n";
1633   for my $sw (sort keys %db_switch_output_port) {
1634      if (exists $db_switch_parent{$sw}) {
1635         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1636         }
1637      else {
1638         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1639         }
1640      }
1641   print "\n";
1642
1643   print "Switch parent and children port inter-connection\n";
1644   print "------------------------------------------------\n";
1645   for my $swport (sort keys %db_switch_connected_on_port) {
1646      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1647      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1648         if (exists $db_switch_output_port{$sw}) {
1649            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1650            }
1651         else {
1652            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1653            }
1654         }
1655      }
1656   return;
1657   }
1658
1659sub cmd_exportsw_dot {
1660
1661   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1662
1663   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1664   my %db_switch_parent            = %{$switch_connection->{parent}};
1665   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1666   my %db_switch_link_with         = %{$switch_connection->{link_with}};
1667   my %db_switch_global            = %{$switch_connection->{switch_db}};
1668
1669   my %db_building= ();
1670   for my $sw (@SWITCH) {
1671      my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
1672      $db_building{$building} ||= {};
1673      $db_building{$building}->{$location} ||= {};
1674      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
1675      }
1676
1677
1678   print "digraph G {\n";
1679
1680   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
1681   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
1682
1683   my $b=0;
1684   for my $building (keys %db_building) {
1685      $b++;
1686
1687      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
1688      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
1689
1690      my $l = 0;
1691      for my $loc (keys %{$db_building{$building}}) {
1692         $l++;
1693
1694         print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
1695#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
1696         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
1697
1698         for my $sw (keys %{$db_building{$building}->{$loc}}) {
1699
1700            print "\"$sw:$db_switch_output_port{$sw}\" [label = $db_switch_output_port{$sw}, color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
1701
1702            my $swname  = $sw;
1703               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
1704            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
1705            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
1706            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
1707
1708
1709            for my $swport (keys %db_switch_connected_on_port) {
1710               my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1711               next if not $sw_connect eq $sw;
1712               next if $port_connect eq $db_switch_output_port{$sw};
1713               print "\"$sw:$port_connect\" [label = $port_connect, color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
1714               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
1715              }
1716            }
1717         }
1718      }
1719
1720#   print "Switch output port and parent port connection\n";
1721#   print "---------------------------------------------\n";
1722   for my $sw (sort keys %db_switch_output_port) {
1723      if (exists $db_switch_parent{$sw}) {
1724#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
1725         }
1726      else {
1727         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
1728         }
1729      }
1730   print "\n";
1731
1732#   print "Switch parent and children port inter-connection\n";
1733#   print "------------------------------------------------\n";
1734   for my $swport (sort keys %db_switch_connected_on_port) {
1735      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1736      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1737         if (exists $db_switch_output_port{$sw}) {
1738            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
1739            }
1740         else {
1741            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
1742            }
1743         }
1744      }
1745
1746print "}\n";
1747   return;
1748   }
1749
1750
1751__END__
1752
1753=head1 NAME
1754
1755klask - ports manager and finder for switch
1756
1757
1758=head1 USAGE
1759
1760 klask updatedb
1761 klask exportdb --format [txt|html]
1762 klask removedb computer*
1763 klask cleandb  --day number_of_day --verbose
1764
1765 klask updatesw
1766 klask exportsw --format [txt|dot]
1767
1768 klask searchdb computer
1769 klask search   computer
1770 klask search-mac-on-switch switch mac_addr
1771
1772 klask ip-free --day number_of_day --format [txt|html] [vlan_name]
1773
1774 klask enable  switch port
1775 klask disable swith port
1776 klask status  swith port
1777
1778
1779=head1 DESCRIPTION
1780
1781klask is a small tool to find where is a host in a big network. klask mean search in brittany.
1782
1783Klask has now a web site dedicated for it !
1784
1785 http://servforge.legi.inpg.fr/projects/klask
1786
1787
1788=head1 COMMANDS
1789
1790
1791=head2 search
1792
1793This 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.
1794
1795
1796=head2 enable
1797
1798This command activate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1799
1800
1801=head2 disable
1802
1803This command deactivate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1804
1805
1806=head2 status
1807
1808This 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.
1809
1810
1811=head2 updatedb
1812
1813This 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.
1814
1815
1816=head2 exportdb
1817
1818This 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...
1819
1820
1821=head2 updatesw
1822
1823This 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.
1824
1825
1826=head2 exportsw --format [txt|dot]
1827
1828This 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.
1829
1830 klask exportsw --format dot > /tmp/map.dot
1831 dot -Tpng /tmp/map.dot > /tmp/map.png
1832
1833
1834
1835=head1 CONFIGURATION
1836
1837Because 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 !
1838
1839Here an example, be aware with indent, it's important in YAML, do not use tabulation !
1840
1841 default:
1842   community: public
1843   snmpport: 161
1844
1845 network:
1846   labnet:
1847     ip-subnet:
1848       - add: 192.168.1.0/24
1849       - add: 192.168.2.0/24
1850     interface: eth0
1851     main-router: gw1.labnet.local
1852
1853   schoolnet:
1854     ip-subnet:
1855       - add: 192.168.6.0/24
1856       - add: 192.168.7.0/24
1857     interface: eth0.38
1858     main-router: gw2.schoolnet.local
1859
1860 switch:
1861   - hostname: sw1.klask.local
1862     portignore:
1863       - 1
1864       - 2
1865
1866   - hostname: sw2.klask.local
1867     location: BatK / 2 / K203
1868     type: HP2424
1869     portignore:
1870       - 1
1871       - 2
1872
1873I 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.
1874
1875
1876=head1 FILES
1877
1878 /etc/klask/klask.conf
1879 /var/lib/klask/klaskdb
1880 /var/lib/klask/switchdb
1881
1882=head1 SEE ALSO
1883
1884Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
1885
1886
1887=head1 VERSION
1888
1889$Id: klask 105 2011-12-12 13:19:48Z g7moreau $
1890
1891
1892=head1 AUTHOR
1893
1894Written by Gabriel Moreau, Grenoble - France
1895
1896
1897=head1 LICENSE AND COPYRIGHT
1898
1899GPL version 2 or later and Perl equivalent
1900
1901Copyright (C) 2005-2009 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.