Perl から Zabbix API を叩いてみる。
今日は Zabbix-JP の勉強会があるので、そのネタ提供ということで、Perl から Zabbix API を叩くサンプルスクリプトを書いてみました。
Zabbix-JP のパッケージは、RHEL5/CentOS5 向けに作られていますが、これらのディストリビューションだとデフォルトのRuby、pythonのバージョンが古いため、Zabcon を始めとした Zabbix API 向けの ライブラリがそのままでは動きません。
そのため、Perl を使って Zabbix API を叩くという需要もそれなりにあるとは思いますが、ちょっとぐぐってみたところ、本家のコミュニティに Zabbix 1.8 API and Perl というのがあるぐらいで Perl の情報はあまり見つからず…
なので自分でサンプルをつくってみました。*1 *2
最後に出力サンプルをつけていますが、このスクリプトがやってくれるのは Zabbix API を直接叩いて、その結果を標準出力に吐き出すだけです。
■使用例
$ perl get_zabbix_data.pl --method host.get $ perl get_zabbix_data.pl --method item.get --zabbix_server 192.168.1.2 --limit 1 --filter '{"hostid":["10001","10017"],"description":"Buffers memory"}'
■前提条件
- Zabbix バージョン: 1.8.3 (Zabbix-JP パッケージ使用)
- Perl の標準モジュールに加えてJSONモジュールが必要(CPANから適宜インストールをしてください。)
- Zabbix API が有効になっているアカウント (Admin もインストール時は API 有効ではないので注意!)
■コマンドラインオプション
- "--method" だけが必須のオプションで、あとのオプションには適当にデフォルト値を入れています。今のところ、"host.get", "item.get", "graph.get" など比較的単純な "get" メソッドのみ対応です。
- "--zabbix_server" はその名の通りのオプションです^^ デフォルトは "localhost" になっています。
- "--limit" は取得するアイテム数の上限を設定するオプションです。デフォルトは"10"にしてあります。
- "--filter" は、取得アイテムフィルタリングオプションなのですが、JSONをそのまま渡してください。どういうフィルタリングができるのかは、本家のAPIドキュメント参照してください^^;
- また、Zabbix にアクセスするユーザはデフォルトではユーザ名: "Admin"、パスワード: "Zabbix" を使いますが、"--user", "--password" オプションを使って、別のユーザアカウントを使うこともできます。(使用するアカウントの API アクセスを有効化するのを忘れないようにしてください。)
このコードは、そのまま使えるものでもないので、適当に切り貼りして使ってもらってかまいません。ですが、「こういう風に使ったよ〜」みたいな話だけ聞かせてもらえると、僕が喜びます^^
何かあれば、twitterで話しかけてもらえればベストエフォートで対応しますw
■スクリプト本体(get_zabbix_data.pl)
#!/usr/bin/perl # Last Modified: 2010/12/18 # Author: Seiichiro, Ishida <twitterID: @sechiro> use strict; use warnings; use Socket; use JSON; use Getopt::Long; # JSON OO Interface my $json = JSON->new->allow_nonref; # Command Line Options my $zabbix_server = "localhost"; my $user = "Admin"; # Zabbix default my $password = "zabbix"; # Zabbix default my $useragent = "zabbi-tan"; my $method; my $limit = 10; # 一度に取得するアイテム数 my $filter; # ex) --method host.get --filter '{"host":"Zabbix server"}' # --method item.get --filter '{"hostid":["10001","10017"],"description":"Buffers memory"}' my $opt_parse = GetOptions ( "zabbix_server=s" => \$zabbix_server, "user=s" => \$user, "password=s" => \$password, "useragent=s" => \$useragent, "method=s" => \$method, "limit=i" => \$limit, "filter=s" => \$filter, ); die "No method specified!" unless ( defined($method) ); my $filter_hash = $json->decode( $filter ) if ( defined($filter) ); # Authentication my $json_auth = $json->encode( create_auth_request_hash($user, $password) ); my $rpc_auth_request = create_rpc_request($zabbix_server, $useragent, $json_auth); my $json_auth_result = $json->decode( get_zabbix_data($zabbix_server, $rpc_auth_request) ); my $auth = $json_auth_result->{result}; my $id = $json_auth_result->{id}; $id++; # Get Zabbix Data my $json_data = $json->encode( create_get_request_hash($auth, $id, $method, $filter_hash, $limit) ); my $rpc_request = create_rpc_request($zabbix_server, $useragent, $json_data); my $json_result = $json->decode( get_zabbix_data($zabbix_server, $rpc_request) ); # output my $pretty_printed = $json->pretty->encode( $json_result ); print $pretty_printed; exit; sub create_auth_request_hash{ my $user = shift; my $password = shift; my %params = ( user => $user, password => $password, ); my %auth_request = ( auth => undef, method => 'user.authenticate', id => 0, params => \%params, jsonrpc => '2.0', ); return \%auth_request; } sub create_get_request_hash{ #for host.get etc. my $auth = shift; my $id = shift; my $method = shift; my $filter_hash = shift; my $limit = shift; my %params = ( output => 'extend', limit => $limit, filter => $filter_hash, ); my %get_request = ( auth => $auth, method => $method, id => $id, params => \%params, jsonrpc => '2.0', ); return \%get_request; } sub create_rpc_request{ my $zabbix_server = shift; my $useragent = shift; my $json_data = shift; my $header = "POST /zabbix/api_jsonrpc.php HTTP/1.1\n" . "Accept: */*\n" . "Connection: close\n" . "Content-Type: application/json-rpc\n"; $header .= "User-Agent: " . $useragent ."\n"; $header .= "Content-Length: " . length($json_data) . "\n"; $header .= "Host: " . $zabbix_server . "\n\n"; return $header . $json_data; } sub get_zabbix_data{ use Socket; my $zabbix_server = shift; my $rpc_request = shift; my $sock; socket( $sock, PF_INET, SOCK_STREAM, getprotobyname('tcp') ) or die "Cannot create socket: $!"; my $packed_remote_host = inet_aton( $zabbix_server ) or die "Cannot pack $zabbix_server: $!"; my $remote_port = 80; # ホスト名とポート番号をパック my $sock_addr = sockaddr_in( $remote_port, $packed_remote_host ) or die "Cannot pack $zabbix_server:$remote_port: $!"; connect( $sock, $sock_addr ) or die "Cannot connect $zabbix_server:$remote_port: $!"; # 書き込みバッファリングをしない。 my $old_handle = select $sock; $| = 1; select $old_handle; print $sock "$rpc_request"; shutdown $sock, 1; # 書き込みを終了する。 # データの読み込み my $json_result; while( my $line = <$sock> ){ if ( $line =~ /jsonrpc/ ){ $json_result = $line; } } close $sock; return $json_result; } __END__
■実行結果サンプル
$ perl get_zabbix_data.pl --method host.get --filter '{"host":"Zabbix server"}'
{ "jsonrpc" : "2.0", "id" : 1, "result" : [ { "dns" : "", "maintenances" : [ { "maintenanceid" : "0" } ], "outbytes" : "0", "snmp_error" : "", "ipmi_ip" : "", "ipmi_authtype" : "0", "ipmi_username" : "", "ipmi_error" : "", "useipmi" : "0", "port" : "10050", "maintenanceid" : "0", "errors_from" : "0", "ipmi_password" : "", "useip" : "1", "ipmi_privilege" : "2", "maintenance_type" : "0", "maintenance_status" : "0", "ip" : "127.0.0.1", "status" : "0", "lastaccess" : "0", "hostid" : "10017", "ipmi_port" : "623", "ipmi_errors_from" : "0", "ipmi_available" : "0", "disable_until" : "0", "proxy_hostid" : "0", "error" : "", "snmp_disable_until" : "0", "maintenance_from" : "0", "available" : "1", "snmp_available" : "0", "host" : "Zabbix server", "inbytes" : "0", "snmp_errors_from" : "0", "ipmi_disable_until" : "0" } ] }
■追記(2010/12/19)
いただいたコメントを参考に"IO::Socket::INET"を使ったバージョンもつくりました。
最後の"sub get_zabbix_data"を以下のコードに差し替えてください。
sub get_zabbix_data{ use IO::Socket::INET; my $zabbix_server = shift; my $rpc_request = shift; my $sock = IO::Socket::INET->new(PeerAddr => "$zabbix_server", PeerPort => 'http(80)', Proto => 'tcp', ) or die "Cannot create socket: $!"; $sock->print("$rpc_request"); # データの読み込み my $json_result; while( my $line = $sock->getline ){ if ( $line =~ /jsonrpc/ ){ $json_result = $line; } } return $json_result; }