source: trunk/klask @ 35

Last change on this file since 35 was 35, checked in by g7moreau, 16 years ago
  • Add command search-mac-on-switch

klask search-mac-on-switch switch_name mac_address

  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 43.6 KB
Line 
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005-2008 Gabriel Moreau.
4#
5# $Id: klask 35 2008-02-12 12:54:43Z g7moreau $
6
7use strict;
8use warnings;
9
10use Net::SNMP;
11use YAML;
12use Net::Netmask;
13use Net::CIDR::Lite;
14use NetAddr::IP;
15use Getopt::Long;
16
17# apt-get install snmp fping libnet-cidr-lite-perl libnet-netmask-perl libnet-snmp-perl libnetaddr-ip-perl libyaml-perl
18# libcrypt-des-perl libcrypt-hcesha-perl libdigest-hmac-perl
19# arping fping bind9-host arpwatch
20
21my $KLASK_VAR      = '/var/cache/klask';
22my $KLASK_CFG_FILE = '/etc/klask.conf';
23my $KLASK_DB_FILE  = "$KLASK_VAR/klaskdb";
24my $KLASK_SW_FILE  = "$KLASK_VAR/switchdb";
25
26test_running_environnement();
27
28my $KLASK_CFG = YAML::LoadFile("$KLASK_CFG_FILE");
29
30my %DEFAULT = %{$KLASK_CFG->{default}};
31my @SWITCH  = @{$KLASK_CFG->{switch}};
32
33my %switch_level = ();
34my %SWITCH_DB = ();
35LEVEL_OF_EACH_SWITCH:
36for my $sw (@SWITCH){
37   $switch_level{$sw->{hostname}} = $sw->{level} || $DEFAULT{switch_level}  || 2;
38   $SWITCH_DB{$sw->{hostname}} = $sw;
39   }
40@SWITCH = sort { $switch_level{$b->{hostname}} <=> $switch_level{$a->{hostname}} } @{$KLASK_CFG->{switch}}; 
41
42my %SWITCH_PORT_COUNT = ();
43
44my %CMD_DB = (
45   help       => \&cmd_help,
46   exportdb   => \&cmd_exportdb,
47   updatedb   => \&cmd_updatedb,
48   searchdb   => \&cmd_searchdb,
49   removedb   => \&cmd_removedb,
50   search     => \&cmd_search,
51   enable     => \&cmd_enable,
52   disable    => \&cmd_disable,
53   status     => \&cmd_status,
54   updatesw   => \&cmd_updatesw,
55   exportsw   => \&cmd_exportsw,
56   dotsw      => \&cmd_exportsw_dot,
57   iplocation => \&cmd_iplocation,
58   'search-mac-on-switch' => \&cmd_search_mac_on_switch,
59   );
60
61my %INTERNAL_PORT_MAP = (
62   0 => 'A',
63   1 => 'B',
64   2 => 'C',
65   3 => 'D',
66   4 => 'E',
67   5 => 'F',
68   6 => 'G',
69   7 => 'H',
70   );
71my %INTERNAL_PORT_MAP_REV = reverse %INTERNAL_PORT_MAP;
72
73my %SWITCH_KIND = (
74   J3299A => { model => 'HP224M',     match => 'HP J3299A ProCurve Switch 224M'  },
75   J4120A => { model => 'HP1600M',    match => 'HP J4120A ProCurve Switch 1600M' },
76   J9029A => { model => 'HP1800-8G',  match => 'PROCURVE J9029A'                 },
77   J4093A => { model => 'HP2424M',    match => 'HP J4093A ProCurve Switch 2424M' },
78   J4813A => { model => 'HP2524',     match => 'HP J4813A ProCurve Switch 2524'  },
79   J4900A => { model => 'HP2626A',    match => 'HP J4900A ProCurve Switch 2626'  },
80   J4900B => { model => 'HP2626B',    match => 'HP J4900B ProCurve Switch 2626'  },
81   J4899B => { model => 'HP2650',     match => 'ProCurve J4899B Switch 2650'     },
82   J9021A => { model => 'HP2810-24G', match => 'ProCurve J9021A Switch 2810-24G' },
83   J4903A => { model => 'HP2824',     match => 'J4903A.+?Switch 2824,'           },
84   J4110A => { model => 'HP8000M',    match => 'HP J4110A ProCurve Switch 8000M' },
85   BS350T => { model => 'BS350T',     match => 'BayStack 350T HW'                },
86   );
87 
88my %OID_NUMBER = (
89   sysDescr    => '1.3.6.1.2.1.1.1.0',
90   sysName     => '1.3.6.1.2.1.1.5.0',
91   sysContact  => '1.3.6.1.2.1.1.4.0',
92   sysLocation => '1.3.6.1.2.1.1.6.0',
93   );
94
95################
96# principal
97################
98
99my $cmd = shift @ARGV || 'help';
100if (defined $CMD_DB{$cmd}) {
101   $CMD_DB{$cmd}->(@ARGV);
102   }
103else {
104   print STDERR "klask: command $cmd not found\n\n";
105   $CMD_DB{help}->();
106   exit 1;
107   }
108
109exit;
110
111sub test_running_environnement {
112   die "Configuration file $KLASK_CFG_FILE does not exists. Klask need it !\n" if not -e "$KLASK_CFG_FILE";
113   die "Var folder $KLASK_VAR does not exists. Klask need it !\n"              if not -d "$KLASK_VAR";
114   }
115
116sub test_switchdb_environnement {
117   die "Switch database $KLASK_SW_FILE does not exists. Launch updatesw before this command !\n" if not -e "$KLASK_SW_FILE";
118   }
119
120sub test_maindb_environnement {
121   die "Main database $KLASK_DB_FILE does not exists. Launch updatedb before this command !\n" if not -e "$KLASK_DB_FILE";
122   }
123
124###
125# fast ping dont l'objectif est de remplir la table arp de la machine
126sub fastping {
127   system "fping -c 1 @_ >/dev/null 2>&1";
128   }
129
130###
131# donne l'@ ip, dns, arp en fonction du dns OU de l'ip
132sub resolve_ip_arp_host {
133   my $param_ip_or_host = shift;
134   my $interface = shift || '*';
135   my $type      = shift || 'fast';
136
137   my %ret = (
138      hostname_fq  => 'unknow',
139      ipv4_address => '0.0.0.0',
140      mac_address  => 'unknow',
141      );
142
143#   my $cmdarping  = `arping -c 1 -w 1 -rR $param 2>/dev/null`;
144
145   # controler que arpwatch tourne !
146   # resultat de la commande arpwatch
147   # /var/lib/arpwatch/arp.dat
148   # 0:13:d3:e1:92:d0        192.168.24.109  1163681980      theo8sv109
149   #my $cmd = "grep  -e '".'\b'."$param_ip_or_host".'\b'."' /var/lib/arpwatch/arp.dat | sort +2rn | head -1";
150#   my $cmd = "grep  -he '".'\b'."$param_ip_or_host".'\b'."' /var/lib/arpwatch/*.dat | sort +2rn | head -1";
151   my $cmd = "grep  -he '".'\b'."$param_ip_or_host".'\b'."' /var/lib/arpwatch/$interface.dat | sort -rn -k 2 | head -1";
152   my $cmd_arpwatch = `$cmd`;
153   chomp $cmd_arpwatch;
154   my ($arp, $ip, $timestamp, $host) = split /\s+/, $cmd_arpwatch;
155#print "OOO $cmd\n";
156#print "TTT arp $arp -> $ip pour host $host\n";
157   $ret{ipv4_address} = $ip        if $ip;
158   $ret{mac_address}  = $arp       if $arp;
159   $ret{timestamp}    = $timestamp if $timestamp;
160
161   my $nowtimestamp = time();
162
163   if ( $type eq 'fast' and ( not defined $timestamp or $timestamp < ( $nowtimestamp - 3 * 3600 ) ) ) {
164      $ret{mac_address} = 'unknow';
165      return %ret;
166      }
167
168  # resultat de la commande arp
169   # tech7meylan.hmg.inpg.fr (194.254.66.240) at 00:14:22:45:28:A9 [ether] on eth0
170   my $cmd_arp  = `arp -a $param_ip_or_host 2>/dev/null`;
171   chomp $cmd_arp;
172   $cmd_arp =~ /(\S*)\s\(([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\)\sat\s([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})/;
173   $ret{hostname_fq}  = $1 if(defined($1));
174   $ret{ipv4_address} = $2 if(defined($2));
175   $ret{mac_address}  = $3 if(defined($3));
176
177#   if ($ret{ipv4_address} eq '0.0.0.0' and $ret{mac_address} eq 'unknow'and $ret{hostname_fq} eq 'unknow') {
178      # resultat de la commande host si le parametre est ip
179      # 250.66.254.194.in-addr.arpa domain name pointer legihp2100.hmg.inpg.fr.
180      my $cmd_host = `host $param_ip_or_host 2>/dev/null`;
181      chomp $cmd_host;
182      $cmd_host =~ m/domain\sname\spointer\s(\S+)\.$/;
183      $ret{hostname_fq} = $1 if defined $1;
184
185      # resultat de la commande host si parametre est hostname
186      # tech7meylan.hmg.inpg.fr has address 194.254.66.240
187      $cmd_host =~ m/\shas\saddress\s([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/;
188      $ret{ipv4_address} = $1 if defined $1;
189
190      $cmd_host =~ m/\b([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.in-addr\.arpa\s/;
191      $ret{ipv4_address} = "$4.$3.$2.$1"     if defined $1 and  defined $2 and  defined $3 and  defined $4;
192      $ret{hostname_fq}  = $param_ip_or_host if not defined $1 and $ret{hostname_fq} eq 'unknow';
193#      }
194
195   unless ($ret{mac_address} eq 'unknow') {
196      my @paquets = ();
197      foreach ( split(/:/, $ret{mac_address}) ) {
198         my @chars = split //, uc("00$_");
199         push @paquets, "$chars[-2]$chars[-1]";
200         }
201      $ret{mac_address} = join ':', @paquets;
202      }
203
204   return %ret;
205   }
206
207# Find Surname of a switch
208sub get_switch_model {
209   my $sw_snmp_description = shift || 'unknow';
210   
211   for my $sw_kind (keys %SWITCH_KIND) {
212      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{match}/;
213     
214      return $SWITCH_KIND{$sw_kind}->{model};
215      }
216     
217   return $sw_snmp_description;
218   }
219
220###
221# va rechercher le nom des switchs pour savoir qui est qui
222sub init_switch_names {
223   my $verbose = shift;
224   
225   printf "%-25s                %-25s %s\n",'Switch','Description','Type';
226#   print "Switch description\n" if $verbose;
227   print "-------------------------------------------------------------------------\n" if $verbose;
228
229   INIT_EACH_SWITCH:
230   for my $sw (@SWITCH) {
231      my %session = ( -hostname   => $sw->{hostname} );
232         $session{-version} = $sw->{version}   || 1;
233         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
234         if (exists $sw->{version} and $sw->{version} eq 3) {
235            $session{-username} = $sw->{username} || 'snmpadmin';
236            }
237         else {
238            $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
239            }
240
241      $sw->{local_session} = \%session;
242
243      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
244      print "$error \n" if $error;
245
246      my $result = $session->get_request(
247         -varbindlist => [
248            $OID_NUMBER{sysDescr},
249            $OID_NUMBER{sysName},
250            $OID_NUMBER{sysContact},
251            $OID_NUMBER{sysLocation},
252            ]
253         );
254      $sw->{description} = $result->{$OID_NUMBER{sysName}} || $sw->{hostname};
255      $sw->{model} = get_switch_model( $result->{$OID_NUMBER{sysDescr}});
256      #$sw->{location} = $result->{"1.3.6.1.2.1.1.6.0"} || $sw->{hostname};
257      #$sw->{contact} = $result->{"1.3.6.1.2.1.1.4.0"} || $sw->{hostname};
258      $session->close;
259
260      # Ligne à virer car on récupère maintenant le modèle du switch
261      my ($desc, $type) = split ':', $sw->{description}, 2;
262#      printf "%-25s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, uc($type)."**" if $verbose;
263      printf "%-25s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, $sw->{model} if $verbose;
264      }
265
266   print "\n" if $verbose;
267   }
268
269###
270# convertit l'hexa (uniquement 2 chiffres) en decimal
271sub hex_to_dec {
272   #00:0F:1F:43:E4:2B
273   my $car = '00' . uc(shift);
274
275   return '00' if $car eq '00UNKNOW';
276   my %table = (
277      "0"=>"0",  "1"=>"1",  "2"=>"2",  "3"=>"3",  "4"=>"4",  "5"=>"5", "6"=>"6", "7"=>"7", "8"=>"8", "9"=>"9",
278      "A"=>"10", "B"=>"11", "C"=>"12", "D"=>"13", "E"=>"14", "F"=>"15"
279      );
280   my @chars = split(//, $car);
281   return $table{$chars[-2]}*16 + $table{$chars[-1]};
282   }
283
284###
285# convertit l'@ arp en decimal
286sub arp_hex_to_dec {
287   #00:0F:1F:43:E4:2B
288   my $arp = shift;
289
290   my @paquets = split /:/, $arp;
291   my $return = '';
292   foreach(@paquets) {
293      $return .= ".".hex_to_dec($_);
294      }
295   return $return;
296   }
297
298###
299# va rechercher le port et le switch sur lequel est la machine
300sub find_switch_port {
301   my $arp = shift;
302   my $switch_proposal = shift || '';
303   
304   my %ret;
305   $ret{switch_description} = "unknow";
306   $ret{switch_port} = "0";
307
308   return %ret if $arp eq 'unknow';;
309
310   my @switch_search = @SWITCH;
311   if ($switch_proposal ne '') {
312      for my $sw (@SWITCH) {
313         next if $sw->{hostname} ne $switch_proposal;
314         unshift @switch_search, $sw;
315         last;
316         }
317      }
318
319   my $research = "1.3.6.1.2.1.17.4.3.1.2".arp_hex_to_dec($arp);
320   
321   LOOP_ON_SWITCH:
322   for my $sw (@switch_search) {
323      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
324      print "$error \n" if $error;
325
326      my $result = $session->get_request(
327         -varbindlist => [$research]
328         );
329      if (not defined($result) or $result->{$research} eq 'noSuchInstance') {
330         $session->close;
331         next LOOP_ON_SWITCH;
332         }
333
334         my $swport = $result->{$research};
335         $session->close;
336
337         # IMPORTANT !!
338         # ceci empeche la detection sur certains port ...
339         # en effet les switch sont relies entre eux par un cable reseau et du coup
340         # tous les arp de toutes les machines sont presentes sur ces ports (ceux choisis ici sont les miens)
341         # cette partie est a ameliore, voir a configurer dans l'entete
342         # 21->24 45->48
343#         my $flag = 0;
344         SWITCH_PORT_IGNORE:
345         foreach my $p (@{$sw->{portignore}}) {
346            next SWITCH_PORT_IGNORE if $swport ne get_numerical_port($sw->{model},$p);
347#            $flag = 1;
348            next LOOP_ON_SWITCH;
349            }
350#         if ($flag == 0) {
351            $ret{switch_hostname}    = $sw->{hostname};
352            $ret{switch_description} = $sw->{description};
353            $ret{switch_port}        = get_human_readable_port($sw->{model}, $swport); # $swport;
354           
355            last LOOP_ON_SWITCH;
356#            }
357#         }
358#      $session->close;
359      }
360   return %ret;
361   }
362
363###
364# va rechercher les port et les switch sur lequel est la machine
365sub find_all_switch_port {
366   my $arp = shift;
367
368   my $ret = {};
369
370   return $ret if $arp eq 'unknow';
371
372   for my $sw (@SWITCH) {
373      $SWITCH_PORT_COUNT{$sw->{hostname}} = {} if not exists $SWITCH_PORT_COUNT{$sw->{hostname}};
374      }
375
376   my $research = "1.3.6.1.2.1.17.4.3.1.2".arp_hex_to_dec($arp);
377   LOOP_ON_ALL_SWITCH:
378   for my $sw (@SWITCH) {
379      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
380      print "$error \n" if $error;
381
382      my $result = $session->get_request(
383         -varbindlist => [$research]
384         );
385
386      if(defined($result) and $result->{$research} ne 'noSuchInstance'){
387         my $swport = $result->{$research};
388
389         $ret->{$sw->{hostname}} = {};
390         $ret->{$sw->{hostname}}{hostname}    = $sw->{hostname};
391         $ret->{$sw->{hostname}}{description} = $sw->{description};
392         $ret->{$sw->{hostname}}{port}        = get_human_readable_port($sw->{model}, $swport);
393
394         $SWITCH_PORT_COUNT{$sw->{hostname}}->{$swport}++;
395         }
396
397      $session->close;
398      }
399   return $ret;
400   }
401
402sub get_list_network {
403
404   return keys %{$KLASK_CFG->{network}};
405   }
406
407sub get_current_interface {
408   my $network = shift;
409
410   return $KLASK_CFG->{network}{$network}{interface};
411   }
412 
413###
414# liste l'ensemble des adresses ip d'un réseau
415sub get_list_ip {
416   my @network = @_;
417
418   my $cidrlist = Net::CIDR::Lite->new;
419
420   for my $net (@network) {
421      my @line  = @{$KLASK_CFG->{network}{$net}{'ip-subnet'}};
422      for my $cmd (@line) {
423         for my $method (keys %$cmd){
424            $cidrlist->add_any($cmd->{$method}) if $method eq 'add';
425            }
426         }
427      }
428
429   my @res = ();
430
431   for my $cidr ($cidrlist->list()) {
432      my $net = new NetAddr::IP $cidr;
433      for my $ip (@$net) {
434         $ip =~ s#/32##;
435         push @res,  $ip;
436         }
437      }
438
439   return @res;
440   }
441
442# liste l'ensemble des routeurs du réseau
443sub get_list_main_router {
444   my @network = @_;
445
446   my @res = ();
447
448   for my $net (@network) {
449      push @res, $KLASK_CFG->{network}{$net}{'main-router'};
450      }
451
452   return @res;
453   }
454
455sub get_human_readable_port {
456   my $sw_model = shift;
457   my $sw_port  = shift;
458   
459   return $sw_port if not $sw_model eq 'HP8000M';
460   
461   my $reste = (($sw_port - 1) % 8) + 1;
462   my $major = int( ($sw_port - 1) / 8 );
463
464   return "$INTERNAL_PORT_MAP{$major}$reste";
465   }
466
467sub get_numerical_port {
468   my $sw_model = shift;
469   my $sw_port  = shift;
470   
471   return $sw_port if not $sw_model eq 'HP8000';
472
473   my $letter = substr($sw_port, 0, 1);
474   
475#   return $port if $letter =~ m/\d/;
476   
477   my $reste =  substr($sw_port, 1);
478   
479   return $INTERNAL_PORT_MAP_REV{$letter} * 8 + $reste;
480   }
481
482################
483# Les commandes
484################
485
486sub cmd_help {
487
488print <<END;
489klask - ports manager and finder for switch
490
491 klask updatedb
492 klask exportdf
493
494 klask searchdb computer
495 klask search   computer
496
497 klask enable  switch port
498 klask disable switch port
499 klask status  switch port
500END
501   }
502
503sub cmd_search {
504   my @computer = @_;
505   
506   init_switch_names();    #nomme les switchs
507   fastping(@computer);
508   for my $clientname (@computer) {
509      my %resol_arp = resolve_ip_arp_host($clientname);          #resolution arp
510      my %where     = find_switch_port($resol_arp{mac_address}); #retrouve l'emplacement
511      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"
512         unless $where{switch_description} eq 'unknow' and $resol_arp{hostname_fq} eq 'unknow' and $resol_arp{mac_address} eq 'unknow';
513      }
514   }
515
516sub cmd_searchdb {
517   my @computer = @_;
518
519   fastping(@computer);
520   my $computerdb = YAML::LoadFile("$KLASK_DB_FILE");
521   
522   LOOP_ON_COMPUTER:
523   for my $clientname (@computer) {
524      my %resol_arp = resolve_ip_arp_host($clientname);      #resolution arp
525      my $ip = $resol_arp{ipv4_address};
526     
527      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
528     
529      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($computerdb->{$ip}{timestamp});
530      $year += 1900;
531      $mon++;
532      my $date = sprintf "%04i-%02i-%02i %02i:%02i", $year,$mon,$mday,$hour,$min;
533
534      printf "%-22s %2s %-30s %-15s %-18s %s\n",
535         $computerdb->{$ip}{switch_name},
536         $computerdb->{$ip}{switch_port},
537         $computerdb->{$ip}{hostname_fq},
538         $ip,
539         $computerdb->{$ip}{mac_address},
540         $date;
541      }
542   }
543
544sub cmd_updatedb {
545   my @network = @_;
546      @network = get_list_network() if not @network;
547
548   test_switchdb_environnement();
549
550   my $computerdb = {};
551      $computerdb = YAML::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
552   my $timestamp = time;
553
554   my %computer_not_detected = ();
555   my $timestamp_last_week = $timestamp - (3600 * 24 * 7);
556
557   my $number_of_computer = get_list_ip(@network); # + 1;
558   my $size_of_database   = keys %$computerdb;
559      $size_of_database   = 1 if $size_of_database == 0;
560   my $i = 0;
561   my $detected_computer = 0;
562
563   init_switch_names('yes');    #nomme les switchs
564
565   { # Remplis le champs portignore des ports d'inter-connection pour chaque switch
566   my $switch_connection = YAML::LoadFile("$KLASK_SW_FILE");
567   my %db_switch_output_port       = %{$switch_connection->{output_port}};
568   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
569   my %db_switch_chained_port = ();
570   for my $swport (keys %db_switch_connected_on_port) {       
571      my ($sw_connect,$port_connect) = split ':', $swport;
572      $db_switch_chained_port{$sw_connect} .= "$port_connect:";
573      }
574   for my $sw (@SWITCH){
575      push @{$sw->{portignore}}, $db_switch_output_port{$sw->{hostname}}  if exists $db_switch_output_port{$sw->{hostname}};
576      if ( exists $db_switch_chained_port{$sw->{hostname}} ) {
577         chop $db_switch_chained_port{$sw->{hostname}};
578         push @{$sw->{portignore}}, split(':',$db_switch_chained_port{$sw->{hostname}});
579         }
580#      print "$sw->{hostname} ++ @{$sw->{portignore}}\n";
581      }
582   }
583
584   my %router_mac_ip = ();
585   DETECT_ALL_ROUTER:
586#   for my $one_router ('194.254.66.254') {
587   for my $one_router ( get_list_main_router(@network) ) {
588      my %resol_arp = resolve_ip_arp_host($one_router);
589      $router_mac_ip{ $resol_arp{mac_address} } = $resol_arp{ipv4_address};
590      }
591
592   ALL_NETWORK:
593   for my $net (@network) {
594
595      my @computer = get_list_ip($net);
596      my $current_interface = get_current_interface($net);
597
598      fastping(@computer);
599
600      LOOP_ON_COMPUTER:
601      for my $one_computer (@computer) {
602         $i++;
603         
604         my $total_percent = int(($i*100)/$number_of_computer);
605
606         my $localtime = time - $timestamp;
607         my ($sec,$min) = localtime($localtime);
608
609         my $time_elapse = 0;
610            $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0;
611         my ($sec_elapse,$min_elapse) = localtime($time_elapse);
612
613         printf "\rComputer scanned: %4i/%i (%2i%%)",  $i,                 $number_of_computer, $total_percent;
614#         printf ", Computer detected: %4i/%i (%2i%%)", $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
615         printf ", detected: %4i/%i (%2i%%)", $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
616         printf " [Time: %02i:%02i / %02i:%02i]", int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
617#         printf "  [%02i:%02i/%02i:%02i]", int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
618         printf " %-14s", $one_computer;
619
620         my %resol_arp = resolve_ip_arp_host($one_computer,$current_interface);
621         
622         # do not search on router connection (why ?)
623         if ( exists $router_mac_ip{$resol_arp{mac_address}}) {
624            $computer_not_detected{$one_computer} = $current_interface;
625            next LOOP_ON_COMPUTER;
626            }
627
628         # do not search on switch inter-connection
629         if (exists $switch_level{$resol_arp{hostname_fq}}) {
630            $computer_not_detected{$one_computer} = $current_interface;
631            next LOOP_ON_COMPUTER;
632            }
633
634         my $switch_proposal = '';
635         if (exists $computerdb->{$resol_arp{ipv4_address}} and exists $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}) {
636            $switch_proposal = $computerdb->{$resol_arp{ipv4_address}}{switch_hostname};
637            }
638
639         # do not have a mac address
640         if ($resol_arp{mac_address} eq 'unknow' or (exists $resol_arp{timestamps} and $resol_arp{timestamps} < ($timestamp - 3 * 3600))) {
641            $computer_not_detected{$one_computer} = $current_interface;
642            next LOOP_ON_COMPUTER;
643            }
644
645         my %where = find_switch_port($resol_arp{mac_address},$switch_proposal);
646
647         #192.168.24.156:
648         #  arp: 00:0B:DB:D5:F6:65
649         #  hostname: pcroyon.hmg.priv
650         #  port: 5
651         #  switch: sw-batH-legi:hp2524
652         #  timestamp: 1164355525
653
654         # do not have a mac address
655#         if ($resol_arp{mac_address} eq 'unknow') {
656#            $computer_not_detected{$one_computer} = $current_interface;
657#            next LOOP_ON_COMPUTER;
658#            }
659
660         # detected on a switch
661         if ($where{switch_description} ne 'unknow') {
662            $detected_computer++;
663            $computerdb->{$resol_arp{ipv4_address}} = {
664               hostname_fq        => $resol_arp{hostname_fq},
665               mac_address        => $resol_arp{mac_address},
666               switch_hostname    => $where{switch_hostname},
667               switch_description => $where{switch_description},
668               switch_port        => $where{switch_port},
669               timestamp          => $timestamp,
670               };
671            next LOOP_ON_COMPUTER;
672            }
673
674         # new in the database but where it is ?
675         if (not exists $computerdb->{$resol_arp{ipv4_address}}) {
676            $detected_computer++;
677            $computerdb->{$resol_arp{ipv4_address}} = {
678               hostname_fq        => $resol_arp{hostname_fq},
679               mac_address        => $resol_arp{mac_address},
680               switch_hostname    => $where{switch_hostname},
681               switch_description => $where{switch_description},
682               switch_port        => $where{switch_port},
683               timestamp          => $resol_arp{timestamp},
684               };
685            }
686
687         # mise a jour du nom de la machine si modification dans le dns
688         $computerdb->{$resol_arp{ipv4_address}}{hostname_fq} = $resol_arp{hostname_fq};
689       
690         # mise à jour de la date de détection si détection plus récente par arpwatch
691         $computerdb->{$resol_arp{ipv4_address}}{timestamp}   = $resol_arp{timestamp} if exists $resol_arp{timestamp} and $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $resol_arp{timestamp};
692
693         # provisoire car changement de nom des attributs
694#         $computerdb->{$resol_arp{ipv4_address}}{mac_address}        = $computerdb->{$resol_arp{ipv4_address}}{arp};
695#         $computerdb->{$resol_arp{ipv4_address}}{switch_description} = $computerdb->{$resol_arp{ipv4_address}}{switch};
696#         $computerdb->{$resol_arp{ipv4_address}}{switch_port}        = $computerdb->{$resol_arp{ipv4_address}}{port};
697       
698         # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine
699#         push @computer_not_detected, $resol_arp{ipv4_address} if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
700         $computer_not_detected{$resol_arp{ipv4_address}} = $current_interface if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
701       
702         }
703      }
704
705   # final end of line at the end of the loop
706   printf "\n";
707
708   my $dirdb = $KLASK_DB_FILE;
709      $dirdb =~ s#/[^/]*$##;
710   mkdir "$dirdb", 0755 unless -d "$dirdb";
711   YAML::DumpFile("$KLASK_DB_FILE", $computerdb);
712
713   for my $one_computer (keys %computer_not_detected) {
714      my $interface = $computer_not_detected{$one_computer};
715      system "arping -c 1 -w 1 -rR -i $interface $one_computer &>/dev/null";
716#      print  "arping -c 1 -w 1 -rR -i $interface $one_computer 2>/dev/null\n";
717      }
718   }
719
720sub cmd_removedb {
721   my @computer = @_;
722
723   test_maindb_environnement();
724
725   my $computerdb = YAML::LoadFile("$KLASK_DB_FILE");
726
727   LOOP_ON_COMPUTER:
728   for my $one_computer (@computer) {
729
730      my %resol_arp = resolve_ip_arp_host($one_computer);
731
732      delete $computerdb->{$resol_arp{ipv4_address}} if exists $computerdb->{$resol_arp{ipv4_address}};
733      }
734
735   my $dirdb = $KLASK_DB_FILE;
736      $dirdb =~ s#/[^/]*$##;
737   mkdir "$dirdb", 0755 unless -d "$dirdb";
738   YAML::DumpFile("$KLASK_DB_FILE", $computerdb);
739   }
740
741sub cmd_exportdb {
742   test_maindb_environnement();
743
744   my $computerdb = YAML::LoadFile("$KLASK_DB_FILE");
745
746   printf "%-24s %-4s            %-30s %-15s %-18s %-s\n", qw(Switch Port Hostname IPv4-Address MAC-Address Date);
747   print "---------------------------------------------------------------------------------------------------------------------------\n";
748
749   LOOP_ON_IP_ADDRESS:
750   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %$computerdb)) {
751   
752#      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow';
753
754      # to be improve in the future
755      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
756
757# dans le futur
758#      next if $computerdb->{$ip}{hostname_fq} eq 'unknow';
759     
760      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($computerdb->{$ip}{timestamp});
761      $year += 1900;
762      $mon++;
763      my $date = sprintf "%04i-%02i-%02i %02i:%02i", $year,$mon,$mday,$hour,$min;
764
765      printf "%-25s  %2s  <-------  %-30s %-15s %-18s %s\n",
766         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
767         $computerdb->{$ip}{switch_port},
768         $computerdb->{$ip}{hostname_fq},
769         $ip,
770         $computerdb->{$ip}{mac_address},
771         $date;
772      }
773   }
774
775sub cmd_iplocation {
776   my $computerdb = YAML::LoadFile("$KLASK_DB_FILE");
777
778   LOOP_ON_IP_ADDRESS:
779   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %$computerdb)) {
780
781      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
782
783      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || '';
784      next if $sw_hostname eq 'unknow';
785 
786      my $sw_location = '';
787      for my $sw (@SWITCH) {
788         next if $sw_hostname ne $sw->{hostname};
789         $sw_location = $sw->{location};
790         last;
791         }
792
793      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq '';
794      }
795   }
796
797sub cmd_enable {
798   my $switch = shift;
799   my $port   = shift;
800   
801   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
802   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
803   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
804   }
805
806sub cmd_disable {
807   my $switch = shift;
808   my $port   = shift;
809   
810   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
811   }
812
813sub cmd_status {
814   my $switch = shift;
815   my $port   = shift;
816   
817   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
818   }
819
820sub cmd_search_mac_on_switch {
821   my $switch_name = shift;
822   my $mac_address = shift;
823   
824   my $research = "1.3.6.1.2.1.17.4.3.1.2".arp_hex_to_dec($mac_address);
825   print "Klask search OID $research on switch $switch_name\n";
826
827   my ($session, $error) = Net::SNMP->session( -hostname => $switch_name );
828   print "$error \n" if $error;
829
830   my $result = $session->get_request(
831      -varbindlist => [$research]
832      );
833   
834   if (not defined($result) or $result->{$research} eq 'noSuchInstance') {
835      print "Klask do not find MAC $mac_address on switch $switch_name\n";
836      $session->close;
837      }
838
839   my $swport = $result->{$research};
840   $session->close;
841
842   print "Klask find MAC $mac_address on switch $switch_name port $swport\n";
843   }
844
845sub cmd_updatesw {
846
847   init_switch_names('yes');    #nomme les switchs
848   print "\n";
849
850   my %where = ();
851   my %db_switch_output_port = ();
852   my %db_switch_ip_hostname = ();
853
854   DETECT_ALL_ROUTER:
855#   for my $one_computer ('194.254.66.254') {
856   for my $one_router ( get_list_main_router(get_list_network()) ) {
857      my %resol_arp = resolve_ip_arp_host($one_router,'*','low');            # resolution arp
858      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
859     
860      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # retrouve les emplacements des routeurs
861      }
862
863   ALL_ROUTER_IP_ADDRESS:
864   for my $ip (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
865   
866      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip}; # /a priori/ idiot car ne sers à rien...
867
868      ALL_SWITCH_CONNECTED:
869      for my $switch_detected ( keys %{$where{$ip}} ) {
870
871         my $switch = $where{$ip}->{$switch_detected};
872
873         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
874         
875         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
876         }
877      }   
878
879#   print "Switch output port\n"; 
880#   print "------------------\n";
881#   for my $sw (sort keys %db_switch_output_port) {
882#      printf "%-25s %2s\n", $sw, $db_switch_output_port{$sw};
883#      }
884#   print "\n";
885
886
887   my %db_switch_link_with = ();
888
889   my @list_switch_ip = ();
890   my @list_switch_ipv4 = ();
891   for my $sw (@SWITCH){
892      push @list_switch_ip, $sw->{hostname};
893      }
894
895   ALL_SWITCH:
896   for my $one_computer (@list_switch_ip) {
897      my %resol_arp = resolve_ip_arp_host($one_computer,'*','low'); # arp resolution
898      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
899     
900      push @list_switch_ipv4,$resol_arp{ipv4_address};
901     
902      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # find port on all switch
903
904      $db_switch_ip_hostname{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
905      }
906     
907   ALL_SWITCH_IP_ADDRESS:
908   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
909   
910      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
911
912      DETECTED_SWITCH:
913      for my $switch_detected ( keys %{$where{$ip}} ) {
914
915         next DETECTED_SWITCH if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostname{$ip}};
916
917         my $switch = $where{$ip}->{$switch_detected};
918
919         next if $switch->{port}     eq '0';
920         next if $switch->{port}     eq $db_switch_output_port{$switch->{hostname}};
921         next if $switch->{hostname} eq $db_switch_ip_hostname{$ip}; # $computerdb->{$ip}{hostname};
922
923         $db_switch_link_with{ $db_switch_ip_hostname{$ip} } ||= {};
924         $db_switch_link_with{ $db_switch_ip_hostname{$ip} }->{ $switch->{hostname} } = $switch->{port};
925         }
926
927      }
928   
929   my %db_switch_connected_on_port = ();
930   my $maybe_more_than_one_switch_connected = 'yes';
931   
932   while ($maybe_more_than_one_switch_connected eq 'yes') {
933      for my $sw (keys %db_switch_link_with) {
934         for my $connect (keys %{$db_switch_link_with{$sw}}) {
935         
936            my $port = $db_switch_link_with{$sw}->{$connect};
937         
938            $db_switch_connected_on_port{"$connect:$port"} ||= {};
939            $db_switch_connected_on_port{"$connect:$port"}->{$sw}++; # Just to define the key
940            }
941         }
942
943      $maybe_more_than_one_switch_connected  = 'no';
944
945      SWITCH_AND_PORT:
946      for my $swport (keys %db_switch_connected_on_port) {
947         
948         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
949         
950         $maybe_more_than_one_switch_connected = 'yes';
951
952         my ($sw_connect,$port_connect) = split ':', $swport;
953         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
954
955         CONNECTED:
956         for my $sw_connected (@sw_on_same_port) {
957           
958            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
959           
960            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
961           
962            for my $other_sw (@sw_on_same_port) {
963               next if $other_sw eq $sw_connected;
964               
965               delete $db_switch_link_with{$other_sw}->{$sw_connect};
966               }
967           
968            # We can not do better for this switch for this loop
969            next SWITCH_AND_PORT;
970            }
971         }
972      }
973
974   my %db_switch_parent =();
975
976   for my $sw (keys %db_switch_link_with) {
977      for my $connect (keys %{$db_switch_link_with{$sw}}) {
978     
979         my $port = $db_switch_link_with{$sw}->{$connect};
980     
981         $db_switch_connected_on_port{"$connect:$port"} ||= {};
982         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
983       
984         $db_switch_parent{$sw} = {switch => $connect, port => $port};
985         }
986      }
987
988   print "Switch output port and parent port connection\n"; 
989   print "---------------------------------------------\n";
990   for my $sw (sort keys %db_switch_output_port) {
991      if (exists $db_switch_parent{$sw}) {
992         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
993         }
994      else {
995         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
996         }
997      }
998   print "\n";
999
1000   print "Switch parent and children port inter-connection\n";
1001   print "------------------------------------------------\n";
1002   for my $swport (sort keys %db_switch_connected_on_port) {       
1003      my ($sw_connect,$port_connect) = split ':', $swport;
1004      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1005         if (exists $db_switch_output_port{$sw}) {
1006            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1007            }
1008         else {
1009            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1010            }
1011         }
1012      }
1013
1014   my $switch_connection = {
1015      output_port       => \%db_switch_output_port,
1016      parent            => \%db_switch_parent,
1017      connected_on_port => \%db_switch_connected_on_port,
1018      link_with         => \%db_switch_link_with,
1019      switch_db         => \%SWITCH_DB,
1020      };
1021     
1022   YAML::DumpFile("$KLASK_SW_FILE", $switch_connection);
1023   }
1024
1025sub cmd_exportsw {
1026   my @ARGV   = @_;
1027
1028   test_switchdb_environnement();
1029
1030   my $format = 'txt';
1031
1032   my $ret = GetOptions(
1033      'format|f=s'  => \$format,
1034      );
1035
1036   my %possible_format = (
1037      txt => \&cmd_exportsw_txt,
1038      dot => \&cmd_exportsw_dot,
1039      );
1040
1041   $format = 'txt' if not defined $possible_format{$format};
1042   
1043   $possible_format{$format}->(@ARGV);
1044   }
1045
1046sub cmd_exportsw_txt {
1047
1048   my $switch_connection = YAML::LoadFile("$KLASK_SW_FILE");
1049
1050   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1051   my %db_switch_parent            = %{$switch_connection->{parent}};
1052   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1053
1054   print "Switch output port and parent port connection\n"; 
1055   print "---------------------------------------------\n";
1056   for my $sw (sort keys %db_switch_output_port) {
1057      if (exists $db_switch_parent{$sw}) {
1058         printf "%-25s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1059         }
1060      else {
1061         printf "%-25s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1062         }
1063      }
1064   print "\n";
1065
1066   print "Switch parent and children port inter-connection\n";
1067   print "------------------------------------------------\n";
1068   for my $swport (sort keys %db_switch_connected_on_port) {       
1069      my ($sw_connect,$port_connect) = split ':', $swport;
1070      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1071         if (exists $db_switch_output_port{$sw}) {
1072            printf "%-25s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1073            }
1074         else {
1075            printf "%-25s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1076            }
1077         }
1078      }
1079   }
1080
1081sub cmd_exportsw_dot {
1082
1083   my $switch_connection = YAML::LoadFile("$KLASK_SW_FILE");
1084   
1085   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1086   my %db_switch_parent            = %{$switch_connection->{parent}};
1087   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1088   my %db_switch_link_with         = %{$switch_connection->{link_with}};
1089   my %db_switch_global            = %{$switch_connection->{switch_db}};
1090
1091   my %db_building= ();
1092   for my $sw (@SWITCH) {
1093      my ($building, $location) = split /\//, $sw->{location}, 2;
1094      $db_building{$building} ||= {};
1095      $db_building{$building}->{$location} ||= {};
1096      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
1097      }
1098 
1099 
1100   print "digraph G {\n";
1101
1102   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
1103   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
1104
1105   my $b=0;
1106   for my $building (keys %db_building) {
1107      $b++;
1108     
1109      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
1110      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
1111
1112      my $l = 0;
1113      for my $loc (keys %{$db_building{$building}}) {
1114         $l++;
1115 
1116         print "\"location$b-$l\" [label = \"$building".'/'.join('\n',split("/",$loc))."\", color = black, fillcolor = orange, style = filled];\n";
1117#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
1118         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
1119
1120         for my $sw (keys %{$db_building{$building}->{$loc}}) {
1121
1122            print "\"$sw:$db_switch_output_port{$sw}\" [label = $db_switch_output_port{$sw}, color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
1123
1124            my $swname  = $sw;
1125               $swname .= '\n-\n'."$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
1126            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
1127            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
1128            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
1129
1130
1131            for my $swport (keys %db_switch_connected_on_port) {
1132               my ($sw_connect,$port_connect) = split ':', $swport;
1133               next if not $sw_connect eq $sw;
1134               next if $port_connect eq $db_switch_output_port{$sw};
1135               print "\"$sw:$port_connect\" [label = $port_connect, color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
1136               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
1137              }
1138            }
1139         }
1140      }
1141
1142#   print "Switch output port and parent port connection\n"; 
1143#   print "---------------------------------------------\n";
1144   for my $sw (sort keys %db_switch_output_port) {
1145      if (exists $db_switch_parent{$sw}) {
1146#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
1147         }
1148      else {
1149         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
1150         }
1151      }
1152   print "\n";
1153
1154#   print "Switch parent and children port inter-connection\n";
1155#   print "------------------------------------------------\n";
1156   for my $swport (sort keys %db_switch_connected_on_port) {       
1157      my ($sw_connect,$port_connect) = split ':', $swport;
1158      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1159         if (exists $db_switch_output_port{$sw}) {
1160            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
1161            }
1162         else {
1163            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
1164            }
1165         }
1166      }
1167
1168print "}\n";
1169   }
1170
1171
1172__END__
1173
1174
1175=head1 NAME
1176
1177klask - ports manager and finder for switch
1178
1179
1180=head1 SYNOPSIS
1181
1182 klask updatedb
1183 klask exportdb
1184
1185 klask updatesw
1186 klask exportsw --format [txt|dot]
1187
1188 klask searchdb computer
1189 klask search   computer
1190
1191 klask enable  switch port
1192 klask disable swith port
1193 klask status  swith port
1194
1195
1196=head1 DESCRIPTION
1197
1198klask is a small tool to find where is a host in a big network. klask mean search in brittany.
1199
1200Klask has now a web site dedicated for it !
1201
1202 http://servforge.legi.inpg.fr/projects/klask
1203
1204
1205=head1 COMMANDS
1206
1207
1208=head2 search
1209
1210This 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.
1211
1212
1213=head2 enable
1214
1215This command activate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1216
1217
1218=head2 disable
1219
1220This command deactivate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1221
1222
1223=head2 status
1224
1225This 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.
1226
1227
1228=head2 updatedb
1229
1230This 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.
1231
1232
1233=head2 exportdb
1234
1235This 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...
1236
1237
1238=head2 updatesw
1239
1240This 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.
1241
1242
1243=head2 exportsw --format [txt|dot]
1244
1245This 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.
1246
1247 klask exportsw --format dot > /tmp/map.dot
1248 dot -Tpng /tmp/map.dot > /tmp/map.png
1249
1250
1251
1252=head1 CONFIGURATION
1253
1254Because 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 !
1255
1256Here an example, be aware with indent, it's important in YAML, do not use tabulation !
1257
1258 default:
1259   community: public
1260   snmpport: 161
1261
1262 network:
1263   labnet:
1264     ip-subnet:
1265       - add: 192.168.1.0/24
1266       - add: 192.168.2.0/24
1267     interface: eth0
1268     main-router: gw1.labnet.local
1269
1270   schoolnet:
1271     ip-subnet:
1272       - add: 192.168.6.0/24
1273       - add: 192.168.7.0/24
1274     interface: eth0.38
1275     main-router: gw2.schoolnet.local
1276
1277 switch:
1278   - hostname: sw1.klask.local
1279     portignore:
1280       - 1
1281       - 2
1282
1283   - hostname: sw2.klask.local
1284     location: BatK / 2 / K203
1285     type: HP2424
1286     portignore:
1287       - 1
1288       - 2
1289
1290I 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.
1291
1292
1293=head1 FILES
1294
1295 /etc/klask.conf
1296 /var/cache/klask/klaskdb
1297 /var/cache/klask/switchdb
1298
1299=head1 SEE ALSO
1300
1301Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
1302
1303
1304=head1 VERSION
1305
13060.4
1307
1308
1309=head1 AUTHOR
1310
1311Written by Gabriel Moreau, Grenoble - France
1312
1313
1314=head1 COPYRIGHT
1315       
1316Copyright (C) 2005-2008 Gabriel Moreau.
1317
1318
1319=head1 LICENCE
1320
1321GPL version 2 or later and Perl equivalent
Note: See TracBrowser for help on using the repository browser.