Initial release

This commit is contained in:
svkalinin 2021-11-28 10:36:31 +03:00
parent f497a2ee71
commit 30d5084231
3 changed files with 361 additions and 4 deletions

View File

@ -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.
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"}
}
```

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module zabbix
go 1.16

287
zabbix.go Normal file
View 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
}