source: trunk/klask @ 155

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