├── LICENSE ├── README.md └── zabbix.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2013 Ryan Day 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial 11 | portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 14 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 15 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 16 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 17 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | zabbix 2 | ====== 3 | 4 | This Go library implements the Zabbix 2.0 API. The Zabbix API is a JSONRPC 5 | based API, although it is not compatable with Go's builtin JSONRPC libraries. 6 | So we implement that JSONRPC, and provide data types that mimic Zabbbix's 7 | return values. 8 | 9 | Connecting to the API 10 | ===================== 11 | 12 | ```go 13 | func main() { 14 | api, err := zabbix.NewAPI("http://zabbix.yourhost.net/api_jsonrpc.php", "User", "Password") 15 | if err != nil { 16 | fmt.Println(err) 17 | return 18 | } 19 | 20 | versionresult, err := api.Version() 21 | if err != nil { 22 | fmt.Println(err) 23 | } 24 | 25 | fmt.Println(versionresult) 26 | 27 | _, err = api.Login() 28 | if err != nil { 29 | fmt.Println(err) 30 | return 31 | } 32 | fmt.Println("Connected to API") 33 | } 34 | ``` 35 | 36 | Making a call 37 | ============= 38 | 39 | I typically wrap the actual API call to hide the messy details. If the 40 | response has an Error field, and the code is greater than 0, the API 41 | will return that Error. Then my wrapper function return a ZabbixError 42 | to the caller. 43 | 44 | ```go 45 | // Find and return a single host object by name 46 | func GetHost(api *zabbix.API, host string) (zabbix.ZabbixHost, error) { 47 | params := make(map[string]interface{}, 0) 48 | filter := make(map[string]string, 0) 49 | filter["host"] = host 50 | params["filter"] = filter 51 | params["output"] = "extend" 52 | params["select_groups"] = "extend" 53 | params["templated_hosts"] = 1 54 | ret, err := api.Host("get", params) 55 | 56 | // This happens if there was an RPC error 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | // If our call was successful 62 | if len(ret) > 0 { 63 | return ret[0], err 64 | } 65 | 66 | // This will be the case if the RPC call was successful, but 67 | // Zabbix had an issue with the data we passed. 68 | return nil, &zabbix.ZabbixError{0,"","Host not found"} 69 | } 70 | ``` 71 | -------------------------------------------------------------------------------- /zabbix.go: -------------------------------------------------------------------------------- 1 | package zabbix 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | ) 10 | 11 | /** 12 | Zabbix and Go's RPC implementations don't play with each other.. at all. 13 | So I've re-created the wheel at bit. 14 | */ 15 | type JsonRPCResponse struct { 16 | Jsonrpc string `json:"jsonrpc"` 17 | Error ZabbixError `json:"error"` 18 | Result interface{} `json:"result"` 19 | Id int `json:"id"` 20 | } 21 | 22 | type JsonRPCRequest struct { 23 | Jsonrpc string `json:"jsonrpc"` 24 | Method string `json:"method"` 25 | Params interface{} `json:"params"` 26 | 27 | // Zabbix 2.0: 28 | // The "user.login" method must be called without the "auth" parameter 29 | Auth string `json:"auth,omitempty"` 30 | Id int `json:"id"` 31 | } 32 | 33 | type ZabbixError struct { 34 | Code int `json:"code"` 35 | Message string `json:"message"` 36 | Data string `json:"data"` 37 | } 38 | 39 | func (z *ZabbixError) Error() string { 40 | return z.Data 41 | } 42 | 43 | type ZabbixHost map[string]interface{} 44 | type ZabbixGraph map[string]interface{} 45 | type ZabbixGraphItem map[string]interface{} 46 | type ZabbixHistoryItem struct { 47 | Clock string `json:"clock"` 48 | Value string `json:"value"` 49 | Itemid string `json:"itemid"` 50 | } 51 | 52 | type API struct { 53 | url string 54 | user string 55 | passwd string 56 | id int 57 | auth string 58 | Client *http.Client 59 | } 60 | 61 | func NewAPI(server, user, passwd string) (*API, error) { 62 | return &API{server, user, passwd, 0, "", &http.Client{}}, nil 63 | } 64 | 65 | func (api *API) GetAuth() string { 66 | return api.auth 67 | } 68 | 69 | /** 70 | Each request establishes its own connection to the server. This makes it easy 71 | to keep request/responses in order without doing any concurrency 72 | */ 73 | 74 | func (api *API) ZabbixRequest(method string, data interface{}) (JsonRPCResponse, error) { 75 | // Setup our JSONRPC Request data 76 | id := api.id 77 | api.id = api.id + 1 78 | jsonobj := JsonRPCRequest{"2.0", method, data, api.auth, id} 79 | encoded, err := json.Marshal(jsonobj) 80 | 81 | if err != nil { 82 | return JsonRPCResponse{}, err 83 | } 84 | 85 | // Setup our HTTP request 86 | request, err := http.NewRequest("POST", api.url, bytes.NewBuffer(encoded)) 87 | if err != nil { 88 | return JsonRPCResponse{}, err 89 | } 90 | request.Header.Add("Content-Type", "application/json-rpc") 91 | if api.auth != "" { 92 | // XXX Not required in practice, check spec 93 | //request.SetBasicAuth(api.user, api.passwd) 94 | //request.Header.Add("Authorization", api.auth) 95 | } 96 | 97 | // Execute the request 98 | response, err := api.Client.Do(request) 99 | if err != nil { 100 | return JsonRPCResponse{}, err 101 | } 102 | 103 | /** 104 | We can't rely on response.ContentLength because it will 105 | be set at -1 for large responses that are chunked. So 106 | we treat each API response as streamed data. 107 | */ 108 | var result JsonRPCResponse 109 | var buf bytes.Buffer 110 | 111 | _, err = io.Copy(&buf, response.Body) 112 | if err != nil { 113 | return JsonRPCResponse{}, err 114 | } 115 | 116 | json.Unmarshal(buf.Bytes(), &result) 117 | 118 | response.Body.Close() 119 | 120 | return result, nil 121 | } 122 | 123 | func (api *API) Login() (bool, error) { 124 | params := make(map[string]string, 0) 125 | params["user"] = api.user 126 | params["password"] = api.passwd 127 | 128 | response, err := api.ZabbixRequest("user.login", params) 129 | if err != nil { 130 | fmt.Printf("Error: %s\n", err) 131 | return false, err 132 | } 133 | 134 | if response.Error.Code != 0 { 135 | return false, &response.Error 136 | } 137 | 138 | api.auth = response.Result.(string) 139 | return true, nil 140 | } 141 | 142 | func (api *API) Logout() (bool, error) { 143 | emptyparams := make(map[string]string, 0) 144 | response, err := api.ZabbixRequest("user.logout", emptyparams) 145 | if err != nil { 146 | return false, err 147 | } 148 | 149 | if response.Error.Code != 0 { 150 | return false, &response.Error 151 | } 152 | 153 | return true, nil 154 | } 155 | 156 | func (api *API) Version() (string, error) { 157 | response, err := api.ZabbixRequest("APIInfo.version", make(map[string]string, 0)) 158 | if err != nil { 159 | return "", err 160 | } 161 | 162 | if response.Error.Code != 0 { 163 | return "", &response.Error 164 | } 165 | 166 | return response.Result.(string), nil 167 | } 168 | 169 | /** 170 | Interface to the user.* calls 171 | */ 172 | func (api *API) User(method string, data interface{}) ([]interface{}, error) { 173 | response, err := api.ZabbixRequest("user."+method, data) 174 | if err != nil { 175 | return nil, err 176 | } 177 | 178 | if response.Error.Code != 0 { 179 | return nil, &response.Error 180 | } 181 | 182 | return response.Result.([]interface{}), nil 183 | } 184 | 185 | /** 186 | Interface to the host.* calls 187 | */ 188 | func (api *API) Host(method string, data interface{}) ([]ZabbixHost, error) { 189 | response, err := api.ZabbixRequest("host."+method, data) 190 | if err != nil { 191 | return nil, err 192 | } 193 | 194 | if response.Error.Code != 0 { 195 | return nil, &response.Error 196 | } 197 | 198 | // XXX uhg... there has got to be a better way to convert the response 199 | // to the type I want to return 200 | res, err := json.Marshal(response.Result) 201 | var ret []ZabbixHost 202 | err = json.Unmarshal(res, &ret) 203 | return ret, nil 204 | } 205 | 206 | /** 207 | Interface to the graph.* calls 208 | */ 209 | func (api *API) Graph(method string, data interface{}) ([]ZabbixGraph, error) { 210 | response, err := api.ZabbixRequest("graph."+method, data) 211 | if err != nil { 212 | return nil, err 213 | } 214 | 215 | if response.Error.Code != 0 { 216 | return nil, &response.Error 217 | } 218 | 219 | // XXX uhg... there has got to be a better way to convert the response 220 | // to the type I want to return 221 | res, err := json.Marshal(response.Result) 222 | var ret []ZabbixGraph 223 | err = json.Unmarshal(res, &ret) 224 | return ret, nil 225 | } 226 | 227 | /** 228 | Interface to the history.* calls 229 | */ 230 | func (api *API) History(method string, data interface{}) ([]ZabbixHistoryItem, error) { 231 | response, err := api.ZabbixRequest("history."+method, data) 232 | if err != nil { 233 | return nil, err 234 | } 235 | 236 | if response.Error.Code != 0 { 237 | return nil, &response.Error 238 | } 239 | 240 | // XXX uhg... there has got to be a better way to convert the response 241 | // to the type I want to return 242 | res, err := json.Marshal(response.Result) 243 | var ret []ZabbixHistoryItem 244 | err = json.Unmarshal(res, &ret) 245 | return ret, nil 246 | } 247 | --------------------------------------------------------------------------------