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 }