├── LICENSE ├── README.md ├── api ├── api.go ├── command_handlers.go ├── objects.go ├── objects_test.go ├── routes.go ├── testdata │ └── contact_status.dat └── write_nagios_command.go ├── auth └── auth.go ├── config └── config.go ├── go.mod ├── go.sum └── main.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Sulochan Acharya 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nagios JSON API 2 | 3 | Intended to provide a JSON API like features for Nagios installations. Its written in golang and therefore systems wanting to run it only need to run a single binary for their distribution. 4 | 5 | Installation: 6 | == 7 | >> Follow [golang installation](https://golang.org/doc/install) to install golang. 8 | >> >> Clone this repo. 9 | >> >> >> Run: go build . from inside repo directory. 10 | >> >> >> >> You can now use the standalone binary to run the service. 11 | 12 | Run: 13 | == 14 | ``` 15 | $ ./go-nagios-api --addr=:9090 --cachefile=/opt/nagios/object.cache --statusfile=/opt/nagios/status.dat --commandfile=/opt/nagios/nagios.cmd 16 | 17 | Or you can provide a configuration file with these parameter in json format (configuration file overwrites cli flags) 18 | 19 | $ ./go-nagios-api --config=nagios-api.json 20 | ``` 21 | It will start the api service on port 8080. If you wish to change the port simply pass --addr=:80 to make it run on port 80. For running in production see init scripts. 22 | 23 | API Calls 24 | == 25 | 26 | #### Hosts and Services 27 | ``` 28 | GET /hosts : get all configured hosts 29 | GET /host/ : get this host 30 | GET /hoststatus : get all hoststatus 31 | GET /hoststatus/ : get hoststatus for this host 32 | GET /hostgroups : get all configured hostgroups 33 | GET /services : get all configured services 34 | GET /servicestatus : get all servicestatus 35 | GET /servicestatus/ : get service status for this service 36 | ``` 37 | 38 | #### External Commands 39 | ``` 40 | POST /disable_notifications 41 | POST /enable_notifications 42 | POST /disable_host_check 43 | POST /enable_host_check 44 | POST /disable_host_notifications 45 | POST /enable_host_notifications 46 | POST /acknowledge_host_problem 47 | POST /acknowledge_service_problem 48 | ... 49 | see code for full list of available commands. 50 | ``` 51 | 52 | #### Examples 53 | ``` 54 | To disable host check for host host1.example.net 55 | curl -i -XPOST http://127.0.0.1:9090/disable_host_check -d '{"hostname": "host1.example.net"}' 56 | 57 | To disable notification for all hosts 58 | curl -i -XPOST http://127.0.0.1:9090/disable_notifications 59 | 60 | To get all configured hostgroups 61 | curl -i http://127.0.0.1:9090/hostgroups 62 | 63 | To disable host check for a particular hostgroup 64 | curl -i -XPOST http://127.0.0.1:9090/disable_hostgroup_host_checks -d '{"hostgroup":"AwesomeHostGroup"}' 65 | 66 | To get details for a given host host1.example.net 67 | curl -i http://127.0.0.1:9090/host/host1.example.net 68 | ``` 69 | 70 | #### Missing feature or command 71 | If there is a feature that you would like to have and is missing please open an issue. Alternatively, 72 | write the feature and make a pull request. 73 | -------------------------------------------------------------------------------- /api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "log" 9 | "net/http" 10 | "os" 11 | "strings" 12 | "sync" 13 | "time" 14 | 15 | "github.com/gorilla/mux" 16 | ) 17 | 18 | type Api struct { 19 | router *mux.Router 20 | addr string 21 | fileObjectCache string 22 | fileCommand string 23 | fileStatus string 24 | statusData *StatusData 25 | staticData *StaticData 26 | mutex sync.RWMutex 27 | } 28 | 29 | func stringInSlice(a string, list []string) bool { 30 | for _, b := range list { 31 | if b == a { 32 | return true 33 | } 34 | } 35 | return false 36 | } 37 | 38 | func NewApi(addr, fileObjectCache, fileCommand, fileStatus string) *Api { 39 | api := &Api{ 40 | addr: addr, 41 | router: mux.NewRouter(), 42 | fileObjectCache: fileObjectCache, 43 | fileCommand: fileCommand, 44 | fileStatus: fileStatus, 45 | } 46 | 47 | api.buildRoutes() 48 | return api 49 | } 50 | 51 | func (s *Api) Run() error { 52 | log.Println("Reading object cache from ", s.fileObjectCache) 53 | log.Println("Writing commands to ", s.fileCommand) 54 | 55 | oc, err := os.Open(s.fileObjectCache) 56 | if err != nil { 57 | return err 58 | } 59 | defer oc.Close() 60 | 61 | s.staticData, err = readObjectCache(oc) 62 | if err != nil { 63 | return fmt.Errorf("Unable to parse object cache file: %s", err) 64 | } 65 | go s.spawnRefreshRoutein() 66 | 67 | http.Handle("/", s.router) 68 | 69 | err = http.ListenAndServe(s.addr, nil) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | return nil 75 | } 76 | 77 | func (s *Api) spawnRefreshRoutein() { 78 | for { 79 | data, err := s.refreshStatusDataFile() 80 | if err != nil { 81 | log.Println("Unable to refresh status data: ", err) 82 | } else { 83 | s.mutex.Lock() 84 | s.statusData = data 85 | s.mutex.Unlock() 86 | } 87 | time.Sleep(60 * time.Second) 88 | } 89 | } 90 | 91 | func readObjectCache(in io.Reader) (*StaticData, error) { 92 | data := NewStaticData() 93 | dat, err := ioutil.ReadAll(in) 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | a := strings.SplitAfterN(string(dat), "}", -1) 99 | for _, i := range a { 100 | lines := strings.Split(i, "\n") 101 | if stringInSlice("define command {", lines) { 102 | // We dont do anything with defined commands for now 103 | } 104 | 105 | if stringInSlice("define contactgroup {", lines) { 106 | // We dont do anything with contactgroup for now 107 | } 108 | 109 | if stringInSlice("define hostgroup {", lines) { 110 | thisgroup := map[string]string{} 111 | for _, i := range lines { 112 | if i == "define hostgroup {" || strings.TrimSpace(i) == "}" || i == "" { 113 | // Ignore these lines 114 | } else { 115 | if len(strings.Fields(i)) != 0 { 116 | thisgroup[strings.TrimSpace(strings.Fields(i)[0])] = strings.Join(strings.Fields(i)[1:], " ") 117 | } 118 | } 119 | } 120 | data.hostgroupList = append(data.hostgroupList, thisgroup) 121 | } 122 | 123 | if stringInSlice("define contact {", lines) { 124 | thiscontact := map[string]string{} 125 | for _, i := range lines { 126 | if i == "define contact {" || strings.TrimSpace(i) == "}" || i == "" { 127 | // Ignore these lines 128 | } else { 129 | if len(strings.Fields(i)) != 0 { 130 | thiscontact[strings.TrimSpace(strings.Fields(i)[0])] = strings.Join(strings.Fields(i)[1:], " ") 131 | } 132 | } 133 | } 134 | data.contactList = append(data.contactList, thiscontact) 135 | } 136 | 137 | if stringInSlice("define host {", lines) { 138 | thishost := map[string]string{} 139 | for _, i := range lines { 140 | if i == "define host {" || strings.TrimSpace(i) == "}" || i == "" { 141 | // Ignore these lines 142 | } else { 143 | if len(strings.Fields(i)) != 0 { 144 | thishost[strings.TrimSpace(strings.Fields(i)[0])] = strings.Join(strings.Fields(i)[1:], " ") 145 | } 146 | } 147 | } 148 | data.hostList = append(data.hostList, thishost) 149 | } 150 | 151 | if stringInSlice("define service {", lines) { 152 | thisservice := map[string]string{} 153 | for _, i := range lines { 154 | if i == "define service {" || strings.TrimSpace(i) == "}" || i == "" { 155 | // Ignore these lines 156 | } else { 157 | if len(strings.Fields(i)) != 0 { 158 | thisservice[strings.TrimSpace(strings.Fields(i)[0])] = strings.Join(strings.Fields(i)[1:], " ") 159 | } 160 | } 161 | } 162 | data.serviceList = append(data.serviceList, thisservice) 163 | } 164 | 165 | } 166 | 167 | return data, nil 168 | } 169 | 170 | type StatusData struct { 171 | Contacts []*ContactStatus 172 | Services []*ServiceStatus 173 | Hosts []*HostStatus 174 | HostServices map[string][]*ServiceStatus 175 | } 176 | 177 | func NewStatusData() *StatusData { 178 | return &StatusData{ 179 | HostServices: make(map[string][]*ServiceStatus), 180 | } 181 | } 182 | 183 | type StaticData struct { 184 | contactList []map[string]string 185 | serviceList []map[string]string 186 | hostList []map[string]string 187 | hostgroupList []map[string]string 188 | } 189 | 190 | func NewStaticData() *StaticData { 191 | return &StaticData{} 192 | } 193 | 194 | type settableType interface { 195 | setField(key, value string) error 196 | setCustomVariable(key, value string) 197 | } 198 | 199 | func parseBlock(o settableType, objecttype string, lines []string) error { 200 | start := objecttype + " {" 201 | for _, i := range lines { 202 | if i == start || i == " }" || i == "" || strings.TrimSpace(strings.Split(i, " ")[0]) == "}" { 203 | // Ignore these lines 204 | } else { 205 | pieces := strings.SplitN(strings.TrimSpace(i), "=", 2) 206 | if strings.HasPrefix(pieces[0], "_") { 207 | o.setCustomVariable(strings.TrimPrefix(pieces[0], "_"), strings.SplitN(pieces[1], ";", 2)[1]) 208 | } else { 209 | o.setField(pieces[0], pieces[1]) 210 | } 211 | } 212 | } 213 | 214 | return nil 215 | } 216 | 217 | func (s *Api) refreshStatusDataFile() (*StatusData, error) { 218 | log.Println("Refreshing data from ", s.fileStatus) 219 | 220 | fh, err := os.Open(s.fileStatus) 221 | if err != nil { 222 | return nil, err 223 | } 224 | defer fh.Close() 225 | 226 | return refreshStatusData(fh) 227 | } 228 | 229 | func refreshStatusData(fh io.Reader) (*StatusData, error) { 230 | data := NewStatusData() 231 | dat, err := ioutil.ReadAll(fh) 232 | if err != nil { 233 | return nil, err 234 | } 235 | 236 | a := strings.SplitAfterN(string(dat), "}", -1) 237 | for _, i := range a { 238 | lines := strings.Split(i, "\n") 239 | if stringInSlice("contactstatus {", lines) { 240 | obj := &ContactStatus{} 241 | parseBlock(obj, "contactstatus", lines) 242 | data.Contacts = append(data.Contacts, obj) 243 | } 244 | 245 | if stringInSlice("servicestatus {", lines) { 246 | obj := &ServiceStatus{} 247 | parseBlock(obj, "servicestatus", lines) 248 | data.Services = append(data.Services, obj) 249 | 250 | data.HostServices[obj.HostName] = append(data.HostServices[obj.HostName], obj) 251 | } 252 | 253 | if stringInSlice("hoststatus {", lines) { 254 | obj := &HostStatus{} 255 | parseBlock(obj, "hoststatus", lines) 256 | data.Hosts = append(data.Hosts, obj) 257 | } 258 | } 259 | 260 | return data, nil 261 | } 262 | 263 | // HandleGetContacts returns all configured contactlist 264 | // GET: /contacts 265 | func (a *Api) HandleGetContacts(w http.ResponseWriter, r *http.Request) { 266 | w.Header().Set("Content-Type", "application/json") 267 | json.NewEncoder(w).Encode(a.staticData.contactList) 268 | } 269 | 270 | // HandleGetAllHostStatus returns hoststatus for all hosts 271 | // GET: /hoststatus 272 | func (a *Api) HandleGetAllHostStatus(w http.ResponseWriter, r *http.Request) { 273 | w.Header().Set("Content-Type", "application/json") 274 | a.mutex.RLock() 275 | defer a.mutex.RUnlock() 276 | json.NewEncoder(w).Encode(a.statusData.Hosts) 277 | } 278 | 279 | // HandleGetHostStatusForHost returns hoststatus for requested host only 280 | // GET: /hoststatus/ 281 | func (a *Api) HandleGetHostStatusForHost(w http.ResponseWriter, r *http.Request) { 282 | vars := mux.Vars(r) 283 | host, ok := vars["hostname"] 284 | if !ok { 285 | http.Error(w, "Could not find host to lookup", 400) 286 | return 287 | } 288 | 289 | a.mutex.RLock() 290 | defer a.mutex.RUnlock() 291 | for _, item := range a.statusData.Hosts { 292 | if item.HostName == host { 293 | w.Header().Set("Content-Type", "application/json") 294 | json.NewEncoder(w).Encode(item) 295 | return 296 | } 297 | } 298 | 299 | http.Error(w, "Host not found", 404) 300 | return 301 | } 302 | 303 | // HandleGetServiceStatus return all servicestatus 304 | // GET: /servicestatus 305 | func (a *Api) HandleGetServiceStatus(w http.ResponseWriter, r *http.Request) { 306 | w.Header().Set("Content-Type", "application/json") 307 | a.mutex.RLock() 308 | defer a.mutex.RUnlock() 309 | json.NewEncoder(w).Encode(a.statusData.Services) 310 | } 311 | 312 | // HandleGetServiceStatusForService returns all servicestatus for requested service only 313 | // GET: /servicestatus/ 314 | func (a *Api) HandleGetServiceStatusForService(w http.ResponseWriter, r *http.Request) { 315 | vars := mux.Vars(r) 316 | service, ok := vars["service"] 317 | if !ok { 318 | http.Error(w, "Could not find service to lookup", 400) 319 | return 320 | } 321 | 322 | var serviceList []*ServiceStatus 323 | a.mutex.RLock() 324 | defer a.mutex.RUnlock() 325 | for _, item := range a.statusData.Services { 326 | if item.ServiceDescription == service { 327 | serviceList = append(serviceList, item) 328 | } 329 | } 330 | w.Header().Set("Content-Type", "application/json") 331 | json.NewEncoder(w).Encode(serviceList) 332 | return 333 | } 334 | 335 | // HandleGetHost retruns host info only on the host requested 336 | // GET: /host/ 337 | func (a *Api) HandleGetHost(w http.ResponseWriter, r *http.Request) { 338 | vars := mux.Vars(r) 339 | host, ok := vars["hostname"] 340 | log.Println(host) 341 | if !ok { 342 | http.Error(w, "Invalid hostname provided", 400) 343 | return 344 | } 345 | 346 | for _, item := range a.staticData.hostList { 347 | if item["host_name"] == host { 348 | w.Header().Set("Content-Type", "application/json") 349 | json.NewEncoder(w).Encode(item) 350 | return 351 | } 352 | } 353 | 354 | http.Error(w, "Host Not Found", 404) 355 | return 356 | } 357 | 358 | // HandleGetServicesForHost retruns all services defined for the given host 359 | // GET: /host//services 360 | func (a *Api) HandleGetServicesForHost(w http.ResponseWriter, r *http.Request) { 361 | vars := mux.Vars(r) 362 | host, ok := vars["hostname"] 363 | if !ok { 364 | http.Error(w, "Invalid hostname provided", 400) 365 | return 366 | } 367 | 368 | a.mutex.RLock() 369 | defer a.mutex.RUnlock() 370 | 371 | sList, ok := a.statusData.HostServices[host] 372 | if !ok { 373 | http.Error(w, "Host Not Found", 404) 374 | return 375 | } 376 | 377 | w.Header().Set("Content-Type", "application/json") 378 | json.NewEncoder(w).Encode(sList) 379 | } 380 | 381 | // HandleGetConfiguredHosts returns a list with configured host names 382 | // GET: /hosts 383 | func (a *Api) HandleGetConfiguredHosts(w http.ResponseWriter, r *http.Request) { 384 | var thesehosts []string 385 | for _, item := range a.staticData.hostList { 386 | h := item["host_name"] 387 | if !stringInSlice(h, thesehosts) { 388 | thesehosts = append(thesehosts, h) 389 | } 390 | } 391 | w.Header().Set("Content-Type", "application/json") 392 | json.NewEncoder(w).Encode(thesehosts) 393 | } 394 | 395 | // HandleGetConfiguredServices returns a list with configured service names 396 | // GET: /services 397 | func (a *Api) HandleGetConfiguredServices(w http.ResponseWriter, r *http.Request) { 398 | var services []string 399 | a.mutex.RLock() 400 | defer a.mutex.RUnlock() 401 | for _, item := range a.statusData.Services { 402 | if !stringInSlice(item.ServiceDescription, services) { 403 | services = append(services, item.ServiceDescription) 404 | } 405 | } 406 | w.Header().Set("Content-Type", "application/json") 407 | json.NewEncoder(w).Encode(services) 408 | } 409 | 410 | type hostGroup struct { 411 | HostGroupName string `json:"hostgroup_name"` 412 | Alias string `json:"alias"` 413 | Members []string `json:"members"` 414 | } 415 | 416 | // HandleGetHostGroups returns all defined hostgroups 417 | // GET: /hostgroups 418 | func (a *Api) HandleGetHostGroups(w http.ResponseWriter, r *http.Request) { 419 | var hg []hostGroup 420 | for _, item := range a.staticData.hostgroupList { 421 | group := hostGroup{HostGroupName: item["hostgroup_name"], Alias: item["alias"], Members: strings.Split(item["members"], ",")} 422 | hg = append(hg, group) 423 | } 424 | w.Header().Set("Content-Type", "application/json") 425 | json.NewEncoder(w).Encode(hg) 426 | } 427 | -------------------------------------------------------------------------------- /api/command_handlers.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | // All external commands https://old.nagios.org/developerinfo/externalcommands/commandlist.php 10 | 11 | // HandleAcknowledgeHostProblem ACKNOWLEDGE_HOST_PROBLEM 12 | // POST: /acknowledge_host_problem/ 13 | // {sticky:bool, notify:bool, persistent:bool, author:string, comment:string} 14 | func (a *Api) HandleAcknowledgeHostProblem(w http.ResponseWriter, r *http.Request) { 15 | decoder := json.NewDecoder(r.Body) 16 | var data struct { 17 | Hostname string 18 | Sticky int 19 | Notify int 20 | Persistent int 21 | Author string 22 | Comment string 23 | } 24 | err := decoder.Decode(&data) 25 | if err != nil { 26 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 27 | return 28 | } 29 | 30 | if data.Hostname == "" { 31 | http.Error(w, "Missing host", 400) 32 | return 33 | } 34 | 35 | if data.Sticky == 0 { 36 | data.Sticky = 2 37 | } 38 | 39 | if data.Notify == 0 { 40 | data.Notify = 1 41 | } 42 | 43 | if data.Persistent == 0 { 44 | data.Persistent = 1 45 | } 46 | 47 | if data.Author == "" { 48 | http.Error(w, "Error: Author filed is required", 400) 49 | return 50 | } 51 | 52 | command := fmt.Sprintf("%s;%s;%d;%d;%d;%s;%s", "ACKNOWLEDGE_HOST_PROBLEM", data.Hostname, data.Sticky, data.Notify, data.Persistent, data.Author, data.Comment) 53 | a.WriteCommandToFile(w, command) 54 | } 55 | 56 | // HandleAcknowledgeServiceProblem ACKNOWLEDGE_SVC_PROBLEM 57 | // POST: /acknowledge_service_problem 58 | // {sticky:bool, notify:bool, persistent:bool, author:string, comment:string} 59 | func (a *Api) HandleAcknowledgeServiceProblem(w http.ResponseWriter, r *http.Request) { 60 | decoder := json.NewDecoder(r.Body) 61 | var data struct { 62 | Hostname string 63 | ServiceDescription string 64 | Sticky int 65 | Notify int 66 | Persistent int 67 | Author string 68 | Comment string 69 | } 70 | err := decoder.Decode(&data) 71 | if err != nil { 72 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 73 | return 74 | } 75 | 76 | if data.Hostname == "" { 77 | http.Error(w, "Missing hostname in data.", 400) 78 | return 79 | } 80 | 81 | if data.ServiceDescription == "" { 82 | http.Error(w, "Missing servicedescription in data.", 400) 83 | return 84 | } 85 | 86 | if data.Sticky == 0 { 87 | data.Sticky = 2 88 | } 89 | 90 | if data.Notify == 0 { 91 | data.Notify = 1 92 | } 93 | 94 | if data.Persistent == 0 { 95 | data.Persistent = 1 96 | } 97 | 98 | command := fmt.Sprintf("%s;%s;%s;%d;%d;%d;%s;%s", "ACKNOWLEDGE_SVC_PROBLEM", data.Hostname, data.ServiceDescription, data.Sticky, data.Notify, data.Persistent, data.Author, data.Comment) 99 | a.WriteCommandToFile(w, command) 100 | } 101 | 102 | // HandleAddHostComment ADD_HOST_COMMENT 103 | // POST: /add_host_comment/ 104 | // {persistent:bool, author:string, comment:string} 105 | func (a *Api) HandleAddHostComment(w http.ResponseWriter, r *http.Request) { 106 | decoder := json.NewDecoder(r.Body) 107 | var data struct { 108 | Hostname string 109 | Persistent int 110 | Author string 111 | Comment string 112 | } 113 | err := decoder.Decode(&data) 114 | if err != nil { 115 | http.Error(w, fmt.Sprintf("Error: %s", err), 500) 116 | return 117 | } 118 | 119 | if data.Hostname == "" { 120 | http.Error(w, "Missing host", 400) 121 | return 122 | } 123 | 124 | if data.Persistent == 0 { 125 | data.Persistent = 1 126 | } 127 | 128 | if data.Author == "" { 129 | http.Error(w, fmt.Sprintf("Error: Author field is required"), 400) 130 | return 131 | } 132 | 133 | if data.Comment == "" { 134 | http.Error(w, fmt.Sprintf("Error: Comment can not be empty"), 400) 135 | return 136 | } 137 | 138 | command := fmt.Sprintf("%s;%s;%d;%s;%s", "ADD_HOST_COMMENT", data.Hostname, data.Persistent, data.Author, data.Comment) 139 | a.WriteCommandToFile(w, command) 140 | 141 | } 142 | 143 | // HandleAddServiceComment ADD_SVC_COMMENT 144 | // POST: /add_svc_comment// 145 | // {persistent:bool, author:string, comment:string} 146 | func (a *Api) HandleAddServiceComment(w http.ResponseWriter, r *http.Request) { 147 | decoder := json.NewDecoder(r.Body) 148 | var data struct { 149 | Hostname string 150 | Service string 151 | Persistent int 152 | Author string 153 | Comment string 154 | } 155 | err := decoder.Decode(&data) 156 | if err != nil { 157 | http.Error(w, fmt.Sprintf("Error: %s", err), 500) 158 | return 159 | } 160 | 161 | if data.Hostname == "" { 162 | http.Error(w, "Missing hostname", 400) 163 | return 164 | } 165 | 166 | if data.Persistent == 0 { 167 | data.Persistent = 1 168 | } 169 | 170 | if data.Author == "" { 171 | http.Error(w, fmt.Sprintf("Error: Author field is required"), 400) 172 | return 173 | } 174 | 175 | if data.Service == "" { 176 | http.Error(w, fmt.Sprintf("Error: ServiceDesc can not be empty"), 400) 177 | return 178 | } 179 | 180 | if data.Comment == "" { 181 | http.Error(w, fmt.Sprintf("Error: Comment can not be empty"), 400) 182 | return 183 | } 184 | 185 | command := fmt.Sprintf("%s;%s;%s;%d;%s;%s", "ADD_SVC_COMMENT", data.Hostname, data.Service, data.Persistent, data.Author, data.Comment) 186 | a.WriteCommandToFile(w, command) 187 | } 188 | 189 | // HandleDeleteAllHostComment DEL_ALL_HOST_COMMENTS 190 | func (a *Api) HandleDeleteAllHostComment(w http.ResponseWriter, r *http.Request) { 191 | decoder := json.NewDecoder(r.Body) 192 | var data struct { 193 | Hostname string 194 | } 195 | err := decoder.Decode(&data) 196 | if err != nil { 197 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 198 | return 199 | } 200 | 201 | if data.Hostname == "" { 202 | http.Error(w, fmt.Sprintf("Error: Hostname field is required"), 400) 203 | return 204 | } 205 | 206 | command := fmt.Sprintf("%s;%s", "DEL_ALL_HOST_COMMENTS", data.Hostname) 207 | a.WriteCommandToFile(w, command) 208 | } 209 | 210 | // HandleDeleteAllServiceComment DEL_ALL_SVC_COMMENTS 211 | func (a *Api) HandleDeleteAllServiceComment(w http.ResponseWriter, r *http.Request) { 212 | decoder := json.NewDecoder(r.Body) 213 | var data struct { 214 | Hostname string 215 | } 216 | err := decoder.Decode(&data) 217 | if err != nil { 218 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 219 | return 220 | } 221 | 222 | if data.Hostname == "" { 223 | http.Error(w, fmt.Sprintf("Error: Hostname field is required"), 400) 224 | return 225 | } 226 | 227 | command := fmt.Sprintf("%s;%s", "DEL_ALL_SVC_COMMENTS", data.Hostname) 228 | a.WriteCommandToFile(w, command) 229 | } 230 | 231 | // HandleDeleteHostComment DEL_HOST_COMMENT 232 | func (a *Api) HandleDeleteHostComment(w http.ResponseWriter, r *http.Request) { 233 | decoder := json.NewDecoder(r.Body) 234 | var data struct { 235 | CommentID string 236 | } 237 | err := decoder.Decode(&data) 238 | if err != nil { 239 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 240 | return 241 | } 242 | 243 | if data.CommentID == "" { 244 | http.Error(w, fmt.Sprintf("Error: CommentID field is required"), 400) 245 | return 246 | } 247 | 248 | command := fmt.Sprintf("%s;%s", "DEL_HOST_COMMENT", data.CommentID) 249 | a.WriteCommandToFile(w, command) 250 | } 251 | 252 | // HandleDeleteServiceComment DEL_SVC_COMMENT 253 | func (a *Api) HandleDeleteServiceComment(w http.ResponseWriter, r *http.Request) { 254 | decoder := json.NewDecoder(r.Body) 255 | var data struct { 256 | CommentID string 257 | } 258 | err := decoder.Decode(&data) 259 | if err != nil { 260 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 261 | return 262 | } 263 | 264 | if data.CommentID == "" { 265 | http.Error(w, fmt.Sprintf("Error: CommentID field is required"), 400) 266 | return 267 | } 268 | 269 | command := fmt.Sprintf("%s;%s", "DEL_SVC_COMMENT", data.CommentID) 270 | a.WriteCommandToFile(w, command) 271 | } 272 | 273 | // HandleDisableAllNotificationBeyondHost DISABLE_ALL_NOTIFICATIONS_BEYOND_HOST 274 | func (a *Api) HandleDisableAllNotificationBeyondHost(w http.ResponseWriter, r *http.Request) { 275 | decoder := json.NewDecoder(r.Body) 276 | var data struct { 277 | Hostname string 278 | } 279 | err := decoder.Decode(&data) 280 | if err != nil { 281 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 282 | return 283 | } 284 | 285 | if data.Hostname == "" { 286 | http.Error(w, fmt.Sprintf("Error: Hostname field is required"), 400) 287 | return 288 | } 289 | 290 | command := fmt.Sprintf("%s;%s", "DISABLE_ALL_NOTIFICATIONS_BEYOND_HOST", data.Hostname) 291 | a.WriteCommandToFile(w, command) 292 | } 293 | 294 | // HandleEnableAllNotificationBeyondHost ENABLE_ALL_NOTIFICATIONS_BEYOND_HOST 295 | func (a *Api) HandleEnableAllNotificationBeyondHost(w http.ResponseWriter, r *http.Request) { 296 | decoder := json.NewDecoder(r.Body) 297 | var data struct { 298 | Hostname string 299 | } 300 | err := decoder.Decode(&data) 301 | if err != nil { 302 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 303 | return 304 | } 305 | 306 | if data.Hostname == "" { 307 | http.Error(w, fmt.Sprintf("Error: Hostname field is required"), 400) 308 | return 309 | } 310 | 311 | command := fmt.Sprintf("%s;%s", "ENABLE_ALL_NOTIFICATIONS_BEYOND_HOST", data.Hostname) 312 | a.WriteCommandToFile(w, command) 313 | } 314 | 315 | // HandleDisableHostgroupHostChecks DISABLE_HOSTGROUP_HOST_CHECKS 316 | func (a *Api) HandleDisableHostgroupHostChecks(w http.ResponseWriter, r *http.Request) { 317 | decoder := json.NewDecoder(r.Body) 318 | var data struct { 319 | Hostgroup string 320 | } 321 | err := decoder.Decode(&data) 322 | if err != nil { 323 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 324 | return 325 | } 326 | 327 | if data.Hostgroup == "" { 328 | http.Error(w, fmt.Sprintf("Error: Hostgroup name field is required"), 400) 329 | return 330 | } 331 | 332 | command := fmt.Sprintf("%s;%s", "DISABLE_HOSTGROUP_HOST_CHECKS", data.Hostgroup) 333 | a.WriteCommandToFile(w, command) 334 | } 335 | 336 | // HandleEnableHostgroupHostChecks ENABLE_HOSTGROUP_HOST_CHECKS 337 | func (a *Api) HandleEnableHostgroupHostChecks(w http.ResponseWriter, r *http.Request) { 338 | decoder := json.NewDecoder(r.Body) 339 | var data struct { 340 | Hostgroup string 341 | } 342 | err := decoder.Decode(&data) 343 | if err != nil { 344 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 345 | return 346 | } 347 | 348 | if data.Hostgroup == "" { 349 | http.Error(w, fmt.Sprintf("Error: Hostgroup name field is required"), 400) 350 | return 351 | } 352 | 353 | command := fmt.Sprintf("%s;%s", "ENABLE_HOSTGROUP_HOST_CHECKS", data.Hostgroup) 354 | a.WriteCommandToFile(w, command) 355 | } 356 | 357 | // HandleDisableHostgroupHostNotification DISABLE_HOSTGROUP_HOST_NOTIFICATIONS 358 | func (a *Api) HandleDisableHostgroupHostNotification(w http.ResponseWriter, r *http.Request) { 359 | decoder := json.NewDecoder(r.Body) 360 | var data struct { 361 | Hostgroup string 362 | } 363 | err := decoder.Decode(&data) 364 | if err != nil { 365 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 366 | return 367 | } 368 | 369 | if data.Hostgroup == "" { 370 | http.Error(w, fmt.Sprintf("Error: Hostgroup name field is required"), 400) 371 | return 372 | } 373 | 374 | command := fmt.Sprintf("%s;%s", "DISABLE_HOSTGROUP_HOST_NOTIFICATIONS", data.Hostgroup) 375 | a.WriteCommandToFile(w, command) 376 | } 377 | 378 | // HandleEnableHostgroupHostNotification ENABLE_HOSTGROUP_HOST_NOTIFICATIONS; 379 | func (a *Api) HandleEnableHostgroupHostNotification(w http.ResponseWriter, r *http.Request) { 380 | decoder := json.NewDecoder(r.Body) 381 | var data struct { 382 | Hostgroup string 383 | } 384 | err := decoder.Decode(&data) 385 | if err != nil { 386 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 387 | return 388 | } 389 | 390 | if data.Hostgroup == "" { 391 | http.Error(w, fmt.Sprintf("Error: Hostgroup name field is required"), 400) 392 | return 393 | } 394 | 395 | command := fmt.Sprintf("%s;%s", "ENABLE_HOSTGROUP_HOST_NOTIFICATIONS", data.Hostgroup) 396 | a.WriteCommandToFile(w, command) 397 | } 398 | 399 | // HandleDisableHostgroupServiceChecks DISABLE_HOSTGROUP_SVC_CHECKS 400 | func (a *Api) HandleDisableHostgroupServiceChecks(w http.ResponseWriter, r *http.Request) { 401 | decoder := json.NewDecoder(r.Body) 402 | var data struct { 403 | Hostgroup string 404 | } 405 | err := decoder.Decode(&data) 406 | if err != nil { 407 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 408 | return 409 | } 410 | 411 | if data.Hostgroup == "" { 412 | http.Error(w, fmt.Sprintf("Error: Hostgroup name field is required"), 400) 413 | return 414 | } 415 | 416 | command := fmt.Sprintf("%s;%s", "DISABLE_HOSTGROUP_SVC_CHECKS", data.Hostgroup) 417 | a.WriteCommandToFile(w, command) 418 | } 419 | 420 | // HandleEnableHostgroupServiceChecks ENABLE_HOSTGROUP_SVC_CHECKS 421 | func (a *Api) HandleEnableHostgroupServiceChecks(w http.ResponseWriter, r *http.Request) { 422 | decoder := json.NewDecoder(r.Body) 423 | var data struct { 424 | Hostgroup string 425 | } 426 | err := decoder.Decode(&data) 427 | if err != nil { 428 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 429 | return 430 | } 431 | 432 | if data.Hostgroup == "" { 433 | http.Error(w, fmt.Sprintf("Error: Hostgroup name field is required"), 400) 434 | return 435 | } 436 | 437 | command := fmt.Sprintf("%s;%s", "ENABLE_HOSTGROUP_SVC_CHECKS", data.Hostgroup) 438 | a.WriteCommandToFile(w, command) 439 | } 440 | 441 | // HandleDisableHostgroupServiceNotifications DISABLE_HOSTGROUP_SVC_NOTIFICATIONS 442 | func (a *Api) HandleDisableHostgroupServiceNotifications(w http.ResponseWriter, r *http.Request) { 443 | decoder := json.NewDecoder(r.Body) 444 | var data struct { 445 | Hostgroup string 446 | } 447 | err := decoder.Decode(&data) 448 | if err != nil { 449 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 450 | return 451 | } 452 | 453 | if data.Hostgroup == "" { 454 | http.Error(w, fmt.Sprintf("Error: Hostgroup name field is required"), 400) 455 | return 456 | } 457 | 458 | command := fmt.Sprintf("%s;%s", "DISABLE_HOSTGROUP_SVC_NOTIFICATIONS", data.Hostgroup) 459 | a.WriteCommandToFile(w, command) 460 | } 461 | 462 | // HandleEnableHostgroupServiceNotifications ENABLE_HOSTGROUP_SVC_NOTIFICATIONS 463 | func (a *Api) HandleEnableHostgroupServiceNotifications(w http.ResponseWriter, r *http.Request) { 464 | decoder := json.NewDecoder(r.Body) 465 | var data struct { 466 | Hostgroup string 467 | } 468 | err := decoder.Decode(&data) 469 | if err != nil { 470 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 471 | return 472 | } 473 | 474 | if data.Hostgroup == "" { 475 | http.Error(w, fmt.Sprintf("Error: Hostgroup name field is required"), 400) 476 | return 477 | } 478 | 479 | command := fmt.Sprintf("%s;%s", "ENABLE_HOSTGROUP_SVC_NOTIFICATIONS", data.Hostgroup) 480 | a.WriteCommandToFile(w, command) 481 | } 482 | 483 | // HandleDisableHostandChildNotifications DISABLE_HOST_AND_CHILD_NOTIFICATIONS 484 | func (a *Api) HandleDisableHostandChildNotifications(w http.ResponseWriter, r *http.Request) { 485 | decoder := json.NewDecoder(r.Body) 486 | var data struct { 487 | Hostname string 488 | } 489 | err := decoder.Decode(&data) 490 | if err != nil { 491 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 492 | return 493 | } 494 | 495 | if data.Hostname == "" { 496 | http.Error(w, fmt.Sprintf("Error: Hostname field is required"), 400) 497 | return 498 | } 499 | 500 | command := fmt.Sprintf("%s;%s", "DISABLE_HOST_AND_CHILD_NOTIFICATIONS", data.Hostname) 501 | a.WriteCommandToFile(w, command) 502 | } 503 | 504 | // ENABLE_HOST_AND_CHILD_NOTIFICATIONS 505 | func (a *Api) HandleEnableHostandChildNotifications(w http.ResponseWriter, r *http.Request) { 506 | decoder := json.NewDecoder(r.Body) 507 | var data struct { 508 | Hostname string 509 | } 510 | err := decoder.Decode(&data) 511 | if err != nil { 512 | http.Error(w, fmt.Sprintf("Error: %s", err), 400) 513 | return 514 | } 515 | 516 | if data.Hostname == "" { 517 | http.Error(w, fmt.Sprintf("Error: Hostname field is required"), 400) 518 | return 519 | } 520 | 521 | command := fmt.Sprintf("%s;%s", "ENABLE_HOST_AND_CHILD_NOTIFICATIONS", data.Hostname) 522 | a.WriteCommandToFile(w, command) 523 | } 524 | 525 | // DISABLE_HOST_CHECK 526 | // POST: /disable_host_check 527 | func (a *Api) HandleDisableHostCheck(w http.ResponseWriter, r *http.Request) { 528 | decoder := json.NewDecoder(r.Body) 529 | var host struct{ Hostname string } 530 | err := decoder.Decode(&host) 531 | if err != nil { 532 | http.Error(w, "Could not decode request body", 400) 533 | return 534 | } 535 | 536 | command := fmt.Sprintf("%s;%s", "DISABLE_HOST_CHECK", host.Hostname) 537 | a.WriteCommandToFile(w, command) 538 | } 539 | 540 | // ENABLE_HOST_CHECK 541 | // POST: /enable_host_check 542 | func (a *Api) HandleEnableHostCheck(w http.ResponseWriter, r *http.Request) { 543 | decoder := json.NewDecoder(r.Body) 544 | var host struct{ Hostname string } 545 | err := decoder.Decode(&host) 546 | if err != nil { 547 | http.Error(w, "Could not decode request body", 400) 548 | return 549 | } 550 | 551 | command := fmt.Sprintf("%s;%s", "ENABLE_HOST_CHECK", host.Hostname) 552 | a.WriteCommandToFile(w, command) 553 | } 554 | 555 | // DISABLE_HOST_NOTIFICATIONS 556 | // POST: /disable_host_notifications 557 | func (a *Api) HandleDisableHostNotifications(w http.ResponseWriter, r *http.Request) { 558 | decoder := json.NewDecoder(r.Body) 559 | var host struct{ Hostname string } 560 | err := decoder.Decode(&host) 561 | if err != nil { 562 | http.Error(w, "Could not decode request body", 400) 563 | return 564 | } 565 | 566 | command := fmt.Sprintf("%s;%s", "DISABLE_HOST_NOTIFICATIONS", host.Hostname) 567 | a.WriteCommandToFile(w, command) 568 | } 569 | 570 | // ENABLE_HOST_NOTIFICATIONS 571 | // POST: /enable_host_notifications 572 | func (a *Api) HandleEnableHostNotifications(w http.ResponseWriter, r *http.Request) { 573 | decoder := json.NewDecoder(r.Body) 574 | var host struct{ Hostname string } 575 | err := decoder.Decode(&host) 576 | if err != nil { 577 | http.Error(w, "Could not decode request body", 400) 578 | return 579 | } 580 | 581 | command := fmt.Sprintf("%s;%s", "ENABLE_HOST_NOTIFICATIONS", host.Hostname) 582 | a.WriteCommandToFile(w, command) 583 | } 584 | 585 | // DISABLE_NOTIFICATIONS 586 | // POST: /disable_notifications 587 | func (a *Api) HandleDisableNotifications(w http.ResponseWriter, r *http.Request) { 588 | command := "DISABLE_NOTIFICATIONS" 589 | a.WriteCommandToFile(w, command) 590 | } 591 | 592 | // ENABLE_NOTIFICATIONS 593 | // POST: /enable_notifications 594 | func (a *Api) HandleEnableNotifications(w http.ResponseWriter, r *http.Request) { 595 | command := "ENABLE_NOTIFICATIONS" 596 | a.WriteCommandToFile(w, command) 597 | } 598 | 599 | // SCHEDULE_FORCED_HOST_CHECK 600 | // SCHEDULE_FORCED_HOST_CHECK;; 601 | func (a *Api) HandleScheduleForcedHostCheck(w http.ResponseWriter, r *http.Request) { 602 | } 603 | 604 | // SCHEDULE_FORCED_HOST_SVC_CHECKS 605 | // SCHEDULE_FORCED_HOST_SVC_CHECKS;; 606 | func (a *Api) HandleScheduleForcedHostServiceChecks(w http.ResponseWriter, r *http.Request) { 607 | } 608 | 609 | // SCHEDULE_FORCED_SVC_CHECK 610 | // SCHEDULE_FORCED_SVC_CHECK;;; 611 | func (a *Api) HandleScheduleForcedServiceCheck(w http.ResponseWriter, r *http.Request) { 612 | } 613 | 614 | // SCHEDULE_HOST_CHECK 615 | // SCHEDULE_HOST_CHECK;; 616 | func (a *Api) HandleScheduleHostCheck(w http.ResponseWriter, r *http.Request) { 617 | } 618 | 619 | // SCHEDULE_HOST_DOWNTIME 620 | // SCHEDULE_HOST_DOWNTIME;;;;;;;; 621 | func (a *Api) HandleScheduleHostDowntime(w http.ResponseWriter, r *http.Request) { 622 | decoder := json.NewDecoder(r.Body) 623 | var data struct { 624 | Hostname string `json:"hostname"` 625 | StartTime int64 `json:"start_time"` 626 | EndTime int64 `json:"end_time"` 627 | Fixed uint8 `json:"fixed"` 628 | TriggerId int64 `json:"trigger_id"` 629 | Duration int64 `json:"duration"` 630 | Author string `json:"author"` 631 | Comment string `json:"comment"` 632 | } 633 | err := decoder.Decode(&data) 634 | if err != nil { 635 | http.Error(w, fmt.Sprintf("Error: %s", err), http.StatusInternalServerError) 636 | return 637 | } 638 | 639 | if data.Hostname == "" { 640 | http.Error(w, "Missing host", http.StatusBadRequest) 641 | return 642 | } 643 | 644 | if data.Author == "" { 645 | http.Error(w, fmt.Sprintf("Error: Author field is required"), http.StatusBadRequest) 646 | return 647 | } 648 | 649 | if data.Comment == "" { 650 | http.Error(w, fmt.Sprintf("Error: Comment can not be empty"), http.StatusBadRequest) 651 | return 652 | } 653 | 654 | if data.StartTime >= data.EndTime { 655 | http.Error(w, "start_time must be less than end_time", http.StatusBadRequest) 656 | } 657 | 658 | if data.Duration == 0 { 659 | http.Error(w, "duration of maintenance must be greater than 0 seconds", http.StatusBadRequest) 660 | } 661 | 662 | command := fmt.Sprintf("%s;%s;%d;%d;%d;%d;%d;%s;%s", "SCHEDULE_HOST_DOWNTIME", data.Hostname, data.StartTime, data.EndTime, data.Fixed, data.TriggerId, data.Duration, data.Author, data.Comment) 663 | a.WriteCommandToFile(w, command) 664 | } 665 | 666 | func (a *Api) WriteCommandToFile(w http.ResponseWriter, command string) { 667 | if err := a.WriteCommand(command); err != nil { 668 | http.Error(w, "Could not execute command", http.StatusInternalServerError) 669 | return 670 | } 671 | } 672 | -------------------------------------------------------------------------------- /api/objects.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "reflect" 7 | "strings" 8 | ) 9 | 10 | type ContactStatus struct { 11 | ContactName string `json:"contact_name"` 12 | HostNotificationPeriod string `json:"host_notification_period"` 13 | HostNotificationsEnabled string `json:"host_notifications_enabled"` 14 | LastHostNotification string `json:"last_host_notification"` 15 | LastServiceNotification string `json:"last_service_notification"` 16 | ModifiedAttributes string `json:"modified_attributes"` 17 | ModifiedHostAttributes string `json:"modified_host_attributes"` 18 | ModifiedServiceAttributes string `json:"modified_service_attributes"` 19 | ServiceNotificationPeriod string `json:"service_notification_period"` 20 | ServiceNotificationsEnabled string `json:"service_notifications_enabled"` 21 | CustomVariables map[string]string `json:"custom_variables,omitempty"` 22 | } 23 | type HostStatus struct { 24 | AcknowledgementType string `json:"acknowledgement_type"` 25 | ActiveChecksEnabled string `json:"active_checks_enabled"` 26 | CheckCommand string `json:"check_command"` 27 | CheckExecutionTime string `json:"check_execution_time"` 28 | CheckInterval string `json:"check_interval"` 29 | CheckLatency string `json:"check_latency"` 30 | CheckOptions string `json:"check_options"` 31 | CheckPeriod string `json:"check_period"` 32 | CheckType string `json:"check_type"` 33 | CurrentAttempt string `json:"current_attempt"` 34 | CurrentEventId string `json:"current_event_id"` 35 | CurrentNotificationId string `json:"current_notification_id"` 36 | CurrentNotificationNumber string `json:"current_notification_number"` 37 | CurrentProblemId string `json:"current_problem_id"` 38 | CurrentState string `json:"current_state"` 39 | EventHandler string `json:"event_handler"` 40 | EventHandlerEnabled string `json:"event_handler_enabled"` 41 | FlapDetectionEnabled string `json:"flap_detection_enabled"` 42 | HasBeenChecked string `json:"has_been_checked"` 43 | HostName string `json:"host_name"` 44 | IsFlapping string `json:"is_flapping"` 45 | LastCheck string `json:"last_check"` 46 | LastEventId string `json:"last_event_id"` 47 | LastHardState string `json:"last_hard_state"` 48 | LastHardStateChange string `json:"last_hard_state_change"` 49 | LastNotification string `json:"last_notification"` 50 | LastProblemId string `json:"last_problem_id"` 51 | LastStateChange string `json:"last_state_change"` 52 | LastTimeDown string `json:"last_time_down"` 53 | LastTimeUnreachable string `json:"last_time_unreachable"` 54 | LastTimeUp string `json:"last_time_up"` 55 | LastUpdate string `json:"last_update"` 56 | LongPluginOutput string `json:"long_plugin_output"` 57 | MaxAttempts string `json:"max_attempts"` 58 | ModifiedAttributes string `json:"modified_attributes"` 59 | NextCheck string `json:"next_check"` 60 | NextNotification string `json:"next_notification"` 61 | NoMoreNotifications string `json:"no_more_notifications"` 62 | NotificationPeriod string `json:"notification_period"` 63 | NotificationsEnabled string `json:"notifications_enabled"` 64 | Obsess string `json:"obsess"` 65 | PassiveChecksEnabled string `json:"passive_checks_enabled"` 66 | PercentStateChange string `json:"percent_state_change"` 67 | PerformanceData string `json:"performance_data"` 68 | PluginOutput string `json:"plugin_output"` 69 | ProblemHasBeenAcknowledged string `json:"problem_has_been_acknowledged"` 70 | ProcessPerformanceData string `json:"process_performance_data"` 71 | RetryInterval string `json:"retry_interval"` 72 | ScheduledDowntimeDepth string `json:"scheduled_downtime_depth"` 73 | ShouldBeScheduled string `json:"should_be_scheduled"` 74 | StateType string `json:"state_type"` 75 | CustomVariables map[string]string `json:"custom_variables,omitempty"` 76 | } 77 | type ProgramStatus struct { 78 | ActiveHostChecksEnabled string `json:"active_host_checks_enabled"` 79 | ActiveOndemandHostCheckStats string `json:"active_ondemand_host_check_stats"` 80 | ActiveOndemandServiceCheckStats string `json:"active_ondemand_service_check_stats"` 81 | ActiveScheduledHostCheckStats string `json:"active_scheduled_host_check_stats"` 82 | ActiveScheduledServiceCheckStats string `json:"active_scheduled_service_check_stats"` 83 | ActiveServiceChecksEnabled string `json:"active_service_checks_enabled"` 84 | CachedHostCheckStats string `json:"cached_host_check_stats"` 85 | CachedServiceCheckStats string `json:"cached_service_check_stats"` 86 | CheckHostFreshness string `json:"check_host_freshness"` 87 | CheckServiceFreshness string `json:"check_service_freshness"` 88 | DaemonMode string `json:"daemon_mode"` 89 | EnableEventHandlers string `json:"enable_event_handlers"` 90 | EnableFlapDetection string `json:"enable_flap_detection"` 91 | EnableNotifications string `json:"enable_notifications"` 92 | ExternalCommandStats string `json:"external_command_stats"` 93 | GlobalHostEventHandler string `json:"global_host_event_handler"` 94 | GlobalServiceEventHandler string `json:"global_service_event_handler"` 95 | LastLogRotation string `json:"last_log_rotation"` 96 | ModifiedHostAttributes string `json:"modified_host_attributes"` 97 | ModifiedServiceAttributes string `json:"modified_service_attributes"` 98 | NagiosPid string `json:"nagios_pid"` 99 | NextCommentId string `json:"next_comment_id"` 100 | NextDowntimeId string `json:"next_downtime_id"` 101 | NextEventId string `json:"next_event_id"` 102 | NextNotificationId string `json:"next_notification_id"` 103 | NextProblemId string `json:"next_problem_id"` 104 | ObsessOverHosts string `json:"obsess_over_hosts"` 105 | ObsessOverServices string `json:"obsess_over_services"` 106 | ParallelHostCheckStats string `json:"parallel_host_check_stats"` 107 | PassiveHostCheckStats string `json:"passive_host_check_stats"` 108 | PassiveHostChecksEnabled string `json:"passive_host_checks_enabled"` 109 | PassiveServiceCheckStats string `json:"passive_service_check_stats"` 110 | PassiveServiceChecksEnabled string `json:"passive_service_checks_enabled"` 111 | ProcessPerformanceData string `json:"process_performance_data"` 112 | ProgramStart string `json:"program_start"` 113 | SerialHostCheckStats string `json:"serial_host_check_stats"` 114 | CustomVariables map[string]string `json:"custom_variables,omitempty"` 115 | } 116 | type ServiceStatus struct { 117 | AcknowledgementType string `json:"acknowledgement_type"` 118 | ActiveChecksEnabled string `json:"active_checks_enabled"` 119 | CheckCommand string `json:"check_command"` 120 | CheckExecutionTime string `json:"check_execution_time"` 121 | CheckInterval string `json:"check_interval"` 122 | CheckLatency string `json:"check_latency"` 123 | CheckOptions string `json:"check_options"` 124 | CheckPeriod string `json:"check_period"` 125 | CheckType string `json:"check_type"` 126 | CurrentAttempt string `json:"current_attempt"` 127 | CurrentEventId string `json:"current_event_id"` 128 | CurrentNotificationId string `json:"current_notification_id"` 129 | CurrentNotificationNumber string `json:"current_notification_number"` 130 | CurrentProblemId string `json:"current_problem_id"` 131 | CurrentState string `json:"current_state"` 132 | EventHandler string `json:"event_handler"` 133 | EventHandlerEnabled string `json:"event_handler_enabled"` 134 | FlapDetectionEnabled string `json:"flap_detection_enabled"` 135 | HasBeenChecked string `json:"has_been_checked"` 136 | HostName string `json:"host_name"` 137 | IsFlapping string `json:"is_flapping"` 138 | LastCheck string `json:"last_check"` 139 | LastEventId string `json:"last_event_id"` 140 | LastHardState string `json:"last_hard_state"` 141 | LastHardStateChange string `json:"last_hard_state_change"` 142 | LastNotification string `json:"last_notification"` 143 | LastProblemId string `json:"last_problem_id"` 144 | LastStateChange string `json:"last_state_change"` 145 | LastTimeCritical string `json:"last_time_critical"` 146 | LastTimeOk string `json:"last_time_ok"` 147 | LastTimeUnknown string `json:"last_time_unknown"` 148 | LastTimeWarning string `json:"last_time_warning"` 149 | LastUpdate string `json:"last_update"` 150 | LongPluginOutput string `json:"long_plugin_output"` 151 | MaxAttempts string `json:"max_attempts"` 152 | ModifiedAttributes string `json:"modified_attributes"` 153 | NextCheck string `json:"next_check"` 154 | NextNotification string `json:"next_notification"` 155 | NoMoreNotifications string `json:"no_more_notifications"` 156 | NotificationPeriod string `json:"notification_period"` 157 | NotificationsEnabled string `json:"notifications_enabled"` 158 | Obsess string `json:"obsess"` 159 | PassiveChecksEnabled string `json:"passive_checks_enabled"` 160 | PercentStateChange string `json:"percent_state_change"` 161 | PerformanceData string `json:"performance_data"` 162 | PluginOutput string `json:"plugin_output"` 163 | ProblemHasBeenAcknowledged string `json:"problem_has_been_acknowledged"` 164 | ProcessPerformanceData string `json:"process_performance_data"` 165 | RetryInterval string `json:"retry_interval"` 166 | ScheduledDowntimeDepth string `json:"scheduled_downtime_depth"` 167 | ServiceDescription string `json:"service_description"` 168 | ShouldBeScheduled string `json:"should_be_scheduled"` 169 | StateType string `json:"state_type"` 170 | CustomVariables map[string]string `json:"custom_variables,omitempty"` 171 | } 172 | 173 | func (o *ContactStatus) setField(key, value string) error { 174 | return setField(o, key, value) 175 | } 176 | 177 | func (o *HostStatus) setField(key, value string) error { 178 | return setField(o, key, value) 179 | } 180 | 181 | func (o *ProgramStatus) setField(key, value string) error { 182 | return setField(o, key, value) 183 | } 184 | 185 | func (o *ServiceStatus) setField(key, value string) error { 186 | return setField(o, key, value) 187 | } 188 | 189 | func (o *ContactStatus) setCustomVariable(key, value string) { 190 | if o.CustomVariables == nil { 191 | o.CustomVariables = make(map[string]string) 192 | } 193 | o.CustomVariables[key] = value 194 | } 195 | 196 | func (o *HostStatus) setCustomVariable(key, value string) { 197 | if o.CustomVariables == nil { 198 | o.CustomVariables = make(map[string]string) 199 | } 200 | o.CustomVariables[key] = value 201 | } 202 | 203 | func (o *ProgramStatus) setCustomVariable(key, value string) { 204 | if o.CustomVariables == nil { 205 | o.CustomVariables = make(map[string]string) 206 | } 207 | o.CustomVariables[key] = value 208 | } 209 | 210 | func (o *ServiceStatus) setCustomVariable(key, value string) { 211 | if o.CustomVariables == nil { 212 | o.CustomVariables = make(map[string]string) 213 | } 214 | o.CustomVariables[key] = value 215 | } 216 | 217 | // setField sets a field in a struct based on the JSON tag associated with the struct 218 | func setField(obj interface{}, name string, value interface{}) error { 219 | val := reflect.ValueOf(obj).Elem() 220 | 221 | for i := 0; i < val.NumField(); i++ { 222 | elem := val.Type().Field(i) 223 | tag := elem.Tag 224 | field := val.Field(i) 225 | 226 | js := tag.Get("json") 227 | if js == "" { 228 | continue 229 | } 230 | 231 | comma := strings.Index(js, ",") 232 | if comma != -1 { 233 | js = js[0:comma] 234 | } 235 | 236 | if js == name { 237 | if !field.CanSet() { 238 | return fmt.Errorf("Cannot set %s field value", name) 239 | } 240 | 241 | valValue := reflect.ValueOf(value) 242 | if field.Type() != valValue.Type() { 243 | return errors.New("Provided value type didn't match obj field type") 244 | } 245 | 246 | field.Set(valValue) 247 | return nil 248 | } 249 | } 250 | return fmt.Errorf("No such field: %s in obj", name) 251 | } 252 | -------------------------------------------------------------------------------- /api/objects_test.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "io/ioutil" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/cheekybits/is" 9 | ) 10 | 11 | type testData struct { 12 | Field string `json:"field_foo,omitempty"` 13 | } 14 | 15 | func (t *testData) setField(k, v string) error { 16 | return setField(t, k, v) 17 | } 18 | 19 | func TestSetField(t *testing.T) { 20 | is := is.New(t) 21 | 22 | o := &testData{} 23 | err := o.setField("field_foo", "123") 24 | is.NoErr(err) 25 | is.Equal("123", o.Field) 26 | } 27 | 28 | func TestParseBlock(t *testing.T) { 29 | is := is.New(t) 30 | 31 | dat, err := ioutil.ReadFile("testdata/contact_status.dat") 32 | is.NoErr(err) 33 | 34 | a := strings.SplitAfterN(string(dat), "}", -1) 35 | is.Equal(2, len(a)) 36 | 37 | lines := strings.Split(a[0], "\n") 38 | 39 | c := &ContactStatus{} 40 | parseBlock(c, "contactstatus", lines) 41 | 42 | // Sample a handful of fields 43 | is.Equal(c.ContactName, "jason") 44 | is.Equal(c.ModifiedAttributes, "0") 45 | is.Equal(c.ModifiedHostAttributes, "0") 46 | is.Equal(c.LastHostNotification, "1481756484") 47 | is.Equal(c.ServiceNotificationPeriod, "24x7") 48 | 49 | is.NotNil(c.CustomVariables) 50 | is.Equal(len(c.CustomVariables), 1) 51 | val, ok := c.CustomVariables["SOMECUSTOMVAR"] 52 | is.OK(ok) 53 | is.Equal(val, "http://example.com/customvar") 54 | } 55 | -------------------------------------------------------------------------------- /api/routes.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/sulochan/go-nagios-api/auth" 5 | 6 | "github.com/justinas/alice" 7 | ) 8 | 9 | func (s *Api) buildRoutes() { 10 | chain := alice.New() 11 | 12 | s.router.Handle("/contacts", chain.Append(auth.AuthHandler).ThenFunc(s.HandleGetContacts)).Methods("GET") 13 | 14 | s.router.Handle("/hosts", chain.Append(auth.AuthHandler).ThenFunc(s.HandleGetConfiguredHosts)).Methods("GET") 15 | s.router.Handle("/host/{hostname:[a-z,A-Z,0-9, _.-]+}", chain.Append(auth.AuthHandler).ThenFunc(s.HandleGetHost)).Methods("GET") 16 | s.router.Handle("/host/{hostname:[a-z,A-Z,0-9, _.-]+}/services", chain.Append(auth.AuthHandler).ThenFunc(s.HandleGetServicesForHost)).Methods("GET") 17 | s.router.Handle("/hoststatus", chain.Append(auth.AuthHandler).ThenFunc(s.HandleGetAllHostStatus)).Methods("GET") 18 | s.router.Handle("/hoststatus/{hostname:[a-z,A-Z,0-9,_.-]+}", chain.Append(auth.AuthHandler).ThenFunc(s.HandleGetHostStatusForHost)).Methods("GET") 19 | s.router.Handle("/hostgroups", chain.Append(auth.AuthHandler).ThenFunc(s.HandleGetHostGroups)).Methods("GET") 20 | 21 | s.router.Handle("/services", chain.Append(auth.AuthHandler).ThenFunc(s.HandleGetConfiguredServices)).Methods("GET") 22 | s.router.Handle("/servicestatus", chain.Append(auth.AuthHandler).ThenFunc(s.HandleGetServiceStatus)).Methods("GET") 23 | s.router.Handle("/servicestatus/{service:[a-z,A-Z,0-9,_.-]+}", chain.Append(auth.AuthHandler).ThenFunc(s.HandleGetServiceStatusForService)).Methods("GET") 24 | 25 | // Nagios External Command Handlers 26 | s.router.Handle("/disable_notifications", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDisableNotifications)).Methods("POST") 27 | s.router.Handle("/enable_notifications", chain.Append(auth.AuthHandler).ThenFunc(s.HandleEnableNotifications)).Methods("POST") 28 | s.router.Handle("/disable_host_check", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDisableHostCheck)).Methods("POST") 29 | s.router.Handle("/enable_host_check", chain.Append(auth.AuthHandler).ThenFunc(s.HandleEnableHostCheck)).Methods("POST") 30 | s.router.Handle("/disable_host_notifications", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDisableHostNotifications)).Methods("POST") 31 | s.router.Handle("/enable_host_notifications", chain.Append(auth.AuthHandler).ThenFunc(s.HandleEnableHostNotifications)).Methods("POST") 32 | s.router.Handle("/acknowledge_host_problem", chain.Append(auth.AuthHandler).ThenFunc(s.HandleAcknowledgeHostProblem)).Methods("POST") 33 | s.router.Handle("/acknowledge_service_problem", chain.Append(auth.AuthHandler).ThenFunc(s.HandleAcknowledgeServiceProblem)).Methods("POST") 34 | s.router.Handle("/add_host_comment", chain.Append(auth.AuthHandler).ThenFunc(s.HandleAddHostComment)).Methods("POST") 35 | s.router.Handle("/add_svc_comment", chain.Append(auth.AuthHandler).ThenFunc(s.HandleAddServiceComment)).Methods("POST") 36 | s.router.Handle("/del_all_host_comment", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDeleteAllHostComment)).Methods("POST") 37 | s.router.Handle("/del_all_svc_comment", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDeleteAllServiceComment)).Methods("POST") 38 | s.router.Handle("/del_host_comment", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDeleteHostComment)).Methods("POST") 39 | s.router.Handle("/del_svc_comment", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDeleteServiceComment)).Methods("POST") 40 | s.router.Handle("/disable_all_notification_beyond_host", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDisableAllNotificationBeyondHost)).Methods("POST") 41 | s.router.Handle("/enable_all_notification_beyond_host", chain.Append(auth.AuthHandler).ThenFunc(s.HandleEnableAllNotificationBeyondHost)).Methods("POST") 42 | s.router.Handle("/disable_hostgroup_host_checks", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDisableHostgroupHostChecks)).Methods("POST") 43 | s.router.Handle("/enable_hostgroup_host_checks", chain.Append(auth.AuthHandler).ThenFunc(s.HandleEnableHostgroupHostChecks)).Methods("POST") 44 | s.router.Handle("/disable_hostgroup_host_notifications", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDisableHostgroupHostNotification)).Methods("POST") 45 | s.router.Handle("/enable_hostgroup_host_notifications", chain.Append(auth.AuthHandler).ThenFunc(s.HandleEnableHostgroupHostNotification)).Methods("POST") 46 | s.router.Handle("/disable_hostgroup_svc_checks", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDisableHostgroupServiceChecks)).Methods("POST") 47 | s.router.Handle("/enable_hostgroup_svc_checks", chain.Append(auth.AuthHandler).ThenFunc(s.HandleEnableHostgroupServiceChecks)).Methods("POST") 48 | s.router.Handle("/disable_hostgroup_svc_notifications", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDisableHostgroupServiceNotifications)).Methods("POST") 49 | s.router.Handle("/enable_hostgroup_svc_notifications", chain.Append(auth.AuthHandler).ThenFunc(s.HandleEnableHostgroupServiceNotifications)).Methods("POST") 50 | s.router.Handle("/disable_host_and_child_notifications", chain.Append(auth.AuthHandler).ThenFunc(s.HandleDisableHostandChildNotifications)).Methods("POST") 51 | s.router.Handle("/enable_host_and_child_notifications", chain.Append(auth.AuthHandler).ThenFunc(s.HandleEnableHostandChildNotifications)).Methods("POST") 52 | s.router.Handle("/schedule_host_downtime", chain.Append(auth.AuthHandler).ThenFunc(s.HandleScheduleHostDowntime)).Methods("POST") 53 | } 54 | -------------------------------------------------------------------------------- /api/testdata/contact_status.dat: -------------------------------------------------------------------------------- 1 | contactstatus { 2 | contact_name=jason 3 | modified_attributes=0 4 | modified_host_attributes=0 5 | modified_service_attributes=0 6 | host_notification_period=24x7 7 | service_notification_period=24x7 8 | last_host_notification=1481756484 9 | last_service_notification=1484082873 10 | host_notifications_enabled=1 11 | service_notifications_enabled=1 12 | _SOMECUSTOMVAR=0;http://example.com/customvar 13 | } 14 | -------------------------------------------------------------------------------- /api/write_nagios_command.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | log "github.com/sirupsen/logrus" 9 | ) 10 | 11 | func (a *Api) WriteCommand(command string) error { 12 | commandToWrite := fmt.Sprintf("[%d] %s\n", time.Now().Unix(), command) 13 | 14 | f, err := os.OpenFile(a.fileCommand, os.O_APPEND|os.O_WRONLY, 0600) 15 | if err != nil { 16 | log.Error(err) 17 | return err 18 | } 19 | defer f.Close() 20 | 21 | if _, err = f.WriteString(commandToWrite); err != nil { 22 | log.Error(err) 23 | return err 24 | } 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "net/http" 4 | 5 | func AuthHandler(next http.Handler) http.Handler { 6 | fn := func(w http.ResponseWriter, r *http.Request) { 7 | next.ServeHTTP(w, r) 8 | } 9 | return http.HandlerFunc(fn) 10 | } 11 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "io/ioutil" 7 | 8 | log "github.com/sirupsen/logrus" 9 | ) 10 | 11 | type Config struct { 12 | Addr string 13 | ObjectCacheFile string 14 | StatusFile string 15 | CommandFile string 16 | } 17 | 18 | var ( 19 | config *Config 20 | configfile *string 21 | objectCacheFile *string 22 | statusFile *string 23 | commandFile *string 24 | addr *string 25 | ) 26 | 27 | func init() { 28 | configfile = flag.String("config", "", "path to config file") 29 | objectCacheFile = flag.String("cachefile", "/usr/local/nagios/var/objects.cache", "Nagios object.cache file location") 30 | statusFile = flag.String("statusfile", "/usr/local/nagios/var/status.dat", "Nagios status.dat file location") 31 | commandFile = flag.String("commandfile", "/usr/local/nagios/var/nagios.cmd", "Nagios command file location") 32 | addr = flag.String("addr", ":9090", "The interface and port to run server on") 33 | flag.Parse() 34 | 35 | if *configfile != "" { 36 | loadConfigFile() 37 | } else { 38 | loadConfigFlags() 39 | } 40 | } 41 | 42 | func loadConfigFlags() { 43 | config = &Config{Addr: *addr, ObjectCacheFile: *objectCacheFile, StatusFile: *statusFile, CommandFile: *commandFile} 44 | } 45 | 46 | func loadConfigFile() { 47 | file, err := ioutil.ReadFile(*configfile) 48 | if err != nil { 49 | log.Fatal("open config: ", err) 50 | } 51 | 52 | temp := new(Config) 53 | if err = json.Unmarshal(file, temp); err != nil { 54 | log.Fatal("parse config: ", err) 55 | } 56 | config = temp 57 | } 58 | 59 | func GetConfig() *Config { 60 | return config 61 | } 62 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sulochan/go-nagios-api 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 7 | github.com/gorilla/mux v1.8.0 8 | github.com/justinas/alice v1.2.0 9 | github.com/sirupsen/logrus v1.7.0 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764= 2 | github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 6 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 7 | github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= 8 | github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= 9 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 10 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 11 | github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= 12 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 13 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 14 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 15 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 16 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 17 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/sulochan/go-nagios-api/api" 7 | "github.com/sulochan/go-nagios-api/config" 8 | ) 9 | 10 | func main() { 11 | conf := config.GetConfig() 12 | api := api.NewApi(conf.Addr, conf.ObjectCacheFile, conf.CommandFile, conf.StatusFile) 13 | 14 | err := api.Run() 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | } 19 | --------------------------------------------------------------------------------