2024-10-15 11:56:15 +02:00
#!/usr/bin/perl
use strict ;
use warnings FATAL = > 'all' ;
use Getopt::Long ;
use File::Find ;
use List::MoreUtils qw( uniq ) ;
use Digest::MD5 qw( md5 ) ;
# Global variables for the parameters
my $ snmpwalk_path = '' ;
my $ module_path = '' ;
my $ no_anonymization ;
my $ help ;
my $ debug ;
my @ modules_to_parse ;
my @ oids_to_keep ;
# For each type that must be anonymized, give the replacement string
my % type_anonymization = (
'STRING' = > 'Anonymized ' ,
'IpAddress' = > '192.168.42.'
) ;
# If the value of the string matches this regex, we won't anonymize it:
# - interface names
# - system counters
# - disk paths
# - device names
# - loop
# - floating values (eg sysLoad)
2025-01-10 15:06:39 +01:00
my $ ignore_anon_regex = qr{ ^"?(lo|eth[ \ d]*|.* memory|.*Swap.*|.*Memory.*|tmpfs|systemStats|systemd-udevd|kdevtmpfs|.*centreontrapd.*|gorgone-.*|[C-Z]: \\ .*|(/[ \ d \ w_-]*) { 1, } |sd[a-z] \ d*|loop \ d+| \ d* \ .? \ d*)"?$ } ;
2024-10-15 11:56:15 +02:00
sub oid_matches {
my ( $ given_oid , $ list ) = @ _ ;
return 0 if ( is_empty ( $ given_oid ) == 1 ) ;
for my $ oid ( @$ list ) {
if ( $ given_oid =~ /^$oid/ ) {
print STDERR "OID $given_oid matches $oid\n" if ( defined ( $ debug ) ) ; ;
return 1 ;
}
}
return 0 ;
}
sub extract_modes {
my ( $ file_to_parse ) = @ _ ;
my @ modes ;
my $ fd ;
open ( $ fd , '<' , $ file_to_parse ) or die "Could not open $file_to_parse to list modules from." ;
for my $ line ( <$fd> ) {
if ( $ line =~ /^.*custom_mode.* ?= ?["']([A-Za-z_:]+)["'];/ or $ line =~ /^.* *=> *["']([A-Za-z_:]+)["'],? *$/ ) {
my $ module_to_push = $ 1 ;
$ module_to_push =~ s/::/\//g ;
$ module_to_push = 'src/' . $ module_to_push . '.pm' ;
print STDERR "Mode found $module_to_push\n" if ( defined ( $ debug ) ) ;
push @ modes , $ module_to_push ;
}
}
return @ modes ;
}
sub extract_oids {
my ( $ file_to_parse ) = @ _ ;
my @ oids ;
my $ fd ;
open ( $ fd , '<' , $ file_to_parse ) or die "Could not open $file_to_parse to get OIDs from." ;
for my $ line ( <$fd> ) {
2025-01-10 15:06:39 +01:00
# Find all strings looking like OIDs
2024-10-15 11:56:15 +02:00
if ( $ line =~ /.*['"](\.1\.[\.0-9]+)['"].*/ ) {
print STDERR "Line $line contains an OID: '$1'\n" if ( defined ( $ debug ) ) ;
push @ oids , $ 1 ;
}
}
return @ oids ;
}
sub is_empty {
my ( $ arg ) = @ _ ;
return 1 if ( ! defined ( $ arg ) or $ arg eq '' ) ;
return 0 ;
}
# Args:
# - path of snmpwalk (mandatory)
# - path of the plugin or the mode to test
sub usage {
return << "END_USAGE" ;
This scripts looks for the strictly necessary OIDs in a snmpwalk file , and excludes all the useless data .
Usage:
slim_walk . pl - - snmpwalk - path = path /to/ file . snmpwalk [ - - module - path = path /to/ file . pm ] [ - - debug ]
- - snmpwalk - path
Define where the snmpwalk file to shrink and anonymize can be found ( mandatory ) .
- - module - path
Define where the Perl module where to look for OIDs to keep can be found .
If this option is omitted , all the relevant modules ( given where the walk can be found and how it ' s named )
will be used .
- - no - anonymization
Disable anonymization .
- - debug
Enable DEBUG messages ( printed on STDERR ) .
Examples:
tests /scripts/s lim_walk . pl - - snmpwalk - path = tests /os/ linux /snmp/ linux . snmpwalk
Will look for all OIDs referenced in src /os/ linux / snmp and linked modules and exclude all data that is not related .
tests /scripts/s lim_walk . pl - - snmpwalk - path = tests /os/ linux /snmp/ linux . snmpwalk - - module - path = src /snmp_standard/mo de / uptime . pm
Will look for all OIDs referenced in src /snmp_standard/mo de / uptime . pm and exclude all data that is not related .
END_USAGE
}
GetOptions (
"snmpwalk-path=s" = > \ $ snmpwalk_path ,
"module-path=s" = > \ $ module_path ,
"no-anonymization" = > \ $ no_anonymization ,
"help" = > \ $ help ,
"debug" = > \ $ debug
) or die ( usage ( ) ) ;
# Control arguments integrity
die ( usage ( ) ) if ( defined ( $ help ) ) ;
die "Argument --snmpwalk-path is mandatory.\n" . usage ( ) if ( is_empty ( $ snmpwalk_path ) == 1 ) ;
die "File $snmpwalk_path does not exist." if ( ! - e $ snmpwalk_path ) ;
print STDERR "Path: $snmpwalk_path exists.\n" if ( defined ( $ debug ) ) ;
# If only the walk path is given, try to deduct the scope
# if name matches an existing mode, then the scope is presumably only this mode => only this .pm file
# else, find all .pm files of modes, custom modes located here and then find the external modules (eg from snmp_standard)
if ( is_empty ( $ module_path ) != 1 ) {
die "Module file $module_path not found." if ( ! - e $ module_path ) ;
die "Module file $module_path is not a regular file." if ( ! - f $ module_path ) ;
push @ modules_to_parse , $ module_path ;
} else {
# No module path: deduct the scope
my ( $ base_path , $ relative_path , $ name ) = $ snmpwalk_path =~ /^(.*\/)?tests\/(.*)\/(.*)\.snmpwalk$/ or die "Not able to split path $snmpwalk_path as snmpwalk path" ;
my $ deducted_path = defined ( $ base_path ) ? $ base_path : '.' ;
$ deducted_path . = "/src/$relative_path" ;
print STDERR "Path $deducted_path name $name.\n" if ( defined ( $ debug ) ) ;
my $ module_file = "$deducted_path/mode/$name.pm" ;
if ( - e $ module_file ) {
print STDERR "There is a $module_file module!\n" if ( defined ( $ debug ) ) ;
# The module has been found, we'll only consider it
push @ modules_to_parse , $ module_file ;
} else {
# There is no module, we'll take all perl files under the path
print STDERR "No $module_file module found! Looking for perl modules in $deducted_path.\n" if ( defined ( $ debug ) ) ;
find (
sub {
return unless - f ;
return unless /\.pm$/ ;
push @ modules_to_parse , $ File:: Find:: name ;
} ,
$ deducted_path
) ;
}
}
# module path has been given or deducted, scope is more obvious
# if some files are named plugins.pm
# list all the modes and custom-modes
for my $ i ( 0 .. $# modules_to_parse ) {
my $ current_module = $ modules_to_parse [ $ i ] ;
print STDERR "$i => $current_module\n" if ( defined ( $ debug ) ) ;
if ( $ current_module =~ /.*\/plugin\.pm/ ) {
# search for modes and custom modes
push @ modules_to_parse , extract_modes ( $ current_module ) ;
}
}
# Now we should have listed all the .pm files that may be linked to the given parameters
# For each .pm file
for my $ module ( @ modules_to_parse ) {
print STDERR "Considering module $module\n" if ( defined ( $ debug ) ) ;
push @ oids_to_keep , extract_oids ( $ module ) ;
}
# Now we have all the oids, presumably with duplicates, let's filter it
# make it more efficient
print STDERR "Number of oids before: $#oids_to_keep\n" if ( defined ( $ debug ) ) ;
@ oids_to_keep = uniq @ oids_to_keep ;
print STDERR "Number of oids after: $#oids_to_keep\n" if ( defined ( $ debug ) ) ;
my $ nb_oids_total = 0 ;
my $ nb_oids_accepted = 0 ;
# For each line of the walk
my $ walk_fd ;
open ( $ walk_fd , '<' , $ snmpwalk_path ) or die "Could not open $snmpwalk_path to purge OIDs from." ;
my $ last_line = '' ;
my $ is_last_line_to_keep = 0 ;
for my $ line ( <$walk_fd> ) {
chomp $ line ;
2025-01-10 15:06:39 +01:00
# remove all CR in the line
$ line =~ s/\r//g ;
2024-10-15 11:56:15 +02:00
# If the line does not begin with an OID
# If the last processed line has been retained,
# Then append it to the last accepted line
# Else
# Ignore
if ( $ line !~ /^\.1/ ) {
# this is not an OID, we may be reading the next part of an unfinished previous line
if ( $ is_last_line_to_keep == 1 ) {
$ last_line . = $ line ;
}
next ;
}
$ nb_oids_total + + ;
if ( $ is_last_line_to_keep == 1 ) {
$ nb_oids_accepted + + ;
print ( "$last_line\n" ) ;
$ last_line = '' ;
$ is_last_line_to_keep = 0 ;
}
my ( $ line_oid , $ line_type , $ line_value ) = $ line =~ /^(\.1\.[\.\d]+) ?= ?(\w+:)? (.*)$/ ;
die "Line $line cound not be parsed." if ( is_empty ( $ line_oid ) == 1 ) ;
next if ( oid_matches ( $ line_oid , \ @ oids_to_keep ) != 1 ) ;
my $ type_str = defined ( $ line_type ) ? ' ' . $ line_type . ' ' : ' ' ;
$ line = $ line_oid . ' =' . $ type_str . $ line_value ;
if ( ! defined ( $ no_anonymization ) and defined ( $ line_type ) and is_empty ( $ line_value ) != 1 and $ line_value ne '""' ) {
$ line_type =~ s/:$// ;
if ( defined ( $ type_anonymization { $ line_type } ) and $ line_value !~ $ ignore_anon_regex ) {
my $ md5_based_index = sprintf ( "%0.3d" , unpack ( 'L' , md5 ( $ line_oid ) ) % 255 ) ;
my $ replacement = $ line_oid . ' = ' . $ line_type . ': ' . $ type_anonymization { $ line_type } . $ md5_based_index ;
$ line = $ replacement ;
}
}
$ last_line = $ line ;
$ is_last_line_to_keep = 1 ;
}
# do not miss the last line
if ( $ is_last_line_to_keep == 1 ) {
$ nb_oids_accepted + + ;
print ( "$last_line\n" ) ;
}
print STDERR "$nb_oids_accepted accepted OIDs out of $nb_oids_total\n" ;
# Remove if it does not match any wanted OID
# The next block replaces beautify_snmpwalk.py