Initial commit
This commit is contained in:
commit
6d24aad64b
2 changed files with 1060 additions and 0 deletions
62
README.md
Normal file
62
README.md
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# mib2zabbix
|
||||
|
||||
This Perl script will generate a Zabbix Template in XML format for an OID tree
|
||||
in an SNMP MIB file.
|
||||
|
||||
### Usage
|
||||
|
||||
mib2zabbix.pl [ -l | -t ] -o <OID> [OPTIONS]...
|
||||
|
||||
Export loaded SNMP MIB OIDs to Zabbix Template XML
|
||||
|
||||
--export-maps export value maps directly to Zabbix database
|
||||
|
||||
-t, --template generate a Zabbix template
|
||||
-f, --filename=PATH output filename (default: stdout)
|
||||
|
||||
-N, --name=STRING template name (default: OID label)
|
||||
-G, --group=STRING template group (default: 'Templates')
|
||||
-e, --enable-items enable template items (default: disabled)
|
||||
* enable with caution *
|
||||
|
||||
-o, --oid=STRING OID tree root to export
|
||||
|
||||
-v, --snmpver=1|2|3 SNMP version (default: 1)
|
||||
-p, --port=PORT SNMP UDP port number (default: 161)
|
||||
|
||||
SNMP Version 1 or 2c specific
|
||||
|
||||
-c, --community=STRING SNMP community string (default: 'public')
|
||||
|
||||
SNMP Version 3 specific
|
||||
|
||||
-L, --level=LEVEL security level (noAuthNoPriv|authNoPriv|authPriv)
|
||||
-n, --context=CONTEXT context name
|
||||
-u, --username=USERNAME security name
|
||||
-a, --auth=PROTOCOL authentication protocol (MD5|SHA)
|
||||
-A, --authpass=PASSPHRASE authentication protocol passphrase
|
||||
-x, --privacy=PROTOCOL privacy protocol (DES|AES)
|
||||
-X, --privpass=PASSPHRASE privacy passphrase
|
||||
|
||||
--check-delay=SECONDS check interval in seconds (default: 300)
|
||||
--disc-delay=SECONDS discovery interval in seconds (default: 86400)
|
||||
--history=DAYS history retention in days (default: 365)
|
||||
--trends=DAYS trends retention in days (default: 3650)
|
||||
|
||||
-h, --help print this message
|
||||
|
||||
### Requirements
|
||||
|
||||
* Perl v5+
|
||||
* Pod::Usage
|
||||
* XML::Simple
|
||||
* Net-SNMP
|
||||
* Correctly configured [MIB files](http://net-snmp.sourceforge.net/tutorial/tutorial-5/commands/mib-options.html)
|
||||
|
||||
### Translations
|
||||
|
||||
* Scalar OID -> Zabbix SNMP Item
|
||||
* Table OID -> Zabbix SNMP Discovery Rule
|
||||
* Table Column OID -> Zabbix Discovery Prototype
|
||||
* Trap/Notification OID -> Zabbix SNMP Trap Item
|
||||
* OID Enums -> Zabbix Value Map
|
||||
998
mib2zabbix.pl
Executable file
998
mib2zabbix.pl
Executable file
|
|
@ -0,0 +1,998 @@
|
|||
#!/usr/bin/perl -w
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
mib2zabbix.pl - SNMP MIB to Zabbix Template
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
mib2zabbix.pl [ -l | -t ] -o <OID> [OPTIONS]...
|
||||
|
||||
Export loaded SNMP MIB OIDs to Zabbix Template XML
|
||||
|
||||
--export-maps export value maps directly to Zabbix database
|
||||
|
||||
-t, --template generate a Zabbix template
|
||||
-f, --filename=PATH output filename (default: stdout)
|
||||
|
||||
-N, --name=STRING template name (default: OID label)
|
||||
-G, --group=STRING template group (default: 'Templates')
|
||||
-e, --enable-items enable template items (default: disabled)
|
||||
* enable with caution *
|
||||
|
||||
-o, --oid=STRING OID tree root to export
|
||||
|
||||
-v, --snmpver=1|2|3 SNMP version (default: 1)
|
||||
-p, --port=PORT SNMP UDP port number (default: 161)
|
||||
|
||||
SNMP Version 1 or 2c specific
|
||||
|
||||
-c, --community=STRING SNMP community string (default: 'public')
|
||||
|
||||
SNMP Version 3 specific
|
||||
|
||||
-L, --level=LEVEL security level (noAuthNoPriv|authNoPriv|authPriv)
|
||||
-n, --context=CONTEXT context name
|
||||
-u, --username=USERNAME security name
|
||||
-a, --auth=PROTOCOL authentication protocol (MD5|SHA)
|
||||
-A, --authpass=PASSPHRASE authentication protocol passphrase
|
||||
-x, --privacy=PROTOCOL privacy protocol (DES|AES)
|
||||
-X, --privpass=PASSPHRASE privacy passphrase
|
||||
|
||||
--check-delay=SECONDS check interval in seconds (default: 300)
|
||||
--disc-delay=SECONDS discovery interval in seconds (default: 86400)
|
||||
--history=DAYS history retention in days (default: 365)
|
||||
--trends=DAYS trends retention in days (default: 3650)
|
||||
|
||||
-h, --help print this message
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
B<mib2zabbix.pl> will export a loaded MIB tree into a Zabbix Template starting
|
||||
from the OID root specified.
|
||||
|
||||
Requires: Perl v5+, Pod::Usage, XML::Simple, Net-SNMP
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
Value mappings query:
|
||||
SELECT * FROM valuemaps JOIN mappings ON mappings.valuemapid = valuemaps.valuemapid
|
||||
|
||||
Delete Value Maps and linked mappings:
|
||||
DELETE FROM valuemaps USING mappings WHERE mappings.valuemapid = valuemaps.valuemapid AND valuemaps.name LIKE 'SEARCH%';
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
- Table indexes not found in iso.org.dod.internet.mgmt.mib-2.host (.1.3.6.1.2.1.25)
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Ryan Armstrong <ryan@cavaliercoder.com>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Guidelines for Authors and Reviewers of MIB Documents
|
||||
https://www.ietf.org/rfc/rfc4181.txt
|
||||
|
||||
Next Generation Structure of Management Information (SMIng) Mappings to the Simple Network Management Protocol (SNMP)
|
||||
https://tools.ietf.org/html/rfc3781
|
||||
|
||||
SNMP Table Basics
|
||||
http://www.webnms.com/snmp/help/snmpapi/snmpv3/table_handling/snmptables_basics.html
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Cwd 'abs_path';
|
||||
use Data::Dumper;
|
||||
use Date::Format;
|
||||
use DBI;
|
||||
use Encode qw(decode encode);
|
||||
use File::Basename;
|
||||
use Pod::Usage;
|
||||
use Getopt::Long;
|
||||
use SNMP;
|
||||
use XML::Simple;
|
||||
|
||||
# Get path info as constants
|
||||
use constant SCRIPT_NAME => basename($0);
|
||||
use constant BASE_PATH => dirname(abs_path($0));
|
||||
|
||||
use constant ZBX_SERVER_CONF => '/etc/zabbix/zabbix_server.conf';
|
||||
use constant ZBX_WEB_CONF => '/usr/share/zabbix/conf/zabbix.conf.php';
|
||||
|
||||
# For Zabbix type constants see:
|
||||
# https://www.zabbix.com/documentation/2.2/manual/api/reference/item/object
|
||||
# Zabbix Item status
|
||||
use constant ZBX_ITEM_ENABLED => 0;
|
||||
use constant ZBX_ITEM_DISABLED => 1;
|
||||
|
||||
# Zabbix Item Type IDs
|
||||
use constant ZBX_ITEM_TYPE_SNMPV1 => 1;
|
||||
use constant ZBX_ITEM_TYPE_SNMPV2 => 4;
|
||||
use constant ZBX_ITEM_TYPE_SNMPV3 => 6;
|
||||
use constant ZBX_ITEM_TYPE_SNMPTRAP => 17;
|
||||
|
||||
# Zabbix Item Value Type IDs
|
||||
use constant ZBX_VAL_TYPE_FLOAT => 0;
|
||||
use constant ZBX_VAL_TYPE_CHAR => 1;
|
||||
use constant ZBX_VAL_TYPE_LOG => 2;
|
||||
use constant ZBX_VAL_TYPE_UINT => 3;
|
||||
use constant ZBX_VAL_TYPE_TEXT => 4;
|
||||
|
||||
# Zabbix Item Storage types (delta)
|
||||
use constant ZBX_ITEM_STORE_ASIS => 0; # Store value as is
|
||||
use constant ZBX_ITEM_STORE_SPEED => 1; # Delta, speed per second
|
||||
use constant ZBX_ITEM_STORE_CHANGE => 2; # Delta, simple change
|
||||
|
||||
# Zabbix Item SNMPv3 constants
|
||||
use constant ZBX_V3_PRIV_DES => 0;
|
||||
use constant ZBX_V3_PRIV_AES => 1;
|
||||
use constant ZBX_V3_AUTH_MD5 => 0;
|
||||
use constant ZBX_V3_AUTH_SHA => 1;
|
||||
use constant ZBX_V3_SEC_NOAUTHNOPRIV => 0;
|
||||
use constant ZBX_V3_SEC_AUTHNOPRIV => 1;
|
||||
use constant ZBX_V3_SEC_AUTHPRIV => 2;
|
||||
|
||||
# SNMP Type -> Zabbix type mapping
|
||||
my $type_map = {
|
||||
BITS => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type
|
||||
COUNTER => ZBX_VAL_TYPE_UINT, # Zabbix 'Numeric Unsigned' value type for an unsigned integer
|
||||
COUNTER32 => ZBX_VAL_TYPE_UINT, # Zabbix 'Numeric Unsigned' value type for an unsigned integer
|
||||
COUNTER64 => ZBX_VAL_TYPE_UINT, # Zabbix 'Numeric Unsigned' value type for an unsigned 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
|
||||
NETADDDR => ZBX_VAL_TYPE_TEXT, # Zabbix 'Text' value type for a network address
|
||||
NOTIF => 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
|
||||
OCTETSTR => 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
|
||||
UNSIGNED32 => ZBX_VAL_TYPE_UINT # Zabbix 'Numeric Unsigned' value type for an unsigned 32bit integer
|
||||
};
|
||||
|
||||
# SNMP Version -> Zabbix item type mapping
|
||||
my $snmpver_map = {
|
||||
1 => ZBX_ITEM_TYPE_SNMPV1, # Zabbix SNMPv1 Agent type for SNMPv1
|
||||
2 => ZBX_ITEM_TYPE_SNMPV2, # Zabbix SNMPv2 Agent type for SNMPv2
|
||||
3 => ZBX_ITEM_TYPE_SNMPV3 # Zabbix SNMPv3 Agent type for SNMPv3
|
||||
};
|
||||
|
||||
# SNMP Auth config -> Zabbix item auth config
|
||||
my $snmpv3_auth_level_map = {
|
||||
'noAuthNoPriv' => ZBX_V3_SEC_NOAUTHNOPRIV,
|
||||
'authNoPriv' => ZBX_V3_SEC_AUTHNOPRIV,
|
||||
'authPriv' => ZBX_V3_SEC_AUTHPRIV
|
||||
};
|
||||
|
||||
my $snmpv3_auth_protocol_map = {
|
||||
'md5' => ZBX_V3_AUTH_MD5,
|
||||
'sha' => ZBX_V3_AUTH_SHA
|
||||
};
|
||||
|
||||
my $snmpv3_sec_protocol_map = {
|
||||
'des' => ZBX_V3_PRIV_DES,
|
||||
'aes' => ZBX_V3_PRIV_AES
|
||||
};
|
||||
|
||||
# Default command line options
|
||||
my $opts = {
|
||||
delay => 300, # 5 minute check interval
|
||||
disc_delay => 3600, # Hourly discovery
|
||||
enableitems => 0, # Disable items
|
||||
group => 'Templates',
|
||||
history => 365,
|
||||
trends => 3650,
|
||||
list => 0,
|
||||
maxdepth => -1,
|
||||
oid => '.1',
|
||||
use_macros => 0,
|
||||
snmpcomm => 'public',
|
||||
snmpport => 161,
|
||||
snmpver => 1,
|
||||
v3auth_level => 'noAuthNoPriv',
|
||||
v3context => '',
|
||||
v3user => '',
|
||||
v3auth_protocol => 'md5',
|
||||
v3auth_pass => '',
|
||||
v3sec_protocol => 'des',
|
||||
v3sec_pass => ''
|
||||
};
|
||||
|
||||
# Get command line options
|
||||
Getopt::Long::Configure ("posix_default", "bundling");
|
||||
GetOptions(
|
||||
'export-maps' => \$opts->{ export_maps }, # Export value maps to database
|
||||
|
||||
't|template' => \$opts->{ template }, # Output an XML Template
|
||||
'f|filename=s' => \$opts->{ filename }, # Filename to output
|
||||
|
||||
'N|name=s' => \$opts->{ name }, # Template name
|
||||
'G|group=s' => \$opts->{ group }, # Template group
|
||||
'o|oid=s' => \$opts->{ oid }, # Root OID to export
|
||||
|
||||
'e|enable-items' => \$opts->{ enableitems }, # Enable template items
|
||||
|
||||
'v|snmpver=i' => \$opts->{ snmpver }, # SNMP Version
|
||||
'p|port=i' => \$opts->{ snmpport }, # SNMP Port
|
||||
|
||||
'c|community=s' => \$opts->{ snmpcomm }, # SNMP Community string
|
||||
|
||||
'L|level=s' => \$opts->{ v3auth_level }, # SNMPv3 Authentication level
|
||||
'n|context=s' => \$opts->{ v3context }, # SNMPv3 Security Context
|
||||
'u|username=s' => \$opts->{ v3user }, # SNMPv3 Authentication username
|
||||
'a|auth=s' => \$opts->{ v3auth_protocol }, # SNMPv3 Authentication protocol
|
||||
'A|authpass=s' => \$opts->{ v3auth_pass }, # SNMPv3 Authentication passphrase
|
||||
'x|privacy=s' => \$opts->{ v3sec_protocol }, # SNMPv3 Privacy protocol
|
||||
'X|privpass=s' => \$opts->{ v2sec_pass}, # SNMPv3 Privacy passphrase
|
||||
|
||||
'check-delay=i' => \$opts->{ delay }, # Update interval in seconds
|
||||
'disc-delay=i' => \$opts->{ disc_delay }, # Update interval in seconds
|
||||
'history=i' => \$opts->{ history }, # History retention in days
|
||||
'trends=i' => \$opts->{ trends }, # Trends retention in days
|
||||
|
||||
'h|help' => \$opts->{ help }
|
||||
) || pod2usage();
|
||||
|
||||
# Print usage if requested
|
||||
pod2usage({ -exitval => 0 }) if ($opts->{ help });
|
||||
|
||||
# Validate SNMPv3 settings
|
||||
if ($opts->{ snmpver } == 3) {
|
||||
$opts->{ snmpcomm } = '';
|
||||
|
||||
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 } };
|
||||
|
||||
die("Unknown authentical protocol '$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 } });
|
||||
$opts->{ v3sec_protocol } = $snmpv3_sec_protocol_map->{ $opts->{ v3sec_protocol } };
|
||||
}
|
||||
|
||||
# Base template for Template Items, Discovery Rules and Item Prototypes
|
||||
# See: https://www.zabbix.com/documentation/2.2/manual/api/reference/item/object
|
||||
my %item_base_template = (
|
||||
allowed_hosts => '',
|
||||
applications => [],
|
||||
authtype => '0',
|
||||
delay_flex => '',
|
||||
ipmi_sensor => '',
|
||||
params => '',
|
||||
password => '',
|
||||
port => '{$SNMP_PORT}', # Use macro for SNMP UDP Port
|
||||
privatekey => '',
|
||||
publickey => '',
|
||||
snmp_community => $opts->{ snmpver } < 3 ? '{$SNMP_COMM}' : '', # Use macro for SNMP Community string
|
||||
snmpv3_authpassphrase => $opts->{ snmpver } == 3 ? '{$SNMP_AUTHPASS}' : '', # 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_securityname => $opts->{ snmpver } == 3 ? '{$SNMP_USER}' : '', # Use macro for SNMPv3 Username
|
||||
status => ($opts->{ enableitems } ? ZBX_ITEM_ENABLED : ZBX_ITEM_DISABLED), # Enabled (0) | Disabled (1)
|
||||
username => '',
|
||||
);
|
||||
|
||||
# Item template for standard Template items
|
||||
my %item_template = (
|
||||
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
|
||||
units => '',
|
||||
valuemap => ''
|
||||
);
|
||||
%item_template = (%item_base_template, %item_template);
|
||||
|
||||
# Discovery rule template
|
||||
my %disc_rule_template = (
|
||||
delay => $opts->{ disc_delay },
|
||||
filter => ':',
|
||||
lifetime => '30',
|
||||
|
||||
# The following items must be created as unique refs for each item
|
||||
host_prototypes => [],
|
||||
item_prototypes => [],
|
||||
graph_prototypes => [],
|
||||
trigger_prototypes => []
|
||||
);
|
||||
%disc_rule_template = (%item_base_template, %disc_rule_template);
|
||||
|
||||
# SNMP Trap template
|
||||
my %trap_template = (
|
||||
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,
|
||||
valuemap => ''
|
||||
);
|
||||
|
||||
# Item prototype template
|
||||
my %item_proto_template = ();
|
||||
%item_proto_template = (%item_template, %item_proto_template);
|
||||
|
||||
# Global value maps array
|
||||
my $valuemaps = {};
|
||||
|
||||
=head2 utf8_santize
|
||||
|
||||
Parameters : (string) $malformed_utf8
|
||||
Returns : (string) $wellformed_utf8
|
||||
Description : Returns a sanitized UTF8 string, removing incompatable characters
|
||||
|
||||
=cut
|
||||
sub utf8_sanitize {
|
||||
my ($malformed_utf8) = @_;
|
||||
|
||||
my $octets = decode('UTF-8', $malformed_utf8, Encode::FB_DEFAULT);
|
||||
return encode('UTF-8', $octets, Encode::FB_CROAK);
|
||||
}
|
||||
|
||||
=head2 oid_path
|
||||
|
||||
Parameters : SNMP::MIB::Node $oid
|
||||
Returns : (String) $oid_path
|
||||
Description : Returns the fully qualified textual path of a MIB node by
|
||||
traversing the node's parents.
|
||||
|
||||
=cut
|
||||
sub oid_path {
|
||||
my ($oid) = @_;
|
||||
|
||||
my $path = $oid->{ label };
|
||||
my $node = $oid;
|
||||
while ($node = $node->{ parent }) {
|
||||
$path = "$node->{ label }.$path";
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
=head2 get_child_by_label
|
||||
|
||||
Parameters : SNMP::MIB::Node $node
|
||||
(string) $child_label
|
||||
Returns : SNMP::MIB::NODE $child
|
||||
Description : Returns the direct descendant OID of the specified OID
|
||||
if the label name matches, otherwise undef.
|
||||
|
||||
=cut
|
||||
sub get_child_by_label {
|
||||
my ($node, $child_label) = @_;
|
||||
|
||||
if ($node && $child_label) {
|
||||
foreach my $child(@{ $node->{ children } }) {
|
||||
if ($child->{ label } eq $child_label) {
|
||||
return $child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
=head2 node_to_item
|
||||
|
||||
Parameters : SNMP::MIB::Node $node
|
||||
(Hash) $template
|
||||
Returns : (Hash) $item
|
||||
Description : Returns a Zabbix Item hash derived from the specified MIB OID
|
||||
|
||||
=cut
|
||||
sub node_to_item {
|
||||
my ($node, $template) = @_;
|
||||
$template = $template || \%item_template;
|
||||
|
||||
# Create item hash
|
||||
my $item = { %{ $template } };
|
||||
|
||||
$item->{ name } = $node->{ label };
|
||||
$item->{ snmp_oid } = $node->{ objectID };
|
||||
if ($node->{ units }) {
|
||||
$item->{ units } = $node->{ units };
|
||||
}
|
||||
|
||||
# Merge in item defaults
|
||||
%{ $item } = (%{ $template }, %{ $item } );
|
||||
|
||||
# Create SNMP Agent item
|
||||
$item->{ type } = $snmpver_map->{ $opts->{ snmpver } };
|
||||
|
||||
# Item key
|
||||
$item->{ key } = "$node->{ moduleID }.$node->{ label }";
|
||||
|
||||
# Map value type (Ignore for OID Table Entry Rows)
|
||||
if ($node->{ type }) {
|
||||
$item->{ value_type } = $type_map->{ $node->{ type } };
|
||||
if (!defined($item->{ value_type })) {
|
||||
print STDERR "No type mapping found for type $node->{ type } in $node->{ objectID }\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Set storage type
|
||||
if ( $node->{ type } ~~ ['COUNTER', 'COUNTER32', 'COUNTER64']) {
|
||||
$item->{ delta } = ZBX_ITEM_STORE_SPEED;
|
||||
$item->{ units } .= '/sec';
|
||||
}
|
||||
|
||||
# Translate SNMP Ticks
|
||||
if ($node->{ type } eq 'TICKS') {
|
||||
$item->{ multiplier } = '1';
|
||||
$item->{ formula } = '.01';
|
||||
$item->{ units } = 'uptime';
|
||||
}
|
||||
|
||||
# Parse item desciption
|
||||
$item->{ description } = utf8_sanitize($node->{ description });
|
||||
if ($item->{ description }) {
|
||||
$item->{ description } =~ s/^\s+|\s+$|\n//g; # Trim left/right whitespace and newlines
|
||||
$item->{ description } =~ s/\s{2,}/ /g; # Remove padding
|
||||
}
|
||||
|
||||
# Process value maps
|
||||
if (scalar keys % {$node->{ enums } }) {
|
||||
# Add valuemap to global list
|
||||
my $map_name = "$node->{ moduleID }::$node->{ label }";
|
||||
|
||||
# If the map_name is longer than 64 characters...
|
||||
if ( length($map_name) > 64 ) {
|
||||
|
||||
# Truncate to 64 characters to match maximum database field length.
|
||||
$map_name = substr($map_name,0,61) . "...";
|
||||
|
||||
}
|
||||
|
||||
|
||||
$valuemaps->{ $map_name } = $node->{ enums };
|
||||
|
||||
# Assign value map to template
|
||||
$item->{ valuemap } = { name => $map_name };
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
=head2 node_to_trapitem
|
||||
|
||||
Parameters : SNMP::MIB::Node $node
|
||||
(Hash) $template
|
||||
Returns : (Hash) $item
|
||||
Description : Returns a Zabbix SNMP Trap Item hash derived from the
|
||||
specified MIB OID
|
||||
|
||||
=cut
|
||||
sub node_to_trapitem {
|
||||
my ($node, $template) = @_;
|
||||
$template = $template || \%trap_template;
|
||||
|
||||
# Create item hash
|
||||
my $item = { %{ $template } };
|
||||
|
||||
$item->{ name } = "SNMP Trap: $node->{ moduleID }::$node->{ label }";
|
||||
|
||||
# Merge in item defaults
|
||||
%{ $item } = (%{ $template }, %{ $item } );
|
||||
|
||||
# Create trap key
|
||||
my $oid = $node->{ objectID };
|
||||
$oid =~ s/\./\\./g;
|
||||
$item->{ key } = "snmptrap[\"\\s$oid\\s\"]";
|
||||
|
||||
# Parse item desciption
|
||||
my $desc = '';
|
||||
if ($node->{ description }) {
|
||||
$desc = $node->{ description };
|
||||
$desc =~ s/^\s+|\s+$|\n//g; # Trim left/right whitespace and newlines
|
||||
$desc =~ s/\s{2,}/ /g; # Remove padding
|
||||
}
|
||||
|
||||
# Append varbinds to description
|
||||
if (defined($node->{ varbinds }) && scalar @{ $node->{ varbinds } }) {
|
||||
my $varcount = scalar @{ $node->{ varbinds } };
|
||||
|
||||
if ($desc ne '') {
|
||||
$desc .= "\n\n";
|
||||
}
|
||||
|
||||
$desc .= "Varbinds:\n";
|
||||
|
||||
for(my $i = 0; $i < $varcount; $i++) {
|
||||
my $varbind_label = $node->{ varbinds }[$i];
|
||||
$desc .= "$i. $varbind_label";
|
||||
|
||||
# Try to find OID for each varbind
|
||||
my $varbind_path = "$node->{ moduleID }::$varbind_label";
|
||||
my $varbind = $SNMP::MIB{ $varbind_path };
|
||||
|
||||
if (defined($varbind)) {
|
||||
$desc .= " ($varbind->{ type })\n" if $varbind->{ type };
|
||||
|
||||
if ($varbind->{ description }) {
|
||||
my $vbdesc = $varbind->{ description };
|
||||
$vbdesc =~ s/[ \t]+/ /g; # Replace long whitespace with single space
|
||||
$vbdesc =~ s/^ ?/ /mg; # Prepend indent to each description line
|
||||
$desc .= "$vbdesc\n\n";
|
||||
}
|
||||
} else {
|
||||
$desc .= "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
$item->{ description } = $desc;
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
=head2 node_is_current
|
||||
|
||||
Parameters : SNMP::MIB::Node $node
|
||||
Returns : (int) 0|1
|
||||
Description : Returns true if the specified OID is not obsolete
|
||||
|
||||
=cut
|
||||
sub node_is_current {
|
||||
my ($node) = @_;
|
||||
|
||||
return (
|
||||
node_is_valid_trap($node)
|
||||
|| (defined($node->{ status }) && $node->{ status } ne 'Obsolete')
|
||||
);
|
||||
}
|
||||
|
||||
=head2 node_is_valid_scalar
|
||||
|
||||
Parameters : SNMP::MIB::Node $node
|
||||
Returns : (int) 0|1
|
||||
Description : Returns true if the specified OID is current, readable and
|
||||
defines a valid value type.
|
||||
|
||||
=cut
|
||||
sub node_is_valid_scalar {
|
||||
my ($node) = @_;
|
||||
|
||||
return (
|
||||
node_is_current($node)
|
||||
&& $node->{ type }
|
||||
&& (
|
||||
$node->{ type } eq 'NOTIF' || $node->{ type } eq 'TRAP'
|
||||
|| ($node->{ access } eq 'ReadOnly' || $node->{ access } eq 'ReadWrite')
|
||||
)
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
=head2 node_is_valid_trap
|
||||
|
||||
Parameters : SNMP::MIB::Node $node
|
||||
Returns : (int) 0|1
|
||||
Description : Returns true if the specified OID is an SNMP Trap
|
||||
|
||||
=cut
|
||||
sub node_is_valid_trap {
|
||||
my ($node) = @_;
|
||||
|
||||
return (
|
||||
defined($node->{ type }) && ($node->{ type } eq 'NOTIF' || $node->{ type } eq 'TRAP')
|
||||
);
|
||||
}
|
||||
|
||||
=head2 node_is_valid_table
|
||||
|
||||
Parameters : SNMP::MIB::Node $node
|
||||
Returns : (int) 0|1
|
||||
Description : Returns true if the specified OID is a valid table which is
|
||||
current, readable and contains a single child (row
|
||||
definition)
|
||||
|
||||
=cut
|
||||
sub node_is_valid_table {
|
||||
my ($node) = @_;
|
||||
|
||||
# The MIB will define a 'SEQUENCE OF' attribute for tables but
|
||||
# SNMP::MIB::NODE does not expose this value. Instead, a table
|
||||
# node must be 'NoAccess' and have a single 'NoAccess' child
|
||||
return (
|
||||
node_is_current($node)
|
||||
|
||||
# Table is NoAccess
|
||||
&& $node->{ access } eq 'NoAccess'
|
||||
|
||||
# Table has one child (row definition)
|
||||
&& (scalar @{ $node->{ children } }) == 1
|
||||
|
||||
# Table row is NoAccess
|
||||
&& $node->{ children }[0]->{ access } eq 'NoAccess'
|
||||
|
||||
# Table row defines atleast one index
|
||||
&& (scalar @{ $node->{ children }[0]->{ indexes } })
|
||||
);
|
||||
}
|
||||
|
||||
=head2 build_template
|
||||
|
||||
Parameters : (hash) $template
|
||||
SNMP::MIB::NODE $node
|
||||
Returns : (void)
|
||||
Description : Traverses a loaded MIB tree from the specified OID node
|
||||
a populates a Zabbix Template hash with items, discovery
|
||||
rules, item prototypes, groups and macros.
|
||||
|
||||
=cut
|
||||
sub build_template {
|
||||
my ($template, $node) = @_;
|
||||
|
||||
# Ignore obsolete OIDs
|
||||
if (node_is_current($node)) {
|
||||
# Create an Item Application name for this node
|
||||
my $appname = "$node->{ moduleID }::$node->{ parent }->{ label }";
|
||||
|
||||
# Is this a scalar value OID?
|
||||
if (node_is_valid_trap($node)) {
|
||||
# Convert the SNMP::MIB::Node to a Zabbix Template SNMP Trap Item
|
||||
my $item = node_to_trapitem($node);
|
||||
|
||||
# Add item applications to template application list
|
||||
$item->{ applications } = [{ name => $appname }];
|
||||
$template->{ apptags }->{ $appname } = 1;
|
||||
|
||||
# Add item to template
|
||||
push(@{ $template->{ items } }, $item );
|
||||
|
||||
# If the snmptrap has children.
|
||||
foreach(@{ $node->{ children } }) {
|
||||
|
||||
# Convert the SNMP::MIB::Node to a Zabbix Template SNMP Trap Item
|
||||
my $item = node_to_trapitem($_);
|
||||
|
||||
# Add item applications to template application list
|
||||
$item->{ applications } = [{ name => $appname }];
|
||||
$template->{ apptags }->{ $appname } = 1;
|
||||
|
||||
# Add item to template
|
||||
push(@{ $template->{ items } }, $item );
|
||||
|
||||
}
|
||||
|
||||
} elsif (node_is_valid_scalar($node)) {
|
||||
|
||||
# Convert the SNMP::MIB::Node to a Zabbix Template Item hash
|
||||
my $item = node_to_item($node);
|
||||
|
||||
# Append '.0' to normal SNMP OIDS
|
||||
$item->{ snmp_oid } = "$item->{ snmp_oid }.0";
|
||||
|
||||
# Add item applications to template application list
|
||||
$item->{ applications } = [{ name => $appname }];
|
||||
$template->{ apptags }->{ $appname } = 1;
|
||||
|
||||
# Add item to template
|
||||
push(@{ $template->{ items } }, $item );
|
||||
|
||||
} elsif (node_is_valid_table($node)) {
|
||||
# Get row OID
|
||||
my $table = $node;
|
||||
my $row = $node->{ children }[0];
|
||||
|
||||
# Validate naming standard
|
||||
if ($table->{ label } !~ /Table/) {
|
||||
print STDERR "Warning: $table->{ moduleID }:: $table->{ label } appears to be a table but does not have the 'Table' suffix\n";
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
# This is a table. Build a discovery rule
|
||||
my $disc_rule = {};
|
||||
$disc_rule = node_to_item($row, \%disc_rule_template);
|
||||
|
||||
# Update fields
|
||||
$disc_rule->{ key } = "$table->{ moduleID }.$disc_rule->{ name }";
|
||||
$disc_rule->{ name } = "$disc_rule->{ name } Discovery";
|
||||
|
||||
# Fetch an arbitrary column OID for Zabbix to use for discovery
|
||||
my $index_oid = $row->{ children }[0];
|
||||
if (!defined($index_oid)) {
|
||||
print STDERR "No index found for table $table->{ moduleID}::$table->{ label } ($table->{ objectID })\n";
|
||||
} else {
|
||||
# Append key ID to path
|
||||
$disc_rule->{ snmp_oid } = "$index_oid->{ objectID }";
|
||||
|
||||
# Remove unrequired fields
|
||||
delete($disc_rule->{ applications });
|
||||
delete($disc_rule->{ data_type });
|
||||
|
||||
# Create new array for prototypes
|
||||
$disc_rule->{ item_prototypes } = [];
|
||||
|
||||
# Add prototypes for each row column
|
||||
foreach my $column(@{ $row->{ children } }) {
|
||||
if (node_is_valid_scalar($column)) {
|
||||
if (my $proto = node_to_item($column, \%item_proto_template)) {
|
||||
$proto->{ name } = "$proto->{ name }.{#SNMPINDEX}";
|
||||
$proto->{ key } = "$disc_rule->{ key }.$column->{ label }\[{#SNMPINDEX}]";
|
||||
$proto->{ snmp_oid } = "$proto->{ snmp_oid }.{#SNMPINDEX}";
|
||||
|
||||
# Add item applications to template application list
|
||||
$proto->{ applications } = [{ name => $appname }];
|
||||
$template->{ apptags }->{ $appname } = 1;
|
||||
|
||||
push(@{ $disc_rule->{ item_prototypes } }, $proto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Add discovery rule to template
|
||||
push(@{ $template->{ discovery_rules } }, $disc_rule);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# Parse children
|
||||
foreach(@{ $node->{ children } }) {
|
||||
build_template($template, $_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub export_valuemaps {
|
||||
my ($oid_root) = @_;
|
||||
|
||||
# Parse MIBs to get a hash of required value maps
|
||||
my $template = {};
|
||||
build_template($template, $oid_root, 0);
|
||||
|
||||
# Fetch DB connection details from Zabbix server config
|
||||
my ($dbhost, $dbname, $dbschema, $dbport, $dbuser, $dbpassword);
|
||||
$dbhost = 'localhost';
|
||||
$dbport = 5432;
|
||||
$dbname = 'zabbix';
|
||||
$dbpassword = '';
|
||||
if ( -e ZBX_SERVER_CONF ) {
|
||||
open (FH, "<${\ZBX_SERVER_CONF}") or die ("Failed to open ${\ZBX_SERVER_CONF} for reading.");
|
||||
while (my $line = <FH>) {
|
||||
my ($key, $val) = ($line =~ m/^(\w*)=(.*)/);
|
||||
if ($key ) {
|
||||
if ($key eq 'DBHost') {
|
||||
$dbhost = $val;
|
||||
} elsif ($key eq 'DBName') {
|
||||
$dbname = $val;
|
||||
} elsif ($key eq 'DBSchema') {
|
||||
$dbschema = $val;
|
||||
} elsif ($key eq 'DBPort') {
|
||||
$dbport = $val;
|
||||
} elsif ($key eq 'DBUser') {
|
||||
$dbuser = $val;
|
||||
} elsif ($key eq 'DBPassword') {
|
||||
$dbpassword = "$val";
|
||||
}
|
||||
}
|
||||
}
|
||||
close(FH);
|
||||
} elsif( -e ZBX_WEB_CONF ) {
|
||||
|
||||
} else {
|
||||
die("No database connection details could be obtained.");
|
||||
}
|
||||
|
||||
# Create a connection string and connect
|
||||
my $dsn = "DBI:Pg:host=$dbhost;dbname=$dbname;port=$dbport;";
|
||||
my $dbh = DBI->connect($dsn, $dbuser, $dbpassword);
|
||||
$dbh || die($DBI::errstr);
|
||||
|
||||
my $results;
|
||||
|
||||
# Get highest valuemap id
|
||||
$results = query($dbh, 'SELECT MAX(valuemapid) AS valuemapid FROM valuemaps');
|
||||
my $valuemapid = @{ $results }[0]->{ valuemapid };
|
||||
|
||||
# Get highest map id
|
||||
$results = query($dbh, 'SELECT MAX(mappingid) AS mappingid FROM mappings');
|
||||
my $mappingid = @{ $results }[0]->{ mappingid };
|
||||
|
||||
# Get existings valuemaps
|
||||
$results = query($dbh, 'SELECT * FROM valuemaps');
|
||||
my $existing_maps = {};
|
||||
foreach my $valuemap(@{ $results }) {
|
||||
$existing_maps->{ $valuemap->{ name } } = $valuemap->{ valuemapid };
|
||||
}
|
||||
|
||||
# Process each map
|
||||
foreach my $map_name (keys %{ $valuemaps }) {
|
||||
|
||||
if (defined($existing_maps->{ $map_name })) {
|
||||
print STDERR "Value map '$map_name' already exists\n";
|
||||
next;
|
||||
}
|
||||
|
||||
# Insert new map
|
||||
$valuemapid++;
|
||||
insert($dbh, "INSERT INTO valuemaps (valuemapid, name) VALUES ($valuemapid, '$map_name')");
|
||||
print ("Added Value Map '$map_name' with ID $valuemapid\n");
|
||||
|
||||
# Add mappings
|
||||
foreach my $key (keys %{ $valuemaps->{ $map_name } }) {
|
||||
my $val = $valuemaps->{ $map_name }->{ $key };
|
||||
|
||||
$mappingid++;
|
||||
insert($dbh, "INSERT INTO mappings (mappingid, valuemapid, value, newvalue) VALUES ($mappingid, $valuemapid, $val, '$key');");
|
||||
print (" - Added mapping '$key' => '$val' with ID $mappingid\n");
|
||||
}
|
||||
}
|
||||
|
||||
# Increment ids for nextid.
|
||||
$valuemapid++;
|
||||
$mappingid++;
|
||||
|
||||
# Now update the tables max ids in the ids table.
|
||||
insert($dbh, "UPDATE ids SET nextid = $valuemapid WHERE table_name = 'valuemaps' AND field_name = 'valuemapid';");
|
||||
insert($dbh, "UPDATE ids SET nextid = $mappingid WHERE table_name = 'mappings' AND field_name = 'mappingid';");
|
||||
|
||||
|
||||
|
||||
# Clean up
|
||||
$dbh->disconnect;
|
||||
}
|
||||
|
||||
sub query {
|
||||
my ($dbh, $sql) = @_;
|
||||
my @results;
|
||||
my $sth = $dbh->prepare($sql);
|
||||
$sth->execute();
|
||||
while(my $row = $sth->fetchrow_hashref()) {
|
||||
push @results, $row;
|
||||
}
|
||||
|
||||
return \@results;
|
||||
}
|
||||
|
||||
sub insert {
|
||||
my ($dbh, $sql) = @_;
|
||||
my $sth = $dbh->prepare($sql);
|
||||
$sth->execute();
|
||||
}
|
||||
|
||||
# Initialize net-snmp
|
||||
$SNMP::save_descriptions = 1;
|
||||
SNMP::initMib();
|
||||
|
||||
# Verify the specified OID exists
|
||||
my $oid_root = $SNMP::MIB{ $opts->{ oid } };
|
||||
if (!$oid_root || $oid_root->{ objectID } ne $opts->{ oid }) {
|
||||
print STDERR "OID $opts->{ oid } not found in MIB tree.\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Export value maps directly to Zabbix database
|
||||
if($opts->{ export_maps }) {
|
||||
export_valuemaps($oid_root);
|
||||
}
|
||||
|
||||
# Build a Zabbix template
|
||||
elsif ($opts->{ template }) {
|
||||
my $suffix = $opts->{ snmpver } > 2 ? " v$opts->{ snmpver }" : '';
|
||||
my $template_name = $opts->{ name } || "Template $oid_root->{ moduleID } - $oid_root->{ label }$suffix";
|
||||
my $template = {
|
||||
name => $template_name,
|
||||
template => $template_name,
|
||||
apptags => {},
|
||||
applications => [],
|
||||
discovery_rules => [],
|
||||
groups => [{
|
||||
name => $opts->{ group }
|
||||
}],
|
||||
items => [],
|
||||
macros => [
|
||||
{ macro => '{$OID}', value => "$oid_root->{ objectID }" },
|
||||
{ macro => '{$OID_PATH}', value => oid_path($oid_root) },
|
||||
{ macro => '{$OID_MOD}', value => $oid_root->{ moduleID } },
|
||||
{ macro => '{$SNMP_PORT}', value => $opts->{ snmpport } }
|
||||
]
|
||||
};
|
||||
|
||||
# Add SNMP connection macros
|
||||
if($opts->{ snmpver } < 3) {
|
||||
push(@{ $template->{ macros } }, { macro => '{$SNMP_COMM}', value => $opts->{ snmpcomm } });
|
||||
} 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);
|
||||
|
||||
# Convert applications hash to array
|
||||
@{ $template->{ applications } } = map { { name => $_ } } keys %{ $template->{ apptags } };
|
||||
delete($template->{ apptags });
|
||||
|
||||
# Build XML document
|
||||
my $time = time();
|
||||
my $output = {
|
||||
version => '2.0',
|
||||
date => time2str("%Y-%m-%dT%H:%M:%SZ", $time),
|
||||
groups => $template->{ groups },
|
||||
templates => [$template],
|
||||
triggers => [],
|
||||
graphs => []
|
||||
};
|
||||
|
||||
# Output stream
|
||||
my $fh = *STDOUT;
|
||||
if ($opts->{ filename }) {
|
||||
open($fh, ">$opts->{ filename }") or die "$!";
|
||||
}
|
||||
|
||||
# Output XML
|
||||
XMLout($output,
|
||||
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 }) {
|
||||
close $fh;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
pod2usage();
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue