| 1 | #!/usr/bin/perl |
|---|
| 2 | |
|---|
| 3 | use JSON; |
|---|
| 4 | use LWP::UserAgent; |
|---|
| 5 | use strict; |
|---|
| 6 | use warnings; |
|---|
| 7 | |
|---|
| 8 | my ($url, $user, $pass, $debug, $template_basename, $api_version, |
|---|
| 9 | $hostname_only, $snmpgroup, $templategroup); |
|---|
| 10 | my (%stats, %prog_api_version); |
|---|
| 11 | |
|---|
| 12 | #$url = "http://localhost/zabbix-1.8.1"; |
|---|
| 13 | #$url = "http://localhost/zabbix-1.8.2"; |
|---|
| 14 | #$url = "http://localhost/zabbix-10922"; |
|---|
| 15 | #$url = "http://localhost/zabbix-10994"; |
|---|
| 16 | $url=$ARGV[0]; |
|---|
| 17 | |
|---|
| 18 | $user = "admin"; |
|---|
| 19 | $pass = "zabbix"; |
|---|
| 20 | $snmpgroup = "SNMP Devices"; |
|---|
| 21 | $templategroup = "Templates"; # must exist |
|---|
| 22 | $hostname_only=1; |
|---|
| 23 | $debug = 2; # 0 none, 1 some debug prints, 2 includes json strings (multiline) |
|---|
| 24 | $template_basename = "Template_auto_"; |
|---|
| 25 | |
|---|
| 26 | # supported API versions |
|---|
| 27 | $prog_api_version{"1.1"}=1; # zabbix 1.8.1 |
|---|
| 28 | $prog_api_version{"1.2"}=1; # zabbix 1.8.2 |
|---|
| 29 | |
|---|
| 30 | sub snmp { # adds nonexistant items for host arg1 to template-id arg2 |
|---|
| 31 | my ($switch, $template_name) = @_; |
|---|
| 32 | my ($formula, $delay, $history, $trends, $priority, $community); |
|---|
| 33 | my (%oid, %delta, %description, %triggerDsc, %triggerXp); |
|---|
| 34 | |
|---|
| 35 | $delay=180; |
|---|
| 36 | $history=365; |
|---|
| 37 | $trends=730; |
|---|
| 38 | $formula=1; |
|---|
| 39 | $priority=4; |
|---|
| 40 | $community="public"; |
|---|
| 41 | |
|---|
| 42 | $delta{'ifOperStatus'}="1"; |
|---|
| 43 | $description{'ifOperStatus'}="Interface oper status on "; |
|---|
| 44 | |
|---|
| 45 | $delta{'ifSpeed'}="1"; |
|---|
| 46 | $description{'ifSpeed'}="Interface speed on "; |
|---|
| 47 | |
|---|
| 48 | $oid{'snVLanByPortStpTopChanges'}="1.3.6.1.4.1.1991.1.1.3.2.1.1.20"; |
|---|
| 49 | $delta{'snVLanByPortStpTopChanges'}="2"; |
|---|
| 50 | $description{'snVLanByPortStpTopChanges'}="STP Topology Change on VLAN #"; |
|---|
| 51 | |
|---|
| 52 | $delta{'ifInErrors'}="1"; |
|---|
| 53 | $description{'ifInErrors'}="Interface input errors on "; |
|---|
| 54 | |
|---|
| 55 | $delta{'ifOutErrors'}="1"; |
|---|
| 56 | $description{'ifOutErrors'}="Interface output errors on "; |
|---|
| 57 | |
|---|
| 58 | $delta{'ifInOctets'}="1"; |
|---|
| 59 | $description{'ifInOctets'}="Interface in octets on "; |
|---|
| 60 | |
|---|
| 61 | $delta{'ifOutOctets'}="1"; |
|---|
| 62 | $description{'ifOutOctets'}="Interface outoctets on "; |
|---|
| 63 | |
|---|
| 64 | $oid{'ifCollision'}="1.3.6.1.2.1.16.1.1.1.13"; |
|---|
| 65 | $delta{'ifCollision'}="2"; |
|---|
| 66 | $description{'ifCollision'}="Interface collisions on "; |
|---|
| 67 | |
|---|
| 68 | $triggerDsc{'ifOperStatus'}="ifOperStatus on {HOSTNAME} changed on if SUBME"; |
|---|
| 69 | $triggerXp{'ifOperStatus'}="{{HOSTNAME}:ifOperStatus.SUBME.diff(0)}=1"; |
|---|
| 70 | |
|---|
| 71 | $triggerDsc{'ifSpeed'}="ifSpeed on {HOSTNAME} changed on if SUBME"; |
|---|
| 72 | $triggerXp{'ifSpeed'}="{{HOSTNAME}:ifSpeed.SUBME.diff(0)}=1"; |
|---|
| 73 | |
|---|
| 74 | $triggerDsc{'ifInErrors'}="ifInErrors on {HOSTNAME} too high on if SUBME"; |
|---|
| 75 | $triggerXp{'ifInErrors'}="{{HOSTNAME}:ifInErrors.SUBME.diff(0)}=1"; |
|---|
| 76 | |
|---|
| 77 | $triggerDsc{'ifOutErrors'}="ifOutErrors on {HOSTNAME} too high on if SUBME"; |
|---|
| 78 | $triggerXp{'ifOutErrors'}="{{HOSTNAME}:ifOutErrors.SUBME.diff(0)}=1"; |
|---|
| 79 | |
|---|
| 80 | $triggerDsc{'snVLanByPortStpTopChanges'}="STP Topology Change occured on {HOSTNAME} on VLAN #SUBME"; |
|---|
| 81 | $triggerXp{'snVLanByPortStpTopChanges'}="{{HOSTNAME}:snVLanByPortStpTopChanges.SUBME.last(0)}>0"; |
|---|
| 82 | |
|---|
| 83 | $triggerDsc{'ifCollision'}="Too many collisions occured on {HOSTNAME} on if SUBME"; |
|---|
| 84 | $triggerXp{'ifCollision'}="{{HOSTNAME}:ifCollision.SUBME.last(0)}>10"; |
|---|
| 85 | |
|---|
| 86 | # END OF USERCONFIG - DON'T TOUCH BELOW HERE! |
|---|
| 87 | |
|---|
| 88 | # get configured item keys and trigger expressions, hash, optionally skip .oO |
|---|
| 89 | # or maybe just accept an json error on creation .... |
|---|
| 90 | $stats{Hosts}++; |
|---|
| 91 | my $template_id = gettemplateid($template_name); |
|---|
| 92 | my %keys; |
|---|
| 93 | my $items = apicall('Item.get', {hostids => $template_id, output => 'extend'}); |
|---|
| 94 | for my $i (0..$#$items){ |
|---|
| 95 | $keys{$items->[$i]->{key_}} = 1; |
|---|
| 96 | } |
|---|
| 97 | $stats{'Items preexisting'} += keys %keys; |
|---|
| 98 | print " read ".(keys %keys)." items from database\n" if $debug; |
|---|
| 99 | my ($key, $tree); |
|---|
| 100 | foreach $key (keys %description){ |
|---|
| 101 | $tree=$key; |
|---|
| 102 | if($oid{$tree}) { $tree = $oid{$tree}; } |
|---|
| 103 | foreach(`/usr/bin/snmpwalk -v1 -cpublic -On $switch $tree`){ |
|---|
| 104 | /\.(.*\.)(\d+)/; |
|---|
| 105 | my $oid=$1; |
|---|
| 106 | my $if=$2; |
|---|
| 107 | if ($keys{$key.".".$if}) { |
|---|
| 108 | $stats{'Items skipped'}++; |
|---|
| 109 | print " skipping preexisting item ($stats{'Items skipped'})\n" if $debug; |
|---|
| 110 | next; |
|---|
| 111 | } |
|---|
| 112 | print " create item desc: $description{$key}port $if delta: $delta{$key} oid: $oid$if\n" if $debug; |
|---|
| 113 | apicall('Item.create', {description => $description{$key}."port ".$if, |
|---|
| 114 | key_ => $key.".".$if, |
|---|
| 115 | hostid => $template_id, |
|---|
| 116 | delay => $delay, |
|---|
| 117 | history => $history, |
|---|
| 118 | trends => $trends, |
|---|
| 119 | status => 0, # default, active |
|---|
| 120 | type => 1, # SNMPv1 |
|---|
| 121 | snmp_community => $community, |
|---|
| 122 | snmp_oid => $oid.$if, |
|---|
| 123 | value_type => 3, # numeric unsigend |
|---|
| 124 | data_type => 0, # decimal, default |
|---|
| 125 | snmp_port => 161, |
|---|
| 126 | delta => 1 # 0=simple 1=delta/second 2=delta |
|---|
| 127 | }); |
|---|
| 128 | $stats{'Items created'}++; |
|---|
| 129 | if( !$triggerXp{$key} ){ next; } |
|---|
| 130 | my $desc=$triggerDsc{$key}; |
|---|
| 131 | my $xp=$triggerXp{$key}; |
|---|
| 132 | $desc =~ s/SUBME/$if/g; |
|---|
| 133 | $xp =~ s/SUBME/$if/g; |
|---|
| 134 | $xp =~ s/\{HOSTNAME\}/$template_name/g; |
|---|
| 135 | print " create trigger descc: $desc xpression: $xp\n" if $debug; |
|---|
| 136 | apicall('Trigger.create', { description => $desc, |
|---|
| 137 | expression => $xp, |
|---|
| 138 | type => 0, |
|---|
| 139 | priority => $priority, |
|---|
| 140 | status => 0, # 0=enabled, default=disabled |
|---|
| 141 | comments => '', |
|---|
| 142 | url => '', |
|---|
| 143 | templateid => 0 # mapped to template via item |
|---|
| 144 | }); |
|---|
| 145 | $stats{'Triggers created'}++; |
|---|
| 146 | } |
|---|
| 147 | } |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | sub apicall { |
|---|
| 151 | my ($method , $params) = @_; |
|---|
| 152 | print "$method:\n" if $debug; |
|---|
| 153 | our ($mreq, $mres, $mua, $auth); |
|---|
| 154 | if (!$mreq){ |
|---|
| 155 | print " setting up LWP... \n" if $debug; |
|---|
| 156 | $mua = LWP::UserAgent->new; |
|---|
| 157 | $mua->agent("swZBXAPI/0.1 "); |
|---|
| 158 | $mreq = HTTP::Request->new(POST => "$url/api_jsonrpc.php"); |
|---|
| 159 | $mreq->content_type('application/json-rpc'); |
|---|
| 160 | } |
|---|
| 161 | my $obj = {jsonrpc => '2.0', |
|---|
| 162 | method => $method, |
|---|
| 163 | params => $params, |
|---|
| 164 | auth => $auth, |
|---|
| 165 | id => $stats{'API calls'}++ |
|---|
| 166 | }; |
|---|
| 167 | if (defined &to_json){ # there is different JSON.pm around.... |
|---|
| 168 | $obj = to_json($obj); |
|---|
| 169 | } else { |
|---|
| 170 | $obj = objToJson($obj); |
|---|
| 171 | } |
|---|
| 172 | $mreq->content($obj); |
|---|
| 173 | print " >".$mreq->content ."\n" if $debug > 1; |
|---|
| 174 | $mres = $mua->request($mreq); |
|---|
| 175 | if (!$mres->is_success) {print $mres->status_line, "\n";} |
|---|
| 176 | print " <".$mres->content ."\n" if $debug > 1; |
|---|
| 177 | if (defined &from_json){ # there is different JSON.pm around.... |
|---|
| 178 | $mres = from_json($mres->content); |
|---|
| 179 | } else { |
|---|
| 180 | $mres = jsonToObj($mres->content); |
|---|
| 181 | } |
|---|
| 182 | if($mres->{result}) { |
|---|
| 183 | print " ".$mres->{result}."\n" if $debug; |
|---|
| 184 | } else { |
|---|
| 185 | die $mres->{error}->{message}." ".$mres->{error}->{data}." in $method\n"; |
|---|
| 186 | } |
|---|
| 187 | if($method eq "user.authenticate") { $auth = $mres->{result} ;} |
|---|
| 188 | return $mres->{result} ; |
|---|
| 189 | } |
|---|
| 190 | |
|---|
| 191 | sub gettemplateid { # returns id of the template, creates it if necessary |
|---|
| 192 | my ($template_name) = @_; |
|---|
| 193 | my $groupid_template = apicall('HostGroup.getObjects', { name => $templategroup})->[0]->{groupid}; |
|---|
| 194 | my $template = apicall('Template.getObjects', { template => $template_name}) if ($api_version eq "1.1"); |
|---|
| 195 | my $template = apicall('Template.getObjects', { host => $template_name}) if ($api_version eq "1.2"); |
|---|
| 196 | my $templateid = 0; |
|---|
| 197 | if ($template =~ /HASH/){ # zabbix bug workaround (ZBX-2205) |
|---|
| 198 | for (keys %$template) { |
|---|
| 199 | $templateid = $template->{$_}->{templateid}; |
|---|
| 200 | } |
|---|
| 201 | } else { # should never be called, array is only returned if no template is found.... |
|---|
| 202 | $templateid = $template->[0]->{templateid}; |
|---|
| 203 | } |
|---|
| 204 | if (!$templateid) { # create hostspecific template |
|---|
| 205 | $stats{'Templates created'}++; |
|---|
| 206 | my $res = apicall('Template.create', { host => $template_name, |
|---|
| 207 | groups => $groupid_template |
|---|
| 208 | }); |
|---|
| 209 | for (keys %$res) { # should be one and one only |
|---|
| 210 | $templateid = $_ if ($api_version eq "1.1"); |
|---|
| 211 | $templateid = $res->{templateids}->[0] if ($api_version eq "1.2"); |
|---|
| 212 | } |
|---|
| 213 | } |
|---|
| 214 | return $templateid; |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | |
|---|
| 218 | apicall('user.authenticate',{password => $pass, user => $user}); |
|---|
| 219 | $api_version = apicall('APIInfo.version', { foo => 1}); |
|---|
| 220 | if (!$prog_api_version{$api_version}) { die "unsupported API version: $api_version\n"; } |
|---|
| 221 | my $groupid = apicall('HostGroup.getObjects', { name => $snmpgroup})->[0]->{groupid}; |
|---|
| 222 | my $hostarray = apicall('Host.get', {groupids => $groupid, extendoutput => 1}); |
|---|
| 223 | for my $i (0..$#$hostarray){ |
|---|
| 224 | print $hostarray->[$i]->{hostid}.",".$hostarray->[$i]->{dns}.",".$hostarray->[$i]->{ip}."\n" if $debug; |
|---|
| 225 | my $hostname = $hostarray->[$i]->{host}; |
|---|
| 226 | if ( $hostname =~ /[a-zA-Z]/ && $hostname_only ) { $hostname =~ s/\..*//g;} |
|---|
| 227 | my $template_name = $template_basename.$hostname; |
|---|
| 228 | my $templateid = gettemplateid($template_name); |
|---|
| 229 | apicall('Template.linkTemplates', {templates => { templateid => $templateid }, hosts => { hostid => $hostarray->[$i]->{hostid}}}) if ($api_version eq "1.1"); |
|---|
| 230 | apicall('Template.massAdd', {templates => $templateid, hosts => $hostarray->[$i]->{hostid}}) if ($api_version eq "1.2"); |
|---|
| 231 | snmp ($hostarray->[$i]->{ip}, $template_name); |
|---|
| 232 | } |
|---|
| 233 | |
|---|
| 234 | print "\n+---------------------+-------+\n"; |
|---|
| 235 | foreach (keys %stats){ |
|---|
| 236 | printf "| %-20s|%6d |\n",$_,$stats{$_}; |
|---|
| 237 | } |
|---|
| 238 | print "+---------------------+-------+\n"; |
|---|