source: trunk/klask @ 41

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