Initial release
This commit is contained in:
parent
f497a2ee71
commit
30d5084231
75
README.md
75
README.md
|
@ -1,5 +1,72 @@
|
||||||
# zabbix-go
|
zabbix
|
||||||
|
======
|
||||||
|
|
||||||
This Go library implements the Zabbix 2.0 JRPC API
|
This Go library implements the Zabbix 2.0 API. The Zabbix API is a JSONRPC
|
||||||
|
based API, although it is not compatable with Go's builtin JSONRPC libraries.
|
||||||
Based on https://github.com/adubkov/zabbix
|
So we implement that JSONRPC, and provide data types that mimic Zabbbix's
|
||||||
|
return values.
|
||||||
|
|
||||||
|
Based on https://github.com/adubkov/zabbix
|
||||||
|
|
||||||
|
Connecting to the API
|
||||||
|
=====================
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
api, err := zabbix.NewAPI("http://zabbix.yourhost.net/api_jsonrpc.php", "User", "Password")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
versionresult, err := api.Version()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(versionresult)
|
||||||
|
|
||||||
|
_, err = api.Login()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Connected to API")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Making a call
|
||||||
|
=============
|
||||||
|
|
||||||
|
I typically wrap the actual API call to hide the messy details. If the
|
||||||
|
response has an Error field, and the code is greater than 0, the API
|
||||||
|
will return that Error. Then my wrapper function return a ZabbixError
|
||||||
|
to the caller.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Find and return a single host object by name
|
||||||
|
func GetHost(api *zabbix.API, host string) (zabbix.ZabbixHost, error) {
|
||||||
|
params := make(map[string]interface{}, 0)
|
||||||
|
filter := make(map[string]string, 0)
|
||||||
|
filter["host"] = host
|
||||||
|
params["filter"] = filter
|
||||||
|
params["output"] = "extend"
|
||||||
|
params["select_groups"] = "extend"
|
||||||
|
params["templated_hosts"] = 1
|
||||||
|
ret, err := api.Host("get", params)
|
||||||
|
|
||||||
|
// This happens if there was an RPC error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If our call was successful
|
||||||
|
if len(ret) > 0 {
|
||||||
|
return ret[0], err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will be the case if the RPC call was successful, but
|
||||||
|
// Zabbix had an issue with the data we passed.
|
||||||
|
return nil, &zabbix.ZabbixError{0,"","Host not found"}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
287
zabbix.go
Normal file
287
zabbix.go
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
package zabbix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Zabbix and Go's RPC implementations don't play with each other.. at all.
|
||||||
|
So I've re-created the wheel at bit.
|
||||||
|
*/
|
||||||
|
type JsonRPCResponse struct {
|
||||||
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
|
Error ZabbixError `json:"error"`
|
||||||
|
Result interface{} `json:"result"`
|
||||||
|
Id int `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonRPCRequest struct {
|
||||||
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Params interface{} `json:"params"`
|
||||||
|
|
||||||
|
// Zabbix 2.0:
|
||||||
|
// The "user.login" method must be called without the "auth" parameter
|
||||||
|
Auth string `json:"auth,omitempty"`
|
||||||
|
Id int `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZabbixError struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZabbixError) Error() string {
|
||||||
|
return z.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZabbixHost map[string]interface{}
|
||||||
|
type ZabbixGraph map[string]interface{}
|
||||||
|
type ZabbixGraphItem map[string]interface{}
|
||||||
|
type ZabbixHistoryItem struct {
|
||||||
|
Clock string `json:"clock"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Itemid string `json:"itemid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ZabbixTemplate map[string]interface{}
|
||||||
|
type ZabbixHostGroup map[string]interface{}
|
||||||
|
|
||||||
|
type API struct {
|
||||||
|
url string
|
||||||
|
user string
|
||||||
|
passwd string
|
||||||
|
id int
|
||||||
|
auth string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(server, user, passwd string) (*API, error) {
|
||||||
|
return &API{server, user, passwd, 0, ""}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) GetAuth() string {
|
||||||
|
return api.auth
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Each request establishes its own connection to the server. This makes it easy
|
||||||
|
to keep request/responses in order without doing any concurrency
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (api *API) ZabbixRequest(method string, data interface{}) (JsonRPCResponse, error) {
|
||||||
|
// Setup our JSONRPC Request data
|
||||||
|
id := api.id
|
||||||
|
api.id = api.id + 1
|
||||||
|
jsonobj := JsonRPCRequest{"2.0", method, data, api.auth, id}
|
||||||
|
encoded, err := json.Marshal(jsonobj)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return JsonRPCResponse{}, err
|
||||||
|
}
|
||||||
|
// Setup our HTTP request
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
MaxIdleConnsPerHost: 10,
|
||||||
|
ResponseHeaderTimeout: 60 * time.Second,
|
||||||
|
DialContext: (&net.Dialer{Timeout: time.Second}).DialContext,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
MaxVersion: tls.VersionTLS11,
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
request, err := http.NewRequest("POST", api.url, bytes.NewBuffer(encoded))
|
||||||
|
if err != nil {
|
||||||
|
return JsonRPCResponse{}, err
|
||||||
|
}
|
||||||
|
request.Header.Add("Content-Type", "application/json-rpc")
|
||||||
|
if api.auth != "" {
|
||||||
|
// XXX Not required in practice, check spec
|
||||||
|
//request.SetBasicAuth(api.user, api.passwd)
|
||||||
|
//request.Header.Add("Authorization", api.auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
response, err := client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
return JsonRPCResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
We can't rely on response.ContentLength because it will
|
||||||
|
be set at -1 for large responses that are chunked. So
|
||||||
|
we treat each API response as streamed data.
|
||||||
|
*/
|
||||||
|
var result JsonRPCResponse
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
_, err = io.Copy(&buf, response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return JsonRPCResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
json.Unmarshal(buf.Bytes(), &result)
|
||||||
|
|
||||||
|
response.Body.Close()
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) Login() (bool, error) {
|
||||||
|
params := make(map[string]string, 0)
|
||||||
|
params["user"] = api.user
|
||||||
|
params["password"] = api.passwd
|
||||||
|
|
||||||
|
response, err := api.ZabbixRequest("user.login", params)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %s\n", err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Error.Code != 0 {
|
||||||
|
return false, &response.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
api.auth = response.Result.(string)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) Version() (string, error) {
|
||||||
|
response, err := api.ZabbixRequest("APIInfo.version", make(map[string]string, 0))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Error.Code != 0 {
|
||||||
|
return "", &response.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Result.(string), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Interface to the user.* calls
|
||||||
|
*/
|
||||||
|
func (api *API) User(method string, data interface{}) ([]interface{}, error) {
|
||||||
|
response, err := api.ZabbixRequest("user."+method, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Error.Code != 0 {
|
||||||
|
return nil, &response.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Result.([]interface{}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Interface to the host.* calls
|
||||||
|
*/
|
||||||
|
func (api *API) Host(method string, data interface{}) ([]ZabbixHost, error) {
|
||||||
|
response, err := api.ZabbixRequest("host."+method, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Error.Code != 0 {
|
||||||
|
return nil, &response.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX uhg... there has got to be a better way to convert the response
|
||||||
|
// to the type I want to return
|
||||||
|
res, err := json.Marshal(response.Result)
|
||||||
|
var ret []ZabbixHost
|
||||||
|
err = json.Unmarshal(res, &ret)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Interface to the graph.* calls
|
||||||
|
*/
|
||||||
|
func (api *API) Graph(method string, data interface{}) ([]ZabbixGraph, error) {
|
||||||
|
response, err := api.ZabbixRequest("graph."+method, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Error.Code != 0 {
|
||||||
|
return nil, &response.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX uhg... there has got to be a better way to convert the response
|
||||||
|
// to the type I want to return
|
||||||
|
res, err := json.Marshal(response.Result)
|
||||||
|
var ret []ZabbixGraph
|
||||||
|
err = json.Unmarshal(res, &ret)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Interface to the history.* calls
|
||||||
|
*/
|
||||||
|
func (api *API) History(method string, data interface{}) ([]ZabbixHistoryItem, error) {
|
||||||
|
response, err := api.ZabbixRequest("history."+method, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Error.Code != 0 {
|
||||||
|
return nil, &response.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX uhg... there has got to be a better way to convert the response
|
||||||
|
// to the type I want to return
|
||||||
|
res, err := json.Marshal(response.Result)
|
||||||
|
var ret []ZabbixHistoryItem
|
||||||
|
err = json.Unmarshal(res, &ret)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Interface to the template.* calls
|
||||||
|
*/
|
||||||
|
func (api *API) Template(method string, data interface{}) ([]ZabbixTemplate, error) {
|
||||||
|
response, err := api.ZabbixRequest("template."+method, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Error.Code != 0 {
|
||||||
|
return nil, &response.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX uhg... there has got to be a better way to convert the response
|
||||||
|
// to the type I want to return
|
||||||
|
res, err := json.Marshal(response.Result)
|
||||||
|
var ret []ZabbixTemplate
|
||||||
|
err = json.Unmarshal(res, &ret)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Interface to the hostgroup.* calls
|
||||||
|
*/
|
||||||
|
func (api *API) HostGroup(method string, data interface{}) ([]ZabbixHostGroup, error) {
|
||||||
|
response, err := api.ZabbixRequest("hostgroup."+method, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Error.Code != 0 {
|
||||||
|
return nil, &response.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := json.Marshal(response.Result)
|
||||||
|
var ret []ZabbixHostGroup
|
||||||
|
err = json.Unmarshal(res, &ret)
|
||||||
|
return ret, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user