source: trunk/klask @ 108

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