source: trunk/klask @ 148

Last change on this file since 148 was 148, checked in by g7moreau, 8 years ago
  • Add Dell N2048 switch model
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 81.0 KB
Line 
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005-2013 Gabriel Moreau.
4#
5# $Id: klask 148 2016-05-12 09:28:04Z g7moreau $
6
7use strict;
8use warnings;
9use version; our $VERSION = qv('0.5.5');
10
11use Readonly;
12use FileHandle;
13use Net::SNMP;
14#use YAML;
15use YAML::Syck;
16use Net::Netmask;
17use Net::CIDR::Lite;
18use NetAddr::IP;
19use Getopt::Long qw(GetOptions);
20use Socket;
21use List::Util 'shuffle';
22
23# apt-get install snmp fping libnet-cidr-lite-perl libnet-netmask-perl libnet-snmp-perl libnetaddr-ip-perl libyaml-perl
24# libcrypt-des-perl libcrypt-hcesha-perl libdigest-hmac-perl
25# arping net-tools fping bind9-host arpwatch
26
27my $KLASK_VAR      = '/var/lib/klask';
28my $KLASK_CFG_FILE = '/etc/klask/klask.conf';
29my $KLASK_DB_FILE  = "$KLASK_VAR/klaskdb";
30my $KLASK_SW_FILE  = "$KLASK_VAR/switchdb";
31
32test_running_environnement();
33
34my $KLASK_CFG = YAML::Syck::LoadFile("$KLASK_CFG_FILE");
35
36my %DEFAULT = %{ $KLASK_CFG->{default} };
37my @SWITCH  = @{ $KLASK_CFG->{switch}  };
38
39my %switch_level = ();
40my %SWITCH_DB    = ();
41LEVEL_OF_EACH_SWITCH:
42for my $sw (@SWITCH){
43   $switch_level{$sw->{hostname}} = $sw->{level} || $DEFAULT{switch_level}  || 2;
44   $SWITCH_DB{$sw->{hostname}} = $sw;
45   }
46@SWITCH = reverse sort { $switch_level{$a->{hostname}} <=> $switch_level{$b->{hostname}} } @{$KLASK_CFG->{switch}};
47
48my %SWITCH_PORT_COUNT = ();
49
50my %CMD_DB = (
51   'help'                 => \&cmd_help,
52   'version'              => \&cmd_version,
53   'exportdb'             => \&cmd_exportdb,
54   'updatedb'             => \&cmd_updatedb,
55   'searchdb'             => \&cmd_searchdb,
56   'removedb'             => \&cmd_removedb,
57   'cleandb'              => \&cmd_cleandb,
58   'search'               => \&cmd_search,
59   'enable'               => \&cmd_enable,
60   'disable'              => \&cmd_disable,
61   'status'               => \&cmd_status,
62   'updatesw'             => \&cmd_updatesw,
63   'exportsw'             => \&cmd_exportsw,
64   'iplocation'           => \&cmd_ip_location,
65   'ip-free'              => \&cmd_ip_free,
66   'search-mac-on-switch' => \&cmd_search_mac_on_switch,
67   'bad-vlan-id'          => \&cmd_bad_vlan_id,
68   'set-vlan-port'        => \&cmd_set_vlan_port,
69   'get-vlan-port'        => \&cmd_get_vlan_port,
70   'set-vlan-name'        => \&cmd_set_vlan_name,
71   'get-vlan-name'        => \&cmd_get_vlan_name,
72   'rebootsw'             => \&cmd_rebootsw,
73   );
74
75Readonly my %INTERNAL_PORT_MAP => (
76   0 => 'A',
77   1 => 'B',
78   2 => 'C',
79   3 => 'D',
80   4 => 'E',
81   5 => 'F',
82   6 => 'G',
83   7 => 'H',
84   );
85Readonly my %INTERNAL_PORT_MAP_REV => reverse %INTERNAL_PORT_MAP;
86
87Readonly my %SWITCH_KIND => (
88   # HP
89   J3299A => { model => 'HP224M',     match => 'HP J3299A ProCurve Switch 224M'    },
90   J4120A => { model => 'HP1600M',    match => 'HP J4120A ProCurve Switch 1600M'   },
91   J9029A => { model => 'HP1800-8G',  match => 'PROCURVE J9029A'                   },
92   J9449A => { model => 'HP1810-8G',  match => 'HP ProCurve 1810G - 8 GE'          },
93   J4093A => { model => 'HP2424M',    match => 'HP J4093A ProCurve Switch 2424M'   },
94   J9279A => { model => 'HP2510G-24', match => 'ProCurve J9279A Switch 2510G-24'   },
95   J9280A => { model => 'HP2510G-48', match => 'ProCurve J9280A Switch 2510G-48'   },
96   J4813A => { model => 'HP2524',     match => 'HP J4813A ProCurve Switch 2524'    },
97   J4900A => { model => 'HP2626A',    match => 'HP J4900A ProCurve Switch 2626'    },
98   J4900B => { model => 'HP2626B',    match => 'J4900B.+?Switch 2626'              },# ProCurve J4900B Switch 2626 # HP J4900B ProCurve Switch 2626
99   J4899B => { model => 'HP2650',     match => 'ProCurve J4899B Switch 2650'       },
100   J9021A => { model => 'HP2810-24G', match => 'ProCurve J9021A Switch 2810-24G'   },
101   J9022A => { model => 'HP2810-48G', match => 'ProCurve J9022A Switch 2810-48G'   },
102   J8692A => { model => 'HP3500-24G', match => 'J8692A Switch 3500yl-24G'          },
103   J4903A => { model => 'HP2824',     match => 'J4903A.+?Switch 2824,'             },
104   J4110A => { model => 'HP8000M',    match => 'HP J4110A ProCurve Switch 8000M'   },
105   JE074A => { model => 'HP5120-24G', match => 'HP Comware.+?A5120-24G EI'         },
106   JD374A => { model => 'HP5500-24F', match => 'HP Comware.+?A5500-24G-SFP EI'     },
107   # BayStack
108   BS350T => { model => 'BS350T',     match => 'BayStack 350T HW'                  },
109   # Nexans
110   N3483G => { model => 'NA3483-6G',  match => 'GigaSwitch V3 TP SFP-I 48V ES3'    },
111   # DELL
112   N2048  => { model => 'DN4064F',    match => 'Dell Networking N2048,'            },
113   N4064F => { model => 'DN4064F',    match => 'Dell Networking N4064F,'           },
114   # 3COM
115   'H3C5500'        => { model => 'H3C5500',        match => 'H3C S5500-SI Series'              },
116   '3C17203'        => { model => '3C17203',        match => '3Com SuperStack 3 24-Port'        },
117   '3C17204'        => { model => '3C17204',        match => '3Com SuperStack 3 48-Port'        },
118   '3CR17562-91'    => { model => '3CR17562-91',    match => '3Com Switch 4500 50-Port'         },
119   '3CR17255-91'    => { model => '3CR17255-91',    match => '3Com Switch 5500G-EI 48-Port'     },
120   '3CR17251-91'    => { model => '3CR17251-91',    match => '3Com Switch 5500G-EI 48-Port'     },
121   '3CR17571-91'    => { model => '3CR17571-91',    match => '3Com Switch 4500 PWR 26-Port'     },
122   '3CRWX220095A'   => { model => '3CRWX220095A',   match => '3Com Wireless LAN Controller'     },
123   '3CR17254-91'    => { model => '3CR17254-91',    match => '3Com Switch 5500G-EI 24-Port'     },
124   '3CRS48G-24S-91' => { model => '3CRS48G-24S-91', match => '3Com Switch 4800G 24-Port'        },
125   '3CRS48G-48S-91' => { model => '3CRS48G-48S-91', match => '3Com Switch 4800G 48-Port'        },
126   '3C17708'        => { model => '3C17708',        match => '3Com Switch 4050'                 },
127   '3C17709'        => { model => '3C17709',        match => '3Com Switch 4060'                 },
128   '3C17707'        => { model => '3C17707',        match => '3Com Switch 4070'                 },
129   '3CR17258-91'    => { model => '3CR17258-91',    match => '3Com Switch 5500G-EI 24-Port SFP' },
130   '3CR17181-91'    => { model => '3CR17181-91',    match => '3Com Switch 5500-EI 28-Port FX'   },
131   '3CR17252-91'    => { model => '3CR17252-91',    match => '3Com Switch 5500G-EI PWR 24-Port' },
132   '3CR17253-91'    => { model => '3CR17253-91',    match => '3Com Switch 5500G-EI PWR 48-Port' },
133   '3CR17250-91'    => { model => '3CR17250-91',    match => '3Com Switch 5500G-EI 24-Port'     },
134   '3CR17561-91'    => { model => '3CR17561-91',    match => '3Com Switch 4500 26-Port'         },
135   '3CR17572-91'    => { model => '3CR17572-91',    match => '3Com Switch 4500 PWR 50-Port'     },
136   '3C17702-US'     => { model => '3C17702-US',     match => '3Com Switch 4900 SX'              },
137   '3C17700'        => { model => '3C17700',        match => '3Com Switch 4900'                 },
138   );
139
140Readonly my %OID_NUMBER => (
141   sysDescription  => '1.3.6.1.2.1.1.1.0',
142   sysName         => '1.3.6.1.2.1.1.5.0',
143   sysContact      => '1.3.6.1.2.1.1.4.0',
144   sysLocation     => '1.3.6.1.2.1.1.6.0',
145   searchPort1     => '1.3.6.1.2.1.17.4.3.1.2',       # BRIDGE-MIB (802.1D).
146   searchPort2     => '1.3.6.1.2.1.17.7.1.2.2.1.2',   # Q-BRIDGE-MIB (802.1Q) add 0 if unknown vlan id
147   vlanPortDefault => '1.3.6.1.2.1.17.7.1.4.5.1.1',   # dot1qPvid
148   vlanStatus      => '1.3.6.1.2.1.17.7.1.4.3.1.5',   # integer 4 Create, 6 Destroy
149   vlanName        => '1.3.6.1.2.1.17.7.1.4.3.1.1',   # string
150   hpicfReset      => '1.3.6.1.4.1.11.2.14.11.1.4.1', # HP reboot switch
151   ifName          => '1.3.6.1.2.1.31.1.1.1.1',       # Interface name (give port number)
152   );
153
154Readonly my $RE_MAC_ADDRESS  => qr{ [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} }xms;
155Readonly my $RE_IPv4_ADDRESS => qr{ [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} }xms;
156
157Readonly my $RE_FLOAT_HOSTNAME => qr{ ^float }xms;
158
159
160################
161# principal
162################
163
164my $cmd = shift @ARGV || 'help';
165if (defined $CMD_DB{$cmd}) {
166   $CMD_DB{$cmd}->(@ARGV);
167   }
168else {
169   print {*STDERR} "klask: command $cmd not found\n\n";
170   $CMD_DB{help}->();
171   exit 1;
172   }
173
174exit;
175
176sub test_running_environnement {
177   die "Configuration file $KLASK_CFG_FILE does not exists. Klask need it !\n" if not -e "$KLASK_CFG_FILE";
178   die "Var folder $KLASK_VAR does not exists. Klask need it !\n"              if not -d "$KLASK_VAR";
179   return;
180   }
181
182sub test_switchdb_environnement {
183   die "Switch database $KLASK_SW_FILE does not exists. Launch updatesw before this command !\n" if not -e "$KLASK_SW_FILE";
184   return;
185   }
186
187sub test_maindb_environnement {
188   die "Main database $KLASK_DB_FILE does not exists. Launch updatedb before this command !\n" if not -e "$KLASK_DB_FILE";
189   return;
190   }
191
192###
193# fast ping dont l'objectif est de remplir la table arp de la machine
194sub fast_ping {
195   # Launch this command without waiting...
196   system "fping -q -c 1 @_ >/dev/null 2>&1 &";
197   return;
198   }
199
200sub shell_command {
201   my $cmd = shift;
202
203   my $fh     = new FileHandle;
204   my $result = '';
205   open $fh, q{-|}, "LANG=C $cmd" or die "Can't exec $cmd\n";
206   $result .= <$fh>;
207   close $fh;
208   chomp $result;
209   return $result;
210   }
211
212###
213# donne l'@ ip, dns, arp en fonction du dns OU de l'ip
214sub resolve_ip_arp_host {
215   my $param_ip_or_host = shift;
216   my $interface = shift || q{*};
217   my $type      = shift || q{fast};
218
219   my %ret = (
220      hostname_fq  => 'unknow',
221      ipv4_address => '0.0.0.0',
222      mac_address  => 'unknow',
223      );
224
225   # perl -MSocket -E 'say inet_ntoa(scalar gethostbyname("tech7meylan.hmg.inpg.fr"))'
226   my $packed_ip = scalar gethostbyname($param_ip_or_host);
227   return %ret if not defined $packed_ip;
228   $ret{ipv4_address} = inet_ntoa($packed_ip);
229
230   # perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("194.254.66.240"), AF_INET)'
231   my $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET);
232   $ret{hostname_fq} = $hostname_fq if defined $hostname_fq;
233
234   # my $cmd = q{grep  -he '\b} . $param_ip_or_host . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
235   #my $cmd = q{grep  -he '\b} . $ret{ipv4_address} . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
236   my $cmd = q{grep  -He '\b} . $ret{ipv4_address} . q{\b' } . "/var/lib/arpwatch/$interface.dat" . '| sed -e \'s|^/var/lib/arpwatch/\(.*\)\.dat:|\1 |;\' | sort -rn -k 4,4 | head -1';
237   #grep -He 194.254.66.252 /var/lib/arpwatch/*.dat | sed -e 's|^/var/lib/arpwatch/\(.*\)\.dat:|\1\t|;' | sort -rn -k 4,4 | head -1
238
239   my $cmd_arpwatch = shell_command $cmd;
240   #my ($arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch;
241   my ($interface2, $arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch;
242
243   $ret{interface}    = $interface2 || $interface;
244   $ret{mac_address}  = $arp       if $arp;
245   $ret{timestamp}    = $timestamp if $timestamp;
246
247   my $nowtimestamp = time;
248
249   if ( $type eq 'fast' and ( not defined $timestamp or $timestamp < ( $nowtimestamp - 45 * 60 ) ) ) { # 45 min
250      $ret{mac_address} = 'unknow';
251      return %ret;
252      }
253
254   # resultat de la commande arp
255   # tech7meylan.hmg.inpg.fr (194.254.66.240) at 00:14:22:45:28:A9 [ether] on eth0
256   # sw2-batF0-legi.hmg.priv (192.168.22.112) at 00:30:c1:76:9c:01 [ether] on eth0.37
257   my $cmd_arp  = shell_command "arp -a $param_ip_or_host -i $ret{interface}";
258   if ( $cmd_arp =~ m{ (\S*) \s \( ( $RE_IPv4_ADDRESS ) \) \s at \s ( $RE_MAC_ADDRESS ) }xms ) {
259      ( $ret{hostname_fq}, $ret{ipv4_address}, $ret{mac_address} )  = ($1, $2, $3);
260      }
261
262   # Normalize MAC Address
263   if ($ret{mac_address} ne 'unknow') {
264      my @paquets = ();
265      foreach ( split m/ : /xms, $ret{mac_address} ) {
266         my @chars = split m//xms, uc "00$_";
267         push @paquets, "$chars[-2]$chars[-1]";
268         }
269      $ret{mac_address} = join q{:}, @paquets;
270      }
271
272   return %ret;
273   }
274
275# Find Surname of a switch
276sub get_switch_model {
277   my $sw_snmp_description = shift || 'unknow';
278
279   for my $sw_kind (keys %SWITCH_KIND) {
280      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{match}/ms; # option xms break search, why ?
281
282      return $SWITCH_KIND{$sw_kind}->{model};
283      }
284
285   return $sw_snmp_description;
286   }
287
288###
289# va rechercher le nom des switchs pour savoir qui est qui
290sub init_switch_names {
291   my $verbose = shift;
292
293   printf "%-26s                %-25s %s\n",'Switch','Description','Type' if $verbose;
294   print "------------------------------------------------------------------------------\n" if $verbose;
295
296   INIT_EACH_SWITCH:
297   for my $sw (@SWITCH) {
298      my %session = ( -hostname   => $sw->{hostname} );
299         $session{-version} = $sw->{version}   || 1;
300         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
301         if (exists $sw->{version} and $sw->{version} eq '3') {
302            $session{-username} = $sw->{username} || 'snmpadmin';
303            }
304         else {
305            $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
306            }
307
308      $sw->{local_session} = \%session;
309
310      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
311      print "$error \n" if $error;
312
313      my $result = $session->get_request(
314         -varbindlist => [
315            $OID_NUMBER{sysDescription},
316            $OID_NUMBER{sysName},
317            $OID_NUMBER{sysContact},
318            $OID_NUMBER{sysLocation},
319            ]
320         );
321      $sw->{description} = $result->{$OID_NUMBER{sysName}} || $sw->{hostname};
322      $sw->{model} = get_switch_model( $result->{$OID_NUMBER{sysDescription}});
323      #$sw->{location} = $result->{"1.3.6.1.2.1.1.6.0"} || $sw->{hostname};
324      #$sw->{contact} = $result->{"1.3.6.1.2.1.1.4.0"} || $sw->{hostname};
325      $session->close;
326
327      # Ligne à virer car on récupère maintenant le modèle du switch
328      my ($desc, $type) = split m/ : /xms, $sw->{description}, 2;
329      printf "%-26s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, $sw->{model} if $verbose;
330      }
331
332   print "\n" if $verbose;
333   return;
334   }
335
336###
337# convertit l'hexa (uniquement 2 chiffres) en decimal
338sub digit_hex_to_dec {
339   #00:0F:1F:43:E4:2B
340   my $car = '00' . uc shift;
341
342   return '00' if $car eq '00UNKNOW';
343   my %table = (
344      '0'=>'0',  '1'=>'1',  '2'=>'2',  '3'=>'3',  '4'=>'4',
345      '5'=>'5',  '6'=>'6',  '7'=>'7',  '8'=>'8',  '9'=>'9',
346      'A'=>'10', 'B'=>'11', 'C'=>'12', 'D'=>'13', 'E'=>'14', 'F'=>'15',
347      );
348   my @chars = split m//xms, $car;
349   return $table{$chars[-2]}*16 + $table{$chars[-1]};
350   }
351
352#--------------------------------------------------------------------------------
353
354sub normalize_mac_address {
355   my $mac_address = shift;
356
357   # D07E-28D1-7AB8 or d07e28-d17ab8
358   if ($mac_address =~ m{^ (?: [0-9A-Fa-f]{4} -){2} [0-9A-Fa-f]{4} $}xms
359      or $mac_address =~ m{^ [0-9A-Fa-f]{6} - [0-9A-Fa-f]{6} $}xms) {
360      $mac_address =~ s/-//g;
361      return join q{:}, unpack('(A2)*', uc($mac_address));
362      }
363
364   return join q{:}, map { substr( uc("00$_"), -2) } split m/ [:-] /xms, $mac_address;
365   }
366
367#--------------------------------------------------------------------------------
368# convertit l'@ mac en decimal
369sub mac_address_hex_to_dec {
370   #00:0F:1F:43:E4:2B
371   my $mac_address = shift;
372
373   my @paquets = split m/ : /xms, $mac_address;
374   my $return = q{};
375   foreach(@paquets) {
376      $return .= q{.} . digit_hex_to_dec($_);
377      }
378   return $return;
379   }
380
381###
382# va rechercher le port et le switch sur lequel est la machine
383sub find_switch_port {
384   my $mac_address     = shift;
385   my $switch_proposal = shift || q{};
386   my $vlan_id = shift || 0;
387
388   my %ret;
389   $ret{switch_description} = 'unknow';
390   $ret{switch_port} = '0';
391
392   return %ret if $mac_address eq 'unknow';;
393
394   my @switch_search = @SWITCH;
395   if ($switch_proposal ne q{}) {
396      for my $sw (@SWITCH) {
397         next if $sw->{hostname} ne $switch_proposal;
398         unshift @switch_search, $sw;
399         last;
400         }
401      }
402
403   my $research1 = $OID_NUMBER{searchPort1} . mac_address_hex_to_dec($mac_address);
404   my $research2 = $OID_NUMBER{searchPort2} .'.'. $vlan_id . mac_address_hex_to_dec($mac_address);
405
406   LOOP_ON_SWITCH:
407   for my $sw (@switch_search) {
408      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
409      print "$error \n" if $error;
410
411      my $result = $session->get_request(
412         -varbindlist => [$research1]
413         );
414      if (not defined $result) {
415         $result = $session->get_request(
416            -varbindlist => [$research2]
417            );
418         $result->{$research1} = $result->{$research2} if defined $result;
419         }
420
421      if (not (defined $result and $result->{$research1} ne 'noSuchInstance')) {
422         $session->close;
423         next LOOP_ON_SWITCH;
424         }
425
426         my $swport = $result->{$research1};
427         $session->close;
428
429         # IMPORTANT !!
430         # ceci empeche la detection sur certains port ...
431         # en effet les switch sont relies entre eux par un cable reseau et du coup
432         # tous les arp de toutes les machines sont presentes sur ces ports (ceux choisis ici sont les miens)
433         # cette partie est a ameliore, voir a configurer dans l'entete
434         # 21->24 45->48
435#         my $flag = 0;
436         SWITCH_PORT_IGNORE:
437         foreach my $p (@{$sw->{portignore}}) {
438            next SWITCH_PORT_IGNORE if $swport ne get_numerical_port($sw->{model},$p);
439#            $flag = 1;
440            next LOOP_ON_SWITCH;
441            }
442#         if ($flag == 0) {
443            $ret{switch_hostname}    = $sw->{hostname};
444            $ret{switch_description} = $sw->{description};
445            $ret{switch_port}        = get_human_readable_port($sw->{model}, $swport); # $swport;
446
447            last LOOP_ON_SWITCH;
448#            }
449#         }
450#      $session->close;
451      }
452   return %ret;
453   }
454
455###
456# va rechercher les port et les switch sur lequel est la machine
457sub find_all_switch_port {
458   my $mac_address = shift;
459   my $vlan_id     = shift || 0;
460
461   my $ret = {};
462
463   return $ret if $mac_address eq 'unknow';
464
465#   for my $sw (@SWITCH) {
466#      next if exists $SWITCH_PORT_COUNT{$sw->{hostname}};
467#
468#      $SWITCH_PORT_COUNT{$sw->{hostname}} = {};
469#      print "DEBUG: SWITCH_PORT_COUNT defined for $sw->{hostname}\n" if $DEBUG xor 2;
470#      }
471
472   my $research1 = $OID_NUMBER{searchPort1} . mac_address_hex_to_dec($mac_address);
473   my $research2 = $OID_NUMBER{searchPort2} .'.'. $vlan_id . mac_address_hex_to_dec($mac_address);
474   LOOP_ON_ALL_SWITCH:
475   for my $sw (@SWITCH) {
476      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
477      print "$error \n" if $error;
478
479      my $result = $session->get_request(
480         -varbindlist => [$research1]
481         );
482      if (not defined $result) {
483         $result = $session->get_request(
484            -varbindlist => [$research2]
485            );
486         $result->{$research1} = $result->{$research2} if defined $result;
487         }
488
489      if (defined $result and $result->{$research1} ne 'noSuchInstance') {
490         my $swport = $result->{$research1};
491
492         $ret->{$sw->{hostname}} = {};
493         $ret->{$sw->{hostname}}{hostname}    = $sw->{hostname};
494         $ret->{$sw->{hostname}}{description} = $sw->{description};
495         $ret->{$sw->{hostname}}{port}        = get_human_readable_port($sw->{model}, $swport);
496
497#         $SWITCH_PORT_COUNT{$sw->{hostname}}->{$swport}++;
498         }
499
500      $session->close;
501      }
502   return $ret;
503   }
504
505sub get_list_network {
506
507   return keys %{$KLASK_CFG->{network}};
508   }
509
510sub get_current_interface {
511   my $vlan_name = shift;
512
513   return $KLASK_CFG->{network}{$vlan_name}{interface};
514   }
515
516sub get_current_vlan_id {
517   my $vlan_name = shift;
518
519   return $KLASK_CFG->{network}{$vlan_name}{'vlan-id'};
520   }
521
522sub get_current_vlan_name_for_interface {
523   my $interface = shift;
524
525   for my $vlan_name (keys %{$KLASK_CFG->{network}}) {
526      next if $KLASK_CFG->{network}{$vlan_name}{interface} ne $interface;
527      return $vlan_name;
528      }
529   }
530
531###
532# liste l'ensemble des adresses ip d'un réseau
533sub get_list_ip {
534   my @vlan_name = @_;
535
536   my $cidrlist = Net::CIDR::Lite->new;
537
538   for my $net (@vlan_name) {
539      my @line  = @{$KLASK_CFG->{network}{$net}{'ip-subnet'}};
540      for my $cmd (@line) {
541         for my $method (keys %{$cmd}){
542            $cidrlist->add_any($cmd->{$method}) if $method eq 'add';
543            }
544         }
545      }
546
547   my @res = ();
548
549   for my $cidr ($cidrlist->list()) {
550      my $net = new NetAddr::IP $cidr;
551      for my $ip (@{$net}) {
552         $ip =~ s{ /32 }{}xms;
553         push @res,  $ip;
554         }
555      }
556
557   return @res;
558   }
559
560# liste l'ensemble des routeurs du réseau
561sub get_list_main_router {
562   my @vlan_name = @_;
563
564   my @res = ();
565
566   for my $net (@vlan_name) {
567      push @res, $KLASK_CFG->{network}{$net}{'main-router'};
568      }
569
570   return @res;
571   }
572
573sub get_human_readable_port {
574   my $sw_model = shift;
575   my $sw_port  = shift;
576
577   if ($sw_model eq 'HP8000M') {
578
579      my $reste = (($sw_port - 1) % 8) + 1;
580      my $major = int (($sw_port - 1) / 8);
581      return "$INTERNAL_PORT_MAP{$major}$reste";
582      }
583
584   if ($sw_model eq 'HP2424M') {
585      if ($sw_port > 24) {
586         
587         my $reste = $sw_port - 24;
588         return "A$reste";
589         }
590      }
591
592   if ($sw_model eq 'HP1600M') {
593      if ($sw_port > 16) {
594         
595         my $reste = $sw_port - 16;
596         return "A$reste";
597         }
598      }
599
600   if ($sw_model eq 'HP2810-48G' or $sw_model eq 'HP2810-24G') {
601      if ($sw_port > 48) {
602         
603         my $reste = $sw_port - 48;
604         return "Trk$reste";
605         }
606      }
607
608   if ($sw_model eq 'HP3500-24G') {
609      if ($sw_port > 289) {
610         
611         my $reste = $sw_port - 289;
612         return "Trk$reste";
613         }
614      }
615
616   return $sw_port;
617   }
618
619sub get_numerical_port {
620   my $sw_model = shift;
621   my $sw_port  = shift;
622
623   if ($sw_model eq 'HP8000M') {
624
625      my $letter = substr $sw_port, 0, 1;
626      my $reste =  substr $sw_port, 1;
627
628      return $INTERNAL_PORT_MAP_REV{$letter} * 8 + $reste;
629      }
630
631   if ($sw_model eq 'HP2424M') {
632      if ($sw_port =~ m/^A/xms ) {
633
634         my $reste =  substr $sw_port, 1;
635
636         return 24 + $reste;
637         }
638      }
639
640   if ($sw_model eq 'HP1600M') {
641      if ($sw_port =~ m/^A/xms ) {
642
643         my $reste =  substr $sw_port, 1;
644
645         return 16 + $reste;
646         }
647      }
648
649   if ($sw_model eq 'HP2810-48G' or $sw_model eq 'HP2810-24G') {
650      if ($sw_port =~ m/^Trk/xms ) {
651
652         my $reste =  substr $sw_port, 3;
653
654         return 48 + $reste;
655         }
656      }
657
658   if ($sw_model eq 'HP3500-24G') {
659      if ($sw_port =~ m/^Trk/xms ) {
660
661         my $reste =  substr $sw_port, 3;
662
663         return 289 + $reste;
664         }
665      }
666
667   return $sw_port;
668   }
669
670################
671# Les commandes
672################
673
674sub cmd_help {
675
676print <<'END';
677klask - ports manager and finder for switch
678
679 klask version
680
681 klask updatedb
682 klask exportdb --format [txt|html]
683 klask removedb computer*
684 klask cleandb  --day number_of_day --verbose
685
686 klask updatesw
687 klask exportsw --format [txt|dot]
688
689 klask searchdb computer
690 klask search   computer
691 klask search-mac-on-switch switch mac_addr
692
693 klask ip-free --day number_of_day --format [txt|html] [vlan_name]
694
695 klask bad-vlan-id
696
697 klask enable  switch port
698 klask disable switch port
699 klask status  switch port
700END
701   return;
702   }
703
704sub cmd_version {
705
706print <<'END';
707Klask - ports manager and finder for switch
708Copyright (C) 2005-2013 Gabriel Moreau
709
710END
711   print ' $Rev: 148 $'."\n";
712   print ' $Date: 2016-05-12 09:28:04 +0000 (Thu, 12 May 2016) $'."\n";
713   print ' $Id: klask 148 2016-05-12 09:28:04Z g7moreau $'."\n";
714   return;
715   }
716
717sub cmd_search {
718   my @computer = @_;
719
720   init_switch_names();    #nomme les switchs
721   fast_ping(@computer);
722
723   LOOP_ON_COMPUTER:
724   for my $clientname (@computer) {
725      my %resol_arp = resolve_ip_arp_host($clientname);          #resolution arp
726      my $vlan_name = get_current_vlan_name_for_interface($resol_arp{interface});
727      my $vlan_id   = get_current_vlan_id($vlan_name);
728      my %where     = find_switch_port($resol_arp{mac_address}, '', $vlan_id); #retrouve l'emplacement
729
730      next LOOP_ON_COMPUTER if $where{switch_description} eq 'unknow' or $resol_arp{hostname_fq} eq 'unknow' or $resol_arp{mac_address} eq 'unknow';
731
732      printf '%-22s %2s %-30s %-15s %18s',
733         $where{switch_hostname},
734         $where{switch_port},
735         $resol_arp{hostname_fq},
736         $resol_arp{ipv4_address},
737         $resol_arp{mac_address}."\n";
738      }
739   return;
740   }
741
742sub cmd_searchdb {
743   my @ARGV  = @_;
744
745   my $kind;
746
747   GetOptions(
748      'kind=s'   => \$kind,
749      );
750
751   my %possible_search = (
752      host  => \&cmd_searchdb_host,
753      mac   => \&cmd_searchdb_mac,
754      );
755
756   $kind = 'host' if not defined $possible_search{$kind};
757
758   $possible_search{$kind}->(@ARGV);
759   return;
760   }
761
762
763sub cmd_searchdb_host {
764   my @computer = @_;
765
766   fast_ping(@computer);
767   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
768
769   LOOP_ON_COMPUTER:
770   for my $clientname (@computer) {
771      my %resol_arp = resolve_ip_arp_host($clientname);      #resolution arp
772      my $ip = $resol_arp{ipv4_address};
773
774      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
775
776      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
777      $year += 1900;
778      $mon++;
779      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
780
781      printf "%-22s %2s %-30s %-15s %-18s %s\n",
782         $computerdb->{$ip}{switch_hostname},
783         $computerdb->{$ip}{switch_port},
784         $computerdb->{$ip}{hostname_fq},
785         $ip,
786         $computerdb->{$ip}{mac_address},
787         $date;
788      }
789   return;
790   }
791
792sub cmd_searchdb_mac {
793   my @mac = map { normalize_mac_address($_) } @_;
794
795   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
796
797   LOOP_ON_MAC:
798   for my $mac (@mac) {
799      LOOP_ON_COMPUTER:
800      for my $ip (keys %{$computerdb}) {
801         next LOOP_ON_COMPUTER if $mac ne $computerdb->{$ip}{mac_address};
802 
803         my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
804         $year += 1900;
805         $mon++;
806         my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
807
808         printf "%-22s %2s %-30s %-15s %-18s %s\n",
809            $computerdb->{$ip}{switch_hostname},
810            $computerdb->{$ip}{switch_port},
811            $computerdb->{$ip}{hostname_fq},
812            $ip,
813            $computerdb->{$ip}{mac_address},
814            $date;
815         #next LOOP_ON_MAC;
816         }
817
818      }
819   return;
820   }
821
822sub cmd_updatedb {
823   my @network = @_;
824      @network = get_list_network() if not @network;
825
826   test_switchdb_environnement();
827
828   my $computerdb = {};
829      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
830   my $timestamp = time;
831
832   my %computer_not_detected = ();
833   my $timestamp_last_week = $timestamp - (3600 * 24 * 7);
834
835   my $number_of_computer = get_list_ip(@network); # + 1;
836   my $size_of_database   = keys %{$computerdb};
837      $size_of_database   = 1 if $size_of_database == 0;
838   my $i = 0;
839   my $detected_computer = 0;
840
841   init_switch_names('yes');    #nomme les switchs
842
843   { # Remplis le champs portignore des ports d'inter-connection pour chaque switch
844   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
845   my %db_switch_output_port       = %{$switch_connection->{output_port}};
846   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
847   my %db_switch_chained_port = ();
848   for my $swport (keys %db_switch_connected_on_port) {
849      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
850      $db_switch_chained_port{$sw_connect} .= "$port_connect:";
851      }
852   for my $sw (@SWITCH){
853      push @{$sw->{portignore}}, $db_switch_output_port{$sw->{hostname}}  if exists $db_switch_output_port{$sw->{hostname}};
854      if ( exists $db_switch_chained_port{$sw->{hostname}} ) {
855         chop $db_switch_chained_port{$sw->{hostname}};
856         push @{$sw->{portignore}}, split m/ : /xms, $db_switch_chained_port{$sw->{hostname}};
857         }
858#      print "$sw->{hostname} ++ @{$sw->{portignore}}\n";
859      }
860   }
861
862   my %router_mac_ip = ();
863   DETECT_ALL_ROUTER:
864#   for my $one_router ('194.254.66.254') {
865   for my $one_router ( get_list_main_router(@network) ) {
866      my %resol_arp = resolve_ip_arp_host($one_router);
867      $router_mac_ip{ $resol_arp{mac_address} } = $resol_arp{ipv4_address};
868      }
869
870   ALL_NETWORK:
871   for my $net (@network) {
872
873      my @computer = get_list_ip($net);
874      my $current_interface = get_current_interface($net);
875
876      fast_ping(@computer);
877
878      LOOP_ON_COMPUTER:
879      for my $one_computer (@computer) {
880         $i++;
881
882         my $total_percent = int (($i*100)/$number_of_computer);
883
884         my $localtime = time - $timestamp;
885         my ($sec,$min) = localtime $localtime;
886
887         my $time_elapse = 0;
888            $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0;
889         my ($sec_elapse,$min_elapse) = localtime $time_elapse;
890
891         printf "\rComputer scanned: %4i/%i (%2i%%)",  $i,                 $number_of_computer, $total_percent;
892         printf ', detected: %4i/%i (%2i%%)', $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
893         printf ' [Time: %02i:%02i / %02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
894         printf ' %-8s %-14s', $current_interface, $one_computer;
895
896         my %resol_arp = resolve_ip_arp_host($one_computer, $current_interface);
897
898         # do not search on router connection (why ?)
899         if ( exists $router_mac_ip{$resol_arp{mac_address}}) {
900            $computer_not_detected{$one_computer} = $current_interface;
901            next LOOP_ON_COMPUTER;
902            }
903
904         # do not search on switch inter-connection
905         if (exists $switch_level{$resol_arp{hostname_fq}}) {
906            $computer_not_detected{$one_computer} = $current_interface;
907            next LOOP_ON_COMPUTER;
908            }
909
910         my $switch_proposal = q{};
911         if (exists $computerdb->{$resol_arp{ipv4_address}} and exists $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}) {
912            $switch_proposal = $computerdb->{$resol_arp{ipv4_address}}{switch_hostname};
913            }
914
915         # do not have a mac address
916         if ($resol_arp{mac_address} eq 'unknow' or (exists $resol_arp{timestamps} and $resol_arp{timestamps} < ($timestamp - 3 * 3600))) {
917            $computer_not_detected{$one_computer} = $current_interface;
918            next LOOP_ON_COMPUTER;
919            }
920
921         my $vlan_name = get_current_vlan_name_for_interface($resol_arp{interface});
922         my $vlan_id   = get_current_vlan_id($vlan_name);
923         my %where = find_switch_port($resol_arp{mac_address},$switch_proposal,$vlan_id);
924
925         #192.168.24.156:
926         #  arp: 00:0B:DB:D5:F6:65
927         #  hostname: pcroyon.hmg.priv
928         #  port: 5
929         #  switch: sw-batH-legi:hp2524
930         #  timestamp: 1164355525
931
932         # do not have a mac address
933#         if ($resol_arp{mac_address} eq 'unknow') {
934#            $computer_not_detected{$one_computer} = $current_interface;
935#            next LOOP_ON_COMPUTER;
936#            }
937
938         # detected on a switch
939         if ($where{switch_description} ne 'unknow') {
940            $detected_computer++;
941            $computerdb->{$resol_arp{ipv4_address}} = {
942               hostname_fq        => $resol_arp{hostname_fq},
943               mac_address        => $resol_arp{mac_address},
944               switch_hostname    => $where{switch_hostname},
945               switch_description => $where{switch_description},
946               switch_port        => $where{switch_port},
947               timestamp          => $timestamp,
948               network            => $net,
949               };
950            next LOOP_ON_COMPUTER;
951            }
952
953         # new in the database but where it is ?
954         if (not exists $computerdb->{$resol_arp{ipv4_address}}) {
955            $detected_computer++;
956            $computerdb->{$resol_arp{ipv4_address}} = {
957               hostname_fq        => $resol_arp{hostname_fq},
958               mac_address        => $resol_arp{mac_address},
959               switch_hostname    => $where{switch_hostname},
960               switch_description => $where{switch_description},
961               switch_port        => $where{switch_port},
962               timestamp          => $resol_arp{timestamp},
963               network            => $net,
964               };
965            }
966
967         # mise a jour du nom de la machine si modification dans le dns
968         $computerdb->{$resol_arp{ipv4_address}}{hostname_fq} = $resol_arp{hostname_fq};
969
970         # mise à jour de la date de détection si détection plus récente par arpwatch
971         $computerdb->{$resol_arp{ipv4_address}}{timestamp}   = $resol_arp{timestamp} if exists $resol_arp{timestamp} and $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $resol_arp{timestamp};
972
973         # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine
974#         push @computer_not_detected, $resol_arp{ipv4_address} if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
975         $computer_not_detected{$resol_arp{ipv4_address}} = $current_interface if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
976
977         }
978      }
979
980   # final end of line at the end of the loop
981   printf "\n";
982
983   my $dirdb = $KLASK_DB_FILE;
984      $dirdb =~ s{ / [^/]* $}{}xms;
985   mkdir "$dirdb", 0755 unless -d "$dirdb";
986   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
987
988   for my $one_computer (keys %computer_not_detected) {
989      my $interface = $computer_not_detected{$one_computer};
990      system "arping -c 1 -w 1 -rR -i $interface $one_computer &>/dev/null";
991#      print  "arping -c 1 -w 1 -rR -i $interface $one_computer 2>/dev/null\n";
992      }
993   return;
994   }
995
996sub cmd_removedb {
997   my @computer = @_;
998
999   test_maindb_environnement();
1000
1001   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1002
1003   LOOP_ON_COMPUTER:
1004   for my $one_computer (@computer) {
1005
1006      if ( $one_computer =~ m/^ $RE_IPv4_ADDRESS $/xms
1007            and exists $computerdb->{$one_computer} ) {
1008         delete $computerdb->{$one_computer};
1009         next;
1010         }
1011
1012      my %resol_arp = resolve_ip_arp_host($one_computer);
1013
1014      delete $computerdb->{$resol_arp{ipv4_address}} if exists $computerdb->{$resol_arp{ipv4_address}};
1015      }
1016
1017   my $dirdb = $KLASK_DB_FILE;
1018      $dirdb =~ s{ / [^/]* $}{}xms;
1019   mkdir "$dirdb", 0755 unless -d "$dirdb";
1020   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
1021   return;
1022   }
1023
1024sub cmd_cleandb {
1025   my @ARGV  = @_;
1026
1027   my $days_to_clean = 15;
1028   my $verbose;
1029   my $database_has_changed;
1030
1031   GetOptions(
1032      'day|d=i'   => \$days_to_clean,
1033      'verbose|v' => \$verbose,
1034      );
1035
1036   my @vlan_name = get_list_network();
1037
1038   my $computerdb = {};
1039      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1040   my $timestamp = time;
1041
1042   my $timestamp_barrier = 3600 * 24 * $days_to_clean;
1043   my $timestamp_3month  = 3600 * 24 * 90;
1044
1045   my %mactimedb = ();
1046   ALL_VLAN:
1047   for my $vlan (shuffle @vlan_name) {
1048
1049      my @ip_list   = shuffle get_list_ip($vlan);
1050     
1051      LOOP_ON_IP_ADDRESS:
1052      for my $ip (@ip_list) {
1053
1054         next LOOP_ON_IP_ADDRESS if
1055            not exists $computerdb->{$ip};
1056           
1057            #&& $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1058         my $ip_timestamp   = $computerdb->{$ip}{timestamp};
1059         my $ip_mac         = $computerdb->{$ip}{mac_address};
1060         my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
1061
1062         $mactimedb{$ip_mac} ||= {
1063            ip          => $ip,
1064            timestamp   => $ip_timestamp,
1065            vlan        => $vlan,
1066            hostname_fq => $ip_hostname_fq,
1067            };
1068         
1069         if (
1070            ( $mactimedb{$ip_mac}->{timestamp} - $ip_timestamp > $timestamp_barrier
1071               or (
1072                  $mactimedb{$ip_mac}->{timestamp} > $ip_timestamp
1073                  and $timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_3month
1074                  )
1075            )
1076            and (
1077               not $mactimedb{$ip_mac}->{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/
1078               or $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/
1079               )) {
1080            print "remove ip $ip\n" if $verbose;
1081            delete $computerdb->{$ip};
1082            $database_has_changed++;
1083            }
1084
1085         elsif (
1086            ( $ip_timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_barrier
1087               or (
1088                  $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}
1089                  and $timestamp - $ip_timestamp > $timestamp_3month
1090                  )
1091            )
1092            and (
1093               not $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/
1094               or $mactimedb{$ip_mac}->{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/
1095               )) {
1096            print "remove ip ".$mactimedb{$ip_mac}->{ip}."\n" if $verbose;
1097            delete $computerdb->{$mactimedb{$ip_mac}->{ip}};
1098            $database_has_changed++;
1099            }
1100
1101         if ( $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}) {
1102            $mactimedb{$ip_mac} = {
1103               ip          => $ip,
1104               timestamp   => $ip_timestamp,
1105               vlan        => $vlan,
1106               hostname_fq => $ip_hostname_fq,
1107               };
1108            }
1109         }
1110      }
1111
1112   if ( $database_has_changed ) {
1113      my $dirdb = $KLASK_DB_FILE;
1114         $dirdb =~ s{ / [^/]* $}{}xms;
1115      mkdir "$dirdb", 0755 unless -d "$dirdb";
1116      YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
1117      }
1118   return;
1119   }
1120
1121sub cmd_exportdb {
1122   @ARGV = @_;
1123
1124   my $format = 'txt';
1125
1126   GetOptions(
1127      'format|f=s'  => \$format,
1128      );
1129
1130   my %possible_format = (
1131      txt  => \&cmd_exportdb_txt,
1132      html => \&cmd_exportdb_html,
1133      );
1134
1135   $format = 'txt' if not defined $possible_format{$format};
1136
1137   $possible_format{$format}->(@ARGV);
1138   return;
1139   }
1140
1141sub cmd_exportdb_txt {
1142   test_maindb_environnement();
1143
1144   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1145
1146   printf "%-27s %-4s            %-40s %-15s %-18s %-16s %s\n", qw(Switch Port Hostname-FQ IPv4-Address MAC-Address Date VLAN);
1147   print "--------------------------------------------------------------------------------------------------------------------------------------------\n";
1148
1149   LOOP_ON_IP_ADDRESS:
1150   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1151
1152#      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow';
1153
1154      # to be improve in the future
1155      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1156
1157# dans le futur
1158#      next if $computerdb->{$ip}{hostname_fq} eq 'unknow';
1159
1160      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1161      $year += 1900;
1162      $mon++;
1163      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1164
1165      printf "%-28s  %2s  <-------  %-40s %-15s %-18s %-16s %s\n",
1166         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
1167         $computerdb->{$ip}{switch_port},
1168         $computerdb->{$ip}{hostname_fq},
1169         $ip,
1170         $computerdb->{$ip}{mac_address},
1171         $date,
1172         $computerdb->{$ip}{network} || '';
1173      }
1174   return;
1175   }
1176
1177sub cmd_exportdb_html {
1178   test_maindb_environnement();
1179
1180   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1181
1182#<link rel="stylesheet" type="text/css" href="style-klask.css" />
1183#<script src="sorttable-klask.js"></script>
1184
1185   print <<'END_HTML';
1186<table class="sortable" summary="Klask Host Database">
1187 <caption>Klask Host Database</caption>
1188 <thead>
1189  <tr>
1190   <th scope="col" class="klask-header-left">Switch</th>
1191   <th scope="col" class="sorttable_nosort">Port</th>
1192   <th scope="col" class="sorttable_nosort">Link</th>
1193   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1194   <th scope="col" class="hklask-ipv4">IPv4-Address</th>
1195   <th scope="col" class="sorttable_alpha">MAC-Address</th>
1196   <th scope="col" class="sorttable_alpha">VLAN</th>
1197   <th scope="col" class="klask-header-right">Date</th>
1198  </tr>
1199 </thead>
1200 <tfoot>
1201  <tr>
1202   <th scope="col" class="klask-footer-left">Switch</th>
1203   <th scope="col" class="fklask-port">Port</th>
1204   <th scope="col" class="fklask-link">Link</th>
1205   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1206   <th scope="col" class="fklask-ipv4">IPv4-Address</th>
1207   <th scope="col" class="fklask-mac">MAC-Address</th>
1208   <th scope="col" class="fklask-vlan">VLAN</th>
1209   <th scope="col" class="klask-footer-right">Date</th>
1210  </tr>
1211 </tfoot>
1212 <tbody>
1213END_HTML
1214
1215   my %mac_count = ();
1216   LOOP_ON_IP_ADDRESS:
1217   foreach my $ip (keys %{$computerdb}) {
1218
1219      # to be improve in the future
1220      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1221
1222      $mac_count{$computerdb->{$ip}{mac_address}}++;
1223      }
1224
1225   my $typerow = 'even';
1226
1227   LOOP_ON_IP_ADDRESS:
1228   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1229
1230      # to be improve in the future
1231      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1232
1233      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1234      $year += 1900;
1235      $mon++;
1236      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1237
1238#      $odd_or_even++;
1239#      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
1240      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1241
1242      my $switch_hostname = $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description} || 'unkown';
1243      chomp $switch_hostname;
1244      my $switch_hostname_sort = sprintf '%s %3s' ,$switch_hostname, $computerdb->{$ip}{switch_port};
1245
1246      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1247
1248      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{mac_address}}, $computerdb->{$ip}{mac_address};
1249
1250      $computerdb->{$ip}{hostname_fq} = 'unknow' if $computerdb->{$ip}{hostname_fq} =~ m/^ \d+ \. \d+ \. \d+ \. \d+ $/xms;
1251      my ( $host_short ) = split m/ \. /xms, $computerdb->{$ip}{hostname_fq};
1252
1253      my $vlan = $computerdb->{$ip}{network} || '';
1254
1255      print <<"END_HTML";
1256  <tr class="$typerow">
1257   <td sorttable_customkey="$switch_hostname_sort">$switch_hostname</td>
1258   <td class="bklask-port">$computerdb->{$ip}{switch_port}</td>
1259   <td><-------</td>
1260   <td sorttable_customkey="$host_short">$computerdb->{$ip}{hostname_fq}</td>
1261   <td sorttable_customkey="$ip_sort">$ip</td>
1262   <td sorttable_customkey="$mac_sort">$computerdb->{$ip}{mac_address}</td>
1263   <td>$vlan</td>
1264   <td>$date</td>
1265  </tr>
1266END_HTML
1267      }
1268
1269   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1270
1271   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1272   my %db_switch_parent            = %{$switch_connection->{parent}};
1273   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1274   my %db_switch                   = %{$switch_connection->{switch_db}};
1275
1276   for my $sw (sort keys %db_switch_output_port) {
1277
1278      my $switch_hostname_sort = sprintf '%s %3s' ,$sw, $db_switch_output_port{$sw};
1279
1280      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1281
1282      if (exists $db_switch_parent{$sw}) {
1283
1284      my $mac_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{mac_address};
1285      my $ipv4_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{ipv4_address};
1286      my $timestamp = $db_switch{$db_switch_parent{$sw}->{switch}}->{timestamp};
1287
1288      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1289      $year += 1900;
1290      $mon++;
1291      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1292
1293      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1294
1295      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1296
1297      my ( $host_short ) = sprintf '%s %3s' , split(m/ \. /xms, $db_switch_parent{$sw}->{switch}, 1), $db_switch_parent{$sw}->{port};
1298
1299      print <<"END_HTML";
1300  <tr class="$typerow">
1301   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1302   <td class="bklask-port">$db_switch_output_port{$sw}</>
1303   <td>+--> $db_switch_parent{$sw}->{port}</td>
1304   <td sorttable_customkey="$host_short">$db_switch_parent{$sw}->{switch}</>
1305   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1306   <td sorttable_customkey="$mac_sort">$mac_address</td>
1307   <td></td>
1308   <td>$date</td>
1309  </tr>
1310END_HTML
1311         }
1312      else {
1313         print <<"END_HTML";
1314  <tr class="$typerow">
1315   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1316   <td class="bklask-port">$db_switch_output_port{$sw}</>
1317   <td>+--></td>
1318   <td sorttable_customkey="router">router</>
1319   <td sorttable_customkey="999999999999"></td>
1320   <td sorttable_customkey="99999"></td>
1321   <td></td>
1322   <td></td>
1323  </tr>
1324END_HTML
1325         }
1326      }
1327
1328   for my $swport (sort keys %db_switch_connected_on_port) {
1329      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1330      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1331
1332         my $switch_hostname_sort = sprintf '%s %3s' ,$sw_connect, $port_connect;
1333
1334      my $mac_address = $db_switch{$sw}->{mac_address};
1335      my $ipv4_address = $db_switch{$sw}->{ipv4_address};
1336      my $timestamp = $db_switch{$sw}->{timestamp};
1337
1338      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1339      $year += 1900;
1340      $mon++;
1341      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year,$mon,$mday,$hour,$min;
1342
1343      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1344
1345      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1346
1347      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1348
1349         if (exists $db_switch_output_port{$sw}) {
1350
1351            my ( $host_short ) = sprintf '%s %3s' , split( m/\./xms, $sw, 1), $db_switch_output_port{$sw};
1352
1353            print <<"END_HTML";
1354  <tr class="$typerow">
1355   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1356   <td class="bklask-port">$port_connect</>
1357   <td>&lt;--+ $db_switch_output_port{$sw}</td>
1358   <td sorttable_customkey="$host_short">$sw</>
1359   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1360   <td sorttable_customkey="$mac_sort">$mac_address</td>
1361   <td></td>
1362   <td>$date</td>
1363  </tr>
1364END_HTML
1365            }
1366         else {
1367            print <<"END_HTML";
1368  <tr class="$typerow">
1369   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1370   <td class="bklask-port">$port_connect</>
1371   <td>&lt;--+</td>
1372   <td sorttable_customkey="$sw">$sw</>
1373   <td sorttable_customkey="">$ipv4_address</td>
1374   <td sorttable_customkey="">$mac_address</td>
1375   <td></td>
1376   <td>$date</td>
1377  </tr>
1378END_HTML
1379            }
1380         }
1381      }
1382
1383   print <<'END_HTML';
1384 </tbody>
1385</table>
1386END_HTML
1387   return;
1388   }
1389
1390sub cmd_bad_vlan_id {
1391   test_maindb_environnement();
1392
1393   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1394
1395   # create a database with the most recent computer by switch port
1396   my %swithportdb = ();
1397   LOOP_ON_IP_ADDRESS:
1398   foreach my $ip (keys %{$computerdb}) {
1399      # to be improve in the future
1400      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1401      next LOOP_ON_IP_ADDRESS if ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}) eq 'unknow';
1402      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{switch_port} eq '0';
1403
1404      my $ip_timestamp   = $computerdb->{$ip}{timestamp};
1405      my $ip_mac         = $computerdb->{$ip}{mac_address};
1406      my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
1407
1408      my $swpt = sprintf "%-28s  %2s",
1409         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
1410         $computerdb->{$ip}{switch_port};
1411      $swithportdb{$swpt} ||= {
1412         ip          => $ip,
1413         timestamp   => $ip_timestamp,
1414         vlan        => $computerdb->{$ip}{network},
1415         hostname_fq => $ip_hostname_fq,
1416         mac_address => $ip_mac,
1417         };
1418
1419      # if float computer, set date 15 day before warning...
1420      my $ip_timestamp_mod = $ip_timestamp;
1421      my $ip_timestamp_ref = $swithportdb{$swpt}->{timestamp};
1422      $ip_timestamp_mod -= 15 * 24 * 3600 if $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/;
1423      $ip_timestamp_ref -= 15 * 24 * 3600 if $swithportdb{$swpt}->{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/;
1424     
1425      if ($ip_timestamp_mod > $ip_timestamp_ref) {
1426         $swithportdb{$swpt} = {
1427            ip          => $ip,
1428            timestamp   => $ip_timestamp,
1429            vlan        => $computerdb->{$ip}{network},
1430            hostname_fq => $ip_hostname_fq,
1431            mac_address => $ip_mac,
1432            };
1433         }
1434      }
1435
1436   LOOP_ON_RECENT_COMPUTER:
1437   foreach my $swpt (keys %swithportdb) {
1438      next LOOP_ON_RECENT_COMPUTER if $swpt =~ m/^\s*0$/;
1439      next LOOP_ON_RECENT_COMPUTER if $swithportdb{$swpt}->{hostname_fq} !~ m/$RE_FLOAT_HOSTNAME/;
1440
1441      my $src_ip = $swithportdb{$swpt}->{ip};
1442      my $src_timestamp = 0;
1443      LOOP_ON_IP_ADDRESS:
1444      foreach my $ip (keys %{$computerdb}) {
1445         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{mac_address} ne  $swithportdb{$swpt}->{mac_address};
1446         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/;
1447         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{timestamp} < $src_timestamp;
1448         
1449         $src_ip = $ip;
1450         $src_timestamp = $computerdb->{$ip}{timestamp};
1451         }
1452
1453      # keep only if float computer is the most recent
1454      next LOOP_ON_RECENT_COMPUTER if $src_timestamp == 0;
1455      next LOOP_ON_RECENT_COMPUTER if $swithportdb{$swpt}->{timestamp} < $src_timestamp;
1456
1457      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $swithportdb{$swpt}->{timestamp};
1458      $year += 1900;
1459      $mon++;
1460      my $date = sprintf '%04i-%02i-%02i/%02i:%02i', $year, $mon, $mday, $hour, $min;
1461
1462      ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$src_ip}{timestamp};
1463      $year += 1900;
1464      $mon++;
1465      my $src_date = sprintf '%04i-%02i-%02i/%02i:%02i', $year, $mon, $mday, $hour, $min;
1466
1467      printf "%s / %-10s +-> %-10s  %s %s %s %s\n",
1468         $swpt, $swithportdb{$swpt}->{vlan}, $computerdb->{$src_ip}{network},
1469         $date,
1470         $src_date,
1471         $computerdb->{$src_ip}{mac_address},
1472         $computerdb->{$src_ip}{hostname_fq};
1473      }
1474   }
1475
1476sub cmd_set_vlan_port {
1477   my $switch_name = shift || q{};
1478   my $mac_address = shift || q{};
1479
1480   if ($switch_name eq q{} or $mac_address eq q{}) {
1481      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
1482      }
1483
1484   $switch_name = join(',', map {$_->{hostname}} @SWITCH ) if $switch_name eq q{*};
1485
1486   for my $sw_name (split /,/, $switch_name) {
1487      if (not defined $SWITCH_DB{$sw_name}) {
1488         die "Switch $sw_name must be defined in klask configuration file\n";
1489         }
1490
1491      my $sw = $SWITCH_DB{$sw_name};
1492      my %session = ( -hostname => $sw->{hostname} );
1493         $session{-version} = $sw->{version}   || 1;
1494         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1495      if (exists $sw->{version} and $sw->{version} eq '3') {
1496         $session{-username} = $sw->{username} || 'snmpadmin';
1497         }
1498      else {
1499         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1500         }
1501
1502      my $research1 = $OID_NUMBER{searchPort1} . mac_address_hex_to_dec($mac_address);
1503      my $research2 = $OID_NUMBER{searchPort2} .'.'. 0 . mac_address_hex_to_dec($mac_address);
1504      print "Klask search OID $research1 on switch $sw_name\n";
1505      print "Klask search OID $research2 on switch $sw_name\n";
1506
1507      my ($session, $error) = Net::SNMP->session( %session );
1508      print "$error \n" if $error;
1509
1510      my $result = $session->get_request(
1511         -varbindlist => [$research1]
1512         );
1513      if (not defined $result) {
1514         $result = $session->get_request(
1515            -varbindlist => [$research2]
1516            );
1517         $result->{$research1} = $result->{$research2} if defined $result;
1518         }
1519
1520      if (defined $result and $result->{$research1} ne 'noSuchInstance') {
1521         my $swport = $result->{$research1};
1522         print "Klask find MAC $mac_address on switch $sw_name port $swport\n";
1523         }
1524      else {
1525         print "Klask do not find MAC $mac_address on switch $sw_name\n";
1526         }
1527
1528      $session->close;
1529      }
1530   return;
1531   }
1532
1533sub cmd_get_vlan_port {
1534   @ARGV = @_;
1535
1536   my $verbose;
1537   GetOptions(
1538      'verbose|v' => \$verbose,
1539      );
1540
1541   my $switch_name = shift @ARGV || q{};
1542   my $switch_port = shift @ARGV || q{};
1543
1544   if ($switch_name eq q{} or $switch_port eq q{}) {
1545      die "Usage: klask get-vlan-port SWITCH_NAME PORT\n";
1546      }
1547
1548   for my $sw_name (split /,/, $switch_name) {
1549      if (not defined $SWITCH_DB{$sw_name}) {
1550         die "Switch $sw_name must be defined in klask configuration file\n";
1551         }
1552
1553      my $sw = $SWITCH_DB{$sw_name};
1554      my %session = ( -hostname => $sw->{hostname} );
1555         $session{-version} = $sw->{version}   || 1;
1556         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1557      if (exists $sw->{version} and $sw->{version} eq '3') {
1558         $session{-username} = $sw->{username} || 'snmpadmin';
1559         }
1560      else {
1561         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1562         }
1563
1564      my $search = $OID_NUMBER{'vlanPortDefault'} . ".$switch_port";
1565
1566      my ($session, $error) = Net::SNMP->session( %session );
1567      print "$error \n" if $error;
1568
1569      my $result = $session->get_request(
1570         -varbindlist => [$search],
1571         );
1572
1573      if (defined $result and $result->{$search} ne 'noSuchInstance') {
1574         my $vlan_id = $result->{$search} || 'empty';
1575         print "Klask VLAN Id $vlan_id on switch $sw_name on port $switch_port\n";
1576         }
1577      else {
1578         print "Klask do not find VLAN Id on switch $sw_name on port $switch_port\n";
1579         }
1580
1581      $session->close;
1582      }
1583   return;
1584   }
1585
1586sub cmd_set_vlan_name {
1587   }
1588
1589# snmpset -v 1 -c public sw1-batG0-legi.hmg.priv "$OID_NUMBER{'hpicfReset'}.0" i 2;
1590sub cmd_rebootsw {
1591   @ARGV = @_;
1592
1593   my $verbose;
1594   GetOptions(
1595      'verbose|v' => \$verbose,
1596      );
1597
1598   my $switch_name = shift @ARGV || q{};
1599
1600   if ($switch_name eq q{}) {
1601      die "Usage: klask rebootsw SWITCH_NAME\n";
1602      }
1603
1604   for my $sw_name (split /,/, $switch_name) {
1605      if (not defined $SWITCH_DB{$sw_name}) {
1606         die "Switch $sw_name must be defined in klask configuration file\n";
1607         }
1608
1609      my $sw = $SWITCH_DB{$sw_name};
1610      my %session = ( -hostname => $sw->{hostname} );
1611         $session{-version} = $sw->{version}   || 1;
1612         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1613      if (exists $sw->{version} and $sw->{version} eq '3') {
1614         $session{-username} = $sw->{username} || 'snmpadmin';
1615         }
1616      else {
1617         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1618         }
1619
1620      my ($session, $error) = Net::SNMP->session( %session );
1621      print "$error \n" if $error;
1622
1623      my $result = $session->set_request(
1624         -varbindlist => ["$OID_NUMBER{'hpicfReset'}.0", INTEGER, 2],
1625         );
1626
1627      $session->close;
1628      }
1629   return;
1630   }
1631
1632sub cmd_get_vlan_name {
1633   my $switch_name = shift || q{};
1634   my $vlan_id     = shift || q{};
1635
1636   if ($switch_name eq q{} or $vlan_id eq q{}) {
1637      die "Usage: klask get-vlan-name SWITCH_NAME VLAN_ID\n";
1638      }
1639
1640   $switch_name = join(',', map {$_->{hostname}} @SWITCH ) if $switch_name eq q{*};
1641
1642   for my $sw_name (split /,/, $switch_name) {
1643      if (not defined $SWITCH_DB{$sw_name}) {
1644         die "Switch $sw_name must be defined in klask configuration file\n";
1645         }
1646
1647      my $sw = $SWITCH_DB{$sw_name};
1648      my %session = ( -hostname => $sw->{hostname} );
1649         $session{-version} = $sw->{version}   || 1;
1650         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1651      if (exists $sw->{version} and $sw->{version} eq '3') {
1652         $session{-username} = $sw->{username} || 'snmpadmin';
1653         }
1654      else {
1655         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1656         }
1657
1658      my $search_vlan_name = $OID_NUMBER{vlanName} . ".$vlan_id";
1659
1660      my ($session, $error) = Net::SNMP->session( %session );
1661      print "$error \n" if $error;
1662
1663      my $result = $session->get_request(
1664         -varbindlist => [$search_vlan_name]
1665         );
1666
1667      if (defined $result and $result->{$search_vlan_name} ne 'noSuchInstance') {
1668         my $vlan_name = $result->{$search_vlan_name} || 'empty';
1669         print "Klask find VLAN $vlan_id on switch $sw_name with name $vlan_name\n";
1670         }
1671      else {
1672         print "Klask do not find VLAN $vlan_id on switch $sw_name\n";
1673         }
1674
1675      $session->close;
1676      }
1677   return;
1678   }
1679
1680sub cmd_ip_location {
1681   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1682
1683   LOOP_ON_IP_ADDRESS:
1684   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1685
1686      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1687
1688      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || q{};
1689      next LOOP_ON_IP_ADDRESS if $sw_hostname eq 'unknow';
1690
1691      my $sw_location = q{};
1692      LOOP_ON_ALL_SWITCH:
1693      for my $sw (@SWITCH) {
1694         next LOOP_ON_ALL_SWITCH if $sw_hostname ne $sw->{hostname};
1695         $sw_location = $sw->{location};
1696         last;
1697         }
1698
1699      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
1700      }
1701   return;
1702   }
1703
1704sub cmd_ip_free {
1705   @ARGV = @_; # VLAN name with option
1706
1707   my $days_to_dead = 365 * 2;
1708   my $format = 'txt';
1709   my $verbose;
1710
1711   GetOptions(
1712      'day|d=i'      => \$days_to_dead,
1713      'format|f=s'   => \$format,
1714      'verbose|v'    => \$verbose,
1715      );
1716
1717   my %possible_format = (
1718      txt  => \&cmd_ip_free_txt,
1719      html => \&cmd_ip_free_html,
1720      none => sub {},
1721      );
1722   $format = 'txt' if not defined $possible_format{$format};
1723
1724   my @vlan_name = @ARGV;
1725   @vlan_name = get_list_network() if not @vlan_name;
1726
1727   my $computerdb = {};
1728      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1729   my $timestamp = time;
1730
1731   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_dead );
1732
1733   my %result_ip = ();
1734
1735   ALL_NETWORK:
1736   for my $vlan (@vlan_name) {
1737
1738      my @ip_list = get_list_ip($vlan);
1739
1740      LOOP_ON_IP_ADDRESS:
1741      for my $ip (@ip_list) {
1742
1743         if (exists $computerdb->{$ip}) {
1744            next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1745           
1746            my $mac_address = $computerdb->{$ip}{mac_address};
1747            LOOP_ON_DATABASE:
1748            foreach my $ip_db (keys %{$computerdb}) {
1749               next LOOP_ON_DATABASE if $computerdb->{$ip_db}{mac_address} ne $mac_address;
1750               next LOOP_ON_IP_ADDRESS if $computerdb->{$ip_db}{timestamp} > $timestamp_barrier;
1751               }
1752            }
1753
1754         my $ip_date_last_detection = '';
1755         if (exists $computerdb->{$ip}) {
1756            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1757            $year += 1900;
1758            $mon++;
1759            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1760            }
1761
1762         my $packed_ip = scalar gethostbyname($ip);
1763         my $hostname_fq = 'unknown';
1764            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip;
1765
1766         next LOOP_ON_IP_ADDRESS if $hostname_fq =~ m/$RE_FLOAT_HOSTNAME/;
1767
1768         $result_ip{$ip} ||= {};
1769         $result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
1770         $result_ip{$ip}->{hostname_fq} = $hostname_fq;
1771         $result_ip{$ip}->{vlan} = $vlan;
1772
1773         printf "VERBOSE_1: %-15s %-12s %s\n", $ip, $vlan, $hostname_fq if $verbose;
1774         }
1775      }
1776
1777   $possible_format{$format}->(%result_ip);
1778   }
1779
1780sub cmd_ip_free_txt {
1781   my %result_ip = @_;
1782   
1783   printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
1784   print "-------------------------------------------------------------------------------\n";
1785   LOOP_ON_IP_ADDRESS:
1786   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1787         printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $result_ip{$ip}->{vlan};
1788      }
1789   }
1790
1791sub cmd_ip_free_html {
1792   my %result_ip = @_;
1793
1794   print <<'END_HTML';
1795<table class="sortable" summary="Klask Free IP Database">
1796 <caption>Klask Free IP Database</caption>
1797 <thead>
1798  <tr>
1799   <th scope="col" class="klask-header-left">IPv4-Address</th>
1800   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1801   <th scope="col" class="sorttable_alpha">VLAN</th>
1802   <th scope="col" class="klask-header-right">Date</th>
1803  </tr>
1804 </thead>
1805 <tfoot>
1806  <tr>
1807   <th scope="col" class="klask-footer-left">IPv4-Address</th>
1808   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1809   <th scope="col" class="fklask-vlan">VLAN</th>
1810   <th scope="col" class="klask-footer-right">Date</th>
1811  </tr>
1812 </tfoot>
1813 <tbody>
1814END_HTML
1815
1816   my $typerow = 'even';
1817
1818   LOOP_ON_IP_ADDRESS:
1819   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1820
1821      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1822
1823      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1824      my ( $host_short ) = split m/ \. /xms, $result_ip{$ip}->{hostname_fq};
1825
1826      print <<"END_HTML";
1827  <tr class="$typerow">
1828   <td sorttable_customkey="$ip_sort">$ip</td>
1829   <td sorttable_customkey="$host_short">$result_ip{$ip}->{hostname_fq}</td>
1830   <td>$result_ip{$ip}->{vlan}</td>
1831   <td>$result_ip{$ip}->{date_last_detection}</td>
1832  </tr>
1833END_HTML
1834      }
1835   print <<'END_HTML';
1836 </tbody>
1837</table>
1838END_HTML
1839   }
1840
1841sub cmd_enable {
1842   my $switch = shift;
1843   my $port   = shift;
1844
1845   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
1846   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
1847   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
1848   return;
1849   }
1850
1851sub cmd_disable {
1852   my $switch = shift;
1853   my $port   = shift;
1854
1855   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
1856   return;
1857   }
1858
1859sub cmd_status {
1860   my $switch = shift;
1861   my $port   = shift;
1862
1863   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
1864   return;
1865   }
1866
1867sub cmd_search_mac_on_switch {
1868   @ARGV = @_;
1869
1870   my $verbose;
1871   my $vlan_id = 0;
1872
1873   GetOptions(
1874      'verbose|v' => \$verbose,
1875      'vlan|l=i'  => \$vlan_id,
1876      );
1877
1878   my $switch_name = shift @ARGV || q{};
1879   my $mac_address = shift @ARGV || q{};
1880
1881   if ($switch_name eq q{} or $mac_address eq q{}) {
1882      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
1883      }
1884
1885   $mac_address = normalize_mac_address($mac_address);
1886   $switch_name = join(',', map {$_->{hostname}} @SWITCH ) if $switch_name eq q{*};
1887
1888   for my $sw_name (split /,/, $switch_name) {
1889      if (not defined $SWITCH_DB{$sw_name}) {
1890         die "Switch $sw_name must be defined in klask configuration file\n";
1891         }
1892
1893      my $sw = $SWITCH_DB{$sw_name};
1894      my %session = ( -hostname => $sw->{hostname} );
1895         $session{-version} = $sw->{version}   || 1;
1896         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1897      if (exists $sw->{version} and $sw->{version} eq '3') {
1898         $session{-username} = $sw->{username} || 'snmpadmin';
1899         }
1900      else {
1901         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1902         }
1903
1904      my $research1 = $OID_NUMBER{searchPort1} . mac_address_hex_to_dec($mac_address);
1905      my $research2 = $OID_NUMBER{searchPort2} .'.'. $vlan_id . mac_address_hex_to_dec($mac_address);
1906      print "Klask search OID $research1 on switch $sw_name\n" if $verbose;
1907      print "Klask search OID $research2 on switch $sw_name\n" if $verbose;
1908
1909      my ($session, $error) = Net::SNMP->session( %session );
1910      print "$error \n" if $error;
1911
1912      my $result = $session->get_request(
1913         -varbindlist => [$research1]
1914         );
1915      if (not defined $result) {
1916         $result = $session->get_request(
1917            -varbindlist => [$research2]
1918            );
1919         $result->{$research1} = $result->{$research2} if defined $result;
1920         }
1921
1922      if (defined $result and $result->{$research1} ne 'noSuchInstance') {
1923         my $swport = $result->{$research1};
1924         print "Klask find MAC $mac_address on switch $sw_name port $swport\n";
1925         }
1926      else {
1927         print "Klask do not find MAC $mac_address on switch $sw_name\n" if $verbose;
1928         }
1929
1930      $session->close;
1931      }
1932   return;
1933   }
1934
1935sub cmd_updatesw {
1936   @ARGV = @_;
1937
1938   my $verbose;
1939
1940   GetOptions(
1941      'verbose|v' => \$verbose,
1942      );
1943
1944   init_switch_names('yes');    #nomme les switchs
1945   print "\n";
1946
1947   my %where = ();
1948   my %db_switch_output_port = ();
1949   my %db_switch_ip_hostnamefq = ();
1950
1951   DETECT_ALL_ROUTER:
1952#   for my $one_computer ('194.254.66.254') {
1953   for my $one_router ( get_list_main_router(get_list_network()) ) {
1954      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
1955
1956      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
1957      print "VERBOSE_1: Router detected $resol_arp{ipv4_address} - $resol_arp{mac_address}\n" if $verbose;
1958
1959      my $vlan_name = get_current_vlan_name_for_interface($resol_arp{interface});
1960      my $vlan_id   = get_current_vlan_id($vlan_name);
1961      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address},$vlan_id); # retrouve les emplacements des routeurs
1962      }
1963
1964   ALL_ROUTER_IP_ADDRESS:
1965   for my $ip_router (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
1966
1967      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip_router}; # /a priori/ idiot car ne sers à rien...
1968
1969      ALL_SWITCH_CONNECTED:
1970      for my $switch_detected ( keys %{$where{$ip_router}} ) {
1971
1972         my $switch = $where{$ip_router}->{$switch_detected};
1973
1974         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
1975
1976         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
1977         print "VERBOSE_2: output port $switch->{hostname} : $switch->{port}\n" if $verbose;
1978         }
1979      }
1980
1981   my %db_switch_link_with = ();
1982
1983   my @list_all_switch = ();
1984   my @list_switch_ipv4 = ();
1985   for my $sw (@SWITCH){
1986      push @list_all_switch, $sw->{hostname};
1987      }
1988
1989   my $timestamp = time;
1990
1991   ALL_SWITCH:
1992   for my $one_computer (@list_all_switch) {
1993      my %resol_arp = resolve_ip_arp_host($one_computer, q{*}, q{low}); # arp resolution
1994      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
1995
1996      push @list_switch_ipv4, $resol_arp{ipv4_address};
1997
1998      my $vlan_name = get_current_vlan_name_for_interface($resol_arp{interface});
1999      my $vlan_id   = get_current_vlan_id($vlan_name);
2000      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address},$vlan_id); # find port on all switch
2001
2002      if ($verbose) {
2003         print "VERBOSE_3: $one_computer $resol_arp{ipv4_address} $resol_arp{mac_address}\n";
2004         print "VERBOSE_3: $one_computer --- ",
2005            join(' + ', keys %{$where{$resol_arp{ipv4_address}}}),
2006            "\n";
2007         }
2008
2009      $db_switch_ip_hostnamefq{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
2010      print "VERBOSE_4: db_switch_ip_hostnamefq $resol_arp{ipv4_address} -> $resol_arp{hostname_fq}\n" if $verbose;
2011
2012      $SWITCH_DB{$one_computer}->{ipv4_address} = $resol_arp{ipv4_address};
2013      $SWITCH_DB{$one_computer}->{mac_address}  = $resol_arp{mac_address};
2014      $SWITCH_DB{$one_computer}->{timestamp}    = $timestamp;
2015      }
2016
2017   ALL_SWITCH_IP_ADDRESS:
2018   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
2019
2020      print "VERBOSE_5: loop on $db_switch_ip_hostnamefq{$ip}\n" if $verbose;
2021
2022      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
2023#      next ALL_SWITCH_IP_ADDRESS if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostnamefq{$ip} };
2024
2025      DETECTED_SWITCH:
2026      for my $switch_detected ( keys %{$where{$ip}} ) {
2027
2028         my $switch = $where{$ip}->{$switch_detected};
2029         print "VERBOSE_6: $db_switch_ip_hostnamefq{$ip} -> $switch->{hostname} : $switch->{port}\n" if $verbose;
2030
2031         next if $switch->{port}     eq '0';
2032         next if $switch->{port}     eq $db_switch_output_port{$switch->{hostname}};
2033         next if $switch->{hostname} eq $db_switch_ip_hostnamefq{$ip}; # $computerdb->{$ip}{hostname};
2034
2035         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} } ||= {};
2036         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} }->{ $switch->{hostname} } = $switch->{port};
2037         print "VERBOSE_7: +++++\n" if $verbose;
2038         }
2039
2040      }
2041
2042   my %db_switch_connected_on_port = ();
2043   my $maybe_more_than_one_switch_connected = 'yes';
2044
2045   while ($maybe_more_than_one_switch_connected eq 'yes') {
2046      for my $sw (keys %db_switch_link_with) {
2047         for my $connect (keys %{$db_switch_link_with{$sw}}) {
2048
2049            my $port = $db_switch_link_with{$sw}->{$connect};
2050
2051            $db_switch_connected_on_port{"$connect:$port"} ||= {};
2052            $db_switch_connected_on_port{"$connect:$port"}->{$sw}++; # Just to define the key
2053            }
2054         }
2055
2056      $maybe_more_than_one_switch_connected  = 'no';
2057
2058      SWITCH_AND_PORT:
2059      for my $swport (keys %db_switch_connected_on_port) {
2060
2061         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
2062
2063         $maybe_more_than_one_switch_connected = 'yes';
2064
2065         my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
2066         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
2067
2068         CONNECTED:
2069         for my $sw_connected (@sw_on_same_port) {
2070
2071            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
2072
2073            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
2074
2075            for my $other_sw (@sw_on_same_port) {
2076               next if $other_sw eq $sw_connected;
2077
2078               delete $db_switch_link_with{$other_sw}->{$sw_connect};
2079               }
2080
2081            # We can not do better for this switch for this loop
2082            next SWITCH_AND_PORT;
2083            }
2084         }
2085      }
2086
2087   my %db_switch_parent =();
2088
2089   for my $sw (keys %db_switch_link_with) {
2090      for my $connect (keys %{$db_switch_link_with{$sw}}) {
2091
2092         my $port = $db_switch_link_with{$sw}->{$connect};
2093
2094         $db_switch_connected_on_port{"$connect:$port"} ||= {};
2095         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
2096
2097         $db_switch_parent{$sw} = {switch => $connect, port => $port};
2098         }
2099      }
2100
2101   print "Switch output port and parent port connection\n";
2102   print "---------------------------------------------\n";
2103   for my $sw (sort keys %db_switch_output_port) {
2104      if (exists $db_switch_parent{$sw}) {
2105         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
2106         }
2107      else {
2108         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
2109         }
2110      }
2111   print "\n";
2112
2113   print "Switch parent and children port inter-connection\n";
2114   print "------------------------------------------------\n";
2115   for my $swport (sort keys %db_switch_connected_on_port) {
2116      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
2117      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
2118         if (exists $db_switch_output_port{$sw}) {
2119            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
2120            }
2121         else {
2122            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
2123            }
2124         }
2125      }
2126
2127   my $switch_connection = {
2128      output_port       => \%db_switch_output_port,
2129      parent            => \%db_switch_parent,
2130      connected_on_port => \%db_switch_connected_on_port,
2131      link_with         => \%db_switch_link_with,
2132      switch_db         => \%SWITCH_DB,
2133      };
2134
2135   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
2136   return;
2137   }
2138
2139sub cmd_exportsw {
2140   @ARGV = @_;
2141
2142   test_switchdb_environnement();
2143
2144   my $format = 'txt';
2145
2146   GetOptions(
2147      'format|f=s'  => \$format,
2148      );
2149
2150   my %possible_format = (
2151      txt => \&cmd_exportsw_txt,
2152      dot => \&cmd_exportsw_dot,
2153      );
2154
2155   $format = 'txt' if not defined $possible_format{$format};
2156
2157   $possible_format{$format}->(@ARGV);
2158   return;
2159   }
2160
2161sub cmd_exportsw_txt {
2162
2163   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
2164
2165   my %db_switch_output_port       = %{$switch_connection->{output_port}};
2166   my %db_switch_parent            = %{$switch_connection->{parent}};
2167   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
2168
2169   print "Switch output port and parent port connection\n";
2170   print "---------------------------------------------\n";
2171   for my $sw (sort keys %db_switch_output_port) {
2172      if (exists $db_switch_parent{$sw}) {
2173         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
2174         }
2175      else {
2176         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
2177         }
2178      }
2179   print "\n";
2180
2181   print "Switch parent and children port inter-connection\n";
2182   print "------------------------------------------------\n";
2183   for my $swport (sort keys %db_switch_connected_on_port) {
2184      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
2185      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
2186         if (exists $db_switch_output_port{$sw}) {
2187            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
2188            }
2189         else {
2190            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
2191            }
2192         }
2193      }
2194   return;
2195   }
2196
2197sub cmd_exportsw_dot {
2198
2199   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
2200
2201   my %db_switch_output_port       = %{$switch_connection->{output_port}};
2202   my %db_switch_parent            = %{$switch_connection->{parent}};
2203   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
2204   my %db_switch_link_with         = %{$switch_connection->{link_with}};
2205   my %db_switch_global            = %{$switch_connection->{switch_db}};
2206
2207   my %db_building= ();
2208   for my $sw (@SWITCH) {
2209      my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
2210      $db_building{$building} ||= {};
2211      $db_building{$building}->{$location} ||= {};
2212      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
2213      }
2214
2215
2216   print "digraph G {\n";
2217   print "rankdir = LR;\n";
2218
2219   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
2220   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
2221
2222   my $b=0;
2223   for my $building (keys %db_building) {
2224      $b++;
2225
2226      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
2227      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
2228
2229      my $l = 0;
2230      for my $loc (keys %{$db_building{$building}}) {
2231         $l++;
2232
2233         print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
2234#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
2235         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
2236
2237         for my $sw (keys %{$db_building{$building}->{$loc}}) {
2238
2239            print "\"$sw:$db_switch_output_port{$sw}\" [label = $db_switch_output_port{$sw}, color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
2240
2241            my $swname  = $sw;
2242               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
2243            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
2244            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
2245            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
2246
2247
2248            for my $swport (keys %db_switch_connected_on_port) {
2249               my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
2250               next if not $sw_connect eq $sw;
2251               next if $port_connect eq $db_switch_output_port{$sw};
2252               print "\"$sw:$port_connect\" [label = $port_connect, color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
2253               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
2254              }
2255            }
2256         }
2257      }
2258
2259#   print "Switch output port and parent port connection\n";
2260#   print "---------------------------------------------\n";
2261   for my $sw (sort keys %db_switch_output_port) {
2262      if (exists $db_switch_parent{$sw}) {
2263#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
2264         }
2265      else {
2266         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
2267         }
2268      }
2269   print "\n";
2270
2271#   print "Switch parent and children port inter-connection\n";
2272#   print "------------------------------------------------\n";
2273   for my $swport (sort keys %db_switch_connected_on_port) {
2274      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
2275      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
2276         if (exists $db_switch_output_port{$sw}) {
2277            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
2278            }
2279         else {
2280            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
2281            }
2282         }
2283      }
2284
2285print "}\n";
2286   return;
2287   }
2288
2289
2290__END__
2291
2292=head1 NAME
2293
2294klask - ports manager and finder for switch
2295
2296
2297=head1 USAGE
2298
2299 klask updatedb
2300 klask exportdb --format [txt|html]
2301 klask removedb computer*
2302 klask cleandb  --day number_of_day --verbose
2303
2304 klask updatesw
2305 klask exportsw --format [txt|dot]
2306
2307 klask searchdb --kind [host|mac] computer [mac-address]
2308 klask search   computer
2309 klask search-mac-on-switch switch mac_addr
2310
2311 klask ip-free --day number_of_day --format [txt|html] [vlan_name]
2312
2313 klask enable  switch port
2314 klask disable swith port
2315 klask status  swith port
2316
2317
2318=head1 DESCRIPTION
2319
2320klask is a small tool to find where is a host in a big network. klask mean search in brittany.
2321
2322Klask has now a web site dedicated for it !
2323
2324 http://servforge.legi.grenoble-inp.fr/projects/klask
2325
2326
2327=head1 COMMANDS
2328
2329
2330=head2 search
2331
2332This 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.
2333
2334
2335=head2 enable
2336
2337This command activate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
2338
2339
2340=head2 disable
2341
2342This command deactivate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
2343
2344
2345=head2 status
2346
2347This 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.
2348
2349
2350=head2 updatedb
2351
2352This command will scan networks and update a database. To know which are the cmputer scan, you have to configure the file /etc/klask/klask.conf This file is easy to read and write because klask use YAML format and not XML.
2353
2354
2355=head2 exportdb
2356
2357This 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...
2358
2359
2360=head2 updatesw
2361
2362This command build a map of your manageable switch on your network. The list of the switch must be given in the file /etc/klask/klask.conf.
2363
2364
2365=head2 exportsw --format [txt|dot]
2366
2367This 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.
2368
2369 klask exportsw --format dot > /tmp/map.dot
2370 dot -Tpng /tmp/map.dot > /tmp/map.png
2371
2372
2373
2374=head1 CONFIGURATION
2375
2376Because klask need many parameters, it's not possible actually to use command line parameters. The configuration is done in a /etc/klask/klask.conf YAML file. This format have many advantage over XML, it's easier to read and to write !
2377
2378Here an example, be aware with indent, it's important in YAML, do not use tabulation !
2379
2380 default:
2381   community: public
2382   snmpport: 161
2383
2384 network:
2385   labnet:
2386     ip-subnet:
2387       - add: 192.168.1.0/24
2388       - add: 192.168.2.0/24
2389     interface: eth0
2390     main-router: gw1.labnet.local
2391
2392   schoolnet:
2393     ip-subnet:
2394       - add: 192.168.6.0/24
2395       - add: 192.168.7.0/24
2396     interface: eth0.38
2397     main-router: gw2.schoolnet.local
2398
2399 switch:
2400   - hostname: sw1.klask.local
2401     portignore:
2402       - 1
2403       - 2
2404
2405   - hostname: sw2.klask.local
2406     location: BatK / 2 / K203
2407     type: HP2424
2408     portignore:
2409       - 1
2410       - 2
2411
2412I 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.
2413
2414
2415=head1 FILES
2416
2417 /etc/klask/klask.conf
2418 /var/lib/klask/klaskdb
2419 /var/lib/klask/switchdb
2420
2421=head1 SEE ALSO
2422
2423Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
2424
2425
2426=head1 VERSION
2427
2428$Id: klask 148 2016-05-12 09:28:04Z g7moreau $
2429
2430
2431=head1 AUTHOR
2432
2433Written by Gabriel Moreau, Grenoble - France
2434
2435
2436=head1 LICENSE AND COPYRIGHT
2437
2438GPL version 2 or later and Perl equivalent
2439
2440Copyright (C) 2005-2013 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.