master
go 205 lines 5.49 KB
Raw
1 //go:build cgo
2
3 package db2
4
5 import (
6 "fmt"
7 "sort"
8 "strings"
9
10 "github.com/netdata/netdata/go/plugins/plugin/ibm.d/modules/db2/contexts"
11 )
12
13 type tablespaceEntry struct {
14 name string
15 meta *tablespaceMetrics
16 metrics tablespaceInstanceMetrics
17 }
18
19 type tablespaceGroupAggregate struct {
20 UsedSize int64
21 FreeSize int64
22 TotalSize int64
23 UsableSize int64
24 State int64
25 }
26
27 func tablespaceGroupKey(meta *tablespaceMetrics) string {
28 if meta == nil {
29 return "UNKNOWN"
30 }
31 parts := []string{}
32 if meta.tbspType != "" {
33 parts = append(parts, strings.ToUpper(meta.tbspType))
34 }
35 if meta.contentType != "" {
36 parts = append(parts, strings.ToUpper(meta.contentType))
37 }
38 if len(parts) == 0 {
39 return "UNKNOWN"
40 }
41 return strings.Join(parts, "/")
42 }
43
44 func (a *tablespaceGroupAggregate) add(m tablespaceInstanceMetrics) {
45 a.UsedSize += m.UsedSize
46 a.FreeSize += m.FreeSize
47 a.TotalSize += m.TotalSize
48 a.UsableSize += m.UsableSize
49 a.State += m.State
50 }
51
52 func (c *Collector) exportTablespaceMetrics() {
53 entries := make([]tablespaceEntry, 0, len(c.mx.tablespaces))
54 for name, metrics := range c.mx.tablespaces {
55 entries = append(entries, tablespaceEntry{
56 name: name,
57 meta: c.tablespaces[name],
58 metrics: metrics,
59 })
60 }
61
62 if len(entries) == 0 {
63 c.clearWarnOnce("db2_tablespace_overflow")
64 return
65 }
66
67 sort.Slice(entries, func(i, j int) bool {
68 return entries[i].name < entries[j].name
69 })
70
71 limit := c.MaxTablespaces
72 if limit <= 0 || limit > len(entries) {
73 limit = len(entries)
74 }
75
76 groupAgg := make(map[string]*tablespaceGroupAggregate)
77 overflowAgg := &tablespaceGroupAggregate{}
78 overflowCount := 0
79 overflowGroups := make(map[string]int)
80 overflowExample := make(map[string]string)
81
82 for idx, entry := range entries {
83 key := tablespaceGroupKey(entry.meta)
84 agg := groupAgg[key]
85 if agg == nil {
86 agg = &tablespaceGroupAggregate{}
87 groupAgg[key] = agg
88 }
89 agg.add(entry.metrics)
90
91 if idx < limit {
92 c.emitPerTablespaceMetrics(entry)
93 continue
94 }
95
96 overflowAgg.add(entry.metrics)
97 overflowCount++
98 overflowGroups[key]++
99 if _, ok := overflowExample[key]; !ok {
100 overflowExample[key] = entry.name
101 }
102 }
103
104 c.emitTablespaceGroupMetrics(groupAgg, overflowAgg, overflowCount)
105
106 if overflowCount > 0 {
107 parts := make([]string, 0, len(overflowGroups))
108 for group, count := range overflowGroups {
109 parts = append(parts, fmt.Sprintf("%s:%d (e.g. %s)", group, count, overflowExample[group]))
110 }
111 sort.Strings(parts)
112 c.warnOnce("db2_tablespace_overflow", "too many tablespaces for per-instance charts (MaxTablespaces=%d). Aggregated %d additional tablespaces: %s", c.MaxTablespaces, overflowCount, strings.Join(parts, ", "))
113 } else {
114 c.clearWarnOnce("db2_tablespace_overflow")
115 }
116 }
117
118 func (c *Collector) emitPerTablespaceMetrics(entry tablespaceEntry) {
119 tspType := "unknown"
120 contentType := "unknown"
121 stateLabel := "unknown"
122 if entry.meta != nil {
123 if entry.meta.tbspType != "" {
124 tspType = entry.meta.tbspType
125 }
126 if entry.meta.contentType != "" {
127 contentType = entry.meta.contentType
128 }
129 if entry.meta.state != "" {
130 stateLabel = entry.meta.state
131 }
132 }
133
134 labels := contexts.TablespaceLabels{
135 Tablespace: entry.name,
136 Type: tspType,
137 Content_type: contentType,
138 State: stateLabel,
139 }
140
141 contexts.Tablespace.Usage.Set(c.State, labels, contexts.TablespaceUsageValues{
142 Used: entry.metrics.UsedPercent,
143 })
144
145 contexts.Tablespace.Size.Set(c.State, labels, contexts.TablespaceSizeValues{
146 Used: entry.metrics.UsedSize,
147 Free: entry.metrics.FreeSize,
148 })
149
150 contexts.Tablespace.UsableSize.Set(c.State, labels, contexts.TablespaceUsableSizeValues{
151 Total: entry.metrics.TotalSize,
152 Usable: entry.metrics.UsableSize,
153 })
154
155 contexts.Tablespace.State.Set(c.State, labels, contexts.TablespaceStateValues{
156 State: entry.metrics.State,
157 })
158 }
159
160 func (c *Collector) emitTablespaceGroupMetrics(groups map[string]*tablespaceGroupAggregate, overflow *tablespaceGroupAggregate, overflowCount int) {
161 keys := make([]string, 0, len(groups))
162 for k := range groups {
163 keys = append(keys, k)
164 }
165 sort.Strings(keys)
166
167 for _, key := range keys {
168 agg := groups[key]
169 labels := contexts.TablespaceGroupLabels{Group: key}
170
171 usedPercent := int64(0)
172 if agg.TotalSize > 0 {
173 usedPercent = agg.UsedSize * 100 * Precision / agg.TotalSize
174 }
175 contexts.TablespaceGroup.Usage.Set(c.State, labels, contexts.TablespaceGroupUsageValues{
176 Used: usedPercent,
177 })
178
179 contexts.TablespaceGroup.Size.Set(c.State, labels, contexts.TablespaceGroupSizeValues{
180 Used: agg.UsedSize,
181 Free: agg.FreeSize,
182 })
183
184 contexts.TablespaceGroup.UsableSize.Set(c.State, labels, contexts.TablespaceGroupUsableSizeValues{
185 Total: agg.TotalSize,
186 Usable: agg.UsableSize,
187 })
188
189 contexts.TablespaceGroup.State.Set(c.State, labels, contexts.TablespaceGroupStateValues{
190 State: agg.State,
191 })
192 }
193
194 if overflowCount > 0 && overflow != nil {
195 labels := contexts.TablespaceGroupLabels{Group: "__other__"}
196 usedPercent := int64(0)
197 if overflow.TotalSize > 0 {
198 usedPercent = overflow.UsedSize * 100 * Precision / overflow.TotalSize
199 }
200 contexts.TablespaceGroup.Usage.Set(c.State, labels, contexts.TablespaceGroupUsageValues{Used: usedPercent})
201 contexts.TablespaceGroup.Size.Set(c.State, labels, contexts.TablespaceGroupSizeValues{Used: overflow.UsedSize, Free: overflow.FreeSize})
202 contexts.TablespaceGroup.UsableSize.Set(c.State, labels, contexts.TablespaceGroupUsableSizeValues{Total: overflow.TotalSize, Usable: overflow.UsableSize})
203 contexts.TablespaceGroup.State.Set(c.State, labels, contexts.TablespaceGroupStateValues{State: overflow.State})
204 }
205 }