source: trunk/klask @ 39

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