source: trunk/klask @ 143

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