-
Notifications
You must be signed in to change notification settings - Fork 1
/
gomonit.go
572 lines (498 loc) · 14.2 KB
/
gomonit.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
// Package gomonit consumes and parses Monit status and event notifications. It disguises as M/Monit collector server.
package gomonit
import (
"encoding/xml"
"errors"
"io"
"net/http"
"time"
"code.google.com/p/go-charset/charset"
// This comment justifies usage of blank import. Thanks golint
_ "code.google.com/p/go-charset/data"
"github.com/fatih/structs"
)
// Monit struct represents root XML node.
type Monit struct {
ID string `xml:"id,attr"`
Incarnation string `xml:"incarnation,attr"`
Version string `xml:"version,attr"`
Server Server `xml:"server"`
Platform Platform `xml:"platform"`
Services []Service `xml:"services>service"`
ServiceGroups []ServiceGroup `xml:"servicegroups>servicegroup"`
Event Event `xml:"event"`
}
// Server represents monit>server node.
type Server struct {
Uptime int `xml:"uptime"`
Poll int `xml:"poll"`
StartDelay int `xml:"startdelay"`
LocalHostname string `xml:"localhostname"`
ControlFile string `xml:"controlfile"`
Httpd Httpd `xml:"httpd"`
Credentials Credentials `xml:"credentials"`
}
// Httpd represents monit>server>httpd node.
type Httpd struct {
Address string `xml:"address"`
Port int `xml:"port"`
Ssl int `xml:"ssl"`
}
// Credentials represents monit>server>credentials node.
type Credentials struct {
Username string `xml:"username"`
Password string `xml:"password"`
}
// Platform represents monit>platform node.
type Platform struct {
Name string `xml:"name"`
Release string `xml:"release"`
Version string `xml:"version"`
Machine string `xml:"machine"`
CPU string `xml:"cpu"`
Memory string `xml:"memory"`
Swap string `xml:"swap"`
}
// Monit service type identifiers.
const (
ServiceTypeFilesystem uint = 0
ServiceTypeDirectory = 1
ServiceTypeFile = 2
ServiceTypeProcess = 3
ServiceTypeSystem = 5
ServiceTypeFifo = 6
ServiceTypeProgram = 7
ServiceTypeNet = 8
)
// Service struct represents generalized monit>services>service XML node. It's used internally for XML parsing purposes and concrete service types should be used.
type Service struct {
Name string `xml:"name,attr"`
Type uint `xml:"type"`
CollectedSec int64 `xml:"collected_sec"`
CollectedUsec int64 `xml:"collected_usec"`
Status uint `xml:"status"`
StatusHint uint `xml:"status_hint"`
Monitor uint `xml:"monitor"`
MonitorMode uint `xml:"monitormode"`
PendingAction uint `xml:"pendingaction"`
Mode string `xml:"mode"`
UID uint `xml:"uid"`
GID uint `xml:"gid"`
Flags uint `xml:"flags"`
Block FilesystemSize `xml:"block"`
Inode FilesystemSize `xml:"inode"`
Timestamp int64 `xml:"timestamp"`
Size uint64 `xml:"size"`
Pid uint `xml:"pid"`
PPid uint `xml:"ppid"`
Euid uint `xml:"euid"`
Uptime uint64 `xml:"uptime"`
Children uint `xml:"children"`
Memory Memory `xml:"memory"`
CPU ProcessCPU `xml:"cpu"`
System ServiceSystem `xml:"system"`
Program ServiceProgram `xml:"program"`
Link Link `xml:"link"`
}
// GetFilesystem returns concrete Filesystem service struct.
func (service *Service) GetFilesystem() (Filesystem, error) {
var filesystem Filesystem
var err error
if service.Type == ServiceTypeFilesystem {
copy(service, &filesystem)
} else {
err = errors.New("Service type is not Filesystem")
}
return filesystem, err
}
// GetDirectory returns concrete Directory service struct.
func (service *Service) GetDirectory() (Directory, error) {
var directory Directory
var err error
if service.Type == ServiceTypeDirectory {
copy(service, &directory)
} else {
err = errors.New("Service type is not Directory")
}
return directory, err
}
// GetFile returns concrete File service struct.
func (service *Service) GetFile() (File, error) {
var file File
var err error
if service.Type == ServiceTypeFile {
copy(service, &file)
} else {
err = errors.New("Service type is not File")
}
return file, err
}
// GetProcess returns concrete Process service struct.
func (service *Service) GetProcess() (Process, error) {
var process Process
var err error
if service.Type == ServiceTypeProcess {
copy(service, &process)
} else {
err = errors.New("Service type is not Process")
}
return process, err
}
// GetSystem returns concrete System service struct.
func (service *Service) GetSystem() (System, error) {
var system System
var err error
if service.Type == ServiceTypeSystem {
copyCommon(service, &system)
system.CPU = service.System.CPU
system.Memory = service.System.Memory
system.Load = service.System.Load
system.Swap = service.System.Swap
} else {
err = errors.New("Service type is not System")
}
return system, err
}
// GetFifo returns concrete Fifo service struct.
func (service *Service) GetFifo() (Fifo, error) {
var fifo Fifo
var err error
if service.Type == ServiceTypeFifo {
copy(service, &fifo)
} else {
err = errors.New("Service type is not Fifo")
}
return fifo, err
}
// GetProgram returns concrete Program service struct.
func (service *Service) GetProgram() (Program, error) {
var program Program
var err error
if service.Type == ServiceTypeProgram {
copyCommon(service, &program)
program.Status = service.Program.Status
program.Started = service.Program.Started
program.Output = service.Program.Output
} else {
err = errors.New("Service type is not Program")
}
return program, err
}
// GetNet returns concrete Net service struct.
func (service *Service) GetNet() (Net, error) {
var net Net
var err error
if service.Type == ServiceTypeNet {
copyCommon(service, &net)
net.State = service.Link.State
net.Speed = service.Link.Speed
net.Duplex = service.Link.Duplex
net.DlPackets = service.Link.DlPackets
net.DlBytes = service.Link.DlBytes
net.DlErrors = service.Link.DlErrors
net.UlPackets = service.Link.UlPackets
net.UlBytes = service.Link.UlBytes
net.UlErrors = service.Link.UlErrors
} else {
err = errors.New("Service type is not Net")
}
return net, err
}
func copy(src interface{}, dest interface{}) {
srcStruct := structs.New(src)
destStruct := structs.New(dest)
for _, destField := range destStruct.Fields() {
destFieldName := destField.Name()
srcField, ok := srcStruct.FieldOk(destFieldName)
if ok {
srcValue := srcField.Value()
if destFieldName == "Timestamp" {
destField.Set(time.Unix(srcValue.(int64), 0))
} else {
destField.Set(srcValue)
}
}
}
copyTime(src, dest)
}
func copyCommon(src interface{}, dest interface{}) {
keys := [9]string{
"Name",
"Type",
"Status",
"StatusHint",
"Monitor",
"MonitorMode",
"PendingAction"}
srcMap := structs.Map(src)
destStruct := structs.New(dest)
for _, key := range keys {
field, ok := destStruct.FieldOk(key)
if ok {
field.Set(srcMap[key])
}
}
copyTime(src, dest)
}
func copyTime(src interface{}, dest interface{}) {
srcMap := structs.Map(src)
destStruct := structs.New(dest)
field := destStruct.Field("Time")
field.Set(time.Unix(srcMap["CollectedSec"].(int64), srcMap["CollectedUsec"].(int64)))
}
// Filesystem represents concrete service XML node.
type Filesystem struct {
Name string
Type uint
Time time.Time
Status uint
StatusHint uint
Monitor uint
MonitorMode uint
PendingAction uint
Mode string
UID uint
GID uint
Flags uint
Block FilesystemSize
Inode FilesystemSize
}
// FilesystemSize represents filesystem size XML node.
type FilesystemSize struct {
Percent float32 `xml:"percent"`
Usage float64 `xml:"usage"`
Total float64 `xml:"total"`
}
// Directory represents concrete service XML node.
type Directory struct {
Name string
Type uint
Time time.Time
Status uint
StatusHint uint
Monitor uint
MonitorMode uint
PendingAction uint
Mode string
UID uint
GID uint
Timestamp time.Time
}
// File represents concrete service XML node.
type File struct {
Name string
Type uint
Time time.Time
Status uint
StatusHint uint
Monitor uint
MonitorMode uint
PendingAction uint
Mode string
UID uint
GID uint
Timestamp time.Time
Size uint64
}
// Process represents concrete service XML node.
type Process struct {
Name string
Type uint
Time time.Time
Status uint
StatusHint uint
Monitor uint
MonitorMode uint
PendingAction uint
Pid uint
PPid uint
Euid uint
Gid uint
Uptime uint64
Children uint
Memory Memory
CPU ProcessCPU
}
// System represents concrete service XML node.
type System struct {
Name string
Type uint
Time time.Time
Status uint
StatusHint uint
Monitor uint
MonitorMode uint
PendingAction uint
CPU SystemCPU
Memory Memory
Load Load
Swap Swap
}
// Fifo represents concrete service XML node.
type Fifo struct {
Name string
Type uint
Time time.Time
Status uint
StatusHint uint
Monitor uint
MonitorMode uint
PendingAction uint
Mode string
UID uint
GID uint
Timestamp time.Time
}
// Program represents concrete service XML node.
type Program struct {
Name string
Type uint
Time time.Time
Status uint
StatusHint uint
Monitor uint
MonitorMode uint
PendingAction uint
Started uint64
Output string
}
// Net represents concrete service XML node.
type Net struct {
Name string
Type uint
Time time.Time
Status uint
StatusHint uint
Monitor uint
MonitorMode uint
PendingAction uint
State uint
Speed uint64
Duplex uint
DlPackets NetLinkCount
DlBytes NetLinkCount
DlErrors NetLinkCount
UlPackets NetLinkCount
UlBytes NetLinkCount
UlErrors NetLinkCount
}
// ServiceSystem represents monit>service>system XML node.
type ServiceSystem struct {
CPU SystemCPU `xml:"cpu"`
Memory Memory `xml:"memory"`
Load Load `xml:"load"`
Swap Swap `xml:"swap"`
}
// ServiceProgram represents monit>service>program XML node.
type ServiceProgram struct {
Status uint `xml:"status"`
Started uint64 `xml:"started"`
Output string `xml:"output"`
}
// Memory represents memory XML node.
type Memory struct {
Percent float64 `xml:"percent"`
PercentTotal float64 `xml:"percenttotal"`
Kilobyte uint `xml:"kilobyte"`
KilobyteTotal uint `xml:"kilobytetotal"`
}
// SystemCPU represents monit>service>system>cpu XML node.
type SystemCPU struct {
User float64 `xml:"user"`
System float64 `xml:"system"`
Wait float64 `xml:"wait"`
}
// ProcessCPU represents monit>service>cpu XML node.
type ProcessCPU struct {
Percent float64 `xml:"percent"`
PercentTotal float64 `xml:"percenttotal"`
}
// Load represents monit>service>system>load XML node.
type Load struct {
Avg01 float64 `xml:"avg01"`
Avg05 float64 `xml:"avg05"`
Avg15 float64 `xml:"avg15"`
}
// Swap represents monit>service>system>swap XML node.
type Swap struct {
Percent float64 `xml:"percent"`
Kilobyte int `xml:"kilobyte"`
}
// Link represents monit>service>link XML node.
type Link struct {
State uint `xml:"state"`
Speed uint64 `xml:"speed"`
Duplex uint `xml:"duplex"`
DlPackets NetLinkCount `xml:"download>packets"`
DlBytes NetLinkCount `xml:"download>bytes"`
DlErrors NetLinkCount `xml:"download>errors"`
UlPackets NetLinkCount `xml:"upload>packets"`
UlBytes NetLinkCount `xml:"upload>bytes"`
UlErrors NetLinkCount `xml:"upload>errors"`
}
// NetLinkCount represents monit>service>link upload/download counter XML node.
type NetLinkCount struct {
Now uint64 `xml:"now"`
Total uint64 `xml:"total"`
}
// ServiceGroup represents monit>servicegroup XML node.
type ServiceGroup struct {
Name string `xml:"name,attr"`
Service string `xml:"service"`
}
// Event represents monit>event XML node.
type Event struct {
CollectedSec int `xml:"collected_sec"`
CollectedUsec int `xml:"collected_usec"`
Service string `xml:"service"`
Type int `xml:"type"`
ID int `xml:"id"`
State int `xml:"state"`
Action int `xml:"action"`
Message string `xml:"message,chardata"`
Token string `xml:"token"`
}
// Decoder implements XML decoder.
type Decoder interface {
DecodeElement(interface{}, *xml.StartElement) error
}
// Parser is Monit notification decoder.
type Parser struct {
Decoder Decoder
}
// NewParser returns a new Parser.
func NewParser(reader io.Reader) *Parser {
decoder := xml.NewDecoder(reader)
decoder.CharsetReader = charset.NewReader
return &Parser{decoder}
}
// Parse returns decoded Monit notification.
func (parser *Parser) Parse() Monit {
var monit Monit
parser.Decoder.DecodeElement(&monit, nil)
return monit
}
// Collector is implementation of M/Monit servers /collector endpoint.
type Collector struct {
Channel chan *Monit
Handler http.HandlerFunc
}
// NewCollector returns a new Collector.
func NewCollector(channel chan *Monit) *Collector {
handler := MakeHTTPHandler(channel)
return &Collector{channel, handler}
}
// ServeHTTP implements an http.Handler that collects Monit monitifications.
func (collector *Collector) ServeHTTP(w http.ResponseWriter, r *http.Request) {
collector.Handler(w, r)
}
// MakeHTTPHandler returns http.HandlerFunc function that parses HTTP request body and
// pipes result to provided channel.
func MakeHTTPHandler(out chan *Monit) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
parser := NewParser(r.Body)
monit := parser.Parse()
out <- &monit
}
}