source: trunk/klask @ 104

Last change on this file since 104 was 104, checked in by g7moreau, 12 years ago
  • Multi VLAN clean DB
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Id Rev
File size: 60.7 KB
Line 
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005-2008 Gabriel Moreau.
4#
5# $Id: klask 104 2011-12-12 13:11:24Z 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 GetOptionsFromArray);;
20use Socket;
21
22# apt-get install snmp fping libnet-cidr-lite-perl libnet-netmask-perl libnet-snmp-perl libnetaddr-ip-perl libyaml-perl
23# libcrypt-des-perl libcrypt-hcesha-perl libdigest-hmac-perl
24# arping net-tools fping bind9-host arpwatch
25
26my $KLASK_VAR      = '/var/lib/klask';
27my $KLASK_CFG_FILE = '/etc/klask/klask.conf';
28my $KLASK_DB_FILE  = "$KLASK_VAR/klaskdb";
29my $KLASK_SW_FILE  = "$KLASK_VAR/switchdb";
30
31#my $DEBUG = 0;
32#GetOptionsFromArray(\@ARGV,
33#   'debug|d=i'   => \$DEBUG,
34#   );
35
36test_running_environnement();
37
38my $KLASK_CFG = YAML::Syck::LoadFile("$KLASK_CFG_FILE");
39
40my %DEFAULT = %{ $KLASK_CFG->{default} };
41my @SWITCH  = @{ $KLASK_CFG->{switch}  };
42
43my %switch_level = ();
44my %SWITCH_DB    = ();
45LEVEL_OF_EACH_SWITCH:
46for my $sw (@SWITCH){
47   $switch_level{$sw->{hostname}} = $sw->{level} || $DEFAULT{switch_level}  || 2;
48   $SWITCH_DB{$sw->{hostname}} = $sw;
49   }
50@SWITCH = reverse sort { $switch_level{$a->{hostname}} <=> $switch_level{$b->{hostname}} } @{$KLASK_CFG->{switch}};
51
52my %SWITCH_PORT_COUNT = ();
53
54my %CMD_DB = (
55   help       => \&cmd_help,
56   version    => \&cmd_version,
57   exportdb   => \&cmd_exportdb,
58   updatedb   => \&cmd_updatedb,
59   searchdb   => \&cmd_searchdb,
60   removedb   => \&cmd_removedb,
61   cleandb   => \&cmd_cleandb,
62   search     => \&cmd_search,
63   enable     => \&cmd_enable,
64   disable    => \&cmd_disable,
65   status     => \&cmd_status,
66   updatesw   => \&cmd_updatesw,
67   exportsw   => \&cmd_exportsw,
68   iplocation => \&cmd_iplocation,
69   'ip-free'  => \&cmd_ip_free,
70   'search-mac-on-switch' => \&cmd_search_mac_on_switch,
71   );
72
73Readonly my %INTERNAL_PORT_MAP => (
74   0 => 'A',
75   1 => 'B',
76   2 => 'C',
77   3 => 'D',
78   4 => 'E',
79   5 => 'F',
80   6 => 'G',
81   7 => 'H',
82   );
83Readonly my %INTERNAL_PORT_MAP_REV => reverse %INTERNAL_PORT_MAP;
84
85Readonly my %SWITCH_KIND => (
86   J3299A => { model => 'HP224M',     match => 'HP J3299A ProCurve Switch 224M'  },
87   J4120A => { model => 'HP1600M',    match => 'HP J4120A ProCurve Switch 1600M' },
88   J9029A => { model => 'HP1800-8G',  match => 'PROCURVE J9029A'                 },
89   J9449A => { model => 'HP1810-8G',  match => 'HP ProCurve 1810G - 8 GE'        },
90   J4093A => { model => 'HP2424M',    match => 'HP J4093A ProCurve Switch 2424M' },
91   J9279A => { model => 'HP2510G-24', match => 'ProCurve J9279A Switch 2510G-24' },
92   J9280A => { model => 'HP2510G-48', match => 'ProCurve J9280A Switch 2510G-48' },
93   J4813A => { model => 'HP2524',     match => 'HP J4813A ProCurve Switch 2524'  },
94   J4900A => { model => 'HP2626A',    match => 'HP J4900A ProCurve Switch 2626'  },
95   J4900B => { model => 'HP2626B',    match => 'J4900B.+?Switch 2626'            },# ProCurve J4900B Switch 2626 # HP J4900B ProCurve Switch 2626
96   J4899B => { model => 'HP2650',     match => 'ProCurve J4899B Switch 2650'     },
97   J9021A => { model => 'HP2810-24G', match => 'ProCurve J9021A Switch 2810-24G' },
98   J9022A => { model => 'HP2810-48G', match => 'ProCurve J9022A Switch 2810-48G' },
99   J4903A => { model => 'HP2824',     match => 'J4903A.+?Switch 2824,'           },
100   J4110A => { model => 'HP8000M',    match => 'HP J4110A ProCurve Switch 8000M' },
101   BS350T => { model => 'BS350T',     match => 'BayStack 350T HW'                },
102   );
103
104Readonly my %OID_NUMBER => (
105   sysDescription  => '1.3.6.1.2.1.1.1.0',
106   sysName         => '1.3.6.1.2.1.1.5.0',
107   sysContact      => '1.3.6.1.2.1.1.4.0',
108   sysLocation     => '1.3.6.1.2.1.1.6.0',
109   searchPort      => '1.3.6.1.2.1.17.4.3.1.2',
110   );
111
112Readonly 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;
113Readonly my $RE_IPv4_ADDRESS => qr{ [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} }xms;
114
115
116################
117# principal
118################
119
120my $cmd = shift @ARGV || 'help';
121if (defined $CMD_DB{$cmd}) {
122   $CMD_DB{$cmd}->(@ARGV);
123   }
124else {
125   print {*STDERR} "klask: command $cmd not found\n\n";
126   $CMD_DB{help}->();
127   exit 1;
128   }
129
130exit;
131
132sub test_running_environnement {
133   die "Configuration file $KLASK_CFG_FILE does not exists. Klask need it !\n" if not -e "$KLASK_CFG_FILE";
134   die "Var folder $KLASK_VAR does not exists. Klask need it !\n"              if not -d "$KLASK_VAR";
135   return;
136   }
137
138sub test_switchdb_environnement {
139   die "Switch database $KLASK_SW_FILE does not exists. Launch updatesw before this command !\n" if not -e "$KLASK_SW_FILE";
140   return;
141   }
142
143sub test_maindb_environnement {
144   die "Main database $KLASK_DB_FILE does not exists. Launch updatedb before this command !\n" if not -e "$KLASK_DB_FILE";
145   return;
146   }
147
148###
149# fast ping dont l'objectif est de remplir la table arp de la machine
150sub fastping {
151   # Launch this command without waiting...
152   system "fping -c 1 @_ >/dev/null 2>&1 &";
153   return;
154   }
155
156sub shell_command {
157   my $cmd = shift;
158
159   my $fh     = new FileHandle;
160   my $result = '';
161   open $fh, q{-|}, "LANG=C $cmd" or die "Can't exec $cmd\n";
162   $result .= <$fh>;
163   close $fh;
164   chomp $result;
165   return $result;
166   }
167
168###
169# donne l'@ ip, dns, arp en fonction du dns OU de l'ip
170sub resolve_ip_arp_host {
171   my $param_ip_or_host = shift;
172   my $interface = shift || q{*};
173   my $type      = shift || q{fast};
174
175   my %ret = (
176      hostname_fq  => 'unknow',
177      ipv4_address => '0.0.0.0',
178      mac_address  => 'unknow',
179      );
180
181   # perl -MSocket -E 'say inet_ntoa(scalar gethostbyname("tech7meylan.hmg.inpg.fr"))'
182   my $packed_ip = scalar gethostbyname($param_ip_or_host);
183   return %ret if not defined $packed_ip;
184   $ret{ipv4_address} = inet_ntoa($packed_ip);
185
186   # perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("194.254.66.240"), AF_INET)'
187   my $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET);
188   $ret{hostname_fq} = $hostname_fq if defined $hostname_fq;
189
190   # my $cmd = q{grep  -he '\b} . $param_ip_or_host . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
191   my $cmd = q{grep  -he '\b} . $ret{ipv4_address} . q{\b' } . "/var/lib/arpwatch/$interface.dat | sort -rn -k 3,3 | head -1";
192   my $cmd_arpwatch = shell_command $cmd;
193   my ($arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch;
194
195   $ret{mac_address}  = $arp       if $arp;
196   $ret{timestamp}    = $timestamp if $timestamp;
197
198   my $nowtimestamp = time;
199
200   if ( $type eq 'fast' and ( not defined $timestamp or $timestamp < ( $nowtimestamp - 45 * 60 ) ) ) { # 45 min
201      $ret{mac_address} = 'unknow';
202      return %ret;
203      }
204
205   # resultat de la commande arp
206   # tech7meylan.hmg.inpg.fr (194.254.66.240) at 00:14:22:45:28:A9 [ether] on eth0
207   # sw2-batF0-legi.hmg.priv (192.168.22.112) at 00:30:c1:76:9c:01 [ether] on eth0.37
208   my $cmd_arp  = shell_command "arp -a $param_ip_or_host";
209   if ( $cmd_arp =~ m{ (\S*) \s \( ( $RE_IPv4_ADDRESS ) \) \s at \s ( $RE_MAC_ADDRESS ) }xms ) {
210      ( $ret{hostname_fq}, $ret{ipv4_address}, $ret{mac_address} )  = ($1, $2, $3);
211      }
212
213   # Normalize MAC Address
214   if ($ret{mac_address} ne 'unknow') {
215      my @paquets = ();
216      foreach ( split m/ : /xms, $ret{mac_address} ) {
217         my @chars = split m//xms, uc "00$_";
218         push @paquets, "$chars[-2]$chars[-1]";
219         }
220      $ret{mac_address} = join q{:}, @paquets;
221      }
222
223   return %ret;
224   }
225
226# Find Surname of a switch
227sub get_switch_model {
228   my $sw_snmp_description = shift || 'unknow';
229
230   for my $sw_kind (keys %SWITCH_KIND) {
231      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{match}/ms; # option xms break search, why ?
232
233      return $SWITCH_KIND{$sw_kind}->{model};
234      }
235
236   return $sw_snmp_description;
237   }
238
239###
240# va rechercher le nom des switchs pour savoir qui est qui
241sub init_switch_names {
242   my $verbose = shift;
243
244   printf "%-26s                %-25s %s\n",'Switch','Description','Type';
245   print "------------------------------------------------------------------------------\n" if $verbose;
246
247   INIT_EACH_SWITCH:
248   for my $sw (@SWITCH) {
249      my %session = ( -hostname   => $sw->{hostname} );
250         $session{-version} = $sw->{version}   || 1;
251         $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
252         if (exists $sw->{version} and $sw->{version} eq '3') {
253            $session{-username} = $sw->{username} || 'snmpadmin';
254            }
255         else {
256            $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
257            }
258
259      $sw->{local_session} = \%session;
260
261      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
262      print "$error \n" if $error;
263
264      my $result = $session->get_request(
265         -varbindlist => [
266            $OID_NUMBER{sysDescription},
267            $OID_NUMBER{sysName},
268            $OID_NUMBER{sysContact},
269            $OID_NUMBER{sysLocation},
270            ]
271         );
272      $sw->{description} = $result->{$OID_NUMBER{sysName}} || $sw->{hostname};
273      $sw->{model} = get_switch_model( $result->{$OID_NUMBER{sysDescription}});
274      #$sw->{location} = $result->{"1.3.6.1.2.1.1.6.0"} || $sw->{hostname};
275      #$sw->{contact} = $result->{"1.3.6.1.2.1.1.4.0"} || $sw->{hostname};
276      $session->close;
277
278      # Ligne à virer car on récupère maintenant le modèle du switch
279      my ($desc, $type) = split m/ : /xms, $sw->{description}, 2;
280      printf "%-26s 0--------->>>> %-25s %s\n", $sw->{hostname}, $desc, $sw->{model} if $verbose;
281      }
282
283   print "\n" if $verbose;
284   return;
285   }
286
287###
288# convertit l'hexa (uniquement 2 chiffres) en decimal
289sub digit_hex_to_dec {
290   #00:0F:1F:43:E4:2B
291   my $car = '00' . uc shift;
292
293   return '00' if $car eq '00UNKNOW';
294   my %table = (
295      '0'=>'0',  '1'=>'1',  '2'=>'2',  '3'=>'3',  '4'=>'4',
296      '5'=>'5',  '6'=>'6',  '7'=>'7',  '8'=>'8',  '9'=>'9',
297      'A'=>'10', 'B'=>'11', 'C'=>'12', 'D'=>'13', 'E'=>'14', 'F'=>'15',
298      );
299   my @chars = split m//xms, $car;
300   return $table{$chars[-2]}*16 + $table{$chars[-1]};
301   }
302
303###
304# convertit l'@ mac en decimal
305sub mac_address_hex_to_dec {
306   #00:0F:1F:43:E4:2B
307   my $mac_address = shift;
308
309   my @paquets = split m/ : /xms, $mac_address;
310   my $return = q{};
311   foreach(@paquets) {
312      $return .= q{.} . digit_hex_to_dec($_);
313      }
314   return $return;
315   }
316
317###
318# va rechercher le port et le switch sur lequel est la machine
319sub find_switch_port {
320   my $mac_address     = shift;
321   my $switch_proposal = shift || q{};
322
323   my %ret;
324   $ret{switch_description} = 'unknow';
325   $ret{switch_port} = '0';
326
327   return %ret if $mac_address eq 'unknow';;
328
329   my @switch_search = @SWITCH;
330   if ($switch_proposal ne q{}) {
331      for my $sw (@SWITCH) {
332         next if $sw->{hostname} ne $switch_proposal;
333         unshift @switch_search, $sw;
334         last;
335         }
336      }
337
338   my $research = $OID_NUMBER{searchPort} . mac_address_hex_to_dec($mac_address);
339
340   LOOP_ON_SWITCH:
341   for my $sw (@switch_search) {
342      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
343      print "$error \n" if $error;
344
345      my $result = $session->get_request(
346         -varbindlist => [$research]
347         );
348      if (not defined $result or $result->{$research} eq 'noSuchInstance') {
349         $session->close;
350         next LOOP_ON_SWITCH;
351         }
352
353         my $swport = $result->{$research};
354         $session->close;
355
356         # IMPORTANT !!
357         # ceci empeche la detection sur certains port ...
358         # en effet les switch sont relies entre eux par un cable reseau et du coup
359         # tous les arp de toutes les machines sont presentes sur ces ports (ceux choisis ici sont les miens)
360         # cette partie est a ameliore, voir a configurer dans l'entete
361         # 21->24 45->48
362#         my $flag = 0;
363         SWITCH_PORT_IGNORE:
364         foreach my $p (@{$sw->{portignore}}) {
365            next SWITCH_PORT_IGNORE if $swport ne get_numerical_port($sw->{model},$p);
366#            $flag = 1;
367            next LOOP_ON_SWITCH;
368            }
369#         if ($flag == 0) {
370            $ret{switch_hostname}    = $sw->{hostname};
371            $ret{switch_description} = $sw->{description};
372            $ret{switch_port}        = get_human_readable_port($sw->{model}, $swport); # $swport;
373
374            last LOOP_ON_SWITCH;
375#            }
376#         }
377#      $session->close;
378      }
379   return %ret;
380   }
381
382###
383# va rechercher les port et les switch sur lequel est la machine
384sub find_all_switch_port {
385   my $mac_address = shift;
386
387   my $ret = {};
388
389   return $ret if $mac_address eq 'unknow';
390
391#   for my $sw (@SWITCH) {
392#      next if exists $SWITCH_PORT_COUNT{$sw->{hostname}};
393#
394#      $SWITCH_PORT_COUNT{$sw->{hostname}} = {};
395#      print "DEBUG: SWITCH_PORT_COUNT defined for $sw->{hostname}\n" if $DEBUG xor 2;
396#      }
397
398   my $research = $OID_NUMBER{searchPort} . mac_address_hex_to_dec($mac_address);
399   LOOP_ON_ALL_SWITCH:
400   for my $sw (@SWITCH) {
401      my ($session, $error) = Net::SNMP->session( %{$sw->{local_session}} );
402      print "$error \n" if $error;
403
404      my $result = $session->get_request(
405         -varbindlist => [$research]
406         );
407
408      if(defined $result and $result->{$research} ne 'noSuchInstance'){
409         my $swport = $result->{$research};
410
411         $ret->{$sw->{hostname}} = {};
412         $ret->{$sw->{hostname}}{hostname}    = $sw->{hostname};
413         $ret->{$sw->{hostname}}{description} = $sw->{description};
414         $ret->{$sw->{hostname}}{port}        = get_human_readable_port($sw->{model}, $swport);
415
416#         $SWITCH_PORT_COUNT{$sw->{hostname}}->{$swport}++;
417         }
418
419      $session->close;
420      }
421   return $ret;
422   }
423
424sub get_list_network {
425
426   return keys %{$KLASK_CFG->{network}};
427   }
428
429sub get_current_interface {
430   my $network = shift;
431
432   return $KLASK_CFG->{network}{$network}{interface};
433   }
434
435###
436# liste l'ensemble des adresses ip d'un réseau
437sub get_list_ip {
438   my @network = @_;
439
440   my $cidrlist = Net::CIDR::Lite->new;
441
442   for my $net (@network) {
443      my @line  = @{$KLASK_CFG->{network}{$net}{'ip-subnet'}};
444      for my $cmd (@line) {
445         for my $method (keys %{$cmd}){
446            $cidrlist->add_any($cmd->{$method}) if $method eq 'add';
447            }
448         }
449      }
450
451   my @res = ();
452
453   for my $cidr ($cidrlist->list()) {
454      my $net = new NetAddr::IP $cidr;
455      for my $ip (@{$net}) {
456         $ip =~ s{ /32 }{}xms;
457         push @res,  $ip;
458         }
459      }
460
461   return @res;
462   }
463
464# liste l'ensemble des routeurs du réseau
465sub get_list_main_router {
466   my @network = @_;
467
468   my @res = ();
469
470   for my $net (@network) {
471      push @res, $KLASK_CFG->{network}{$net}{'main-router'};
472      }
473
474   return @res;
475   }
476
477sub get_human_readable_port {
478   my $sw_model = shift;
479   my $sw_port  = shift;
480
481   if ($sw_model eq 'HP8000M') {
482
483      my $reste = (($sw_port - 1) % 8) + 1;
484      my $major = int (($sw_port - 1) / 8);
485      return "$INTERNAL_PORT_MAP{$major}$reste";
486      }
487
488   if ($sw_model eq 'HP2424M') {
489      if ($sw_port > 24) {
490         
491         my $reste = $sw_port - 24;
492         return "A$reste";
493         }
494      }
495
496   if ($sw_model eq 'HP1600M') {
497      if ($sw_port > 16) {
498         
499         my $reste = $sw_port - 16;
500         return "A$reste";
501         }
502      }
503
504   return $sw_port;
505   }
506
507sub get_numerical_port {
508   my $sw_model = shift;
509   my $sw_port  = shift;
510
511   if ($sw_model eq 'HP8000M') {
512
513      my $letter = substr $sw_port, 0, 1;
514      my $reste =  substr $sw_port, 1;
515
516      return $INTERNAL_PORT_MAP_REV{$letter} * 8 + $reste;
517      }
518
519   if ($sw_model eq 'HP2424M') {
520      if ($sw_port =~ m/^A/xms ) {
521
522         my $reste =  substr $sw_port, 1;
523
524         return 24 + $reste;
525         }
526      }
527
528   if ($sw_model eq 'HP1600M') {
529      if ($sw_port =~ m/^A/xms ) {
530
531         my $reste =  substr $sw_port, 1;
532
533         return 16 + $reste;
534         }
535      }
536
537   return $sw_port;
538   }
539
540################
541# Les commandes
542################
543
544sub cmd_help {
545
546print <<'END';
547klask - ports manager and finder for switch
548
549 klask updatedb
550 klask exportdb --format [txt|html]
551 klask removedb computer*
552 klask cleandb  --day number_of_day --verbose
553
554 klask updatesw
555 klask exportsw --format [txt|dot]
556
557 klask searchdb computer
558 klask search   computer
559 klask search-mac-on-switch switch mac_addr
560
561 klask ip-free --day number_of_day --format [txt|html] [vlan_name]
562
563 klask enable  switch port
564 klask disable switch port
565 klask status  switch port
566END
567   return;
568   }
569
570sub cmd_version {
571
572print <<'END';
573Klask - ports manager and finder for switch
574Copyright (C) 2005-2008 Gabriel Moreau
575
576END
577   print ' $Rev: 104 $'."\n";
578   print ' $Date: 2011-12-12 13:11:24 +0000 (Mon, 12 Dec 2011) $'."\n";
579   print ' $Id: klask 104 2011-12-12 13:11:24Z g7moreau $'."\n";
580   return;
581   }
582
583sub cmd_search {
584   my @computer = @_;
585
586   init_switch_names();    #nomme les switchs
587   fastping(@computer);
588   for my $clientname (@computer) {
589      my %resol_arp = resolve_ip_arp_host($clientname);          #resolution arp
590      my %where     = find_switch_port($resol_arp{mac_address}); #retrouve l'emplacement
591      printf '%-22s %2i %-30s %-15s %18s', $where{switch_description}, $where{switch_port}, $resol_arp{hostname_fq}, $resol_arp{ipv4_address}, $resol_arp{mac_address}."\n"
592         unless $where{switch_description} eq 'unknow' and $resol_arp{hostname_fq} eq 'unknow' and $resol_arp{mac_address} eq 'unknow';
593      }
594   return;
595   }
596
597sub cmd_searchdb {
598   my @computer = @_;
599
600   fastping(@computer);
601   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
602
603   LOOP_ON_COMPUTER:
604   for my $clientname (@computer) {
605      my %resol_arp = resolve_ip_arp_host($clientname);      #resolution arp
606      my $ip = $resol_arp{ipv4_address};
607
608      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
609
610      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
611      $year += 1900;
612      $mon++;
613      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
614
615      printf "%-22s %2s %-30s %-15s %-18s %s\n",
616         $computerdb->{$ip}{switch_name},
617         $computerdb->{$ip}{switch_port},
618         $computerdb->{$ip}{hostname_fq},
619         $ip,
620         $computerdb->{$ip}{mac_address},
621         $date;
622      }
623   return;
624   }
625
626sub cmd_updatedb {
627   my @network = @_;
628      @network = get_list_network() if not @network;
629
630   test_switchdb_environnement();
631
632   my $computerdb = {};
633      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
634   my $timestamp = time;
635
636   my %computer_not_detected = ();
637   my $timestamp_last_week = $timestamp - (3600 * 24 * 7);
638
639   my $number_of_computer = get_list_ip(@network); # + 1;
640   my $size_of_database   = keys %{$computerdb};
641      $size_of_database   = 1 if $size_of_database == 0;
642   my $i = 0;
643   my $detected_computer = 0;
644
645   init_switch_names('yes');    #nomme les switchs
646
647   { # Remplis le champs portignore des ports d'inter-connection pour chaque switch
648   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
649   my %db_switch_output_port       = %{$switch_connection->{output_port}};
650   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
651   my %db_switch_chained_port = ();
652   for my $swport (keys %db_switch_connected_on_port) {
653      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
654      $db_switch_chained_port{$sw_connect} .= "$port_connect:";
655      }
656   for my $sw (@SWITCH){
657      push @{$sw->{portignore}}, $db_switch_output_port{$sw->{hostname}}  if exists $db_switch_output_port{$sw->{hostname}};
658      if ( exists $db_switch_chained_port{$sw->{hostname}} ) {
659         chop $db_switch_chained_port{$sw->{hostname}};
660         push @{$sw->{portignore}}, split m/ : /xms, $db_switch_chained_port{$sw->{hostname}};
661         }
662#      print "$sw->{hostname} ++ @{$sw->{portignore}}\n";
663      }
664   }
665
666   my %router_mac_ip = ();
667   DETECT_ALL_ROUTER:
668#   for my $one_router ('194.254.66.254') {
669   for my $one_router ( get_list_main_router(@network) ) {
670      my %resol_arp = resolve_ip_arp_host($one_router);
671      $router_mac_ip{ $resol_arp{mac_address} } = $resol_arp{ipv4_address};
672      }
673
674   ALL_NETWORK:
675   for my $net (@network) {
676
677      my @computer = get_list_ip($net);
678      my $current_interface = get_current_interface($net);
679
680      #fastping(@computer);
681
682      LOOP_ON_COMPUTER:
683      for my $one_computer (@computer) {
684         $i++;
685
686         my $total_percent = int (($i*100)/$number_of_computer);
687
688         my $localtime = time - $timestamp;
689         my ($sec,$min) = localtime $localtime;
690
691         my $time_elapse = 0;
692            $time_elapse = $localtime * ( 100 - $total_percent) / $total_percent if $total_percent != 0;
693         my ($sec_elapse,$min_elapse) = localtime $time_elapse;
694
695         printf "\rComputer scanned: %4i/%i (%2i%%)",  $i,                 $number_of_computer, $total_percent;
696         printf ', detected: %4i/%i (%2i%%)', $detected_computer, $size_of_database,   int(($detected_computer*100)/$size_of_database);
697         printf ' [Time: %02i:%02i / %02i:%02i]', int($localtime/60), $localtime % 60, int($time_elapse/60), $time_elapse % 60;
698         printf ' %-8s %-14s', $current_interface, $one_computer;
699
700         my %resol_arp = resolve_ip_arp_host($one_computer, $current_interface);
701
702         # do not search on router connection (why ?)
703         if ( exists $router_mac_ip{$resol_arp{mac_address}}) {
704            $computer_not_detected{$one_computer} = $current_interface;
705            next LOOP_ON_COMPUTER;
706            }
707
708         # do not search on switch inter-connection
709         if (exists $switch_level{$resol_arp{hostname_fq}}) {
710            $computer_not_detected{$one_computer} = $current_interface;
711            next LOOP_ON_COMPUTER;
712            }
713
714         my $switch_proposal = q{};
715         if (exists $computerdb->{$resol_arp{ipv4_address}} and exists $computerdb->{$resol_arp{ipv4_address}}{switch_hostname}) {
716            $switch_proposal = $computerdb->{$resol_arp{ipv4_address}}{switch_hostname};
717            }
718
719         # do not have a mac address
720         if ($resol_arp{mac_address} eq 'unknow' or (exists $resol_arp{timestamps} and $resol_arp{timestamps} < ($timestamp - 3 * 3600))) {
721            $computer_not_detected{$one_computer} = $current_interface;
722            next LOOP_ON_COMPUTER;
723            }
724
725         my %where = find_switch_port($resol_arp{mac_address},$switch_proposal);
726
727         #192.168.24.156:
728         #  arp: 00:0B:DB:D5:F6:65
729         #  hostname: pcroyon.hmg.priv
730         #  port: 5
731         #  switch: sw-batH-legi:hp2524
732         #  timestamp: 1164355525
733
734         # do not have a mac address
735#         if ($resol_arp{mac_address} eq 'unknow') {
736#            $computer_not_detected{$one_computer} = $current_interface;
737#            next LOOP_ON_COMPUTER;
738#            }
739
740         # detected on a switch
741         if ($where{switch_description} ne 'unknow') {
742            $detected_computer++;
743            $computerdb->{$resol_arp{ipv4_address}} = {
744               hostname_fq        => $resol_arp{hostname_fq},
745               mac_address        => $resol_arp{mac_address},
746               switch_hostname    => $where{switch_hostname},
747               switch_description => $where{switch_description},
748               switch_port        => $where{switch_port},
749               timestamp          => $timestamp,
750               network            => $net,
751               };
752            next LOOP_ON_COMPUTER;
753            }
754
755         # new in the database but where it is ?
756         if (not exists $computerdb->{$resol_arp{ipv4_address}}) {
757            $detected_computer++;
758            $computerdb->{$resol_arp{ipv4_address}} = {
759               hostname_fq        => $resol_arp{hostname_fq},
760               mac_address        => $resol_arp{mac_address},
761               switch_hostname    => $where{switch_hostname},
762               switch_description => $where{switch_description},
763               switch_port        => $where{switch_port},
764               timestamp          => $resol_arp{timestamp},
765               network            => $net,
766               };
767            }
768
769         # mise a jour du nom de la machine si modification dans le dns
770         $computerdb->{$resol_arp{ipv4_address}}{hostname_fq} = $resol_arp{hostname_fq};
771
772         # mise à jour de la date de détection si détection plus récente par arpwatch
773         $computerdb->{$resol_arp{ipv4_address}}{timestamp}   = $resol_arp{timestamp} if exists $resol_arp{timestamp} and $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $resol_arp{timestamp};
774
775         # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine
776#         push @computer_not_detected, $resol_arp{ipv4_address} if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
777         $computer_not_detected{$resol_arp{ipv4_address}} = $current_interface if $computerdb->{$resol_arp{ipv4_address}}{timestamp} < $timestamp_last_week;
778
779         }
780      }
781
782   # final end of line at the end of the loop
783   printf "\n";
784
785   my $dirdb = $KLASK_DB_FILE;
786      $dirdb =~ s{ / [^/]* $}{}xms;
787   mkdir "$dirdb", 0755 unless -d "$dirdb";
788   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
789
790   for my $one_computer (keys %computer_not_detected) {
791      my $interface = $computer_not_detected{$one_computer};
792      system "arping -c 1 -w 1 -rR -i $interface $one_computer &>/dev/null";
793#      print  "arping -c 1 -w 1 -rR -i $interface $one_computer 2>/dev/null\n";
794      }
795   return;
796   }
797
798sub cmd_removedb {
799   my @computer = @_;
800
801   test_maindb_environnement();
802
803   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
804
805   LOOP_ON_COMPUTER:
806   for my $one_computer (@computer) {
807
808      if ( $one_computer =~ m/^ $RE_IPv4_ADDRESS $/xms
809            and exists $computerdb->{$one_computer} ) {
810         delete $computerdb->{$one_computer};
811         next;
812         }
813
814      my %resol_arp = resolve_ip_arp_host($one_computer);
815
816      delete $computerdb->{$resol_arp{ipv4_address}} if exists $computerdb->{$resol_arp{ipv4_address}};
817      }
818
819   my $dirdb = $KLASK_DB_FILE;
820      $dirdb =~ s{ / [^/]* $}{}xms;
821   mkdir "$dirdb", 0755 unless -d "$dirdb";
822   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
823   return;
824   }
825
826sub cmd_cleandb {
827   my @options = @_;
828
829   my $days_to_clean = 15;
830   my $verbose;
831   my $database_has_changed;
832
833   my $ret = GetOptionsFromArray(\@options,
834      'day|d=i'   => \$days_to_clean,
835      'verbose|v' => \$verbose,
836      );
837
838   my @vlan_name = get_list_network();
839
840   my $computerdb = {};
841      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
842   my $timestamp = time;
843
844   my $timestamp_barrier = 3600 * 24 * $days_to_clean;
845
846   my %mactimedb = ();
847   ALL_VLAN:
848   for my $vlan (@vlan_name) {
849
850      my @ip_list   = get_list_ip($vlan);
851     
852      LOOP_ON_IP_ADDRESS:
853      for my $ip (@ip_list) {
854
855         next LOOP_ON_IP_ADDRESS if
856            not exists $computerdb->{$ip};
857           
858            #&& $computerdb->{$ip}{timestamp} > $timestamp_barrier;
859         my $ip_timestamp   = $computerdb->{$ip}{timestamp};
860         my $ip_mac         = $computerdb->{$ip}{mac_address};
861         my $ip_hostname_fq = $computerdb->{$ip}{hostname_fq};
862
863         $mactimedb{$ip_mac} ||= {
864            ip          => $ip,
865            timestamp   => $ip_timestamp,
866            vlan        => $vlan,
867            hostname_fq => $ip_hostname_fq,
868            };
869         
870         if ( $mactimedb{$ip_mac}->{timestamp} - $ip_timestamp > $timestamp_barrier
871            and not $mactimedb{$ip_mac}->{hostname_fq} =~ m/^float/ ) {
872            print "remove ip $ip\n" if $verbose;
873            delete $computerdb->{$ip};
874            $database_has_changed++;
875            }
876
877         if ( $ip_timestamp - $mactimedb{$ip_mac}->{timestamp} > $timestamp_barrier
878            and not $ip_hostname_fq =~ m/^float/ ) {
879            print "remove ip ".$mactimedb{$ip_mac}->{ip}."\n" if $verbose;
880            delete $computerdb->{$mactimedb{$ip_mac}->{ip}};
881            $database_has_changed++;
882            }
883
884         if ( $ip_timestamp > $mactimedb{$ip_mac}->{timestamp}) {
885            $mactimedb{$ip_mac} = {
886               ip          => $ip,
887               timestamp   => $ip_timestamp,
888               vlan        => $vlan,
889               hostname_fq => $ip_hostname_fq,
890               };
891            }
892         }
893      }
894
895   if ( $database_has_changed ) {
896      my $dirdb = $KLASK_DB_FILE;
897         $dirdb =~ s{ / [^/]* $}{}xms;
898      mkdir "$dirdb", 0755 unless -d "$dirdb";
899      YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
900      }
901   return;
902   }
903
904sub cmd_exportdb {
905   my @ARGV   = @_;
906
907   my $format = 'txt';
908
909   my $ret = GetOptions(
910      'format|f=s'  => \$format,
911      );
912
913   my %possible_format = (
914      txt  => \&cmd_exportdb_txt,
915      html => \&cmd_exportdb_html,
916      );
917
918   $format = 'txt' if not defined $possible_format{$format};
919
920   $possible_format{$format}->(@ARGV);
921   return;
922   }
923
924sub cmd_exportdb_txt {
925   test_maindb_environnement();
926
927   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
928
929   printf "%-27s %-4s            %-40s %-15s %-18s %-16s %s\n", qw(Switch Port Hostname-FQ IPv4-Address MAC-Address Date VLAN);
930   print "--------------------------------------------------------------------------------------------------------------------------------------------\n";
931
932   LOOP_ON_IP_ADDRESS:
933   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
934
935#      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq 'unknow';
936
937      # to be improve in the future
938      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
939
940# dans le futur
941#      next if $computerdb->{$ip}{hostname_fq} eq 'unknow';
942
943      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
944      $year += 1900;
945      $mon++;
946      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
947
948      printf "%-28s  %2s  <-------  %-40s %-15s %-18s %-16s %s\n",
949         $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description},
950         $computerdb->{$ip}{switch_port},
951         $computerdb->{$ip}{hostname_fq},
952         $ip,
953         $computerdb->{$ip}{mac_address},
954         $date,
955         $computerdb->{$ip}{network} || '';
956      }
957   return;
958   }
959
960sub cmd_exportdb_html {
961   test_maindb_environnement();
962
963   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
964
965#<link rel="stylesheet" type="text/css" href="style-klask.css" />
966#<script src="sorttable-klask.js"></script>
967
968   print <<'END_HTML';
969<table class="sortable" summary="Klask Host Database">
970 <caption>Klask Host Database</caption>
971 <thead>
972  <tr>
973   <th scope="col" class="klask-header-left">Switch</th>
974   <th scope="col" class="sorttable_nosort">Port</th>
975   <th scope="col" class="sorttable_nosort">Link</th>
976   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
977   <th scope="col" class="hklask-ipv4">IPv4-Address</th>
978   <th scope="col" class="sorttable_alpha">MAC-Address</th>
979   <th scope="col" class="sorttable_alpha">VLAN</th>
980   <th scope="col" class="klask-header-right">Date</th>
981  </tr>
982 </thead>
983 <tfoot>
984  <tr>
985   <th scope="col" class="klask-footer-left">Switch</th>
986   <th scope="col" class="fklask-port">Port</th>
987   <th scope="col" class="fklask-link">Link</th>
988   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
989   <th scope="col" class="fklask-ipv4">IPv4-Address</th>
990   <th scope="col" class="fklask-mac">MAC-Address</th>
991   <th scope="col" class="fklask-vlan">VLAN</th>
992   <th scope="col" class="klask-footer-right">Date</th>
993  </tr>
994 </tfoot>
995 <tbody>
996END_HTML
997
998   my %mac_count = ();
999   LOOP_ON_IP_ADDRESS:
1000   foreach my $ip (keys %{$computerdb}) {
1001
1002      # to be improve in the future
1003      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1004
1005      $mac_count{$computerdb->{$ip}{mac_address}}++;
1006      }
1007
1008   my $typerow = 'even';
1009
1010   LOOP_ON_IP_ADDRESS:
1011   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1012
1013      # to be improve in the future
1014      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1015
1016      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1017      $year += 1900;
1018      $mon++;
1019      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1020
1021#      $odd_or_even++;
1022#      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
1023      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1024
1025      my $switch_hostname = $computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description} || 'unkown';
1026      chomp $switch_hostname;
1027      my $switch_hostname_sort = sprintf '%s %3s' ,$switch_hostname, $computerdb->{$ip}{switch_port};
1028
1029      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1030
1031      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{mac_address}}, $computerdb->{$ip}{mac_address};
1032
1033      $computerdb->{$ip}{hostname_fq} = 'unknow' if $computerdb->{$ip}{hostname_fq} =~ m/^ \d+ \. \d+ \. \d+ \. \d+ $/xms;
1034      my ( $host_short ) = split m/ \. /xms, $computerdb->{$ip}{hostname_fq};
1035
1036      my $vlan = $computerdb->{$ip}{network} || '';
1037
1038      print <<"END_HTML";
1039  <tr class="$typerow">
1040   <td sorttable_customkey="$switch_hostname_sort">$switch_hostname</td>
1041   <td class="bklask-port">$computerdb->{$ip}{switch_port}</td>
1042   <td><-------</td>
1043   <td sorttable_customkey="$host_short">$computerdb->{$ip}{hostname_fq}</td>
1044   <td sorttable_customkey="$ip_sort">$ip</td>
1045   <td sorttable_customkey="$mac_sort">$computerdb->{$ip}{mac_address}</td>
1046   <td>$vlan</td>
1047   <td>$date</td>
1048  </tr>
1049END_HTML
1050      }
1051
1052   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1053
1054   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1055   my %db_switch_parent            = %{$switch_connection->{parent}};
1056   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1057   my %db_switch                   = %{$switch_connection->{switch_db}};
1058
1059   for my $sw (sort keys %db_switch_output_port) {
1060
1061      my $switch_hostname_sort = sprintf '%s %3s' ,$sw, $db_switch_output_port{$sw};
1062
1063      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1064
1065      if (exists $db_switch_parent{$sw}) {
1066
1067      my $mac_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{mac_address};
1068      my $ipv4_address = $db_switch{$db_switch_parent{$sw}->{switch}}->{ipv4_address};
1069      my $timestamp = $db_switch{$db_switch_parent{$sw}->{switch}}->{timestamp};
1070
1071      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1072      $year += 1900;
1073      $mon++;
1074      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1075
1076      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1077
1078      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1079
1080      my ( $host_short ) = sprintf '%s %3s' , split(m/ \. /xms, $db_switch_parent{$sw}->{switch}, 1), $db_switch_parent{$sw}->{port};
1081
1082      print <<"END_HTML";
1083  <tr class="$typerow">
1084   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1085   <td class="bklask-port">$db_switch_output_port{$sw}</>
1086   <td>+--> $db_switch_parent{$sw}->{port}</td>
1087   <td sorttable_customkey="$host_short">$db_switch_parent{$sw}->{switch}</>
1088   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1089   <td sorttable_customkey="$mac_sort">$mac_address</td>
1090   <td></td>
1091   <td>$date</td>
1092  </tr>
1093END_HTML
1094         }
1095      else {
1096         print <<"END_HTML";
1097  <tr class="$typerow">
1098   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
1099   <td class="bklask-port">$db_switch_output_port{$sw}</>
1100   <td>+--></td>
1101   <td sorttable_customkey="router">router</>
1102   <td sorttable_customkey="999999999999"></td>
1103   <td sorttable_customkey="99999"></td>
1104   <td></td>
1105   <td></td>
1106  </tr>
1107END_HTML
1108         }
1109      }
1110
1111   for my $swport (sort keys %db_switch_connected_on_port) {
1112      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1113      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1114
1115         my $switch_hostname_sort = sprintf '%s %3s' ,$sw_connect, $port_connect;
1116
1117      my $mac_address = $db_switch{$sw}->{mac_address};
1118      my $ipv4_address = $db_switch{$sw}->{ipv4_address};
1119      my $timestamp = $db_switch{$sw}->{timestamp};
1120
1121      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $timestamp;
1122      $year += 1900;
1123      $mon++;
1124      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year,$mon,$mday,$hour,$min;
1125
1126      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ipv4_address;
1127
1128      my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
1129
1130      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1131
1132         if (exists $db_switch_output_port{$sw}) {
1133
1134            my ( $host_short ) = sprintf '%s %3s' , split( m/\./xms, $sw, 1), $db_switch_output_port{$sw};
1135
1136            print <<"END_HTML";
1137  <tr class="$typerow">
1138   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1139   <td class="bklask-port">$port_connect</>
1140   <td>&lt;--+ $db_switch_output_port{$sw}</td>
1141   <td sorttable_customkey="$host_short">$sw</>
1142   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
1143   <td sorttable_customkey="$mac_sort">$mac_address</td>
1144   <td></td>
1145   <td>$date</td>
1146  </tr>
1147END_HTML
1148            }
1149         else {
1150            print <<"END_HTML";
1151  <tr class="$typerow">
1152   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
1153   <td class="bklask-port">$port_connect</>
1154   <td>&lt;--+</td>
1155   <td sorttable_customkey="$sw">$sw</>
1156   <td sorttable_customkey="">$ipv4_address</td>
1157   <td sorttable_customkey="">$mac_address</td>
1158   <td></td>
1159   <td>$date</td>
1160  </tr>
1161END_HTML
1162            }
1163         }
1164      }
1165
1166   print <<'END_HTML';
1167 </tbody>
1168</table>
1169END_HTML
1170   return;
1171   }
1172
1173sub cmd_iplocation {
1174   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
1175
1176   LOOP_ON_IP_ADDRESS:
1177   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
1178
1179      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{hostname_fq} eq ($computerdb->{$ip}{switch_hostname} || $computerdb->{$ip}{switch_description}); # switch on himself !
1180
1181      my $sw_hostname = $computerdb->{$ip}{switch_hostname} || q{};
1182      next if $sw_hostname eq 'unknow';
1183
1184      my $sw_location = q{};
1185      for my $sw (@SWITCH) {
1186         next if $sw_hostname ne $sw->{hostname};
1187         $sw_location = $sw->{location};
1188         last;
1189         }
1190
1191      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
1192      }
1193   return;
1194   }
1195
1196sub cmd_ip_free {
1197   my @vlan_name = @_;
1198
1199   my $days_to_dead = 365 * 2;
1200   my $format = 'txt';
1201   my $verbose;
1202
1203   my $ret = GetOptionsFromArray(\@vlan_name,
1204      'day|d=i'      => \$days_to_dead,
1205      'format|f=s'   => \$format,
1206      'verbose|v'    => \$verbose,
1207      );
1208
1209   my %possible_format = (
1210      txt  => \&cmd_ip_free_txt,
1211      html => \&cmd_ip_free_html,
1212      none => sub {},
1213      );
1214   $format = 'txt' if not defined $possible_format{$format};
1215
1216   @vlan_name = get_list_network() if not @vlan_name;
1217
1218   my $computerdb = {};
1219      $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE") if -e "$KLASK_DB_FILE";
1220   my $timestamp = time;
1221
1222   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_dead );
1223
1224   my %result_ip = ();
1225
1226   ALL_NETWORK:
1227   for my $vlan (@vlan_name) {
1228
1229      my @ip_list = get_list_ip($vlan);
1230
1231      LOOP_ON_IP_ADDRESS:
1232      for my $ip (@ip_list) {
1233
1234         next LOOP_ON_IP_ADDRESS if
1235            exists $computerdb->{$ip}
1236            && $computerdb->{$ip}{timestamp} > $timestamp_barrier;
1237
1238         my $ip_date_last_detection = '';
1239         if (exists $computerdb->{$ip}) {
1240            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $computerdb->{$ip}{timestamp};
1241            $year += 1900;
1242            $mon++;
1243            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
1244            }
1245
1246         $result_ip{$ip} ||= {};
1247         $result_ip{$ip}->{date_last_detection} = $ip_date_last_detection;
1248
1249         my $packed_ip = scalar gethostbyname($ip);
1250         my $hostname_fq = 'unknown';
1251            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown' if defined $packed_ip;
1252         $result_ip{$ip}->{hostname_fq} = $hostname_fq;
1253
1254         $result_ip{$ip}->{vlan} = $vlan;
1255
1256         printf "VERBOSE_1: %-15s %-12s %s\n", $ip, $vlan, $hostname_fq if $verbose;
1257         }
1258      }
1259
1260   $possible_format{$format}->(%result_ip);
1261   }
1262
1263sub cmd_ip_free_txt {
1264   my %result_ip = @_;
1265   
1266   printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
1267   print "-------------------------------------------------------------------------------\n";
1268   LOOP_ON_IP_ADDRESS:
1269   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1270         printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{hostname_fq}, $result_ip{$ip}->{date_last_detection}, $result_ip{$ip}->{vlan};
1271      }
1272   }
1273
1274sub cmd_ip_free_html {
1275   my %result_ip = @_;
1276
1277   print <<'END_HTML';
1278<table class="sortable" summary="Klask Free IP Database">
1279 <caption>Klask Free IP Database</caption>
1280 <thead>
1281  <tr>
1282   <th scope="col" class="klask-header-left">IPv4-Address</th>
1283   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
1284   <th scope="col" class="sorttable_alpha">VLAN</th>
1285   <th scope="col" class="klask-header-right">Date</th>
1286  </tr>
1287 </thead>
1288 <tfoot>
1289  <tr>
1290   <th scope="col" class="klask-footer-left">IPv4-Address</th>
1291   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
1292   <th scope="col" class="fklask-vlan">VLAN</th>
1293   <th scope="col" class="klask-footer-right">Date</th>
1294  </tr>
1295 </tfoot>
1296 <tbody>
1297END_HTML
1298
1299   my $typerow = 'even';
1300
1301   LOOP_ON_IP_ADDRESS:
1302   foreach my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
1303
1304      $typerow = $typerow eq 'even' ? 'odd' : 'even';
1305
1306      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
1307      my ( $host_short ) = split m/ \. /xms, $result_ip{$ip}->{hostname_fq};
1308
1309      print <<"END_HTML";
1310  <tr class="$typerow">
1311   <td sorttable_customkey="$ip_sort">$ip</td>
1312   <td sorttable_customkey="$host_short">$result_ip{$ip}->{hostname_fq}</td>
1313   <td>$result_ip{$ip}->{vlan}</td>
1314   <td>$result_ip{$ip}->{date_last_detection}</td>
1315  </tr>
1316END_HTML
1317      }
1318   print <<'END_HTML';
1319 </tbody>
1320</table>
1321END_HTML
1322   }
1323
1324sub cmd_enable {
1325   my $switch = shift;
1326   my $port   = shift;
1327
1328   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
1329   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
1330   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
1331   return;
1332   }
1333
1334sub cmd_disable {
1335   my $switch = shift;
1336   my $port   = shift;
1337
1338   system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
1339   return;
1340   }
1341
1342sub cmd_status {
1343   my $switch = shift;
1344   my $port   = shift;
1345
1346   system "snmpget -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port";
1347   return;
1348   }
1349
1350sub cmd_search_mac_on_switch {
1351   my $switch_name = shift || q{};
1352   my $mac_address = shift || q{};
1353
1354   if ($switch_name eq q{} or $mac_address eq q{}) {
1355      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
1356      }
1357
1358   if (not defined $SWITCH_DB{$switch_name}) {
1359      die "Switch $switch_name must be defined in klask configuration file\n";
1360      }
1361
1362   my $sw = $SWITCH_DB{$switch_name};
1363   my %session = ( -hostname => $sw->{hostname} );
1364      $session{-version} = $sw->{version}   || 1;
1365      $session{-port}    = $sw->{snmpport}  || $DEFAULT{snmpport}  || 161;
1366   if (exists $sw->{version} and $sw->{version} eq '3') {
1367      $session{-username} = $sw->{username} || 'snmpadmin';
1368      }
1369   else {
1370      $session{-community} = $sw->{community} || $DEFAULT{community} || 'public';
1371      }
1372
1373   my $research = $OID_NUMBER{searchPort} . mac_address_hex_to_dec($mac_address);
1374   print "Klask search OID $research on switch $switch_name\n";
1375
1376   my ($session, $error) = Net::SNMP->session( %session );
1377   print "$error \n" if $error;
1378
1379   my $result = $session->get_request(
1380      -varbindlist => [$research]
1381      );
1382
1383   if (not defined $result or $result->{$research} eq 'noSuchInstance') {
1384      print "Klask do not find MAC $mac_address on switch $switch_name\n";
1385      $session->close;
1386      }
1387
1388   my $swport = $result->{$research};
1389   $session->close;
1390
1391   print "Klask find MAC $mac_address on switch $switch_name port $swport\n";
1392   return;
1393   }
1394
1395sub cmd_updatesw {
1396   my @options = @_;
1397
1398   my $verbose;
1399
1400   my $ret = GetOptionsFromArray(\@options,
1401      'verbose|v' => \$verbose,
1402      );
1403
1404   init_switch_names('yes');    #nomme les switchs
1405   print "\n";
1406
1407   my %where = ();
1408   my %db_switch_output_port = ();
1409   my %db_switch_ip_hostnamefq = ();
1410
1411   DETECT_ALL_ROUTER:
1412#   for my $one_computer ('194.254.66.254') {
1413   for my $one_router ( get_list_main_router(get_list_network()) ) {
1414      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
1415
1416      next DETECT_ALL_ROUTER if $resol_arp{mac_address} eq 'unknow';
1417      print "VERBOSE_1: Router detected $resol_arp{ipv4_address} - $resol_arp{mac_address}\n" if $verbose;
1418
1419      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # retrouve les emplacements des routeurs
1420      }
1421
1422   ALL_ROUTER_IP_ADDRESS:
1423   for my $ip_router (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
1424
1425      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip_router}; # /a priori/ idiot car ne sers à rien...
1426
1427      ALL_SWITCH_CONNECTED:
1428      for my $switch_detected ( keys %{$where{$ip_router}} ) {
1429
1430         my $switch = $where{$ip_router}->{$switch_detected};
1431
1432         next ALL_SWITCH_CONNECTED if $switch->{port} eq '0';
1433
1434         $db_switch_output_port{$switch->{hostname}} = $switch->{port};
1435         print "VERBOSE_2: output port $switch->{hostname} : $switch->{port}\n" if $verbose;
1436         }
1437      }
1438
1439   my %db_switch_link_with = ();
1440
1441   my @list_all_switch = ();
1442   my @list_switch_ipv4 = ();
1443   for my $sw (@SWITCH){
1444      push @list_all_switch, $sw->{hostname};
1445      }
1446
1447   my $timestamp = time;
1448
1449   ALL_SWITCH:
1450   for my $one_computer (@list_all_switch) {
1451      my %resol_arp = resolve_ip_arp_host($one_computer, q{*}, q{low}); # arp resolution
1452      next ALL_SWITCH if $resol_arp{mac_address} eq 'unknow';
1453
1454      push @list_switch_ipv4, $resol_arp{ipv4_address};
1455
1456      $where{$resol_arp{ipv4_address}} = find_all_switch_port($resol_arp{mac_address}); # find port on all switch
1457
1458      if ($verbose) {
1459         print "VERBOSE_3: $one_computer $resol_arp{ipv4_address} $resol_arp{mac_address}\n";
1460         print "VERBOSE_3: $one_computer --- ",
1461            join(' + ', keys %{$where{$resol_arp{ipv4_address}}}),
1462            "\n";
1463         }
1464
1465      $db_switch_ip_hostnamefq{$resol_arp{ipv4_address}} = $resol_arp{hostname_fq};
1466      print "VERBOSE_4: db_switch_ip_hostnamefq $resol_arp{ipv4_address} -> $resol_arp{hostname_fq}\n" if $verbose;
1467
1468      $SWITCH_DB{$one_computer}->{ipv4_address} = $resol_arp{ipv4_address};
1469      $SWITCH_DB{$one_computer}->{mac_address}  = $resol_arp{mac_address};
1470      $SWITCH_DB{$one_computer}->{timestamp}    = $timestamp;
1471      }
1472
1473   ALL_SWITCH_IP_ADDRESS:
1474   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
1475
1476      print "VERBOSE_5: loop on $db_switch_ip_hostnamefq{$ip}\n" if $verbose;
1477
1478      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
1479#      next ALL_SWITCH_IP_ADDRESS if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostnamefq{$ip} };
1480
1481      DETECTED_SWITCH:
1482      for my $switch_detected ( keys %{$where{$ip}} ) {
1483
1484         my $switch = $where{$ip}->{$switch_detected};
1485         print "VERBOSE_6: $db_switch_ip_hostnamefq{$ip} -> $switch->{hostname} : $switch->{port}\n" if $verbose;
1486
1487         next if $switch->{port}     eq '0';
1488         next if $switch->{port}     eq $db_switch_output_port{$switch->{hostname}};
1489         next if $switch->{hostname} eq $db_switch_ip_hostnamefq{$ip}; # $computerdb->{$ip}{hostname};
1490
1491         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} } ||= {};
1492         $db_switch_link_with{ $db_switch_ip_hostnamefq{$ip} }->{ $switch->{hostname} } = $switch->{port};
1493         print "VERBOSE_7: +++++\n" if $verbose;
1494         }
1495
1496      }
1497
1498   my %db_switch_connected_on_port = ();
1499   my $maybe_more_than_one_switch_connected = 'yes';
1500
1501   while ($maybe_more_than_one_switch_connected eq 'yes') {
1502      for my $sw (keys %db_switch_link_with) {
1503         for my $connect (keys %{$db_switch_link_with{$sw}}) {
1504
1505            my $port = $db_switch_link_with{$sw}->{$connect};
1506
1507            $db_switch_connected_on_port{"$connect:$port"} ||= {};
1508            $db_switch_connected_on_port{"$connect:$port"}->{$sw}++; # Just to define the key
1509            }
1510         }
1511
1512      $maybe_more_than_one_switch_connected  = 'no';
1513
1514      SWITCH_AND_PORT:
1515      for my $swport (keys %db_switch_connected_on_port) {
1516
1517         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
1518
1519         $maybe_more_than_one_switch_connected = 'yes';
1520
1521         my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1522         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
1523
1524         CONNECTED:
1525         for my $sw_connected (@sw_on_same_port) {
1526
1527            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
1528
1529            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
1530
1531            for my $other_sw (@sw_on_same_port) {
1532               next if $other_sw eq $sw_connected;
1533
1534               delete $db_switch_link_with{$other_sw}->{$sw_connect};
1535               }
1536
1537            # We can not do better for this switch for this loop
1538            next SWITCH_AND_PORT;
1539            }
1540         }
1541      }
1542
1543   my %db_switch_parent =();
1544
1545   for my $sw (keys %db_switch_link_with) {
1546      for my $connect (keys %{$db_switch_link_with{$sw}}) {
1547
1548         my $port = $db_switch_link_with{$sw}->{$connect};
1549
1550         $db_switch_connected_on_port{"$connect:$port"} ||= {};
1551         $db_switch_connected_on_port{"$connect:$port"}->{$sw} = $port;
1552
1553         $db_switch_parent{$sw} = {switch => $connect, port => $port};
1554         }
1555      }
1556
1557   print "Switch output port and parent port connection\n";
1558   print "---------------------------------------------\n";
1559   for my $sw (sort keys %db_switch_output_port) {
1560      if (exists $db_switch_parent{$sw}) {
1561         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1562         }
1563      else {
1564         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1565         }
1566      }
1567   print "\n";
1568
1569   print "Switch parent and children port inter-connection\n";
1570   print "------------------------------------------------\n";
1571   for my $swport (sort keys %db_switch_connected_on_port) {
1572      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1573      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1574         if (exists $db_switch_output_port{$sw}) {
1575            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1576            }
1577         else {
1578            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1579            }
1580         }
1581      }
1582
1583   my $switch_connection = {
1584      output_port       => \%db_switch_output_port,
1585      parent            => \%db_switch_parent,
1586      connected_on_port => \%db_switch_connected_on_port,
1587      link_with         => \%db_switch_link_with,
1588      switch_db         => \%SWITCH_DB,
1589      };
1590
1591   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
1592   return;
1593   }
1594
1595sub cmd_exportsw {
1596   my @ARGV   = @_;
1597
1598   test_switchdb_environnement();
1599
1600   my $format = 'txt';
1601
1602   my $ret = GetOptions(
1603      'format|f=s'  => \$format,
1604      );
1605
1606   my %possible_format = (
1607      txt => \&cmd_exportsw_txt,
1608      dot => \&cmd_exportsw_dot,
1609      );
1610
1611   $format = 'txt' if not defined $possible_format{$format};
1612
1613   $possible_format{$format}->(@ARGV);
1614   return;
1615   }
1616
1617sub cmd_exportsw_txt {
1618
1619   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1620
1621   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1622   my %db_switch_parent            = %{$switch_connection->{parent}};
1623   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1624
1625   print "Switch output port and parent port connection\n";
1626   print "---------------------------------------------\n";
1627   for my $sw (sort keys %db_switch_output_port) {
1628      if (exists $db_switch_parent{$sw}) {
1629         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{port}, $db_switch_parent{$sw}->{switch};
1630         }
1631      else {
1632         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
1633         }
1634      }
1635   print "\n";
1636
1637   print "Switch parent and children port inter-connection\n";
1638   print "------------------------------------------------\n";
1639   for my $swport (sort keys %db_switch_connected_on_port) {
1640      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1641      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1642         if (exists $db_switch_output_port{$sw}) {
1643            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
1644            }
1645         else {
1646            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
1647            }
1648         }
1649      }
1650   return;
1651   }
1652
1653sub cmd_exportsw_dot {
1654
1655   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
1656
1657   my %db_switch_output_port       = %{$switch_connection->{output_port}};
1658   my %db_switch_parent            = %{$switch_connection->{parent}};
1659   my %db_switch_connected_on_port = %{$switch_connection->{connected_on_port}};
1660   my %db_switch_link_with         = %{$switch_connection->{link_with}};
1661   my %db_switch_global            = %{$switch_connection->{switch_db}};
1662
1663   my %db_building= ();
1664   for my $sw (@SWITCH) {
1665      my ($building, $location) = split m/ \/ /xms, $sw->{location}, 2;
1666      $db_building{$building} ||= {};
1667      $db_building{$building}->{$location} ||= {};
1668      $db_building{$building}->{$location}{ $sw->{hostname} } = 'y';
1669      }
1670
1671
1672   print "digraph G {\n";
1673
1674   print "site [label = \"site\", color = black, fillcolor = gold, shape = invhouse, style = filled];\n";
1675   print "internet [label = \"internet\", color = black, fillcolor = cyan, shape = house, style = filled];\n";
1676
1677   my $b=0;
1678   for my $building (keys %db_building) {
1679      $b++;
1680
1681      print "\"building$b\" [label = \"$building\", color = black, fillcolor = gold, style = filled];\n";
1682      print "site -> \"building$b\" [len = 2, color = firebrick];\n";
1683
1684      my $l = 0;
1685      for my $loc (keys %{$db_building{$building}}) {
1686         $l++;
1687
1688         print "\"location$b-$l\" [label = \"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color = black, fillcolor = orange, style = filled];\n";
1689#         print "\"location$b-$l\" [label = \"$building / $loc\", color = black, fillcolor = orange, style = filled];\n";
1690         print "\"building$b\" -> \"location$b-$l\" [len = 2, color = firebrick]\n";
1691
1692         for my $sw (keys %{$db_building{$building}->{$loc}}) {
1693
1694            print "\"$sw:$db_switch_output_port{$sw}\" [label = $db_switch_output_port{$sw}, color = black, fillcolor = lightblue,  peripheries = 2, style = filled];\n";
1695
1696            my $swname  = $sw;
1697               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{model}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{model};
1698            print "\"$sw\" [label = \"$swname\", color = black, fillcolor = palegreen, shape = rect, style = filled];\n";
1699            print "\"location$b-$l\" -> \"$sw\" [len = 2, color = firebrick, arrowtail = dot]\n";
1700            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, arrowhead = normal, arrowtail = invdot]\n";
1701
1702
1703            for my $swport (keys %db_switch_connected_on_port) {
1704               my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1705               next if not $sw_connect eq $sw;
1706               next if $port_connect eq $db_switch_output_port{$sw};
1707               print "\"$sw:$port_connect\" [label = $port_connect, color = black, fillcolor = plum,  peripheries = 1, style = filled];\n";
1708               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, arrowhead= normal, arrowtail = inv]\n";
1709              }
1710            }
1711         }
1712      }
1713
1714#   print "Switch output port and parent port connection\n";
1715#   print "---------------------------------------------\n";
1716   for my $sw (sort keys %db_switch_output_port) {
1717      if (exists $db_switch_parent{$sw}) {
1718#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{switch}, $db_switch_parent{$sw}->{port};
1719         }
1720      else {
1721         printf "   \"%s:%s\" -> internet\n", $sw, $db_switch_output_port{$sw};
1722         }
1723      }
1724   print "\n";
1725
1726#   print "Switch parent and children port inter-connection\n";
1727#   print "------------------------------------------------\n";
1728   for my $swport (sort keys %db_switch_connected_on_port) {
1729      my ($sw_connect,$port_connect) = split m/ : /xms, $swport;
1730      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
1731         if (exists $db_switch_output_port{$sw}) {
1732            printf "   \"%s:%s\" -> \"%s:%s\" [color = navyblue]\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
1733            }
1734         else {
1735            printf "   \"%s\"   -> \"%s%s\"\n", $sw, $sw_connect, $port_connect;
1736            }
1737         }
1738      }
1739
1740print "}\n";
1741   return;
1742   }
1743
1744
1745__END__
1746
1747=head1 NAME
1748
1749klask - ports manager and finder for switch
1750
1751
1752=head1 USAGE
1753
1754 klask updatedb
1755 klask exportdb --format [txt|html]
1756 klask removedb computer*
1757 klask cleandb  --day number_of_day --verbose
1758
1759 klask updatesw
1760 klask exportsw --format [txt|dot]
1761
1762 klask searchdb computer
1763 klask search   computer
1764 klask search-mac-on-switch switch mac_addr
1765
1766 klask ip-free --day number_of_day --format [txt|html] [vlan_name]
1767
1768 klask enable  switch port
1769 klask disable swith port
1770 klask status  swith port
1771
1772
1773=head1 DESCRIPTION
1774
1775klask is a small tool to find where is a host in a big network. klask mean search in brittany.
1776
1777Klask has now a web site dedicated for it !
1778
1779 http://servforge.legi.inpg.fr/projects/klask
1780
1781
1782=head1 COMMANDS
1783
1784
1785=head2 search
1786
1787This 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.
1788
1789
1790=head2 enable
1791
1792This command activate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1793
1794
1795=head2 disable
1796
1797This command deactivate a port on a switch by snmp. So you need to give the switch and the port number on the command line.
1798
1799
1800=head2 status
1801
1802This 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.
1803
1804
1805=head2 updatedb
1806
1807This 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.
1808
1809
1810=head2 exportdb
1811
1812This 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...
1813
1814
1815=head2 updatesw
1816
1817This 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.
1818
1819
1820=head2 exportsw --format [txt|dot]
1821
1822This 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.
1823
1824 klask exportsw --format dot > /tmp/map.dot
1825 dot -Tpng /tmp/map.dot > /tmp/map.png
1826
1827
1828
1829=head1 CONFIGURATION
1830
1831Because 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 !
1832
1833Here an example, be aware with indent, it's important in YAML, do not use tabulation !
1834
1835 default:
1836   community: public
1837   snmpport: 161
1838
1839 network:
1840   labnet:
1841     ip-subnet:
1842       - add: 192.168.1.0/24
1843       - add: 192.168.2.0/24
1844     interface: eth0
1845     main-router: gw1.labnet.local
1846
1847   schoolnet:
1848     ip-subnet:
1849       - add: 192.168.6.0/24
1850       - add: 192.168.7.0/24
1851     interface: eth0.38
1852     main-router: gw2.schoolnet.local
1853
1854 switch:
1855   - hostname: sw1.klask.local
1856     portignore:
1857       - 1
1858       - 2
1859
1860   - hostname: sw2.klask.local
1861     location: BatK / 2 / K203
1862     type: HP2424
1863     portignore:
1864       - 1
1865       - 2
1866
1867I 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.
1868
1869
1870=head1 FILES
1871
1872 /etc/klask/klask.conf
1873 /var/lib/klask/klaskdb
1874 /var/lib/klask/switchdb
1875
1876=head1 SEE ALSO
1877
1878Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
1879
1880
1881=head1 VERSION
1882
1883$Id: klask 104 2011-12-12 13:11:24Z g7moreau $
1884
1885
1886=head1 AUTHOR
1887
1888Written by Gabriel Moreau, Grenoble - France
1889
1890
1891=head1 LICENSE AND COPYRIGHT
1892
1893GPL version 2 or later and Perl equivalent
1894
1895Copyright (C) 2005-2009 Gabriel Moreau.
Note: See TracBrowser for help on using the repository browser.