/
column.go
301 lines (264 loc) · 9.82 KB
/
column.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
package main
import (
"fmt"
"strings"
)
// VirtualColumnResolveFunc is used to define the virtual key mapping in the VirtualColumnMap
type VirtualColumnResolveFunc func(d *DataRow, col *Column) interface{}
// VirtualColumnMapEntry is used to define the virtual key mapping in the VirtualColumnMap
type VirtualColumnMapEntry struct {
noCopy noCopy
Name string
StatusKey PeerStatusKey
ResolveFunc VirtualColumnResolveFunc
}
// VirtualColumnList maps the virtual columns with the peer status map entry.
// Must have either a StatusKey or a ResolveFunc set
var VirtualColumnList = []VirtualColumnMapEntry{
// access things from the peer status by StatusKey
{Name: "key", StatusKey: PeerKey},
{Name: "name", StatusKey: PeerName},
{Name: "addr", StatusKey: PeerAddr},
{Name: "status", StatusKey: PeerState},
{Name: "bytes_send", StatusKey: BytesSend},
{Name: "bytes_received", StatusKey: BytesReceived},
{Name: "queries", StatusKey: Queries},
{Name: "last_error", StatusKey: LastError},
{Name: "last_online", StatusKey: LastOnline},
{Name: "last_update", StatusKey: LastUpdate},
{Name: "response_time", StatusKey: ResponseTime},
{Name: "idling", StatusKey: Idling},
{Name: "last_query", StatusKey: LastQuery},
{Name: "section", StatusKey: Section},
{Name: "parent", StatusKey: PeerParent},
{Name: "configtool", StatusKey: ConfigTool},
{Name: "federation_key", StatusKey: SubKey},
{Name: "federation_name", StatusKey: SubName},
{Name: "federation_addr", StatusKey: SubAddr},
{Name: "federation_type", StatusKey: SubType},
// calculated columns by ResolveFunc
{Name: "lmd_last_cache_update", ResolveFunc: func(d *DataRow, _ *Column) interface{} { return d.LastUpdate }},
{Name: "lmd_version", ResolveFunc: func(_ *DataRow, _ *Column) interface{} { return fmt.Sprintf("%s-%s", NAME, Version()) }},
{Name: "state_order", ResolveFunc: VirtualColStateOrder},
{Name: "last_state_change_order", ResolveFunc: VirtualColLastStateChangeOrder},
{Name: "has_long_plugin_output", ResolveFunc: VirtualColHasLongPluginOutput},
{Name: "services_with_state", ResolveFunc: VirtualColServicesWithInfo},
{Name: "services_with_info", ResolveFunc: VirtualColServicesWithInfo},
{Name: "comments", ResolveFunc: VirtualColComments},
{Name: "comments_with_info", ResolveFunc: VirtualColCommentsWithInfo},
{Name: "downtimes", ResolveFunc: VirtualColDowntimes},
{Name: "downtimes_with_info", ResolveFunc: VirtualColDowntimesWithInfo},
{Name: "members_with_state", ResolveFunc: VirtualColMembersWithState},
{Name: "custom_variables", ResolveFunc: VirtualColCustomVariables},
{Name: "total_services", ResolveFunc: VirtualColTotalServices},
{Name: "localtime", ResolveFunc: VirtualColLocaltime},
{Name: "empty", ResolveFunc: func(_ *DataRow, _ *Column) interface{} { return "" }}, // return empty string as placeholder for nonexisting columns
}
// VirtualColumnMap maps is the lookup map for the VirtualColumnList
var VirtualColumnMap = map[string]*VirtualColumnMapEntry{}
// ServiceMember is a host_name / description pair
type ServiceMember [2]string
// FetchType defines if and how the column is updated.
//go:generate stringer -type=FetchType
type FetchType uint8
// placeholder to return in GetEmptyValue, no need to create empty lists over and over
var emptyInterfaceList = make([]interface{}, 0)
var emptyStringMap = make(map[string]string)
var emptyInt64List = []int64{}
const (
// Static is used for all columns which are updated once at start.
Static FetchType = iota + 1
// Dynamic columns are updated periodically.
Dynamic
// None columns are never updated and either calculated on the fly
None
)
// DataType defines the data type of a column.
//go:generate stringer -type=DataType
type DataType uint16
const (
// StringCol is used for string columns.
StringCol DataType = iota + 1
// StringListCol is used for string list columns.
StringListCol
// IntCol is used for integer columns.
IntCol
// Int64Col is used for large integer columns.
Int64Col
// Int64ListCol is used for integer list columns.
Int64ListCol
// FloatCol is used for float columns.
FloatCol
// JSONCol is used for generic json data columns (handled as string).
JSONCol
// CustomVarCol is a list of custom variables
CustomVarCol
// ServiceMemberListCol is a list of host_name/servicename pairs
ServiceMemberListCol
// InterfaceListCol is a list of arbitrary data
InterfaceListCol
// StringLargeCol is used for large strings
StringLargeCol
)
// StorageType defines how this column is stored
//go:generate stringer -type=StorageType
type StorageType uint8
const (
// LocalStore columns are store in the DataRow.data* fields
LocalStore StorageType = iota + 1
// RefStore are referenced columns
RefStore
// VirtualStore is calculated on the fly
VirtualStore
)
// OptionalFlags is used to set flags for optional columns.
type OptionalFlags uint32
const (
// NoFlags is set if there are no flags at all.
NoFlags OptionalFlags = 0
// LMD flag is set if the remote site is a LMD backend.
LMD OptionalFlags = 1 << iota
// MultiBackend flag is set if the remote connection returns more than one site
MultiBackend
// LMDSub is a sub peer from within a remote LMD connection
LMDSub
// HTTPSub is a sub peer from within a remote HTTP connection (MultiBackend)
HTTPSub
// Shinken flag is set if the remote site is a shinken installation.
Shinken
// Icinga2 flag is set if the remote site is a icinga 2 installation.
Icinga2
// Naemon flag is set if the remote site is a naemon installation.
Naemon
// HasDependencyColumn flag is set if the remote site has depends_exec and depends_notify columns
HasDependencyColumn
// HasLastUpdate flag is set if the remote site has a last_update column for hosts and services
HasLastUpdateColumn
// HasLMDLastUpdate flag is set if the remote site has a lmd_last_cache_update column for hosts and services
HasLMDLastCacheUpdateColumn
// HasLocaltimeColumn flag is set if the remote site has a localtime column
HasLocaltimeColumn
)
var OptionalFlagsStrings = map[OptionalFlags]string{
LMD: "LMD",
MultiBackend: "MultiBackend",
LMDSub: "LMDSub",
HTTPSub: "HTTPSub",
Shinken: "Shinken",
Icinga2: "Icinga2",
Naemon: "Naemon",
HasDependencyColumn: "HasDependencyColumn",
HasLastUpdateColumn: "HasLastUpdateColumn",
HasLMDLastCacheUpdateColumn: "HasLMDLastCacheUpdateColumn",
HasLocaltimeColumn: "HasLocaltimeColumn",
}
// String returns the string representation of used flags
func (f *OptionalFlags) String() string {
if *f == NoFlags {
return "[<none>]"
}
return ("[" + strings.Join(f.List(), ", ") + "]")
}
// List returns a string list of used flags
func (f *OptionalFlags) List() (list []string) {
list = make([]string, 0)
if *f == NoFlags {
return
}
for fl, name := range OptionalFlagsStrings {
if f.HasFlag(fl) {
list = append(list, name)
}
}
return
}
// HasFlag returns true if flags are present
func (f *OptionalFlags) HasFlag(flag OptionalFlags) bool {
if flag == 0 {
return true
}
if *f&flag != 0 {
return true
}
return false
}
// SetFlag set a flag
func (f *OptionalFlags) SetFlag(flag OptionalFlags) {
*f |= flag
}
// Clear removes all flags
func (f *OptionalFlags) Clear() {
*f = NoFlags
}
// Column is the definition of a single column within a DataRow.
type Column struct {
noCopy noCopy
Name string // name and primary key
Description string // human description
DataType DataType // Type of this column
FetchType FetchType // flag wether this columns needs to be updated
StorageType StorageType // flag how this column is stored
Optional OptionalFlags // flags if this column is used for certain backends only
Index int // position in the DataRow data* fields
RefCol *Column // reference to column in other table, ex.: host_alias
RefColTableName TableName // shortcut to Column.RefCol.Table.Name
Table *Table // reference to the table holding this column
VirtualMap *VirtualColumnMapEntry // reference to resolver for virtual columns
}
// NewColumn adds a column object.
func NewColumn(table *Table, name string, storage StorageType, update FetchType, datatype DataType, restrict OptionalFlags, refCol *Column, description string) {
col := &Column{
Table: table,
Name: name,
Description: description,
StorageType: storage,
FetchType: update,
DataType: datatype,
RefCol: refCol,
Optional: restrict,
}
if col.Table == nil {
log.Panicf("missing table for %s", col.Name)
}
if col.StorageType == VirtualStore {
col.VirtualMap = VirtualColumnMap[name]
if col.VirtualMap == nil {
log.Panicf("missing VirtualMap for %s in %s", col.Name, table.Name)
}
}
if col.StorageType == RefStore {
if col.RefCol == nil {
log.Panicf("missing RefCol for %s in %s", col.Name, table.Name)
}
col.RefColTableName = refCol.Table.Name
}
if table.ColumnsIndex == nil {
table.ColumnsIndex = make(map[string]*Column)
}
table.ColumnsIndex[col.Name] = col
table.Columns = append(table.Columns, col)
}
// String returns the string representation of a column list
func (c *Column) String() string {
return c.Name
}
// GetEmptyValue returns an empty placeholder representation for the given column type
func (c *Column) GetEmptyValue() interface{} {
switch c.DataType {
case StringCol, StringLargeCol:
return ""
case IntCol, Int64Col, FloatCol:
return -1
case Int64ListCol:
return emptyInt64List
case StringListCol, ServiceMemberListCol, InterfaceListCol:
return emptyInterfaceList
case CustomVarCol:
return emptyStringMap
case JSONCol:
return "{}"
default:
log.Panicf("type %s not supported", c.DataType)
}
return ""
}