source: trunk/klask @ 96

Last change on this file since 96 was 96, checked in by g7moreau, 13 years ago
  • Cleanup code
  • Give interface when updatedb
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 60.1 KB
Line 
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005-2008 Gabriel Moreau.
4#
5# $Id: klask 96 2011-03-30 16:16:42Z 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: 96 $'."\n";
578   print ' $Date: 2011-03-30 16:16:42 +0000 (Wed, 30 Mar 2011) $'."\n";
579   print ' $Id: klask 96 2011-03-30 16:16:42Z 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
1189   my $ret = GetOptionsFromArray(\@vlan_name,
1190      'day|d=i'      => \$days_to_dead,
1191      'format|f=s'   => \$format,
1192      );
1193
1194   my %possible_format = (
1195      txt  => \&cmd_ip_free_txt,
1196      html => \&cmd_ip_free_html,
1197      );
1198   $format = 'txt' if not defined $possible_format{$format};
1199
1200   @vlan_name = get_list_network() if not @vlan_name;
1201
1202   my $computerdb = {};
1203      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1204   my $timestamp = time;
1205
1206   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_dead );
1207
1208   my %result_ip = ();
1209
1210   ALL_NETWORK:
1211   for my $vlan (@vlan_name) {
1212
1213      my @ip_list = get_list_ip($vlan);
1214#      my $current_interface = get_current_interface($vlan);
1215     
1216      LOOP_ON_IP_ADDRESS:
1217      for my $ip (@ip_list) {
1218
1219         next LOOP_ON_IP_ADDRESS if
1220            exists $computerdb->{$ip}
1221            && $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1222
1223         my $ip_date_last_detection = '';
1224         if (exists $computerdb->{$ip}) {
1225            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1226            $year += 1900;
1227            $mon++;
1228            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1229            }
1230
1231         $result_ip{$ip} ||= {};
1232         $result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
1233
1234         my $packed_ip = scalar gethostbyname($ip);
1235         my $hostname_fq = 'unknown';
1236            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip;
1237         $result_ip{$ip}->{hostname_fq} = $hostname_fq;
1238         
1239         $result_ip{$ip}->{vlan} = $vlan;
1240         }
1241      }
1242
1243   $possible_format{$format}->(%result_ip);
1244   }
1245
1246sub cmd_ip_free_txt {
1247   my %result_ip = @_;
1248   
1249   printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
1250   print "-------------------------------------------------------------------------------\n";
1251   LOOP_ON_IP_ADDRESS:
1252   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1253         printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $result_ip{$ip}->{vlan};
1254      }
1255   }
1256
1257sub cmd_ip_free_html {
1258   my %result_ip = @_;
1259
1260   print <<'END_HTML';
1261<table class="sortable" summary="Klask Free IP Database">
1262 <caption>Klask Free IP Database</caption>
1263 <thead>
1264  <tr>
1265   <th scope="col" class="klask-header-left">IPv4-Address</th>
1266   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1267   <th scope="col" class="sorttable_alpha">VLAN</th>
1268   <th scope="col" class="klask-header-right">Date</th>
1269  </tr>
1270 </thead>
1271 <tfoot>
1272  <tr>
1273   <th scope="col" class="klask-footer-left">IPv4-Address</th>
1274   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1275   <th scope="col" class="fklask-vlan">VLAN</th>
1276   <th scope="col" class="klask-footer-right">Date</th>
1277  </tr>
1278 </tfoot>
1279 <tbody>
1280END_HTML
1281
1282   my $typerow = 'even';
1283
1284   LOOP_ON_IP_ADDRESS:
1285   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1286
1287      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1288
1289      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1290      my ( $host_short ) = split m/ \. /xms, $result_ip{$ip}->{hostname_fq};
1291
1292      print <<"END_HTML";
1293  <tr class="$typerow">
1294   <td sorttable_customkey="$ip_sort">$ip</td>
1295   <td sorttable_customkey="$host_short">$result_ip{$ip}->{hostname_fq}</td>
1296   <td>$result_ip{$ip}->{vlan}</td>
1297   <td>$result_ip{$ip}->{date_last_detection}</td>
1298  </tr>
1299END_HTML
1300      }
1301   print <<'END_HTML';
1302 </tbody>
1303</table>
1304END_HTML
1305   }
1306
1307sub cmd_enable {
1308   my $switch = shift;
1309   my $port   = shift;
1310
1311   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
1312   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
1313   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
1314   return;
1315   }
1316
1317sub cmd_disable {
1318   my $switch = shift;
1319   my $port   = shift;
1320
1321   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
1322   return;
1323   }
1324
1325sub cmd_status {
1326   my $switch = shift;
1327   my $port   = shift;
1328
1329   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
1330   return;
1331   }
1332
1333sub cmd_search_mac_on_switch {
1334   my $switch_name = shift || q{};
1335   my $mac_address = shift || q{};
1336
1337   if ($switch_name eq q{} or $mac_address eq q{}) {
1338      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
1339      }
1340
1341   if (not defined $SWITCH_DB{$switch_name}) {
1342      die "Switch $switch_name must be defined in klask configuration file\n";
1343      }
1344
1345   my $sw = $SWITCH_DB{$switch_name};
1346   my %session = ( -hostname => $sw->{hostname} );
1347      $session{-version} = $sw->{version}   || 1;
1348      $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1349   if (exists $sw->{version} and $sw->{version} eq '3') {
1350      $session{-username} = $sw->{username} || 'snmpadmin';
1351      }
1352   else {
1353      $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1354      }
1355
1356   my $research = $OID_NUMBER{searchPort} . mac_address_hex_to_dec($mac_address);
1357   print "Klask search OID $research on switch $switch_name\n";
1358
1359   my ($session, $error) = Net::SNMP->session( %session );
1360   print "$error \n" if $error;
1361
1362   my $result = $session->get_request(
1363      -varbindlist => [$research]
1364      );
1365
1366   if (not defined $result or $result->{$research} eq 'noSuchInstance') {
1367      print "Klask do not find MAC $mac_address on switch $switch_name\n";
1368      $session->close;
1369      }
1370
1371   my $swport = $result->{$research};
1372   $session->close;
1373
1374   print "Klask find MAC $mac_address on switch $switch_name port $swport\n";
1375   return;
1376   }
1377
1378sub cmd_updatesw {
1379   my @options = @_;
1380
1381   my $verbose;
1382
1383   my $ret = GetOptionsFromArray(\@options,
1384      'verbose|v' => \$verbose,
1385      );
1386
1387   init_switch_names('yes');    #nomme les switchs
1388   print "\n";
1389
1390   my %where = ();
1391   my %db_switch_output_port = ();
1392   my %db_switch_ip_hostnamefq = ();
1393
1394   DETECT_ALL_ROUTER:
1395#   for my $one_computer ('194.254.66.254') {
1396   for my $one_router ( get_list_main_router(get_list_network()) ) {
1397      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
1398
1399      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
1400      print "VERBOSE_1: Router detected $resol_arp{ipv4_address} - $resol_arp{mac_address}\n" if $verbose;
1401
1402      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # retrouve les emplacements des routeurs
1403      }
1404
1405   ALL_ROUTER_IP_ADDRESS:
1406   for my $ip_router (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
1407
1408      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip_router}; # /a priori/ idiot car ne sers à rien...
1409
1410      ALL_SWITCH_CONNECTED:
1411      for my $switch_detected ( keys %{$where{$ip_router}} ) {
1412
1413         my $switch = $where{$ip_router}->{$switch_detected};
1414
1415         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
1416
1417         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
1418         print "VERBOSE_2: output port $switch->{hostname} : $switch->{port}\n" if $verbose;
1419         }
1420      }
1421
1422   my %db_switch_link_with = ();
1423
1424   my @list_all_switch = ();
1425   my @list_switch_ipv4 = ();
1426   for my $sw (@SWITCH){
1427      push @list_all_switch, $sw->{hostname};
1428      }
1429
1430   my $timestamp = time;
1431
1432   ALL_SWITCH:
1433   for my $one_computer (@list_all_switch) {
1434      my %resol_arp = resolve_ip_arp_host($one_computer, q{*}, q{low}); # arp resolution
1435      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
1436
1437      push @list_switch_ipv4, $resol_arp{ipv4_address};
1438
1439      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # find port on all switch
1440
1441      if ($verbose) {
1442         print "VERBOSE_3: $one_computer $resol_arp{ipv4_address} $resol_arp{mac_address}\n";
1443         print "VERBOSE_3: $one_computer --- ",
1444            join(' + ', keys %{$where{$resol_arp{ipv4_address}}}),
1445            "\n";
1446         }
1447
1448      $db_switch_ip_hostnamefq{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
1449      print "VERBOSE_4: db_switch_ip_hostnamefq $resol_arp{ipv4_address} -> $resol_arp{hostname_fq}\n" if $verbose;
1450
1451      $SWITCH_DB{$one_computer}->{ipv4_address} = $resol_arp{ipv4_address};
1452      $SWITCH_DB{$one_computer}->{mac_address}  = $resol_arp{mac_address};
1453      $SWITCH_DB{$one_computer}->{timestamp}    = $timestamp;
1454      }
1455
1456   ALL_SWITCH_IP_ADDRESS:
1457   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
1458
1459      print "VERBOSE_5: loop on $db_switch_ip_hostnamefq{$ip}\n" if $verbose;
1460
1461      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
1462#      next ALL_SWITCH_IP_ADDRESS if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostnamefq{$ip} };
1463
1464      DETECTED_SWITCH:
1465      for my $switch_detected ( keys %{$where{$ip}} ) {
1466
1467         my $switch = $where{$ip}->{$switch_detected};
1468         print "VERBOSE_6: $db_switch_ip_hostnamefq{$ip} -> $switch->{hostname} : $switch->{port}\n" if $verbose;
1469
1470         next if $switch->{port}     eq '0';
1471         next if $switch->{port}     eq $db_switch_output_port{$switch->{hostname}};
1472         next if $switch->{hostname} eq $db_switch_ip_hostnamefq{$ip}; # $computerdb->{$ip}{hostname};
1473
1474         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} } ||= {};
1475         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} }->{ $switch->{hostname} } = $switch->{port};
1476         print "VERBOSE_7: +++++\n" if $verbose;
1477         }
1478
1479      }
1480
1481   my %db_switch_connected_on_port = ();
1482   my $maybe_more_than_one_switch_connected = 'yes';
1483
1484   while ($maybe_more_than_one_switch_connected eq 'yes') {
1485      for my $sw (keys %db_switch_link_with) {
1486         for my $connect (keys %{$db_switch_link_with{$sw}}) {
1487
1488            my $port = $db_switch_link_with{$sw}->{$connect};
1489
1490            $db_switch_connected_on_port{"$connect:$port"} ||= {};
1491            $db_switch_connected_on_port{"$connect:$port"}->{$sw}++; # Just to define the key
1492            }
1493         }
1494
1495      $maybe_more_than_one_switch_connected  = 'no';
1496
1497      SWITCH_AND_PORT:
1498      for my $swport (keys %db_switch_connected_on_port) {
1499
1500         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
1501
1502         $maybe_more_than_one_switch_connected = 'yes';
1503
1504         my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1505         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
1506
1507         CONNECTED:
1508         for my $sw_connected (@sw_on_same_port) {
1509
1510            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
1511
1512            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
1513
1514            for my $other_sw (@sw_on_same_port) {
1515               next if $other_sw eq $sw_connected;
1516
1517               delete $db_switch_link_with{$other_sw}->{$sw_connect};
1518               }
1519
1520            # We can not do better for this switch for this loop
1521            next SWITCH_AND_PORT;
1522            }
1523         }
1524      }
1525
1526   my %db_switch_parent =();
1527
1528   for my $sw (keys %db_switch_link_with) {
1529      for my $connect (keys %{$db_switch_link_with{$sw}}) {
1530
1531         my $port = $db_switch_link_with{$sw}->{$connect};
1532
1533         $db_switch_connected_on_port{"$connect:$port"} ||= {};
1534         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
1535
1536         $db_switch_parent{$sw} = {switch => $connect, port => $port};
1537         }
1538      }
1539
1540   print "Switch output port and parent port connection\n";
1541   print "---------------------------------------------\n";
1542   for my $sw (sort keys %db_switch_output_port) {
1543      if (exists $db_switch_parent{$sw}) {
1544         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1545         }
1546      else {
1547         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1548         }
1549      }
1550   print "\n";
1551
1552   print "Switch parent and children port inter-connection\n";
1553   print "------------------------------------------------\n";
1554   for my $swport (sort keys %db_switch_connected_on_port) {
1555      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1556      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1557         if (exists $db_switch_output_port{$sw}) {
1558            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1559            }
1560         else {
1561            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1562            }
1563         }
1564      }
1565
1566   my $switch_connection = {
1567      output_port       => \%db_switch_output_port,
1568      parent            => \%db_switch_parent,
1569      connected_on_port => \%db_switch_connected_on_port,
1570      link_with         => \%db_switch_link_with,
1571      switch_db         => \%SWITCH_DB,
1572      };
1573
1574   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
1575   return;
1576   }
1577
1578sub cmd_exportsw {
1579   my @ARGV   = @_;
1580
1581   test_switchdb_environnement();
1582
1583   my $format = 'txt';
1584
1585   my $ret = GetOptions(
1586      'format|f=s'  => \$format,
1587      );
1588
1589   my %possible_format = (
1590      txt => \&cmd_exportsw_txt,
1591      dot => \&cmd_exportsw_dot,
1592      );
1593
1594   $format = 'txt' if not defined $possible_format{$format};
1595
1596   $possible_format{$format}->(@ARGV);
1597   return;
1598   }
1599
1600sub cmd_exportsw_txt {
1601
1602   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1603
1604   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1605   my %db_switch_parent            = %{$switch_connection->{parent}};
1606   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1607
1608   print "Switch output port and parent port connection\n";
1609   print "---------------------------------------------\n";
1610   for my $sw (sort keys %db_switch_output_port) {
1611      if (exists $db_switch_parent{$sw}) {
1612         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1613         }
1614      else {
1615         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1616         }
1617      }
1618   print "\n";
1619
1620   print "Switch parent and children port inter-connection\n";
1621   print "------------------------------------------------\n";
1622   for my $swport (sort keys %db_switch_connected_on_port) {
1623      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1624      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1625         if (exists $db_switch_output_port{$sw}) {
1626            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1627            }
1628         else {
1629            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1630            }
1631         }
1632      }
1633   return;
1634   }
1635
1636sub cmd_exportsw_dot {
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   my %db_switch_link_with         = %{$switch_connection->{link_with}};
1644   my %db_switch_global            = %{$switch_connection->{switch_db}};
1645
1646   my %db_building= ();
1647   for my $sw (@SWITCH) {
1648      my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
1649      $db_building{$building} ||= {};
1650      $db_building{$building}->{$location} ||= {};
1651      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
1652      }
1653
1654
1655   print "digraph G {\n";
1656
1657   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
1658   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
1659
1660   my $b=0;
1661   for my $building (keys %db_building) {
1662      $b++;
1663
1664      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
1665      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
1666
1667      my $l = 0;
1668      for my $loc (keys %{$db_building{$building}}) {
1669         $l++;
1670
1671         print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
1672#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
1673         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
1674
1675         for my $sw (keys %{$db_building{$building}->{$loc}}) {
1676
1677            print "\"$sw:$db_switch_output_port{$sw}\" [label = $db_switch_output_port{$sw}, color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
1678
1679            my $swname  = $sw;
1680               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
1681            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
1682            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
1683            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
1684
1685
1686            for my $swport (keys %db_switch_connected_on_port) {
1687               my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1688               next if not $sw_connect eq $sw;
1689               next if $port_connect eq $db_switch_output_port{$sw};
1690               print "\"$sw:$port_connect\" [label = $port_connect, color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
1691               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
1692              }
1693            }
1694         }
1695      }
1696
1697#   print "Switch output port and parent port connection\n";
1698#   print "---------------------------------------------\n";
1699   for my $sw (sort keys %db_switch_output_port) {
1700      if (exists $db_switch_parent{$sw}) {
1701#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
1702         }
1703      else {
1704         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
1705         }
1706      }
1707   print "\n";
1708
1709#   print "Switch parent and children port inter-connection\n";
1710#   print "------------------------------------------------\n";
1711   for my $swport (sort keys %db_switch_connected_on_port) {
1712      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1713      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1714         if (exists $db_switch_output_port{$sw}) {
1715            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
1716            }
1717         else {
1718            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
1719            }
1720         }
1721      }
1722
1723print "}\n";
1724   return;
1725   }
1726
1727
1728__END__
1729
1730=head1 NAME
1731
1732klask - ports manager and finder for switch
1733
1734
1735=head1 USAGE
1736
1737 klask updatedb
1738 klask exportdb --format [txt|html]
1739 klask removedb computer*
1740 klask cleandb  --day number_of_day --verbose
1741
1742 klask updatesw
1743 klask exportsw --format [txt|dot]
1744
1745 klask searchdb computer
1746 klask search   computer
1747 klask search-mac-on-switch switch mac_addr
1748
1749 klask ip-free --day number_of_day --format [txt|html] [vlan_name]
1750
1751 klask enable  switch port
1752 klask disable swith port
1753 klask status  swith port
1754
1755
1756=head1 DESCRIPTION
1757
1758klask is a small tool to find where is a host in a big network. klask mean search in brittany.
1759
1760Klask has now a web site dedicated for it !
1761
1762 http://servforge.legi.inpg.fr/projects/klask
1763
1764
1765=head1 COMMANDS
1766
1767
1768=head2 search
1769
1770This 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.
1771
1772
1773=head2 enable
1774
1775This command activate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1776
1777
1778=head2 disable
1779
1780This command deactivate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1781
1782
1783=head2 status
1784
1785This 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.
1786
1787
1788=head2 updatedb
1789
1790This 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.
1791
1792
1793=head2 exportdb
1794
1795This 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...
1796
1797
1798=head2 updatesw
1799
1800This 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.
1801
1802
1803=head2 exportsw --format [txt|dot]
1804
1805This 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.
1806
1807 klask exportsw --format dot > /tmp/map.dot
1808 dot -Tpng /tmp/map.dot > /tmp/map.png
1809
1810
1811
1812=head1 CONFIGURATION
1813
1814Because 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 !
1815
1816Here an example, be aware with indent, it's important in YAML, do not use tabulation !
1817
1818 default:
1819   community: public
1820   snmpport: 161
1821
1822 network:
1823   labnet:
1824     ip-subnet:
1825       - add: 192.168.1.0/24
1826       - add: 192.168.2.0/24
1827     interface: eth0
1828     main-router: gw1.labnet.local
1829
1830   schoolnet:
1831     ip-subnet:
1832       - add: 192.168.6.0/24
1833       - add: 192.168.7.0/24
1834     interface: eth0.38
1835     main-router: gw2.schoolnet.local
1836
1837 switch:
1838   - hostname: sw1.klask.local
1839     portignore:
1840       - 1
1841       - 2
1842
1843   - hostname: sw2.klask.local
1844     location: BatK / 2 / K203
1845     type: HP2424
1846     portignore:
1847       - 1
1848       - 2
1849
1850I 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.
1851
1852
1853=head1 FILES
1854
1855 /etc/klask/klask.conf
1856 /var/cache/klask/klaskdb
1857 /var/cache/klask/switchdb
1858
1859=head1 SEE ALSO
1860
1861Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
1862
1863
1864=head1 VERSION
1865
1866$Id: klask 96 2011-03-30 16:16:42Z g7moreau $
1867
1868
1869=head1 AUTHOR
1870
1871Written by Gabriel Moreau, Grenoble - France
1872
1873
1874=head1 LICENSE AND COPYRIGHT
1875
1876GPL version 2 or later and Perl equivalent
1877
1878Copyright (C) 2005-2009 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.