source: trunk/klask @ 147

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