Javaアプリから Zabbix API を使う
概要
前回 curl で疎通確認を行ったが、今回はこれらを Java で実装する。
前回記事:Zabbix API を使ってみる - Status Code 303 - See Other
Zabbix API は、JSON 形式で API と通信するため、JSON 形式で通信できるようセットアップする必要がある。
また、Zabbix API は、API の種類が多く、そのリクエストパラメータとそのレスポンスデータの種類も
オブジェクトリスト・オブジェクト・文字リスト・基本データ型など多種多様なので、これらをなるべく扱えるよう設計する。
また、最後には拡張方法の方針について触れる。
ライブラリ
今回は、Jersey (Jersey)を用いる。
JAX-RS (JAX-RS - Wikipedia)という、RESTFUL なアーキテクチャを実装するための API として有名。
しかし、jersey には、クライアントの機能を提供するライブラリもあり、それらは結構使い易いらしい。
参考:HTTPクライアントとして使うjersey-client – Akira Koyasu's WebLog
導入
まずは、ライブラリをダウンロードして、それらに対してビルドパスを設定する。
まずは、HTTP クライアント用ライブラリの導入。
Jersey-client 本体
- jersey-client.jar
Jersey-client に必要なライブラリ
- jersey-common.jar
- javax.ws.rs-api-2.0.1.jar
- javax.annotation-api-1.2.jar
- javax.inject-2.4.0-b31.jar
- hk2-api-2.4.0-b31.jar
- hk2-locator-2.4.0-b31.jar
- hk2-utils-2.4.0-b31.jar
- jersey-guava-2.22.1.jar
- jersey-entity-filtering-2.22.2.jar
- jersey-media-json-jackson-2.22.2.jar
- jackson-core-2.7.1.jar
- jackson-databind-2.5.4.jar
- jackson-jaxrs-base-2.5.4.jar
- jackson-jaxrs-json-provider-2.5.4.jar
JSON を扱うためのライブラリ
上記ライブラリだけでも HTTP 通信は可能だが、JSON には対応できないらしく、専用ライブラリを導入する必要がある。
java - MessageBodyWriter not found for media type=application/json - Stack Overflow
- jackson-annotations-2.5.4.jar
- jackson-module-jaxb-annotations-2.5.4.jar
Lombok
ボイラープレートコード削減ライブラリ。
Lombok ライブラリ - Status Code 303 - See Other
実装内容
今回作成したのは、大きく分けて3つ
この他、API に応じて以下を作成する。
- API 結果型 (値格納)
Zabbix API クラス
今回は、実装量がある程度多いため、コメントは結構適当。
package api.zabbix; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; import lombok.Data; import resources.properties.Property; public class ZabbixAPIAccess { // Zabbix の設定ファイル static Property property = new Property("zabbix"); // トークン格納先 private String token; // トークンの管理を担当、結果はレスポンス形式。 public Response request(ZabbixParam params) throws JsonProcessingException, IOException { // トークン取得条件。本来はタイムアウトとか考慮だろうけど、かなり期間長いみたいなので適当 if (token == null) { token = getToken(); } Map<String, Object> p = getWrappingRequest(params); p.put("auth", token); return getResponse(p); } // レスポンスデータをオブジェクト形式にマッピング。第1引数にリクエストデータ、第2引数にマッピングクラス記述 public <T> T request(ZabbixParam params, Class<T> klass) throws JsonProcessingException, IOException { ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); JavaType type = mapper.getTypeFactory().constructParametrizedType(ZabbixResponse.class, ZabbixResponse.class, klass); ZabbixResponse<T> value = mapper.readValue((InputStream) request(params).getEntity(), type); return value.result; } // レスポンスデータをリスト形式にマッピング。第1引数にリクエストデータ、第2引数に各要素に適用するマッピングクラス記述 public <E> List<E> requestWithList(ZabbixParam params, Class<E> componentKlass) throws JsonProcessingException, IOException { ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); TypeFactory factory = mapper.getTypeFactory(); ZabbixResponse<List<E>> value = mapper.readValue((InputStream) request(params).getEntity(), factory .constructParametrizedType(ZabbixResponse.class, ZabbixResponse.class, factory.constructCollectionType(ArrayList.class, componentKlass))); return value.result; } // パラメータをAPI 仕様の形式にラッピング private Map<String, Object> getWrappingRequest(ZabbixParam params) { Map<String, Object> result = new HashMap<>(); result.put("jsonrpc", "2.0"); result.put("method", params.getMethod()); result.put("id", 1); result.put("params", params.getParameters()); return result; } // 通信 private Response getResponse(Map<String, Object> params) { return ClientBuilder.newClient().target(property.getString("zabbix.api.url")).request() .post(Entity.entity(params, MediaType.APPLICATION_JSON)); } // トークン取得 private String getToken() throws JsonProcessingException, IOException { ZabbixParam loginParams = new MapParam(new ZabbixParam("user.login")) .put("user", property.getString("zabbix.user")).put("password", property.getString("zabbix.password")); return new ObjectMapper().readTree((InputStream) getResponse(getWrappingRequest(loginParams)).getEntity()) .get("result").asText(); } // 一時的なレスポンスデータ格納先。使わないため private @Data private static class ZabbixResponse<T> { private String jsonrpc; private T result; private int id; } }
パラメータ構築クラス
メソッドとパラメータを格納。
パラメータがリストやマップの場合は、下記 Decorator クラスを使った方が良い。
package api.zabbix; import lombok.Data; @Data public class ZabbixParam { // (Zabbix APIの) メソッド名 private final String method; // リクエストパラメータ private Object parameters; }
ZabbixParameter がMapだったときに活躍しそうな、Decorator クラス。
package api.zabbix; import java.util.LinkedHashMap; import java.util.Map; public class MapParam extends ZabbixParam { public MapParam(ZabbixParam param){ super(param.getMethod()); super.setParameters(new LinkedHashMap<String, Object>()); } @SuppressWarnings("unchecked") public MapParam put(String key, Object value) { ((Map<String, Object>)super.getParameters()).put(key, value); return this; } }
ZabbixParameter が List だったときに活躍しそうな、Decorator クラス。
package api.zabbix; import java.util.ArrayList; import java.util.List; public class ListParam<T> extends ZabbixParam { public ListParam(ZabbixParam param){ super(param.getMethod()); super.setParameters(new ArrayList<T>()); } @SuppressWarnings("unchecked") public ListParam<T> add(T elements) { ((List<T>)super.getParameters()).add(elements); return this; } }
利用方法
今回は、例として次の流れでロジックを構成する。
- ホストグループ作成
- 作成したホストグループ情報取得
- 作成したホストグループ削除
対応する Zabbix API リファレンスはこちら。(※対応 Zabbix バージョン 2.4)
- hostgroup.create [Zabbix Documentation 2.4]
- hostgroup.get [Zabbix Documentation 2.4]
- hostgroup.delete [Zabbix Documentation 2.4]
なお、API 仕様はバージョンによって、変わることがあるので注意すること。
Zabbix 設定ファイル (zabbix.properties)
API 通信できるユーザ名・そのパスワード、そして API 問い合わせ先の設定。
zabbix.user = xxxx zabbix.password = yyyy zabbix.api.url = http://xxx.xxx.com/zabbix/api_jsonrpc.php
値格納クラス
API リファレンスから、どんなデータが返ってくるか分かるので、それら用のマッピングクラスを作成。
- ホストグループクラス (ホストグループ取得に使う)
package api.zabbix.vo; import com.fasterxml.jackson.annotation.JsonProperty; public class HostGroup { // JSON では groupid 要素をココにマッピング @JsonProperty("groupid") public String id; // ホストグループ名 public String name; public String internal; }
- ホストグループ ID リスト格納クラス。(ホストグループ作成時・削除時使用)
package api.zabbix.vo; import java.util.List; import com.fasterxml.jackson.annotation.JsonProperty; public class HostGroupIds { @JsonProperty("groupids") public List<String> groupIds; }
ロジッククラス
上記クラスを利用して、以下のようにロジックに記述する。
package api.zabbix; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import com.fasterxml.jackson.core.JsonProcessingException; import api.zabbix.vo.HostGroup; import api.zabbix.vo.HostGroupIds; public class Main { public static void main(String[] args) throws JsonProcessingException, IOException { ZabbixAPIAccess api = new ZabbixAPIAccess(); // ホストグループ作成 ZabbixParam createHostGroupParams = new MapParam(new ZabbixParam("hostgroup.create")).put("name", "test"); HostGroupIds createValue = api.request(createHostGroupParams, HostGroupIds.class); // ホストグループ一覧取得 (フィルターで上記作成ホストグループだけ取得) Map<String, Object> filter = new HashMap<>(); filter.put("name", "test"); ZabbixParam getHostGroupParams = new MapParam(new ZabbixParam("hostgroup.get")).put("output", "extend").put("filter", filter); List<HostGroup> getValue = api.requestWithList(getHostGroupParams, HostGroup.class); System.out.println(getValue); // 上記のホストグループ削除 ZabbixParam deleteHostGroupParams = new ListParam<String>(new ZabbixParam("hostgroup.delete")).add(createValue.groupIds.get(0)); HostGroupIds deleteValue = api.request(deleteHostGroupParams, HostGroupIds.class); System.out.println(deleteValue.groupIds); } }
出力結果
[{groupid=59, name=test, internal=0, flags=0}] [59]
拡張手順
他の API 利用
次の手順で実装。
- Zabbix API 仕様を調べる
- 値格納用データクラス作成
- Main ロジック記述
例:ホストを作成する
- Zabbix API 仕様
host.create [Zabbix Documentation 2.4]
host.get [Zabbix Documentation 2.4]
host.delete [Zabbix Documentation 2.4]
- 値格納用クラスの追加
package api.zabbix.vo; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; @Data public class Host { @JsonProperty("hostid") public String id; public int available; public String name; public String host; @JsonProperty("status") public String status; }
package api.zabbix.vo; import java.util.List; import com.fasterxml.jackson.annotation.JsonProperty; public class HostIds { @JsonProperty("hostids") public List<String> ids; }
ロジックの記述(追加分)
// ホスト作成 List<Map<String, Object>> interfaces = new ArrayList<>(); Map<String, Object> if0 = new HashMap<>(); if0.put("type", 1); if0.put("main", 1); if0.put("useip", 1); if0.put("ip", "127.0.0.1"); if0.put("dns", ""); if0.put("port", "10050"); interfaces.add(if0); List<Map<String, Object>> groups = new ArrayList<>(); Map<String, Object> group = new HashMap<>(); group.put("groupid", createValue.groupIds.get(0)); // hostgroup.create から取得 groups.add(group); ZabbixParam createHostParams = new MapParam(new ZabbixParam("host.create")).put("host", "test_server") .put("interfaces", interfaces).put("groups", groups); HostIds createHostValue = api.request(createHostParams, HostIds.class); System.out.println(createHostValue.ids); // ホスト取得 Map<String, Object> filter = new HashMap<>(); filter.put("host", "test_server"); ZabbixParam getHostParams = new MapParam(new ZabbixParam("host.get")).put("output", "extend").put("filter", filter); List<Host> getValue = api.requestWithList(getHostParams, Host.class); System.out.println(getValue); // ホスト削除 ZabbixParam deleteHostParams = new ListParam<String>(new ZabbixParam("host.delete")).add(getValue.get(0).id); Response deleteHostValue = api.request(deleteHostParams); System.out.println(deleteHostValue.readEntity(String.class));
これでホスト作成・取得・削除も実装できる。
便利なリクエストパラメータを構築するクラスが欲しい
ZabbixParam クラスに対する、Decorator 実装を行う。