source: trunk/klask @ 151

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