source: trunk/klask @ 92

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