source: trunk/klask @ 97

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