Add support for Zabbix v2.4 template schema

Add support for Zabbix v2.4 template schema:

Zabbix template version

    -z, --zabbix_ver=2|3        Zabbix Template Schema Version (default: 3)
This commit is contained in:
Steven Yu 2016-08-18 15:46:21 +10:00 committed by GitHub
parent 20bfdd8a70
commit f3ddebdc80

View file

@ -1,4 +1,5 @@
#!/usr/bin/perl #!/usr/bin/perl
=pod =pod
=head1 NAME =head1 NAME
@ -36,6 +37,10 @@ SNMP Version 3 specific
-x, --privacy=PROTOCOL privacy protocol (DES|AES) -x, --privacy=PROTOCOL privacy protocol (DES|AES)
-X, --privpass=PASSPHRASE privacy passphrase -X, --privpass=PASSPHRASE privacy passphrase
Zabbix template version
-z, --zabbix_ver=2|3 Zabbix Template Schema Version (default: 3)
Zabbix item configuration Zabbix item configuration
--check-delay=SECONDS check interval in seconds (default: 60) --check-delay=SECONDS check interval in seconds (default: 60)
@ -43,6 +48,8 @@ Zabbix item configuration
--history=DAYS history retention in days (default: 7) --history=DAYS history retention in days (default: 7)
--trends=DAYS trends retention in days (default: 365) --trends=DAYS trends retention in days (default: 365)
Help
-h, --help print this message -h, --help print this message
=head1 DESCRIPTION =head1 DESCRIPTION
@ -50,12 +57,14 @@ Zabbix item configuration
B<mib2zabbix.pl> will export a loaded MIB tree into a Zabbix Template starting B<mib2zabbix.pl> will export a loaded MIB tree into a Zabbix Template starting
from the OID root specified. from the OID root specified.
Requires: Zabbix v3, Perl v5, Pod::Usage, XML::Simple, Net-SNMP Requires: Zabbix v2.4 or 3, Perl v5, Pod::Usage, XML::Simple 2.20, Net-SNMP
=head1 AUTHOR =head1 AUTHOR
Ryan Armstrong <ryan@cavaliercoder.com> Ryan Armstrong <ryan@cavaliercoder.com>
Steven Yu <steven@wordofeternity.org> -- Zabbix v2.4 support
=head1 SEE ALSO =head1 SEE ALSO
Guidelines for Authors and Reviewers of MIB Documents Guidelines for Authors and Reviewers of MIB Documents
@ -68,11 +77,17 @@ https://tools.ietf.org/html/rfc3781
SNMP Table Basics SNMP Table Basics
http://www.webnms.com/snmp/help/snmpapi/snmpv3/table_handling/snmptables_basics.html http://www.webnms.com/snmp/help/snmpapi/snmpv3/table_handling/snmptables_basics.html
Zabbix Template Schema
v2.4 - https://www.zabbix.com/documentation/2.4/manual/xml_export_import/hosts
v3 - https://www.zabbix.com/documentation/3.0/manual/xml_export_import/hosts
=head1 SUBROUTINES =head1 SUBROUTINES
=cut =cut
use strict; use strict;
#use warnings; #use warnings;
use Cwd 'abs_path'; use Cwd 'abs_path';
@ -128,22 +143,32 @@ use constant ZBX_V3_SEC_AUTHPRIV => 2;
# SNMP Type -> Zabbix type mapping # SNMP Type -> Zabbix type mapping
my $type_map = { my $type_map = {
'BITS' => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type 'BITS' => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type
'COUNTER' => ZBX_VAL_TYPE_UINT, # Zabbix 'Numeric Unsigned' value type for an unsigned integer 'COUNTER' => ZBX_VAL_TYPE_UINT
'COUNTER32' => ZBX_VAL_TYPE_UINT, # Zabbix 'Numeric Unsigned' value type for an unsigned integer , # Zabbix 'Numeric Unsigned' value type for an unsigned integer
'COUNTER64' => ZBX_VAL_TYPE_UINT, # Zabbix 'Numeric Unsigned' value type for an unsigned integer 'COUNTER32' => ZBX_VAL_TYPE_UINT
'GAUGE' => ZBX_VAL_TYPE_UINT, # Zabbix 'Numeric Unsigned' value type for an unsigned integer , # Zabbix 'Numeric Unsigned' value type for an unsigned integer
'GAUGE32' => ZBX_VAL_TYPE_UINT, # Zabbix 'Numeric Unsigned' value type for an unsigned integer 'COUNTER64' => ZBX_VAL_TYPE_UINT
'INTEGER' => ZBX_VAL_TYPE_FLOAT, # Zabbix 'Numeric Float' value type for an signed integer , # Zabbix 'Numeric Unsigned' value type for an unsigned integer
'INTEGER32' => ZBX_VAL_TYPE_FLOAT, # Zabbix 'Numeric Float' value type for an signed 32 bit integer 'GAUGE' => ZBX_VAL_TYPE_UINT
, # Zabbix 'Numeric Unsigned' value type for an unsigned integer
'GAUGE32' => ZBX_VAL_TYPE_UINT
, # Zabbix 'Numeric Unsigned' value type for an unsigned integer
'INTEGER' => ZBX_VAL_TYPE_FLOAT
, # Zabbix 'Numeric Float' value type for an signed integer
'INTEGER32' => ZBX_VAL_TYPE_FLOAT
, # Zabbix 'Numeric Float' value type for an signed 32 bit integer
'IPADDR' => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type for an IP address 'IPADDR' => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type for an IP address
'NETADDDR' => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type for a network address 'NETADDDR' =>
ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type for a network address
'NOTIF' => ZBX_ITEM_TYPE_SNMPTRAP, # Zabbix 'SNMP Trap' item type 'NOTIF' => ZBX_ITEM_TYPE_SNMPTRAP, # Zabbix 'SNMP Trap' item type
'TRAP' => ZBX_ITEM_TYPE_SNMPTRAP, # Zabbix 'SNMP Trap' item type 'TRAP' => ZBX_ITEM_TYPE_SNMPTRAP, # Zabbix 'SNMP Trap' item type
'OBJECTID' => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type for an OID 'OBJECTID' => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type for an OID
'OCTETSTR' => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type 'OCTETSTR' => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type
'OPAQUE' => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type 'OPAQUE' => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type
'TICKS' => ZBX_VAL_TYPE_UINT, # Zabbix 'Numeric Unsigned' for a Module 232 timestamp 'TICKS' =>
'UNSIGNED32' => ZBX_VAL_TYPE_UINT # Zabbix 'Numeric Unsigned' value type for an unsigned 32bit integer ZBX_VAL_TYPE_UINT, # Zabbix 'Numeric Unsigned' for a Module 232 timestamp
'UNSIGNED32' =>
ZBX_VAL_TYPE_UINT # Zabbix 'Numeric Unsigned' value type for an unsigned 32bit integer
}; };
# SNMP Version -> Zabbix item type mapping # SNMP Version -> Zabbix item type mapping
@ -191,7 +216,8 @@ my $opts = {
v3auth_protocol => 'md5', v3auth_protocol => 'md5',
v3auth_pass => '', v3auth_pass => '',
v3sec_protocol => 'des', v3sec_protocol => 'des',
v3sec_pass => '' v3sec_pass => '',
zabbix_ver => 3
}; };
# Capture calling args # Capture calling args
@ -221,6 +247,8 @@ GetOptions(
'x|privacy=s' => \$opts->{v3sec_protocol}, # SNMPv3 Privacy protocol 'x|privacy=s' => \$opts->{v3sec_protocol}, # SNMPv3 Privacy protocol
'X|privpass=s' => \$opts->{v2sec_pass}, # SNMPv3 Privacy passphrase 'X|privpass=s' => \$opts->{v2sec_pass}, # SNMPv3 Privacy passphrase
'z|zabbix_ver=i' => \$opts->{zabbix_ver}, # Zabbix version
'check-delay=i' => \$opts->{delay}, # Update interval in seconds 'check-delay=i' => \$opts->{delay}, # Update interval in seconds
'disc-delay=i' => \$opts->{disc_delay}, # Update interval in seconds 'disc-delay=i' => \$opts->{disc_delay}, # Update interval in seconds
'history=i' => \$opts->{history}, # History retention in days 'history=i' => \$opts->{history}, # History retention in days
@ -236,14 +264,19 @@ pod2usage({ -exitval => 0 }) if ($opts->{ help });
if ( $opts->{snmpver} == 3 ) { if ( $opts->{snmpver} == 3 ) {
$opts->{snmpcomm} = ''; $opts->{snmpcomm} = '';
die("Unknown authentication level '$opts->{ v3auth_level }'") unless defined($snmpv3_auth_level_map->{ $opts->{ v3auth_level } }); die("Unknown authentication level '$opts->{ v3auth_level }'")
unless defined( $snmpv3_auth_level_map->{ $opts->{v3auth_level} } );
$opts->{v3auth_level} = $snmpv3_auth_level_map->{ $opts->{v3auth_level} }; $opts->{v3auth_level} = $snmpv3_auth_level_map->{ $opts->{v3auth_level} };
die("Unknown authentical protocol '$opts->{ v3auth_protocol }'") unless defined($snmpv3_auth_protocol_map->{ $opts->{ v3auth_protocol } }); die("Unknown authentical protocol '$opts->{ v3auth_protocol }'")
$opts->{ v3auth_protocol } = $snmpv3_auth_protocol_map->{ $opts->{ v3auth_protocol } }; unless defined( $snmpv3_auth_protocol_map->{ $opts->{v3auth_protocol} } );
$opts->{v3auth_protocol} =
$snmpv3_auth_protocol_map->{ $opts->{v3auth_protocol} };
die("Unknown privacy protocol '$opts->{ v3sec_protocol }'") unless defined ($snmpv3_sec_protocol_map->{ $opts->{ v3sec_protocol } }); die("Unknown privacy protocol '$opts->{ v3sec_protocol }'")
$opts->{ v3sec_protocol } = $snmpv3_sec_protocol_map->{ $opts->{ v3sec_protocol } }; unless defined( $snmpv3_sec_protocol_map->{ $opts->{v3sec_protocol} } );
$opts->{v3sec_protocol} =
$snmpv3_sec_protocol_map->{ $opts->{v3sec_protocol} };
} }
# Base template for Template Items, Discovery Rules and Item Prototypes # Base template for Template Items, Discovery Rules and Item Prototypes
@ -259,20 +292,33 @@ my %item_base_template = (
port => '{$SNMP_PORT}', # Use macro for SNMP UDP Port port => '{$SNMP_PORT}', # Use macro for SNMP UDP Port
privatekey => '', privatekey => '',
publickey => '', publickey => '',
snmp_community => $opts->{ snmpver } < 3 ? '{$SNMP_COMMUNITY}' : '', # Use macro for SNMP Community string snmp_community => $opts->{snmpver} < 3
snmpv3_authpassphrase => $opts->{ snmpver } == 3 ? '{$SNMP_AUTHPASS}' : '', # Use macro for SNMPv3 Authentication passphrase ? '{$SNMP_COMMUNITY}'
snmpv3_authprotocol => $opts->{ snmpver } == 3 ? $opts->{ v3auth_protocol } : '0', : '', # Use macro for SNMP Community string
snmpv3_contextname => $opts->{ snmpver } == 3 ? '{$SNMP_CONTEXT}' : '', # Use macro for SNMPv3 context name snmpv3_authpassphrase => $opts->{snmpver} == 3
snmpv3_privpassphrase => $opts->{ snmpver } == 3 ? '{$SNMP_PRIVPASS}' : '', # Use macro for SNMPv3 Privacy passphrase ? '{$SNMP_AUTHPASS}'
snmpv3_privprotocol => $opts->{ snmpver } == 3 ? $opts->{ v3sec_protocol } : '0', : '', # Use macro for SNMPv3 Authentication passphrase
snmpv3_authprotocol => $opts->{snmpver} == 3 ? $opts->{v3auth_protocol}
: '0',
snmpv3_contextname => $opts->{snmpver} == 3 ? '{$SNMP_CONTEXT}'
: '', # Use macro for SNMPv3 context name
snmpv3_privpassphrase => $opts->{snmpver} == 3
? '{$SNMP_PRIVPASS}'
: '', # Use macro for SNMPv3 Privacy passphrase
snmpv3_privprotocol => $opts->{snmpver} == 3 ? $opts->{v3sec_protocol}
: '0',
snmpv3_securitylevel => $opts->{snmpver} == 3 ? $opts->{v3auth_level} : '0', snmpv3_securitylevel => $opts->{snmpver} == 3 ? $opts->{v3auth_level} : '0',
snmpv3_securityname => $opts->{ snmpver } == 3 ? '{$SNMP_USER}' : '', # Use macro for SNMPv3 Username snmpv3_securityname => $opts->{snmpver} == 3 ? '{$SNMP_USER}'
status => ($opts->{ enableitems } ? ZBX_ITEM_ENABLED : ZBX_ITEM_DISABLED), # Enabled (0) | Disabled (1) : '', # Use macro for SNMPv3 Username
status => ( $opts->{enableitems} ? ZBX_ITEM_ENABLED : ZBX_ITEM_DISABLED )
, # Enabled (0) | Disabled (1)
username => '', username => '',
); );
# Item template for standard Template items my %item_template;
my %item_template = (
# Item template for standard Template items for Zabbix v3
my %item_template_v3 = (
data_type => '0', data_type => '0',
delay => $opts->{delay}, # Update internal seconds delay => $opts->{delay}, # Update internal seconds
delta => '0', # Change delta delta => '0', # Change delta
@ -285,7 +331,27 @@ my %item_template = (
valuemap => '', valuemap => '',
logtimefmt => '', logtimefmt => '',
); );
%item_template = (%item_base_template, %item_template);
# Item template for standard Template items for Zabbix v2
my %item_template_v2 = (
data_type => '0',
delay => $opts->{delay}, # Update internal seconds
delta => '0', # Change delta
formula => '1', # Multiplier factor
history => $opts->{history}, # History retention in days
inventory_link => '0',
multiplier => '0', # Enable multiplier
trends => $opts->{trends}, # Trends retention in days
valuemap => '',
units => '',
);
if ( $opts->{zabbix_ver} == 3 ) {
%item_template = ( %item_base_template, %item_template_v3 );
}
else {
%item_template = ( %item_base_template, %item_template_v2 );
}
# Discovery rule template # Discovery rule template
my %disc_rule_template = ( my %disc_rule_template = (
@ -305,8 +371,8 @@ my %disc_rule_template = (
); );
%disc_rule_template = ( %item_base_template, %disc_rule_template ); %disc_rule_template = ( %item_base_template, %disc_rule_template );
# SNMP Trap template # SNMP Trap template for Zabbix v3
my %trap_template = ( my %trap_template_v3 = (
allowed_hosts => '', allowed_hosts => '',
applications => [], applications => [],
authtype => 0, authtype => 0,
@ -344,10 +410,47 @@ my %trap_template = (
valuemap => '' valuemap => ''
); );
# Item prototype template # SNMP Trap template for Zabbix v2
my %item_proto_template = ( my %trap_template_v2 = (
application_prototypes => undef, allowed_hosts => '',
applications => [],
authtype => 0,
data_type => 0,
delay => '0',
delay_flex => '',
delta => 0,
description => '',
formula => 1,
history => $opts->{history},
inventory_link => 0,
ipmi_sensor => '',
logtimefmt => 'hh:mm:ss dd/MM/yyyy',
multiplier => '0',
params => '',
password => '',
port => '',
privatekey => '',
publickey => '',
snmp_community => '',
snmp_oid => '',
snmpv3_authpassphrase => '',
snmpv3_authprotocol => 0,
snmpv3_contextname => '',
snmpv3_privpassphrase => '',
snmpv3_privprotocol => 0,
snmpv3_securitylevel => 0,
snmpv3_securityname => '',
status => ( $opts->{enableitems} ? ZBX_ITEM_ENABLED : ZBX_ITEM_DISABLED ),
trends => $opts->{trends},
type => ZBX_ITEM_TYPE_SNMPTRAP,
units => '',
username => '',
value_type => ZBX_VAL_TYPE_LOG
); );
# Item prototype template
my %item_proto_template = ( application_prototypes => undef, );
%item_proto_template = ( %item_template, %item_proto_template ); %item_proto_template = ( %item_template, %item_proto_template );
# Global value maps array # Global value maps array
@ -360,6 +463,7 @@ Returns : (string) $wellformed_utf8
Description : Returns a sanitized UTF8 string, removing incompatable characters Description : Returns a sanitized UTF8 string, removing incompatable characters
=cut =cut
sub utf8_sanitize { sub utf8_sanitize {
my ($malformed_utf8) = @_; my ($malformed_utf8) = @_;
@ -375,6 +479,7 @@ Description : Returns the fully qualified textual path of a MIB node by
traversing the node's parents. traversing the node's parents.
=cut =cut
sub oid_path { sub oid_path {
my ($oid) = @_; my ($oid) = @_;
@ -395,6 +500,7 @@ Returns : (Hash) $item
Description : Returns a Zabbix Item hash derived from the specified MIB OID Description : Returns a Zabbix Item hash derived from the specified MIB OID
=cut =cut
sub node_to_item { sub node_to_item {
my ( $node, $template ) = @_; my ( $node, $template ) = @_;
$template = $template || \%item_template; $template = $template || \%item_template;
@ -405,40 +511,53 @@ sub node_to_item {
$item->{name} = $node->{label}; $item->{name} = $node->{label};
$item->{snmp_oid} = $node->{objectID}; $item->{snmp_oid} = $node->{objectID};
if ( $node->{units} ) { if ( $node->{units} ) {
# Convert unit to Zabbix postfix # Convert unit to Zabbix postfix
# See 'Units' section of https://www.zabbix.com/documentation/3.0/manual/config/items/item # See 'Units' section of https://www.zabbix.com/documentation/3.0/manual/config/items/item
if ( $node->{units} =~ /^seconds$/ ) { if ( $node->{units} =~ /^seconds$/ ) {
$item->{units} = 's'; $item->{units} = 's';
} elsif ($node->{ units } =~ /^(hundreds of seconds)$/i) { }
elsif ( $node->{units} =~ /^(hundreds of seconds)$/i ) {
$item->{units} = 's'; $item->{units} = 's';
$item->{multiplier} = '1'; $item->{multiplier} = '1';
$item->{formula} = '100'; $item->{formula} = '100';
} elsif ($node->{ units } =~ /^(milliseconds|milli-seconds)$/i) { }
elsif ( $node->{units} =~ /^(milliseconds|milli-seconds)$/i ) {
$item->{units} = 's'; $item->{units} = 's';
$item->{multiplier} = '1'; $item->{multiplier} = '1';
$item->{formula} = '.001'; $item->{formula} = '.001';
} elsif ($node->{ units } =~ /^microseconds$/i) { }
elsif ( $node->{units} =~ /^microseconds$/i ) {
$item->{units} = 's'; $item->{units} = 's';
$item->{multiplier} = '1'; $item->{multiplier} = '1';
$item->{formula} = '.000001'; $item->{formula} = '.000001';
} elsif ($node->{ units } =~ /^(octets|bytes)$/i) { }
elsif ( $node->{units} =~ /^(octets|bytes)$/i ) {
$item->{units} = 'B'; $item->{units} = 'B';
} elsif ($node->{ units } =~ /^(k-octets|kbytes|kb)$/i) { }
elsif ( $node->{units} =~ /^(k-octets|kbytes|kb)$/i ) {
$item->{units} = 'B'; $item->{units} = 'B';
$item->{multiplier} = '1'; $item->{multiplier} = '1';
$item->{formula} = '.001'; $item->{formula} = '.001';
} elsif ($node->{ units } =~ /^(bits per second)$/i) { }
elsif ( $node->{units} =~ /^(bits per second)$/i ) {
$item->{units} = 'b'; $item->{units} = 'b';
} elsif ($node->{ units } =~ /^(kbps|kilobits per second)$/i) { }
elsif ( $node->{units} =~ /^(kbps|kilobits per second)$/i ) {
$item->{units} = 'b'; $item->{units} = 'b';
$item->{multiplier} = '1'; $item->{multiplier} = '1';
$item->{formula} = '.001'; $item->{formula} = '.001';
} elsif ($node->{ units } =~ /^percent$/i) { }
elsif ( $node->{units} =~ /^percent$/i ) {
$item->{units} = '%'; $item->{units} = '%';
} elsif ($node->{ units } =~ /\/s$/i) { }
elsif ( $node->{units} =~ /\/s$/i ) {
# truncate /s (/sec will be added later) # truncate /s (/sec will be added later)
$item->{units} = substr( $node->{units}, 0, -2 ) . "/sec"; $item->{units} = substr( $node->{units}, 0, -2 ) . "/sec";
} else { }
else {
# default to original # default to original
$item->{units} = $node->{units}; $item->{units} = $node->{units};
} }
@ -457,7 +576,8 @@ sub node_to_item {
if ( $node->{type} ) { if ( $node->{type} ) {
$item->{value_type} = $type_map->{ $node->{type} }; $item->{value_type} = $type_map->{ $node->{type} };
if ( !defined( $item->{value_type} ) ) { if ( !defined( $item->{value_type} ) ) {
print STDERR "No type mapping found for type $node->{ type } in $node->{ objectID }\n"; print STDERR
"No type mapping found for type $node->{ type } in $node->{ objectID }\n";
} }
} }
@ -467,9 +587,11 @@ sub node_to_item {
if ( $item->{units} =~ /^s$/ ) { if ( $item->{units} =~ /^s$/ ) {
$item->{units} = '/sec'; $item->{units} = '/sec';
} elsif ($item->{ units } =~ /^b$/i) { }
elsif ( $item->{units} =~ /^b$/i ) {
$item->{units} .= 'ps'; $item->{units} .= 'ps';
} else { }
else {
$item->{units} .= '/sec'; $item->{units} .= '/sec';
} }
} }
@ -484,11 +606,13 @@ sub node_to_item {
# Parse item desciption # Parse item desciption
$item->{description} = utf8_sanitize( $node->{description} ); $item->{description} = utf8_sanitize( $node->{description} );
if ( $item->{description} ) { if ( $item->{description} ) {
$item->{ description } =~ s/^\s+|\s+$|\n//g; # Trim left/right whitespace and newlines $item->{description} =~
s/^\s+|\s+$|\n//g; # Trim left/right whitespace and newlines
$item->{description} =~ s/\s{2,}/ /g; # Remove padding $item->{description} =~ s/\s{2,}/ /g; # Remove padding
} }
# Process value maps # Process value maps
if ( $opts->{zabbix_ver} == 3 ) {
if ( scalar keys %{ $node->{enums} } ) { if ( scalar keys %{ $node->{enums} } ) {
my $map_name = "$node->{ moduleID }::$node->{ label }"; my $map_name = "$node->{ moduleID }::$node->{ label }";
@ -498,18 +622,29 @@ sub node_to_item {
$map_name = substr( $map_name, 0, 61 ) . "..."; $map_name = substr( $map_name, 0, 61 ) . "...";
} }
# add template value map # add template value map for Zabbix version 3
$valuemaps->{$map_name}->{'mappings'} = []; $valuemaps->{$map_name}->{'mappings'} = [];
foreach ( keys %{ $node->{enums} } ) { foreach ( keys %{ $node->{enums} } ) {
push(@{ $valuemaps->{ $map_name }->{ 'mappings' } }, { push(
@{ $valuemaps->{$map_name}->{'mappings'} },
{
'value' => $node->{enums}->{$_}, 'value' => $node->{enums}->{$_},
'newvalue' => $_ 'newvalue' => $_
}); }
);
} }
# Assign value map to item # Assign value map to item
$item->{valuemap} = { name => $map_name }; $item->{valuemap} = { name => $map_name };
} }
else {
my $map_name = "$node->{ moduleID }::$node->{ label }";
# Assign value map to item
$item->{valuemap} = { name => $map_name };
}
}
return $item; return $item;
} }
@ -523,9 +658,17 @@ Description : Returns a Zabbix SNMP Trap Item hash derived from the
specified MIB OID specified MIB OID
=cut =cut
sub node_to_trapitem { sub node_to_trapitem {
my ( $node, $template ) = @_; my ( $node, $template ) = @_;
$template = $template || \%trap_template;
if ( $opts->{zabbix_ver} == 3 ) {
$template = $template || \%trap_template_v3;
}
else {
$template = $template || \%trap_template_v2;
}
# Create item hash # Create item hash
my $item = { %{$template} }; my $item = { %{$template} };
@ -571,11 +714,14 @@ sub node_to_trapitem {
if ( $varbind->{description} ) { if ( $varbind->{description} ) {
my $vbdesc = $varbind->{description}; my $vbdesc = $varbind->{description};
$vbdesc =~ s/[ \t]+/ /g; # Replace long whitespace with single space $vbdesc =~
$vbdesc =~ s/^ ?/ /mg; # Prepend indent to each description line s/[ \t]+/ /g; # Replace long whitespace with single space
$vbdesc =~
s/^ ?/ /mg; # Prepend indent to each description line
$desc .= "$vbdesc\n\n"; $desc .= "$vbdesc\n\n";
} }
} else { }
else {
$desc .= "\n"; $desc .= "\n";
} }
} }
@ -592,13 +738,12 @@ Returns : (int) 0|1
Description : Returns true if the specified OID is not obsolete Description : Returns true if the specified OID is not obsolete
=cut =cut
sub node_is_current { sub node_is_current {
my ($node) = @_; my ($node) = @_;
return ( return ( node_is_valid_trap($node)
node_is_valid_trap($node) || ( defined( $node->{status} ) && $node->{status} ne 'Obsolete' ) );
|| (defined($node->{ status }) && $node->{ status } ne 'Obsolete')
);
} }
=head2 node_is_valid_scalar =head2 node_is_valid_scalar
@ -609,6 +754,7 @@ Description : Returns true if the specified OID is current, readable and
defines a valid value type. defines a valid value type.
=cut =cut
sub node_is_valid_scalar { sub node_is_valid_scalar {
my ($node) = @_; my ($node) = @_;
@ -616,8 +762,10 @@ sub node_is_valid_scalar {
node_is_current($node) node_is_current($node)
&& $node->{type} && $node->{type}
&& ( && (
$node->{ type } eq 'NOTIF' || $node->{ type } eq 'TRAP' $node->{type} eq 'NOTIF'
|| ($node->{ access } eq 'ReadOnly' || $node->{ access } eq 'ReadWrite') || $node->{type} eq 'TRAP'
|| ( $node->{access} eq 'ReadOnly'
|| $node->{access} eq 'ReadWrite' )
) )
); );
@ -630,12 +778,12 @@ Returns : (int) 0|1
Description : Returns true if the specified OID is an SNMP Trap Description : Returns true if the specified OID is an SNMP Trap
=cut =cut
sub node_is_valid_trap { sub node_is_valid_trap {
my ($node) = @_; my ($node) = @_;
return ( return ( defined( $node->{type} )
defined($node->{ type }) && ($node->{ type } eq 'NOTIF' || $node->{ type } eq 'TRAP') && ( $node->{type} eq 'NOTIF' || $node->{type} eq 'TRAP' ) );
);
} }
=head2 node_is_valid_table =head2 node_is_valid_table
@ -647,6 +795,7 @@ Description : Returns true if the specified OID is a valid table which is
definition) definition)
=cut =cut
sub node_is_valid_table { sub node_is_valid_table {
my ($node) = @_; my ($node) = @_;
@ -680,16 +829,19 @@ Description : Traverses a loaded MIB tree from the specified OID node
rules, item prototypes, groups and macros. rules, item prototypes, groups and macros.
=cut =cut
sub build_template { sub build_template {
my ( $template, $node ) = @_; my ( $template, $node ) = @_;
# Ignore obsolete OIDs # Ignore obsolete OIDs
if ( node_is_current($node) ) { if ( node_is_current($node) ) {
# Create an Item Application name for this node # Create an Item Application name for this node
my $appname = "$node->{ moduleID }::$node->{ parent }->{ label }"; my $appname = "$node->{ moduleID }::$node->{ parent }->{ label }";
# Is this a scalar value OID? # Is this a scalar value OID?
if ( node_is_valid_trap($node) ) { if ( node_is_valid_trap($node) ) {
# Convert the SNMP::MIB::Node to a Zabbix Template SNMP Trap Item # Convert the SNMP::MIB::Node to a Zabbix Template SNMP Trap Item
my $item = node_to_trapitem($node); my $item = node_to_trapitem($node);
@ -715,7 +867,8 @@ sub build_template {
} }
} elsif (node_is_valid_scalar($node)) { }
elsif ( node_is_valid_scalar($node) ) {
# Convert the SNMP::MIB::Node to a Zabbix Template Item hash # Convert the SNMP::MIB::Node to a Zabbix Template Item hash
my $item = node_to_item($node); my $item = node_to_item($node);
@ -730,18 +883,22 @@ sub build_template {
# Add item to template # Add item to template
push( @{ $template->{items} }, $item ); push( @{ $template->{items} }, $item );
} elsif (node_is_valid_table($node)) { }
elsif ( node_is_valid_table($node) ) {
# Get row OID # Get row OID
my $table = $node; my $table = $node;
my $row = $node->{children}[0]; my $row = $node->{children}[0];
# Validate naming standard # Validate naming standard
if ( $table->{label} !~ /Table/ ) { if ( $table->{label} !~ /Table/ ) {
print STDERR "Warning: $table->{ moduleID }:: $table->{ label } appears to be a table but does not have the 'Table' suffix\n"; print STDERR
"Warning: $table->{ moduleID }:: $table->{ label } appears to be a table but does not have the 'Table' suffix\n";
} }
if ( $row->{label} !~ /Entry/ ) { if ( $row->{label} !~ /Entry/ ) {
print STDERR "Warning: $row->{ moduleID }:: $row->{ label } appears to be a table entry but does not have the 'Entry; suffix\n"; print STDERR
"Warning: $row->{ moduleID }:: $row->{ label } appears to be a table entry but does not have the 'Entry; suffix\n";
} }
# This is a table. Build a discovery rule # This is a table. Build a discovery rule
@ -757,7 +914,8 @@ sub build_template {
foreach my $column ( @{ $row->{children} } ) { foreach my $column ( @{ $row->{children} } ) {
if ( node_is_valid_scalar($column) ) { if ( node_is_valid_scalar($column) ) {
if ( $column->{label} =~ m/Descr$/ ) { if ( $column->{label} =~ m/Descr$/ ) {
$disc_rule->{ snmp_oid } .= "{#SNMPVALUE},$column->{ objectID },"; $disc_rule->{snmp_oid} .=
"{#SNMPVALUE},$column->{ objectID },";
$index = '{#SNMPVALUE}'; $index = '{#SNMPVALUE}';
} }
} }
@ -767,19 +925,26 @@ sub build_template {
# See: https://www.zabbix.com/documentation/3.0/manual/discovery/low_level_discovery#discovery_of_snmp_oids # See: https://www.zabbix.com/documentation/3.0/manual/discovery/low_level_discovery#discovery_of_snmp_oids
foreach my $column ( @{ $row->{children} } ) { foreach my $column ( @{ $row->{children} } ) {
if ( node_is_valid_scalar($column) ) { if ( node_is_valid_scalar($column) ) {
my $new_snmp_oid = $disc_rule->{ snmp_oid } . "{#" . uc($column->{ label }) . "}," . $column->{ objectID } . ","; my $new_snmp_oid =
$disc_rule->{snmp_oid} . "{#"
. uc( $column->{label} ) . "},"
. $column->{objectID} . ",";
if ( length($new_snmp_oid) <= 255 ) { if ( length($new_snmp_oid) <= 255 ) {
$disc_rule->{snmp_oid} = $new_snmp_oid; $disc_rule->{snmp_oid} = $new_snmp_oid;
} }
} }
} }
$disc_rule->{ snmp_oid } = substr($disc_rule->{ snmp_oid }, 0, -1) . "]"; $disc_rule->{snmp_oid} =
substr( $disc_rule->{snmp_oid}, 0, -1 ) . "]";
# Fetch an arbitrary column OID for Zabbix to use for discovery # Fetch an arbitrary column OID for Zabbix to use for discovery
my $index_oid = $row->{children}[0]; my $index_oid = $row->{children}[0];
if ( !defined($index_oid) ) { if ( !defined($index_oid) ) {
print STDERR "No index found for table $table->{ moduleID}::$table->{ label } ($table->{ objectID })\n"; print STDERR
} else { "No index found for table $table->{ moduleID}::$table->{ label } ($table->{ objectID })\n";
}
else {
# Remove unrequired fields # Remove unrequired fields
delete( $disc_rule->{applications} ); delete( $disc_rule->{applications} );
delete( $disc_rule->{data_type} ); delete( $disc_rule->{data_type} );
@ -790,10 +955,13 @@ sub build_template {
# Add prototypes for each row column # Add prototypes for each row column
foreach my $column ( @{ $row->{children} } ) { foreach my $column ( @{ $row->{children} } ) {
if ( node_is_valid_scalar($column) ) { if ( node_is_valid_scalar($column) ) {
if (my $proto = node_to_item($column, \%item_proto_template)) { if ( my $proto =
node_to_item( $column, \%item_proto_template ) )
{
$proto->{name} = "$proto->{ name } for $index"; $proto->{name} = "$proto->{ name } for $index";
$proto->{key} = "$column->{ label }\[$index]"; $proto->{key} = "$column->{ label }\[$index]";
$proto->{ snmp_oid } = "$proto->{ snmp_oid }.{#SNMPINDEX}"; $proto->{snmp_oid} =
"$proto->{ snmp_oid }.{#SNMPINDEX}";
# Add item applications to template application list # Add item applications to template application list
$proto->{applications} = [ { name => $appname } ]; $proto->{applications} = [ { name => $appname } ];
@ -808,7 +976,9 @@ sub build_template {
push( @{ $template->{discovery_rules} }, $disc_rule ); push( @{ $template->{discovery_rules} }, $disc_rule );
} }
} }
} else { }
else {
# Parse children # Parse children
foreach ( @{ $node->{children} } ) { foreach ( @{ $node->{children} } ) {
build_template( $template, $_ ); build_template( $template, $_ );
@ -822,7 +992,7 @@ SNMP::initMib();
# Verify the specified OID exists # Verify the specified OID exists
if ( $opts->{oid} !~ m/^\./ ) { if ( $opts->{oid} !~ m/^\./ ) {
$opts->{ oid } = "." . $opts->{ oid } $opts->{oid} = "." . $opts->{oid};
} }
my $oid_root = $SNMP::MIB{ $opts->{oid} }; my $oid_root = $SNMP::MIB{ $opts->{oid} };
@ -831,9 +1001,11 @@ if (!$oid_root || $oid_root->{ objectID } ne $opts->{ oid }) {
exit 1; exit 1;
# Build a Zabbix template # Build a Zabbix template
} else { }
else {
my $suffix = $opts->{snmpver} > 2 ? " v$opts->{ snmpver }" : ''; my $suffix = $opts->{snmpver} > 2 ? " v$opts->{ snmpver }" : '';
my $template_name = $opts->{ name } || "Template $oid_root->{ moduleID } - $oid_root->{ label }$suffix"; my $template_name = $opts->{name}
|| "Template $oid_root->{ moduleID } - $oid_root->{ label }$suffix";
my $template = { my $template = {
name => $template_name, name => $template_name,
template => $template_name, template => $template_name,
@ -841,9 +1013,7 @@ if (!$oid_root || $oid_root->{ objectID } ne $opts->{ oid }) {
apptags => {}, apptags => {},
applications => [], applications => [],
discovery_rules => [], discovery_rules => [],
groups => [{ groups => [ { name => $opts->{group} } ],
name => $opts->{ group }
}],
items => [], items => [],
macros => [ macros => [
{ macro => '{$MIB2ZABBIX_CMD}', value => $cmd }, { macro => '{$MIB2ZABBIX_CMD}', value => $cmd },
@ -856,22 +1026,41 @@ if (!$oid_root || $oid_root->{ objectID } ne $opts->{ oid }) {
# Add SNMP connection macros # Add SNMP connection macros
if ( $opts->{snmpver} < 3 ) { if ( $opts->{snmpver} < 3 ) {
push(@{ $template->{ macros } }, { macro => '{$SNMP_COMMUNITY}', value => $opts->{ snmpcomm } }); push(
} elsif($opts->{ snmpver } == 3) { @{ $template->{macros} },
push(@{ $template->{ macros } }, { macro => '{$SNMP_USER}', value => $opts->{ v3user } }); { macro => '{$SNMP_COMMUNITY}', value => $opts->{snmpcomm} }
push(@{ $template->{ macros } }, { macro => '{$SNMP_CONTEXT}', value => $opts->{ v3context } }); );
push(@{ $template->{ macros } }, { macro => '{$SNMP_AUTHPASS}', value => $opts->{ v3auth_pass } }); }
push(@{ $template->{ macros } }, { macro => '{$SNMP_PRIVPASS}', value => $opts->{ v3sec_pass } }); elsif ( $opts->{snmpver} == 3 ) {
}; push(
@{ $template->{macros} },
{ macro => '{$SNMP_USER}', value => $opts->{v3user} }
);
push(
@{ $template->{macros} },
{ macro => '{$SNMP_CONTEXT}', value => $opts->{v3context} }
);
push(
@{ $template->{macros} },
{ macro => '{$SNMP_AUTHPASS}', value => $opts->{v3auth_pass} }
);
push(
@{ $template->{macros} },
{ macro => '{$SNMP_PRIVPASS}', value => $opts->{v3sec_pass} }
);
}
build_template( $template, $oid_root, 0 ); build_template( $template, $oid_root, 0 );
# Convert applications hash to array # Convert applications hash to array
@{ $template->{ applications } } = map { { name => $_ } } keys %{ $template->{ apptags } }; @{ $template->{applications} } =
map { { name => $_ } } keys %{ $template->{apptags} };
delete( $template->{apptags} ); delete( $template->{apptags} );
# Build XML document # Build XML document
my $time = time(); my $time = time();
my $output = {
# version3
my $output_v3 = {
version => '3.0', version => '3.0',
date => time2str( "%Y-%m-%dT%H:%M:%SZ", $time ), date => time2str( "%Y-%m-%dT%H:%M:%SZ", $time ),
groups => $template->{groups}, groups => $template->{groups},
@ -881,6 +1070,16 @@ if (!$oid_root || $oid_root->{ objectID } ne $opts->{ oid }) {
value_maps => [$valuemaps] value_maps => [$valuemaps]
}; };
# version 2
my $output_v2 = {
version => '2.0',
date => time2str( "%Y-%m-%dT%H:%M:%SZ", $time ),
groups => $template->{groups},
templates => [$template],
triggers => [],
graphs => []
};
# Output stream # Output stream
my $fh = *STDOUT; my $fh = *STDOUT;
if ( $opts->{filename} ) { if ( $opts->{filename} ) {
@ -888,7 +1087,11 @@ if (!$oid_root || $oid_root->{ objectID } ne $opts->{ oid }) {
} }
# Output XML # Output XML
XMLout($output,
if ( $opts->{zabbix_ver} == 3 ) {
XMLout(
$output_v3,
OutputFile => \$fh, OutputFile => \$fh,
XMLDecl => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", XMLDecl => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
RootName => 'zabbix_export', RootName => 'zabbix_export',
@ -909,6 +1112,29 @@ if (!$oid_root || $oid_root->{ objectID } ne $opts->{ oid }) {
'mappings' => 'mapping' 'mappings' => 'mapping'
} }
); );
}
else {
XMLout(
$output_v2,
OutputFile => \$fh,
XMLDecl => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
RootName => 'zabbix_export',
NoAttr => 1,
SuppressEmpty => undef,
GroupTags => {
'applications' => 'application',
'groups' => 'group',
'templates' => 'template',
'items' => 'item',
'macros' => 'macro',
'discovery_rules' => 'discovery_rule',
'item_prototypes' => 'item_prototype',
'trigger_prototypes' => 'trigger_prototype',
'graph_prototypes' => 'graph_prototype',
'host_prototypes' => 'host_prototype'
}
);
}
if ( $opts->{filename} ) { if ( $opts->{filename} ) {
close $fh; close $fh;