source: trunk/klask @ 84

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