source: trunk/klask @ 141

Last change on this file since 141 was 141, checked in by g7moreau, 10 years ago
  • Begin klask rebootsw and get_vlan_port
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 79.2 KB
Line 
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005-2013 Gabriel Moreau.
4#
5# $Id: klask 141 2014-05-20 19:01:54Z 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   JD374A => { model => 'HP5500-24F', match => 'HP Comware.+?A5500-24G-SFP EI'     },
106   # BayStack
107   BS350T => { model => 'BS350T',     match => 'BayStack 350T HW'                  },
108   # Nexans
109   N3483G => { model => 'NA3483-6G',  match => 'GigaSwitch V3 TP SFP-I 48V ES3'    },
110   # 3COM
111   'H3C5500'        => { model => 'H3C5500',        match => 'H3C S5500-SI Series'              },
112   '3C17203'        => { model => '3C17203',        match => '3Com SuperStack 3 24-Port'        },
113   '3C17204'        => { model => '3C17204',        match => '3Com SuperStack 3 48-Port'        },
114   '3CR17562-91'    => { model => '3CR17562-91',    match => '3Com Switch 4500 50-Port'         },
115   '3CR17255-91'    => { model => '3CR17255-91',    match => '3Com Switch 5500G-EI 48-Port'     },
116   '3CR17251-91'    => { model => '3CR17251-91',    match => '3Com Switch 5500G-EI 48-Port'     },
117   '3CR17571-91'    => { model => '3CR17571-91',    match => '3Com Switch 4500 PWR 26-Port'     },
118   '3CRWX220095A'   => { model => '3CRWX220095A',   match => '3Com Wireless LAN Controller'     },
119   '3CR17254-91'    => { model => '3CR17254-91',    match => '3Com Switch 5500G-EI 24-Port'     },
120   '3CRS48G-24S-91' => { model => '3CRS48G-24S-91', match => '3Com Switch 4800G 24-Port'        },
121   '3CRS48G-48S-91' => { model => '3CRS48G-48S-91', match => '3Com Switch 4800G 48-Port'        },
122   '3C17708'        => { model => '3C17708',        match => '3Com Switch 4050'                 },
123   '3C17709'        => { model => '3C17709',        match => '3Com Switch 4060'                 },
124   '3C17707'        => { model => '3C17707',        match => '3Com Switch 4070'                 },
125   '3CR17258-91'    => { model => '3CR17258-91',    match => '3Com Switch 5500G-EI 24-Port SFP' },
126   '3CR17181-91'    => { model => '3CR17181-91',    match => '3Com Switch 5500-EI 28-Port FX'   },
127   '3CR17252-91'    => { model => '3CR17252-91',    match => '3Com Switch 5500G-EI PWR 24-Port' },
128   '3CR17253-91'    => { model => '3CR17253-91',    match => '3Com Switch 5500G-EI PWR 48-Port' },
129   '3CR17250-91'    => { model => '3CR17250-91',    match => '3Com Switch 5500G-EI 24-Port'     },
130   '3CR17561-91'    => { model => '3CR17561-91',    match => '3Com Switch 4500 26-Port'         },
131   '3CR17572-91'    => { model => '3CR17572-91',    match => '3Com Switch 4500 PWR 50-Port'     },
132   '3C17702-US'     => { model => '3C17702-US',     match => '3Com Switch 4900 SX'              },
133   '3C17700'        => { model => '3C17700',        match => '3Com Switch 4900'                 },
134   );
135
136Readonly my %OID_NUMBER => (
137   sysDescription  => '1.3.6.1.2.1.1.1.0',
138   sysName         => '1.3.6.1.2.1.1.5.0',
139   sysContact      => '1.3.6.1.2.1.1.4.0',
140   sysLocation     => '1.3.6.1.2.1.1.6.0',
141   searchPort1     => '1.3.6.1.2.1.17.4.3.1.2',       # BRIDGE-MIB (802.1D).
142   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
143   vlanPortDefault => '1.3.6.1.2.1.17.7.1.4.5.1.1',   # dot1qPvid
144   vlanStatus      => '1.3.6.1.2.1.17.7.1.4.3.1.5',   # integer 4 Create, 6 Destroy
145   vlanName        => '1.3.6.1.2.1.17.7.1.4.3.1.1',   # string
146   hpicfReset      => '1.3.6.1.4.1.11.2.14.11.1.4.1', # HP reboot switch
147   );
148
149Readonly 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;
150Readonly my $RE_IPv4_ADDRESS => qr{ [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} }xms;
151
152Readonly my $RE_FLOAT_HOSTNAME => qr{ ^float }xms;
153
154
155################
156# principal
157################
158
159my $cmd = shift @ARGV || 'help';
160if (defined $CMD_DB{$cmd}) {
161   $CMD_DB{$cmd}->(@ARGV);
162   }
163else {
164   print {*STDERR} "klask: command $cmd not found\n\n";
165   $CMD_DB{help}->();
166   exit 1;
167   }
168
169exit;
170
171sub test_running_environnement {
172   die "Configuration file $KLASK_CFG_FILE does not exists. Klask need it !\n" if not -e "$KLASK_CFG_FILE";
173   die "Var folder $KLASK_VAR does not exists. Klask need it !\n"              if not -d "$KLASK_VAR";
174   return;
175   }
176
177sub test_switchdb_environnement {
178   die "Switch database $KLASK_SW_FILE does not exists. Launch updatesw before this command !\n" if not -e "$KLASK_SW_FILE";
179   return;
180   }
181
182sub test_maindb_environnement {
183   die "Main database $KLASK_DB_FILE does not exists. Launch updatedb before this command !\n" if not -e "$KLASK_DB_FILE";
184   return;
185   }
186
187###
188# fast ping dont l'objectif est de remplir la table arp de la machine
189sub fast_ping {
190   # Launch this command without waiting...
191   system "fping -q -c 1 @_ >/dev/null 2>&1 &";
192   return;
193   }
194
195sub shell_command {
196   my $cmd = shift;
197
198   my $fh     = new FileHandle;
199   my $result = '';
200   open $fh, q{-|}, "LANG=C $cmd" or die "Can't exec $cmd\n";
201   $result .= <$fh>;
202   close $fh;
203   chomp $result;
204   return $result;
205   }
206
207###
208# donne l'@ ip, dns, arp en fonction du dns OU de l'ip
209sub resolve_ip_arp_host {
210   my $param_ip_or_host = shift;
211   my $interface = shift || q{*};
212   my $type      = shift || q{fast};
213
214   my %ret = (
215      hostname_fq  => 'unknow',
216      ipv4_address => '0.0.0.0',
217      mac_address  => 'unknow',
218      );
219
220   # perl -MSocket -E 'say inet_ntoa(scalar gethostbyname("tech7meylan.hmg.inpg.fr"))'
221   my $packed_ip = scalar gethostbyname($param_ip_or_host);
222   return %ret if not defined $packed_ip;
223   $ret{ipv4_address} = inet_ntoa($packed_ip);
224
225   # perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("194.254.66.240"), AF_INET)'
226   my $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET);
227   $ret{hostname_fq} = $hostname_fq if defined $hostname_fq;
228
229   # my $cmd = q{grep  -he '\b} . $param_ip_or_host . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
230   my $cmd = q{grep  -he '\b} . $ret{ipv4_address} . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
231   my $cmd_arpwatch = shell_command $cmd;
232   my ($arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch;
233
234   $ret{mac_address}  = $arp       if $arp;
235   $ret{timestamp}    = $timestamp if $timestamp;
236
237   my $nowtimestamp = time;
238
239   if ( $type eq 'fast' and ( not defined $timestamp or $timestamp < ( $nowtimestamp - 45 * 60 ) ) ) { # 45 min
240      $ret{mac_address} = 'unknow';
241      return %ret;
242      }
243
244   # resultat de la commande arp
245   # tech7meylan.hmg.inpg.fr (194.254.66.240) at 00:14:22:45:28:A9 [ether] on eth0
246   # sw2-batF0-legi.hmg.priv (192.168.22.112) at 00:30:c1:76:9c:01 [ether] on eth0.37
247   my $cmd_arp  = shell_command "arp -a $param_ip_or_host";
248   if ( $cmd_arp =~ m{ (\S*) \s \( ( $RE_IPv4_ADDRESS ) \) \s at \s ( $RE_MAC_ADDRESS ) }xms ) {
249      ( $ret{hostname_fq}, $ret{ipv4_address}, $ret{mac_address} )  = ($1, $2, $3);
250      }
251
252   # Normalize MAC Address
253   if ($ret{mac_address} ne 'unknow') {
254      my @paquets = ();
255      foreach ( split m/ : /xms, $ret{mac_address} ) {
256         my @chars = split m//xms, uc "00$_";
257         push @paquets, "$chars[-2]$chars[-1]";
258         }
259      $ret{mac_address} = join q{:}, @paquets;
260      }
261
262   return %ret;
263   }
264
265# Find Surname of a switch
266sub get_switch_model {
267   my $sw_snmp_description = shift || 'unknow';
268
269   for my $sw_kind (keys %SWITCH_KIND) {
270      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{match}/ms; # option xms break search, why ?
271
272      return $SWITCH_KIND{$sw_kind}->{model};
273      }
274
275   return $sw_snmp_description;
276   }
277
278###
279# va rechercher le nom des switchs pour savoir qui est qui
280sub init_switch_names {
281   my $verbose = shift;
282
283   printf "%-26s                %-25s %s\n",'Switch','Description','Type' if $verbose;
284   print "------------------------------------------------------------------------------\n" if $verbose;
285
286   INIT_EACH_SWITCH:
287   for my $sw (@SWITCH) {
288      my %session = ( -hostname   => $sw->{hostname} );
289         $session{-version} = $sw->{version}   || 1;
290         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
291         if (exists $sw->{version} and $sw->{version} eq '3') {
292            $session{-username} = $sw->{username} || 'snmpadmin';
293            }
294         else {
295            $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
296            }
297
298      $sw->{local_session} = \%session;
299
300      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
301      print "$error \n" if $error;
302
303      my $result = $session->get_request(
304         -varbindlist => [
305            $OID_NUMBER{sysDescription},
306            $OID_NUMBER{sysName},
307            $OID_NUMBER{sysContact},
308            $OID_NUMBER{sysLocation},
309            ]
310         );
311      $sw->{description} = $result->{$OID_NUMBER{sysName}} || $sw->{hostname};
312      $sw->{model} = get_switch_model( $result->{$OID_NUMBER{sysDescription}});
313      #$sw->{location} = $result->{"1.3.6.1.2.1.1.6.0"} || $sw->{hostname};
314      #$sw->{contact} = $result->{"1.3.6.1.2.1.1.4.0"} || $sw->{hostname};
315      $session->close;
316
317      # Ligne à virer car on récupère maintenant le modèle du switch
318      my ($desc, $type) = split m/ : /xms, $sw->{description}, 2;
319      printf "%-26s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, $sw->{model} if $verbose;
320      }
321
322   print "\n" if $verbose;
323   return;
324   }
325
326###
327# convertit l'hexa (uniquement 2 chiffres) en decimal
328sub digit_hex_to_dec {
329   #00:0F:1F:43:E4:2B
330   my $car = '00' . uc shift;
331
332   return '00' if $car eq '00UNKNOW';
333   my %table = (
334      '0'=>'0',  '1'=>'1',  '2'=>'2',  '3'=>'3',  '4'=>'4',
335      '5'=>'5',  '6'=>'6',  '7'=>'7',  '8'=>'8',  '9'=>'9',
336      'A'=>'10', 'B'=>'11', 'C'=>'12', 'D'=>'13', 'E'=>'14', 'F'=>'15',
337      );
338   my @chars = split m//xms, $car;
339   return $table{$chars[-2]}*16 + $table{$chars[-1]};
340   }
341
342#--------------------------------------------------------------------------------
343
344sub normalize_mac_address {
345   my $mac_address = shift;
346
347   # D07E-28D1-7AB8 or d07e28-d17ab8
348   if ($mac_address =~ m{^ (?: [0-9A-Fa-f]{4} -){2} [0-9A-Fa-f]{4} $}xms
349      or $mac_address =~ m{^ [0-9A-Fa-f]{6} - [0-9A-Fa-f]{6} $}xms) {
350      $mac_address =~ s/-//g;
351      return join q{:}, unpack('(A2)*', uc($mac_address));
352      }
353
354   return join q{:}, map { substr( uc("00$_"), -2) } split m/ [:-] /xms, $mac_address;
355   }
356
357#--------------------------------------------------------------------------------
358# convertit l'@ mac en decimal
359sub mac_address_hex_to_dec {
360   #00:0F:1F:43:E4:2B
361   my $mac_address = shift;
362
363   my @paquets = split m/ : /xms, $mac_address;
364   my $return = q{};
365   foreach(@paquets) {
366      $return .= q{.} . digit_hex_to_dec($_);
367      }
368   return $return;
369   }
370
371###
372# va rechercher le port et le switch sur lequel est la machine
373sub find_switch_port {
374   my $mac_address     = shift;
375   my $switch_proposal = shift || q{};
376
377   my %ret;
378   $ret{switch_description} = 'unknow';
379   $ret{switch_port} = '0';
380
381   return %ret if $mac_address eq 'unknow';;
382
383   my @switch_search = @SWITCH;
384   if ($switch_proposal ne q{}) {
385      for my $sw (@SWITCH) {
386         next if $sw->{hostname} ne $switch_proposal;
387         unshift @switch_search, $sw;
388         last;
389         }
390      }
391
392   my $research1 = $OID_NUMBER{searchPort1} . mac_address_hex_to_dec($mac_address);
393   my $research2 = $OID_NUMBER{searchPort2} . mac_address_hex_to_dec($mac_address);
394
395   LOOP_ON_SWITCH:
396   for my $sw (@switch_search) {
397      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
398      print "$error \n" if $error;
399
400      my $result = $session->get_request(
401         -varbindlist => [$research1]
402         );
403      if (not defined $result) {
404         $result = $session->get_request(
405            -varbindlist => [$research2]
406            );
407         $result->{$research1} = $result->{$research2} if defined $result;
408         }
409
410      if (not (defined $result and $result->{$research1} ne 'noSuchInstance')) {
411         $session->close;
412         next LOOP_ON_SWITCH;
413         }
414
415         my $swport = $result->{$research1};
416         $session->close;
417
418         # IMPORTANT !!
419         # ceci empeche la detection sur certains port ...
420         # en effet les switch sont relies entre eux par un cable reseau et du coup
421         # tous les arp de toutes les machines sont presentes sur ces ports (ceux choisis ici sont les miens)
422         # cette partie est a ameliore, voir a configurer dans l'entete
423         # 21->24 45->48
424#         my $flag = 0;
425         SWITCH_PORT_IGNORE:
426         foreach my $p (@{$sw->{portignore}}) {
427            next SWITCH_PORT_IGNORE if $swport ne get_numerical_port($sw->{model},$p);
428#            $flag = 1;
429            next LOOP_ON_SWITCH;
430            }
431#         if ($flag == 0) {
432            $ret{switch_hostname}    = $sw->{hostname};
433            $ret{switch_description} = $sw->{description};
434            $ret{switch_port}        = get_human_readable_port($sw->{model}, $swport); # $swport;
435
436            last LOOP_ON_SWITCH;
437#            }
438#         }
439#      $session->close;
440      }
441   return %ret;
442   }
443
444###
445# va rechercher les port et les switch sur lequel est la machine
446sub find_all_switch_port {
447   my $mac_address = shift;
448
449   my $ret = {};
450
451   return $ret if $mac_address eq 'unknow';
452
453#   for my $sw (@SWITCH) {
454#      next if exists $SWITCH_PORT_COUNT{$sw->{hostname}};
455#
456#      $SWITCH_PORT_COUNT{$sw->{hostname}} = {};
457#      print "DEBUG: SWITCH_PORT_COUNT defined for $sw->{hostname}\n" if $DEBUG xor 2;
458#      }
459
460   my $research1 = $OID_NUMBER{searchPort1} . mac_address_hex_to_dec($mac_address);
461   my $research2 = $OID_NUMBER{searchPort2} . mac_address_hex_to_dec($mac_address);
462   LOOP_ON_ALL_SWITCH:
463   for my $sw (@SWITCH) {
464      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
465      print "$error \n" if $error;
466
467      my $result = $session->get_request(
468         -varbindlist => [$research1]
469         );
470      if (not defined $result) {
471         $result = $session->get_request(
472            -varbindlist => [$research2]
473            );
474         $result->{$research1} = $result->{$research2} if defined $result;
475         }
476
477      if (defined $result and $result->{$research1} ne 'noSuchInstance') {
478         my $swport = $result->{$research1};
479
480         $ret->{$sw->{hostname}} = {};
481         $ret->{$sw->{hostname}}{hostname}    = $sw->{hostname};
482         $ret->{$sw->{hostname}}{description} = $sw->{description};
483         $ret->{$sw->{hostname}}{port}        = get_human_readable_port($sw->{model}, $swport);
484
485#         $SWITCH_PORT_COUNT{$sw->{hostname}}->{$swport}++;
486         }
487
488      $session->close;
489      }
490   return $ret;
491   }
492
493sub get_list_network {
494
495   return keys %{$KLASK_CFG->{network}};
496   }
497
498sub get_current_interface {
499   my $vlan_name = shift;
500
501   return $KLASK_CFG->{network}{$vlan_name}{interface};
502   }
503
504###
505# liste l'ensemble des adresses ip d'un réseau
506sub get_list_ip {
507   my @vlan_name = @_;
508
509   my $cidrlist = Net::CIDR::Lite->new;
510
511   for my $net (@vlan_name) {
512      my @line  = @{$KLASK_CFG->{network}{$net}{'ip-subnet'}};
513      for my $cmd (@line) {
514         for my $method (keys %{$cmd}){
515            $cidrlist->add_any($cmd->{$method}) if $method eq 'add';
516            }
517         }
518      }
519
520   my @res = ();
521
522   for my $cidr ($cidrlist->list()) {
523      my $net = new NetAddr::IP $cidr;
524      for my $ip (@{$net}) {
525         $ip =~ s{ /32 }{}xms;
526         push @res,  $ip;
527         }
528      }
529
530   return @res;
531   }
532
533# liste l'ensemble des routeurs du réseau
534sub get_list_main_router {
535   my @vlan_name = @_;
536
537   my @res = ();
538
539   for my $net (@vlan_name) {
540      push @res, $KLASK_CFG->{network}{$net}{'main-router'};
541      }
542
543   return @res;
544   }
545
546sub get_human_readable_port {
547   my $sw_model = shift;
548   my $sw_port  = shift;
549
550   if ($sw_model eq 'HP8000M') {
551
552      my $reste = (($sw_port - 1) % 8) + 1;
553      my $major = int (($sw_port - 1) / 8);
554      return "$INTERNAL_PORT_MAP{$major}$reste";
555      }
556
557   if ($sw_model eq 'HP2424M') {
558      if ($sw_port > 24) {
559         
560         my $reste = $sw_port - 24;
561         return "A$reste";
562         }
563      }
564
565   if ($sw_model eq 'HP1600M') {
566      if ($sw_port > 16) {
567         
568         my $reste = $sw_port - 16;
569         return "A$reste";
570         }
571      }
572
573   if ($sw_model eq 'HP2810-48G' or $sw_model eq 'HP2810-24G') {
574      if ($sw_port > 48) {
575         
576         my $reste = $sw_port - 48;
577         return "Trk$reste";
578         }
579      }
580
581   if ($sw_model eq 'HP3500-24G') {
582      if ($sw_port > 289) {
583         
584         my $reste = $sw_port - 289;
585         return "Trk$reste";
586         }
587      }
588
589   return $sw_port;
590   }
591
592sub get_numerical_port {
593   my $sw_model = shift;
594   my $sw_port  = shift;
595
596   if ($sw_model eq 'HP8000M') {
597
598      my $letter = substr $sw_port, 0, 1;
599      my $reste =  substr $sw_port, 1;
600
601      return $INTERNAL_PORT_MAP_REV{$letter} * 8 + $reste;
602      }
603
604   if ($sw_model eq 'HP2424M') {
605      if ($sw_port =~ m/^A/xms ) {
606
607         my $reste =  substr $sw_port, 1;
608
609         return 24 + $reste;
610         }
611      }
612
613   if ($sw_model eq 'HP1600M') {
614      if ($sw_port =~ m/^A/xms ) {
615
616         my $reste =  substr $sw_port, 1;
617
618         return 16 + $reste;
619         }
620      }
621
622   if ($sw_model eq 'HP2810-48G' or $sw_model eq 'HP2810-24G') {
623      if ($sw_port =~ m/^Trk/xms ) {
624
625         my $reste =  substr $sw_port, 3;
626
627         return 48 + $reste;
628         }
629      }
630
631   if ($sw_model eq 'HP3500-24G') {
632      if ($sw_port =~ m/^Trk/xms ) {
633
634         my $reste =  substr $sw_port, 3;
635
636         return 289 + $reste;
637         }
638      }
639
640   return $sw_port;
641   }
642
643################
644# Les commandes
645################
646
647sub cmd_help {
648
649print <<'END';
650klask - ports manager and finder for switch
651
652 klask version
653
654 klask updatedb
655 klask exportdb --format [txt|html]
656 klask removedb computer*
657 klask cleandb  --day number_of_day --verbose
658
659 klask updatesw
660 klask exportsw --format [txt|dot]
661
662 klask searchdb computer
663 klask search   computer
664 klask search-mac-on-switch switch mac_addr
665
666 klask ip-free --day number_of_day --format [txt|html] [vlan_name]
667
668 klask bad-vlan-id
669
670 klask enable  switch port
671 klask disable switch port
672 klask status  switch port
673END
674   return;
675   }
676
677sub cmd_version {
678
679print <<'END';
680Klask - ports manager and finder for switch
681Copyright (C) 2005-2013 Gabriel Moreau
682
683END
684   print ' $Rev: 141 $'."\n";
685   print ' $Date: 2014-05-20 19:01:54 +0000 (Tue, 20 May 2014) $'."\n";
686   print ' $Id: klask 141 2014-05-20 19:01:54Z g7moreau $'."\n";
687   return;
688   }
689
690sub cmd_search {
691   my @computer = @_;
692
693   init_switch_names();    #nomme les switchs
694   fast_ping(@computer);
695
696   LOOP_ON_COMPUTER:
697   for my $clientname (@computer) {
698      my %resol_arp = resolve_ip_arp_host($clientname);          #resolution arp
699      my %where     = find_switch_port($resol_arp{mac_address}); #retrouve l'emplacement
700
701      next LOOP_ON_COMPUTER if $where{switch_description} eq 'unknow' or $resol_arp{hostname_fq} eq 'unknow' or $resol_arp{mac_address} eq 'unknow';
702
703      printf '%-22s %2i %-30s %-15s %18s',
704         $where{switch_hostname},
705         $where{switch_port},
706         $resol_arp{hostname_fq},
707         $resol_arp{ipv4_address},
708         $resol_arp{mac_address}."\n";
709      }
710   return;
711   }
712
713sub cmd_searchdb {
714   my @ARGV  = @_;
715
716   my $kind;
717
718   GetOptions(
719      'kind=s'   => \$kind,
720      );
721
722   my %possible_search = (
723      host  => \&cmd_searchdb_host,
724      mac   => \&cmd_searchdb_mac,
725      );
726
727   $kind = 'host' if not defined $possible_search{$kind};
728
729   $possible_search{$kind}->(@ARGV);
730   return;
731   }
732
733
734sub cmd_searchdb_host {
735   my @computer = @_;
736
737   fast_ping(@computer);
738   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
739
740   LOOP_ON_COMPUTER:
741   for my $clientname (@computer) {
742      my %resol_arp = resolve_ip_arp_host($clientname);      #resolution arp
743      my $ip = $resol_arp{ipv4_address};
744
745      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
746
747      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
748      $year += 1900;
749      $mon++;
750      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
751
752      printf "%-22s %2s %-30s %-15s %-18s %s\n",
753         $computerdb->{$ip}{switch_hostname},
754         $computerdb->{$ip}{switch_port},
755         $computerdb->{$ip}{hostname_fq},
756         $ip,
757         $computerdb->{$ip}{mac_address},
758         $date;
759      }
760   return;
761   }
762
763sub cmd_searchdb_mac {
764   my @mac = map { normalize_mac_address($_) } @_;
765
766   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
767
768   LOOP_ON_MAC:
769   for my $mac (@mac) {
770      LOOP_ON_COMPUTER:
771      for my $ip (keys %{$computerdb}) {
772         next LOOP_ON_COMPUTER if $mac ne $computerdb->{$ip}{mac_address};
773 
774         my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
775         $year += 1900;
776         $mon++;
777         my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
778
779         printf "%-22s %2s %-30s %-15s %-18s %s\n",
780            $computerdb->{$ip}{switch_hostname},
781            $computerdb->{$ip}{switch_port},
782            $computerdb->{$ip}{hostname_fq},
783            $ip,
784            $computerdb->{$ip}{mac_address},
785            $date;
786         #next LOOP_ON_MAC;
787         }
788
789      }
790   return;
791   }
792
793sub cmd_updatedb {
794   my @network = @_;
795      @network = get_list_network() if not @network;
796
797   test_switchdb_environnement();
798
799   my $computerdb = {};
800      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
801   my $timestamp = time;
802
803   my %computer_not_detected = ();
804   my $timestamp_last_week = $timestamp - (3600 * 24 * 7);
805
806   my $number_of_computer = get_list_ip(@network); # + 1;
807   my $size_of_database   = keys %{$computerdb};
808      $size_of_database   = 1 if $size_of_database == 0;
809   my $i = 0;
810   my $detected_computer = 0;
811
812   init_switch_names('yes');    #nomme les switchs
813
814   { # Remplis le champs portignore des ports d'inter-connection pour chaque switch
815   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
816   my %db_switch_output_port       = %{$switch_connection->{output_port}};
817   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
818   my %db_switch_chained_port = ();
819   for my $swport (keys %db_switch_connected_on_port) {
820      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
821      $db_switch_chained_port{$sw_connect} .= "$port_connect:";
822      }
823   for my $sw (@SWITCH){
824      push @{$sw->{portignore}}, $db_switch_output_port{$sw->{hostname}}  if exists $db_switch_output_port{$sw->{hostname}};
825      if ( exists $db_switch_chained_port{$sw->{hostname}} ) {
826         chop $db_switch_chained_port{$sw->{hostname}};
827         push @{$sw->{portignore}}, split m/ : /xms, $db_switch_chained_port{$sw->{hostname}};
828         }
829#      print "$sw->{hostname} ++ @{$sw->{portignore}}\n";
830      }
831   }
832
833   my %router_mac_ip = ();
834   DETECT_ALL_ROUTER:
835#   for my $one_router ('194.254.66.254') {
836   for my $one_router ( get_list_main_router(@network) ) {
837      my %resol_arp = resolve_ip_arp_host($one_router);
838      $router_mac_ip{ $resol_arp{mac_address} } = $resol_arp{ipv4_address};
839      }
840
841   ALL_NETWORK:
842   for my $net (@network) {
843
844      my @computer = get_list_ip($net);
845      my $current_interface = get_current_interface($net);
846
847      fast_ping(@computer);
848
849      LOOP_ON_COMPUTER:
850      for my $one_computer (@computer) {
851         $i++;
852
853         my $total_percent = int (($i*100)/$number_of_computer);
854
855         my $localtime = time - $timestamp;
856         my ($sec,$min) = localtime $localtime;
857
858         my $time_elapse = 0;
859            $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0;
860         my ($sec_elapse,$min_elapse) = localtime $time_elapse;
861
862         printf "\rComputer scanned: %4i/%i (%2i%%)",  $i,                 $number_of_computer, $total_percent;
863         printf ', detected: %4i/%i (%2i%%)', $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
864         printf ' [Time: %02i:%02i / %02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
865         printf ' %-8s %-14s', $current_interface, $one_computer;
866
867         my %resol_arp = resolve_ip_arp_host($one_computer, $current_interface);
868
869         # do not search on router connection (why ?)
870         if ( exists $router_mac_ip{$resol_arp{mac_address}}) {
871            $computer_not_detected{$one_computer} = $current_interface;
872            next LOOP_ON_COMPUTER;
873            }
874
875         # do not search on switch inter-connection
876         if (exists $switch_level{$resol_arp{hostname_fq}}) {
877            $computer_not_detected{$one_computer} = $current_interface;
878            next LOOP_ON_COMPUTER;
879            }
880
881         my $switch_proposal = q{};
882         if (exists $computerdb->{$resol_arp{ipv4_address}} and exists $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}) {
883            $switch_proposal = $computerdb->{$resol_arp{ipv4_address}}{switch_hostname};
884            }
885
886         # do not have a mac address
887         if ($resol_arp{mac_address} eq 'unknow' or (exists $resol_arp{timestamps} and $resol_arp{timestamps} < ($timestamp - 3 * 3600))) {
888            $computer_not_detected{$one_computer} = $current_interface;
889            next LOOP_ON_COMPUTER;
890            }
891
892         my %where = find_switch_port($resol_arp{mac_address},$switch_proposal);
893
894         #192.168.24.156:
895         #  arp: 00:0B:DB:D5:F6:65
896         #  hostname: pcroyon.hmg.priv
897         #  port: 5
898         #  switch: sw-batH-legi:hp2524
899         #  timestamp: 1164355525
900
901         # do not have a mac address
902#         if ($resol_arp{mac_address} eq 'unknow') {
903#            $computer_not_detected{$one_computer} = $current_interface;
904#            next LOOP_ON_COMPUTER;
905#            }
906
907         # detected on a switch
908         if ($where{switch_description} ne 'unknow') {
909            $detected_computer++;
910            $computerdb->{$resol_arp{ipv4_address}} = {
911               hostname_fq        => $resol_arp{hostname_fq},
912               mac_address        => $resol_arp{mac_address},
913               switch_hostname    => $where{switch_hostname},
914               switch_description => $where{switch_description},
915               switch_port        => $where{switch_port},
916               timestamp          => $timestamp,
917               network            => $net,
918               };
919            next LOOP_ON_COMPUTER;
920            }
921
922         # new in the database but where it is ?
923         if (not exists $computerdb->{$resol_arp{ipv4_address}}) {
924            $detected_computer++;
925            $computerdb->{$resol_arp{ipv4_address}} = {
926               hostname_fq        => $resol_arp{hostname_fq},
927               mac_address        => $resol_arp{mac_address},
928               switch_hostname    => $where{switch_hostname},
929               switch_description => $where{switch_description},
930               switch_port        => $where{switch_port},
931               timestamp          => $resol_arp{timestamp},
932               network            => $net,
933               };
934            }
935
936         # mise a jour du nom de la machine si modification dans le dns
937         $computerdb->{$resol_arp{ipv4_address}}{hostname_fq} = $resol_arp{hostname_fq};
938
939         # mise à jour de la date de détection si détection plus récente par arpwatch
940         $computerdb->{$resol_arp{ipv4_address}}{timestamp}   = $resol_arp{timestamp} if exists $resol_arp{timestamp} and $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $resol_arp{timestamp};
941
942         # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine
943#         push @computer_not_detected, $resol_arp{ipv4_address} if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
944         $computer_not_detected{$resol_arp{ipv4_address}} = $current_interface if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
945
946         }
947      }
948
949   # final end of line at the end of the loop
950   printf "\n";
951
952   my $dirdb = $KLASK_DB_FILE;
953      $dirdb =~ s{ / [^/]* $}{}xms;
954   mkdir "$dirdb", 0755 unless -d "$dirdb";
955   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
956
957   for my $one_computer (keys %computer_not_detected) {
958      my $interface = $computer_not_detected{$one_computer};
959      system "arping -c 1 -w 1 -rR -i $interface $one_computer &>/dev/null";
960#      print  "arping -c 1 -w 1 -rR -i $interface $one_computer 2>/dev/null\n";
961      }
962   return;
963   }
964
965sub cmd_removedb {
966   my @computer = @_;
967
968   test_maindb_environnement();
969
970   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
971
972   LOOP_ON_COMPUTER:
973   for my $one_computer (@computer) {
974
975      if ( $one_computer =~ m/^ $RE_IPv4_ADDRESS $/xms
976            and exists $computerdb->{$one_computer} ) {
977         delete $computerdb->{$one_computer};
978         next;
979         }
980
981      my %resol_arp = resolve_ip_arp_host($one_computer);
982
983      delete $computerdb->{$resol_arp{ipv4_address}} if exists $computerdb->{$resol_arp{ipv4_address}};
984      }
985
986   my $dirdb = $KLASK_DB_FILE;
987      $dirdb =~ s{ / [^/]* $}{}xms;
988   mkdir "$dirdb", 0755 unless -d "$dirdb";
989   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
990   return;
991   }
992
993sub cmd_cleandb {
994   my @ARGV  = @_;
995
996   my $days_to_clean = 15;
997   my $verbose;
998   my $database_has_changed;
999
1000   GetOptions(
1001      'day|d=i'   => \$days_to_clean,
1002      'verbose|v' => \$verbose,
1003      );
1004
1005   my @vlan_name = get_list_network();
1006
1007   my $computerdb = {};
1008      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1009   my $timestamp = time;
1010
1011   my $timestamp_barrier = 3600 * 24 * $days_to_clean;
1012   my $timestamp_3month  = 3600 * 24 * 90;
1013
1014   my %mactimedb = ();
1015   ALL_VLAN:
1016   for my $vlan (shuffle @vlan_name) {
1017
1018      my @ip_list   = shuffle get_list_ip($vlan);
1019     
1020      LOOP_ON_IP_ADDRESS:
1021      for my $ip (@ip_list) {
1022
1023         next LOOP_ON_IP_ADDRESS if
1024            not exists $computerdb->{$ip};
1025           
1026            #&& $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1027         my $ip_timestamp   = $computerdb->{$ip}{timestamp};
1028         my $ip_mac         = $computerdb->{$ip}{mac_address};
1029         my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
1030
1031         $mactimedb{$ip_mac} ||= {
1032            ip          => $ip,
1033            timestamp   => $ip_timestamp,
1034            vlan        => $vlan,
1035            hostname_fq => $ip_hostname_fq,
1036            };
1037         
1038         if (
1039            ( $mactimedb{$ip_mac}->{timestamp} - $ip_timestamp > $timestamp_barrier
1040               or (
1041                  $mactimedb{$ip_mac}->{timestamp} > $ip_timestamp
1042                  and $timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_3month
1043                  )
1044            )
1045            and (
1046               not $mactimedb{$ip_mac}->{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/
1047               or $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/
1048               )) {
1049            print "remove ip $ip\n" if $verbose;
1050            delete $computerdb->{$ip};
1051            $database_has_changed++;
1052            }
1053
1054         elsif (
1055            ( $ip_timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_barrier
1056               or (
1057                  $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}
1058                  and $timestamp - $ip_timestamp > $timestamp_3month
1059                  )
1060            )
1061            and (
1062               not $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/
1063               or $mactimedb{$ip_mac}->{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/
1064               )) {
1065            print "remove ip ".$mactimedb{$ip_mac}->{ip}."\n" if $verbose;
1066            delete $computerdb->{$mactimedb{$ip_mac}->{ip}};
1067            $database_has_changed++;
1068            }
1069
1070         if ( $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}) {
1071            $mactimedb{$ip_mac} = {
1072               ip          => $ip,
1073               timestamp   => $ip_timestamp,
1074               vlan        => $vlan,
1075               hostname_fq => $ip_hostname_fq,
1076               };
1077            }
1078         }
1079      }
1080
1081   if ( $database_has_changed ) {
1082      my $dirdb = $KLASK_DB_FILE;
1083         $dirdb =~ s{ / [^/]* $}{}xms;
1084      mkdir "$dirdb", 0755 unless -d "$dirdb";
1085      YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
1086      }
1087   return;
1088   }
1089
1090sub cmd_exportdb {
1091   @ARGV = @_;
1092
1093   my $format = 'txt';
1094
1095   GetOptions(
1096      'format|f=s'  => \$format,
1097      );
1098
1099   my %possible_format = (
1100      txt  => \&cmd_exportdb_txt,
1101      html => \&cmd_exportdb_html,
1102      );
1103
1104   $format = 'txt' if not defined $possible_format{$format};
1105
1106   $possible_format{$format}->(@ARGV);
1107   return;
1108   }
1109
1110sub cmd_exportdb_txt {
1111   test_maindb_environnement();
1112
1113   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1114
1115   printf "%-27s %-4s            %-40s %-15s %-18s %-16s %s\n", qw(Switch Port Hostname-FQ IPv4-Address MAC-Address Date VLAN);
1116   print "--------------------------------------------------------------------------------------------------------------------------------------------\n";
1117
1118   LOOP_ON_IP_ADDRESS:
1119   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1120
1121#      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow';
1122
1123      # to be improve in the future
1124      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1125
1126# dans le futur
1127#      next if $computerdb->{$ip}{hostname_fq} eq 'unknow';
1128
1129      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1130      $year += 1900;
1131      $mon++;
1132      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1133
1134      printf "%-28s  %2s  <-------  %-40s %-15s %-18s %-16s %s\n",
1135         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
1136         $computerdb->{$ip}{switch_port},
1137         $computerdb->{$ip}{hostname_fq},
1138         $ip,
1139         $computerdb->{$ip}{mac_address},
1140         $date,
1141         $computerdb->{$ip}{network} || '';
1142      }
1143   return;
1144   }
1145
1146sub cmd_exportdb_html {
1147   test_maindb_environnement();
1148
1149   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1150
1151#<link rel="stylesheet" type="text/css" href="style-klask.css" />
1152#<script src="sorttable-klask.js"></script>
1153
1154   print <<'END_HTML';
1155<table class="sortable" summary="Klask Host Database">
1156 <caption>Klask Host Database</caption>
1157 <thead>
1158  <tr>
1159   <th scope="col" class="klask-header-left">Switch</th>
1160   <th scope="col" class="sorttable_nosort">Port</th>
1161   <th scope="col" class="sorttable_nosort">Link</th>
1162   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1163   <th scope="col" class="hklask-ipv4">IPv4-Address</th>
1164   <th scope="col" class="sorttable_alpha">MAC-Address</th>
1165   <th scope="col" class="sorttable_alpha">VLAN</th>
1166   <th scope="col" class="klask-header-right">Date</th>
1167  </tr>
1168 </thead>
1169 <tfoot>
1170  <tr>
1171   <th scope="col" class="klask-footer-left">Switch</th>
1172   <th scope="col" class="fklask-port">Port</th>
1173   <th scope="col" class="fklask-link">Link</th>
1174   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1175   <th scope="col" class="fklask-ipv4">IPv4-Address</th>
1176   <th scope="col" class="fklask-mac">MAC-Address</th>
1177   <th scope="col" class="fklask-vlan">VLAN</th>
1178   <th scope="col" class="klask-footer-right">Date</th>
1179  </tr>
1180 </tfoot>
1181 <tbody>
1182END_HTML
1183
1184   my %mac_count = ();
1185   LOOP_ON_IP_ADDRESS:
1186   foreach my $ip (keys %{$computerdb}) {
1187
1188      # to be improve in the future
1189      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1190
1191      $mac_count{$computerdb->{$ip}{mac_address}}++;
1192      }
1193
1194   my $typerow = 'even';
1195
1196   LOOP_ON_IP_ADDRESS:
1197   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1198
1199      # to be improve in the future
1200      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1201
1202      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1203      $year += 1900;
1204      $mon++;
1205      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1206
1207#      $odd_or_even++;
1208#      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
1209      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1210
1211      my $switch_hostname = $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description} || 'unkown';
1212      chomp $switch_hostname;
1213      my $switch_hostname_sort = sprintf '%s %3s' ,$switch_hostname, $computerdb->{$ip}{switch_port};
1214
1215      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1216
1217      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{mac_address}}, $computerdb->{$ip}{mac_address};
1218
1219      $computerdb->{$ip}{hostname_fq} = 'unknow' if $computerdb->{$ip}{hostname_fq} =~ m/^ \d+ \. \d+ \. \d+ \. \d+ $/xms;
1220      my ( $host_short ) = split m/ \. /xms, $computerdb->{$ip}{hostname_fq};
1221
1222      my $vlan = $computerdb->{$ip}{network} || '';
1223
1224      print <<"END_HTML";
1225  <tr class="$typerow">
1226   <td sorttable_customkey="$switch_hostname_sort">$switch_hostname</td>
1227   <td class="bklask-port">$computerdb->{$ip}{switch_port}</td>
1228   <td><-------</td>
1229   <td sorttable_customkey="$host_short">$computerdb->{$ip}{hostname_fq}</td>
1230   <td sorttable_customkey="$ip_sort">$ip</td>
1231   <td sorttable_customkey="$mac_sort">$computerdb->{$ip}{mac_address}</td>
1232   <td>$vlan</td>
1233   <td>$date</td>
1234  </tr>
1235END_HTML
1236      }
1237
1238   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1239
1240   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1241   my %db_switch_parent            = %{$switch_connection->{parent}};
1242   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1243   my %db_switch                   = %{$switch_connection->{switch_db}};
1244
1245   for my $sw (sort keys %db_switch_output_port) {
1246
1247      my $switch_hostname_sort = sprintf '%s %3s' ,$sw, $db_switch_output_port{$sw};
1248
1249      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1250
1251      if (exists $db_switch_parent{$sw}) {
1252
1253      my $mac_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{mac_address};
1254      my $ipv4_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{ipv4_address};
1255      my $timestamp = $db_switch{$db_switch_parent{$sw}->{switch}}->{timestamp};
1256
1257      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1258      $year += 1900;
1259      $mon++;
1260      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1261
1262      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1263
1264      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1265
1266      my ( $host_short ) = sprintf '%s %3s' , split(m/ \. /xms, $db_switch_parent{$sw}->{switch}, 1), $db_switch_parent{$sw}->{port};
1267
1268      print <<"END_HTML";
1269  <tr class="$typerow">
1270   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1271   <td class="bklask-port">$db_switch_output_port{$sw}</>
1272   <td>+--> $db_switch_parent{$sw}->{port}</td>
1273   <td sorttable_customkey="$host_short">$db_switch_parent{$sw}->{switch}</>
1274   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1275   <td sorttable_customkey="$mac_sort">$mac_address</td>
1276   <td></td>
1277   <td>$date</td>
1278  </tr>
1279END_HTML
1280         }
1281      else {
1282         print <<"END_HTML";
1283  <tr class="$typerow">
1284   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1285   <td class="bklask-port">$db_switch_output_port{$sw}</>
1286   <td>+--></td>
1287   <td sorttable_customkey="router">router</>
1288   <td sorttable_customkey="999999999999"></td>
1289   <td sorttable_customkey="99999"></td>
1290   <td></td>
1291   <td></td>
1292  </tr>
1293END_HTML
1294         }
1295      }
1296
1297   for my $swport (sort keys %db_switch_connected_on_port) {
1298      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1299      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1300
1301         my $switch_hostname_sort = sprintf '%s %3s' ,$sw_connect, $port_connect;
1302
1303      my $mac_address = $db_switch{$sw}->{mac_address};
1304      my $ipv4_address = $db_switch{$sw}->{ipv4_address};
1305      my $timestamp = $db_switch{$sw}->{timestamp};
1306
1307      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1308      $year += 1900;
1309      $mon++;
1310      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year,$mon,$mday,$hour,$min;
1311
1312      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1313
1314      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1315
1316      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1317
1318         if (exists $db_switch_output_port{$sw}) {
1319
1320            my ( $host_short ) = sprintf '%s %3s' , split( m/\./xms, $sw, 1), $db_switch_output_port{$sw};
1321
1322            print <<"END_HTML";
1323  <tr class="$typerow">
1324   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1325   <td class="bklask-port">$port_connect</>
1326   <td>&lt;--+ $db_switch_output_port{$sw}</td>
1327   <td sorttable_customkey="$host_short">$sw</>
1328   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1329   <td sorttable_customkey="$mac_sort">$mac_address</td>
1330   <td></td>
1331   <td>$date</td>
1332  </tr>
1333END_HTML
1334            }
1335         else {
1336            print <<"END_HTML";
1337  <tr class="$typerow">
1338   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1339   <td class="bklask-port">$port_connect</>
1340   <td>&lt;--+</td>
1341   <td sorttable_customkey="$sw">$sw</>
1342   <td sorttable_customkey="">$ipv4_address</td>
1343   <td sorttable_customkey="">$mac_address</td>
1344   <td></td>
1345   <td>$date</td>
1346  </tr>
1347END_HTML
1348            }
1349         }
1350      }
1351
1352   print <<'END_HTML';
1353 </tbody>
1354</table>
1355END_HTML
1356   return;
1357   }
1358
1359sub cmd_bad_vlan_id {
1360   test_maindb_environnement();
1361
1362   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1363
1364   # create a database with the most recent computer by switch port
1365   my %swithportdb = ();
1366   LOOP_ON_IP_ADDRESS:
1367   foreach my $ip (keys %{$computerdb}) {
1368      # to be improve in the future
1369      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1370      next LOOP_ON_IP_ADDRESS if ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}) eq 'unknow';
1371      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{switch_port} eq '0';
1372
1373      my $ip_timestamp   = $computerdb->{$ip}{timestamp};
1374      my $ip_mac         = $computerdb->{$ip}{mac_address};
1375      my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
1376
1377      my $swpt = sprintf "%-28s  %2s",
1378         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
1379         $computerdb->{$ip}{switch_port};
1380      $swithportdb{$swpt} ||= {
1381         ip          => $ip,
1382         timestamp   => $ip_timestamp,
1383         vlan        => $computerdb->{$ip}{network},
1384         hostname_fq => $ip_hostname_fq,
1385         mac_address => $ip_mac,
1386         };
1387
1388      # if float computer, set date 15 day before warning...
1389      my $ip_timestamp_mod = $ip_timestamp;
1390      my $ip_timestamp_ref = $swithportdb{$swpt}->{timestamp};
1391      $ip_timestamp_mod -= 15 * 24 * 3600 if $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/;
1392      $ip_timestamp_ref -= 15 * 24 * 3600 if $swithportdb{$swpt}->{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/;
1393     
1394      if ($ip_timestamp_mod > $ip_timestamp_ref) {
1395         $swithportdb{$swpt} = {
1396            ip          => $ip,
1397            timestamp   => $ip_timestamp,
1398            vlan        => $computerdb->{$ip}{network},
1399            hostname_fq => $ip_hostname_fq,
1400            mac_address => $ip_mac,
1401            };
1402         }
1403      }
1404
1405   LOOP_ON_RECENT_COMPUTER:
1406   foreach my $swpt (keys %swithportdb) {
1407      next LOOP_ON_RECENT_COMPUTER if $swpt =~ m/^\s*0$/;
1408      next LOOP_ON_RECENT_COMPUTER if $swithportdb{$swpt}->{hostname_fq} !~ m/$RE_FLOAT_HOSTNAME/;
1409
1410      my $src_ip = $swithportdb{$swpt}->{ip};
1411      my $src_timestamp = 0;
1412      LOOP_ON_IP_ADDRESS:
1413      foreach my $ip (keys %{$computerdb}) {
1414         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{mac_address} ne  $swithportdb{$swpt}->{mac_address};
1415         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} =~ m/$RE_FLOAT_HOSTNAME/;
1416         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{timestamp} < $src_timestamp;
1417         
1418         $src_ip = $ip;
1419         $src_timestamp = $computerdb->{$ip}{timestamp};
1420         }
1421
1422      # keep only if float computer is the most recent
1423      next LOOP_ON_RECENT_COMPUTER if $src_timestamp == 0;
1424      next LOOP_ON_RECENT_COMPUTER if $swithportdb{$swpt}->{timestamp} < $src_timestamp;
1425
1426      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $swithportdb{$swpt}->{timestamp};
1427      $year += 1900;
1428      $mon++;
1429      my $date = sprintf '%04i-%02i-%02i/%02i:%02i', $year, $mon, $mday, $hour, $min;
1430
1431      ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$src_ip}{timestamp};
1432      $year += 1900;
1433      $mon++;
1434      my $src_date = sprintf '%04i-%02i-%02i/%02i:%02i', $year, $mon, $mday, $hour, $min;
1435
1436      printf "%s / %-10s +-> %-10s  %s %s %s %s\n",
1437         $swpt, $swithportdb{$swpt}->{vlan}, $computerdb->{$src_ip}{network},
1438         $date,
1439         $src_date,
1440         $computerdb->{$src_ip}{mac_address},
1441         $computerdb->{$src_ip}{hostname_fq};
1442      }
1443   }
1444
1445sub cmd_set_vlan_port {
1446   my $switch_name = shift || q{};
1447   my $mac_address = shift || q{};
1448
1449   if ($switch_name eq q{} or $mac_address eq q{}) {
1450      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
1451      }
1452
1453   $switch_name = join(',', map {$_->{hostname}} @SWITCH ) if $switch_name eq q{*};
1454
1455   for my $sw_name (split /,/, $switch_name) {
1456      if (not defined $SWITCH_DB{$sw_name}) {
1457         die "Switch $sw_name must be defined in klask configuration file\n";
1458         }
1459
1460      my $sw = $SWITCH_DB{$sw_name};
1461      my %session = ( -hostname => $sw->{hostname} );
1462         $session{-version} = $sw->{version}   || 1;
1463         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1464      if (exists $sw->{version} and $sw->{version} eq '3') {
1465         $session{-username} = $sw->{username} || 'snmpadmin';
1466         }
1467      else {
1468         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1469         }
1470
1471      my $research1 = $OID_NUMBER{searchPort1} . mac_address_hex_to_dec($mac_address);
1472      my $research2 = $OID_NUMBER{searchPort2} . mac_address_hex_to_dec($mac_address);
1473      print "Klask search OID $research1 on switch $sw_name\n";
1474      print "Klask search OID $research2 on switch $sw_name\n";
1475
1476      my ($session, $error) = Net::SNMP->session( %session );
1477      print "$error \n" if $error;
1478
1479      my $result = $session->get_request(
1480         -varbindlist => [$research1]
1481         );
1482      if (not defined $result) {
1483         $result = $session->get_request(
1484            -varbindlist => [$research2]
1485            );
1486         $result->{$research1} = $result->{$research2} if defined $result;
1487         }
1488
1489      if (defined $result and $result->{$research1} ne 'noSuchInstance') {
1490         my $swport = $result->{$research1};
1491         print "Klask find MAC $mac_address on switch $sw_name port $swport\n";
1492         }
1493      else {
1494         print "Klask do not find MAC $mac_address on switch $sw_name\n";
1495         }
1496
1497      $session->close;
1498      }
1499   return;
1500   }
1501
1502sub cmd_get_vlan_port {
1503   @ARGV = @_;
1504
1505   my $verbose;
1506   GetOptions(
1507      'verbose|v' => \$verbose,
1508      );
1509
1510   my $switch_name = shift @ARGV || q{};
1511   my $switch_port = shift @ARGV || q{};
1512
1513   if ($switch_name eq q{} or $switch_port eq q{}) {
1514      die "Usage: klask get-vlan-port SWITCH_NAME PORT\n";
1515      }
1516
1517   for my $sw_name (split /,/, $switch_name) {
1518      if (not defined $SWITCH_DB{$sw_name}) {
1519         die "Switch $sw_name must be defined in klask configuration file\n";
1520         }
1521
1522      my $sw = $SWITCH_DB{$sw_name};
1523      my %session = ( -hostname => $sw->{hostname} );
1524         $session{-version} = $sw->{version}   || 1;
1525         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1526      if (exists $sw->{version} and $sw->{version} eq '3') {
1527         $session{-username} = $sw->{username} || 'snmpadmin';
1528         }
1529      else {
1530         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1531         }
1532
1533      my $search = $OID_NUMBER{'hpicfReset'} . ".$switch_port";
1534
1535      my ($session, $error) = Net::SNMP->session( %session );
1536      print "$error \n" if $error;
1537
1538      my $result = $session->get_request(
1539         -varbindlist => [$search],
1540         );
1541
1542      if (defined $result and $result->{$search} ne 'noSuchInstance') {
1543         my $vlan_id = $result->{$search} || 'empty';
1544         print "Klask VLAN Id $vlan_id on switch $sw_name on port $switch_port\n";
1545         }
1546      else {
1547         print "Klask do not find VLAN Id $vlan_id on switch $sw_name on port $switch_port\n";
1548         }
1549
1550      $session->close;
1551      }
1552   return;
1553   }
1554
1555sub cmd_set_vlan_name {
1556   }
1557
1558# snmpset -v 1 -c public sw1-batG0-legi.hmg.priv "$OID_NUMBER{'hpicfReset'}.0" i 2;
1559sub cmd_rebootsw {
1560   @ARGV = @_;
1561
1562   my $verbose;
1563   GetOptions(
1564      'verbose|v' => \$verbose,
1565      );
1566
1567   my $switch_name = shift @ARGV || q{};
1568
1569   if ($switch_name eq q{} or $mac_address eq q{}) {
1570      die "Usage: klask rebootsw SWITCH_NAME\n";
1571      }
1572
1573   for my $sw_name (split /,/, $switch_name) {
1574      if (not defined $SWITCH_DB{$sw_name}) {
1575         die "Switch $sw_name must be defined in klask configuration file\n";
1576         }
1577
1578      my $sw = $SWITCH_DB{$sw_name};
1579      my %session = ( -hostname => $sw->{hostname} );
1580         $session{-version} = $sw->{version}   || 1;
1581         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1582      if (exists $sw->{version} and $sw->{version} eq '3') {
1583         $session{-username} = $sw->{username} || 'snmpadmin';
1584         }
1585      else {
1586         $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1587         }
1588
1589      my ($session, $error) = Net::SNMP->session( %session );
1590      print "$error \n" if $error;
1591
1592      my $result = $session->set_request(
1593         -delay       => 1,
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 141 2014-05-20 19:01:54Z 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.