blob: fdb5bb4c15cd2d6b90ada1551ca674dbfbdcef0a [file] [log] [blame]
Humera Kousera4442952020-11-23 23:51:19 +05301/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package dmiserver
18
19import (
Humera Kouser18b275c2020-11-30 11:30:36 +053020 "context"
Humera Kousera4442952020-11-23 23:51:19 +053021 "math/rand"
22 "sync"
23 "time"
24
David K. Bainbridgec415efe2021-08-19 13:05:21 +000025 log "github.com/sirupsen/logrus"
26 "google.golang.org/protobuf/types/known/timestamppb"
27
Humera Kousera4442952020-11-23 23:51:19 +053028 dmi "github.com/opencord/device-management-interface/go/dmi"
29)
30
Humera Kouser18b275c2020-11-30 11:30:36 +053031//MetricGenerationFunc to generate the metrics to the kafka bus
32type MetricGenerationFunc func(*dmi.Component, *DmiAPIServer) *dmi.Metric
33
Humera Kousera4442952020-11-23 23:51:19 +053034// MetricTriggerConfig is the configuration of a metric and the time at which it will be exported
35type MetricTriggerConfig struct {
36 cfg dmi.MetricConfig
37 t time.Time
38}
39
40//DmiMetricsGenerator has the attributes for generating metrics
41type DmiMetricsGenerator struct {
42 apiSrv *DmiAPIServer
43 configuredMetrics map[dmi.MetricNames]MetricTriggerConfig
44 access sync.Mutex
Humera Kouser18b275c2020-11-30 11:30:36 +053045 mgCancelFunc context.CancelFunc
Humera Kousera4442952020-11-23 23:51:19 +053046}
47
48var dmiMG DmiMetricsGenerator
49
50//StartMetricGenerator starts the metric generator
51func StartMetricGenerator(apiSrv *DmiAPIServer) {
Humera Kouser18b275c2020-11-30 11:30:36 +053052 log.Debugf("StartMetricGenerator invoked")
Humera Kousera4442952020-11-23 23:51:19 +053053 // Seed the rand for use later on
54 rand.Seed(time.Now().UnixNano())
55
56 dmiMG = DmiMetricsGenerator{
57 apiSrv: apiSrv,
58 }
59 dmiMG.configuredMetrics = make(map[dmi.MetricNames]MetricTriggerConfig)
60
61 // Add CPU usage as a default Metric reported every 60 secs
62 cpuMetricConfig := dmi.MetricConfig{
63 MetricId: dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE,
64 IsConfigured: true,
65 PollInterval: 60,
66 }
67 dmiMG.configuredMetrics[dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE] = MetricTriggerConfig{
68 cfg: cpuMetricConfig,
69 t: time.Unix(0, 0),
70 }
71
72 // Add FAN speed metric as a default Metric reported every 120 secs
73 fanSpeedMetricConfig := dmi.MetricConfig{
74 MetricId: dmi.MetricNames_METRIC_FAN_SPEED,
75 IsConfigured: true,
76 PollInterval: 120,
77 }
78 dmiMG.configuredMetrics[dmi.MetricNames_METRIC_FAN_SPEED] = MetricTriggerConfig{
79 cfg: fanSpeedMetricConfig,
80 t: time.Unix(0, 0),
81 }
82
83 // Add RAM usage percentage metric reported every 60 seconds
84 ramUsageMetricConfig := dmi.MetricConfig{
85 MetricId: dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE,
86 IsConfigured: false,
87 PollInterval: 60,
88 }
89 dmiMG.configuredMetrics[dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE] = MetricTriggerConfig{
90 cfg: ramUsageMetricConfig,
91 t: time.Unix(0, 0),
92 }
93
94 // Add DISK usage percentage metric reported every 60 seconds
95 diskUsageMetricConfig := dmi.MetricConfig{
96 MetricId: dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE,
97 IsConfigured: false,
98 PollInterval: 60,
99 }
100 dmiMG.configuredMetrics[dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE] = MetricTriggerConfig{
101 cfg: diskUsageMetricConfig,
102 t: time.Unix(0, 0),
103 }
104 // Add Inner Surrounding TEMP usage percentage metric reported every 120 seconds
105 innerTempUsageMetricConfig := dmi.MetricConfig{
106 MetricId: dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP,
107 IsConfigured: true,
108 PollInterval: 120,
109 }
110 dmiMG.configuredMetrics[dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP] = MetricTriggerConfig{
111 cfg: innerTempUsageMetricConfig,
112 t: time.Unix(0, 0),
113 }
114
Humera Kouser18b275c2020-11-30 11:30:36 +0530115 StartGeneratingMetrics()
116}
117
118// StartGeneratingMetrics starts the goroutine which submits metrics to the metrics channel
119func StartGeneratingMetrics() {
120 if dmiMG.apiSrv == nil {
121 // Metric Generator is not yet initialized/started.
122 // Means that the device is not managed on the DMI interface
123 return
124 }
125
126 // initialize a new context
127 var mgCtx context.Context
128 mgCtx, dmiMG.mgCancelFunc = context.WithCancel(context.Background())
129
130 go generateMetrics(mgCtx)
131}
132
133func generateMetrics(ctx context.Context) {
134loop:
135 for {
136 select {
137 case <-ctx.Done():
138 log.Infof("Stopping generation of metrics ")
139 break loop
140 default:
Humera Kousera4442952020-11-23 23:51:19 +0530141 c := make(map[dmi.MetricNames]MetricTriggerConfig)
142
143 dmiMG.access.Lock()
144 for k, v := range dmiMG.configuredMetrics {
145 c[k] = v
146 }
147 dmiMG.access.Unlock()
148
149 now := time.Now()
150 // For all the supported metrics
151 for k, v := range c {
152 if dmiMG.apiSrv.root == nil || dmiMG.apiSrv.root.Children == nil {
153 // inventory might not yet be created or somehow disappeared
154 break
155 }
156
157 if k == dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE && v.cfg.IsConfigured {
158 if now.Before(v.t) {
159 continue
160 }
161 updateConfiguredMetrics(now, dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE, &v)
162
163 // Get the CPUs
164 for _, cpu := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_CPU) {
165 m := generateCPUUsageMetric(cpu, dmiMG.apiSrv)
166 logger.Debugf("Got metric %v", m)
167 sendOutMetric(m, dmiMG.apiSrv)
168 }
169 } else if k == dmi.MetricNames_METRIC_FAN_SPEED && v.cfg.IsConfigured {
170 if now.Before(v.t) {
171 continue
172 }
173 updateConfiguredMetrics(now, dmi.MetricNames_METRIC_FAN_SPEED, &v)
174
175 // Get the FANs
176 for _, fan := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_FAN) {
177 m := generateFanSpeedMetric(fan, dmiMG.apiSrv)
178 logger.Debugf("Got metric %v", m)
179 sendOutMetric(m, dmiMG.apiSrv)
180 }
181 } else if k == dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE && v.cfg.IsConfigured {
182 if now.Before(v.t) {
183 continue
184 }
185 updateConfiguredMetrics(now, dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE, &v)
186 // Get the RAM
187 for _, ram := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_MEMORY) {
188 m := generateRAMUsageMetric(ram, dmiMG.apiSrv)
189 logger.Debugf("Got metric for ram usage percentage %v", m)
190 sendOutMetric(m, dmiMG.apiSrv)
191 }
192 } else if k == dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE && v.cfg.IsConfigured {
193 if now.Before(v.t) {
194 continue
195 }
196 updateConfiguredMetrics(now, dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE, &v)
197 // Get the DISK
198 for _, disk := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_STORAGE) {
199 m := generateDiskUsageMetric(disk, dmiMG.apiSrv)
200 logger.Debugf("Got metric for disk usage percentage %v", m)
201 sendOutMetric(m, dmiMG.apiSrv)
202 }
203 } else if k == dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP && v.cfg.IsConfigured {
204 if now.Before(v.t) {
205 continue
206 }
207 updateConfiguredMetrics(now, dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP, &v)
208 // Get the INNER SURROUNDING TEMPERATURE
209 for _, isTemp := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_SENSOR) {
210 m := generateInnerSurroundingTempMetric(isTemp, dmiMG.apiSrv)
211 logger.Debugf("Got metric for inner surrounding temperature %v", m)
212 sendOutMetric(m, dmiMG.apiSrv)
213 }
214 }
215 }
216 time.Sleep(1 * time.Second)
217 }
Humera Kouser18b275c2020-11-30 11:30:36 +0530218 }
Humera Kousera4442952020-11-23 23:51:19 +0530219}
220
221func sendOutMetric(metric interface{}, apiSrv *DmiAPIServer) {
222 select {
223 case apiSrv.metricChannel <- metric:
224 default:
225 logger.Debugf("Channel not ready dropping Metric")
226 }
227}
228
229func updateConfiguredMetrics(curr time.Time, typ dmi.MetricNames, old *MetricTriggerConfig) {
230 dmiMG.access.Lock()
231 dmiMG.configuredMetrics[typ] = MetricTriggerConfig{
232 cfg: old.cfg,
233 t: curr.Add(time.Second * time.Duration(old.cfg.PollInterval)),
234 }
235 dmiMG.access.Unlock()
236}
237
238func updateMetricIDAndMetaData(id dmi.MetricNames, c *dmi.Component, apiSrv *DmiAPIServer, m *dmi.Metric) *dmi.Metric {
239 m.MetricId = id
240 m.MetricMetadata = &dmi.MetricMetaData{
241 DeviceUuid: &dmi.Uuid{
242 Uuid: apiSrv.uuid,
243 },
244 ComponentUuid: c.Uuid,
245 ComponentName: c.Name,
246 }
247 return m
248}
249
250func generateCPUUsageMetric(cpu *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
251 var met dmi.Metric
252 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE, cpu, apiSrv, &met)
253 met.Value = &dmi.ComponentSensorData{
254 Value: generateRand(1, 20),
ssiddiqui6ca40702021-03-08 18:20:21 +0530255 Type: dmi.DataValueType_VALUE_TYPE_OTHER,
256 Scale: dmi.ValueScale_VALUE_SCALE_UNITS,
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000257 Timestamp: timestamppb.Now(),
Humera Kousera4442952020-11-23 23:51:19 +0530258 }
259 return &met
260}
261
262func generateFanSpeedMetric(fan *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
263 var met dmi.Metric
264 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_FAN_SPEED, fan, apiSrv, &met)
265 met.Value = &dmi.ComponentSensorData{
266 Value: generateRand(3000, 4000),
ssiddiqui6ca40702021-03-08 18:20:21 +0530267 Type: dmi.DataValueType_VALUE_TYPE_RPM,
268 Scale: dmi.ValueScale_VALUE_SCALE_UNITS,
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000269 Timestamp: timestamppb.Now(),
Humera Kousera4442952020-11-23 23:51:19 +0530270 }
271 return &met
272}
273
274// return a random number RAND which is: lValue < RAND < hValue
275func generateRand(lValue, hValue int32) int32 {
276 if lValue >= hValue {
277 return 0
278 }
279
280 diff := hValue - lValue
281
282 randVal := rand.Int31n(diff)
283
284 return lValue + randVal
285}
286
287//UpdateMetricConfig Adds/Updates the passed metric configuration
288func UpdateMetricConfig(newCfg *dmi.MetricConfig) {
289 dmiMG.access.Lock()
290 dmiMG.configuredMetrics[newCfg.GetMetricId()] = MetricTriggerConfig{
291 cfg: *newCfg,
292 t: time.Unix(0, 0),
293 }
294 dmiMG.access.Unlock()
295 logger.Infof("Metric updated %v", newCfg)
296}
297
298func generateRAMUsageMetric(ram *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
299 var met dmi.Metric
300 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE, ram, apiSrv, &met)
301 met.Value = &dmi.ComponentSensorData{
302 Value: generateRand(1, 8),
ssiddiqui6ca40702021-03-08 18:20:21 +0530303 Type: dmi.DataValueType_VALUE_TYPE_OTHER,
304 Scale: dmi.ValueScale_VALUE_SCALE_GIGA,
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000305 Timestamp: timestamppb.Now(),
Humera Kousera4442952020-11-23 23:51:19 +0530306 }
307 return &met
308}
309
310func generateDiskUsageMetric(disk *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
311 var met dmi.Metric
312 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE, disk, apiSrv, &met)
313 met.Value = &dmi.ComponentSensorData{
314 Value: generateRand(50, 500),
ssiddiqui6ca40702021-03-08 18:20:21 +0530315 Type: dmi.DataValueType_VALUE_TYPE_OTHER,
316 Scale: dmi.ValueScale_VALUE_SCALE_GIGA,
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000317 Timestamp: timestamppb.Now(),
Humera Kousera4442952020-11-23 23:51:19 +0530318 }
319 return &met
320}
321
322func generateInnerSurroundingTempMetric(istemp *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
323 var met dmi.Metric
324 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP, istemp, apiSrv, &met)
325 met.Value = &dmi.ComponentSensorData{
326 Value: generateRand(30, 40),
ssiddiqui6ca40702021-03-08 18:20:21 +0530327 Type: dmi.DataValueType_VALUE_TYPE_CELSIUS,
328 Scale: dmi.ValueScale_VALUE_SCALE_UNITS,
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000329 Timestamp: timestamppb.Now(),
Humera Kousera4442952020-11-23 23:51:19 +0530330 }
331 return &met
332}
333
334// get the metrics list
335func getMetricsList() []*dmi.MetricConfig {
336 components := make(map[dmi.MetricNames]MetricTriggerConfig)
337 dmiMG.access.Lock()
338
339 for key, value := range dmiMG.configuredMetrics {
340 components[key] = value
341 }
342
343 dmiMG.access.Unlock()
344
345 var toRet []*dmi.MetricConfig
346 for _, v := range components {
347 metricConfig := v.cfg
348 toRet = append(toRet, &metricConfig)
349 }
350 logger.Debugf("Metrics list %+v", toRet)
351 return toRet
352}
353
354func getMetric(comp *dmi.Component, metricID dmi.MetricNames) *dmi.Metric {
355 switch metricID {
356 case dmi.MetricNames_METRIC_FAN_SPEED:
357 metric := generateFanSpeedMetric(comp, dmiMG.apiSrv)
358 return metric
359
360 case dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE:
361 metric := generateCPUUsageMetric(comp, dmiMG.apiSrv)
362 return metric
363
364 case dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE:
365 metric := generateRAMUsageMetric(comp, dmiMG.apiSrv)
366 return metric
367
368 case dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE:
369 metric := generateDiskUsageMetric(comp, dmiMG.apiSrv)
370 return metric
371
372 case dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP:
373 metric := generateInnerSurroundingTempMetric(comp, dmiMG.apiSrv)
374 return metric
375 }
376 return nil
377}
Humera Kouser18b275c2020-11-30 11:30:36 +0530378
379// StopGeneratingMetrics stops the goroutine which submits metrics to the metrics channel
380func StopGeneratingMetrics() {
381 if dmiMG.mgCancelFunc != nil {
382 dmiMG.mgCancelFunc()
383 }
384}
385
386// StopMetricGenerator stops the generation of metrics and cleans up all local context
387func StopMetricGenerator() {
388 logger.Debugf("StopMetricGenerator invoked")
389
390 StopGeneratingMetrics()
391
392 dmiMG.access.Lock()
393 // reset it to an empty map
394 dmiMG.configuredMetrics = make(map[dmi.MetricNames]MetricTriggerConfig)
395 dmiMG.access.Unlock()
396}