source: trunk/klask @ 73

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