blob: 66f7c8950d9c0f6c91eada291d8fb1ad7242b503 [file] [log] [blame]
Humera Kousera4442952020-11-23 23:51:19 +05301/*
Joey Armstrong14628cd2023-01-10 08:38:31 -05002 * Copyright 2018-2023 Open Networking Foundation (ONF) and the ONF Contributors
Humera Kousera4442952020-11-23 23:51:19 +05303
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
Abhay Kumarc5723cc2023-06-08 12:09:30 +053031// MetricGenerationFunc to generate the metrics to the kafka bus
Humera Kouser18b275c2020-11-30 11:30:36 +053032type 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
Abhay Kumarc5723cc2023-06-08 12:09:30 +053040// DmiMetricsGenerator has the attributes for generating metrics
Humera Kousera4442952020-11-23 23:51:19 +053041type 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
Abhay Kumarc5723cc2023-06-08 12:09:30 +053050// StartMetricGenerator starts the metric generator
Humera Kousera4442952020-11-23 23:51:19 +053051func 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{
Elia Battistone8d1fa42022-04-01 10:47:37 +0200241 DeviceUuid: apiSrv.uuid,
Humera Kousera4442952020-11-23 23:51:19 +0530242 ComponentUuid: c.Uuid,
243 ComponentName: c.Name,
244 }
245 return m
246}
247
248func generateCPUUsageMetric(cpu *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
249 var met dmi.Metric
250 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE, cpu, apiSrv, &met)
251 met.Value = &dmi.ComponentSensorData{
252 Value: generateRand(1, 20),
ssiddiqui6ca40702021-03-08 18:20:21 +0530253 Type: dmi.DataValueType_VALUE_TYPE_OTHER,
254 Scale: dmi.ValueScale_VALUE_SCALE_UNITS,
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000255 Timestamp: timestamppb.Now(),
Humera Kousera4442952020-11-23 23:51:19 +0530256 }
257 return &met
258}
259
260func generateFanSpeedMetric(fan *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
261 var met dmi.Metric
262 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_FAN_SPEED, fan, apiSrv, &met)
263 met.Value = &dmi.ComponentSensorData{
264 Value: generateRand(3000, 4000),
ssiddiqui6ca40702021-03-08 18:20:21 +0530265 Type: dmi.DataValueType_VALUE_TYPE_RPM,
266 Scale: dmi.ValueScale_VALUE_SCALE_UNITS,
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000267 Timestamp: timestamppb.Now(),
Humera Kousera4442952020-11-23 23:51:19 +0530268 }
269 return &met
270}
271
272// return a random number RAND which is: lValue < RAND < hValue
273func generateRand(lValue, hValue int32) int32 {
274 if lValue >= hValue {
275 return 0
276 }
277
278 diff := hValue - lValue
279
280 randVal := rand.Int31n(diff)
281
282 return lValue + randVal
283}
284
Abhay Kumarc5723cc2023-06-08 12:09:30 +0530285// UpdateMetricConfig Adds/Updates the passed metric configuration
Humera Kousera4442952020-11-23 23:51:19 +0530286func UpdateMetricConfig(newCfg *dmi.MetricConfig) {
287 dmiMG.access.Lock()
288 dmiMG.configuredMetrics[newCfg.GetMetricId()] = MetricTriggerConfig{
289 cfg: *newCfg,
290 t: time.Unix(0, 0),
291 }
292 dmiMG.access.Unlock()
293 logger.Infof("Metric updated %v", newCfg)
294}
295
296func generateRAMUsageMetric(ram *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
297 var met dmi.Metric
298 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE, ram, apiSrv, &met)
299 met.Value = &dmi.ComponentSensorData{
300 Value: generateRand(1, 8),
ssiddiqui6ca40702021-03-08 18:20:21 +0530301 Type: dmi.DataValueType_VALUE_TYPE_OTHER,
302 Scale: dmi.ValueScale_VALUE_SCALE_GIGA,
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000303 Timestamp: timestamppb.Now(),
Humera Kousera4442952020-11-23 23:51:19 +0530304 }
305 return &met
306}
307
308func generateDiskUsageMetric(disk *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
309 var met dmi.Metric
310 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE, disk, apiSrv, &met)
311 met.Value = &dmi.ComponentSensorData{
312 Value: generateRand(50, 500),
ssiddiqui6ca40702021-03-08 18:20:21 +0530313 Type: dmi.DataValueType_VALUE_TYPE_OTHER,
314 Scale: dmi.ValueScale_VALUE_SCALE_GIGA,
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000315 Timestamp: timestamppb.Now(),
Humera Kousera4442952020-11-23 23:51:19 +0530316 }
317 return &met
318}
319
320func generateInnerSurroundingTempMetric(istemp *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
321 var met dmi.Metric
322 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP, istemp, apiSrv, &met)
323 met.Value = &dmi.ComponentSensorData{
324 Value: generateRand(30, 40),
ssiddiqui6ca40702021-03-08 18:20:21 +0530325 Type: dmi.DataValueType_VALUE_TYPE_CELSIUS,
326 Scale: dmi.ValueScale_VALUE_SCALE_UNITS,
David K. Bainbridgec415efe2021-08-19 13:05:21 +0000327 Timestamp: timestamppb.Now(),
Humera Kousera4442952020-11-23 23:51:19 +0530328 }
329 return &met
330}
331
332// get the metrics list
333func getMetricsList() []*dmi.MetricConfig {
334 components := make(map[dmi.MetricNames]MetricTriggerConfig)
335 dmiMG.access.Lock()
336
337 for key, value := range dmiMG.configuredMetrics {
338 components[key] = value
339 }
340
341 dmiMG.access.Unlock()
342
343 var toRet []*dmi.MetricConfig
344 for _, v := range components {
345 metricConfig := v.cfg
346 toRet = append(toRet, &metricConfig)
347 }
348 logger.Debugf("Metrics list %+v", toRet)
349 return toRet
350}
351
352func getMetric(comp *dmi.Component, metricID dmi.MetricNames) *dmi.Metric {
353 switch metricID {
354 case dmi.MetricNames_METRIC_FAN_SPEED:
355 metric := generateFanSpeedMetric(comp, dmiMG.apiSrv)
356 return metric
357
358 case dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE:
359 metric := generateCPUUsageMetric(comp, dmiMG.apiSrv)
360 return metric
361
362 case dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE:
363 metric := generateRAMUsageMetric(comp, dmiMG.apiSrv)
364 return metric
365
366 case dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE:
367 metric := generateDiskUsageMetric(comp, dmiMG.apiSrv)
368 return metric
369
370 case dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP:
371 metric := generateInnerSurroundingTempMetric(comp, dmiMG.apiSrv)
372 return metric
373 }
374 return nil
375}
Humera Kouser18b275c2020-11-30 11:30:36 +0530376
377// StopGeneratingMetrics stops the goroutine which submits metrics to the metrics channel
378func StopGeneratingMetrics() {
379 if dmiMG.mgCancelFunc != nil {
380 dmiMG.mgCancelFunc()
381 }
382}
383
384// StopMetricGenerator stops the generation of metrics and cleans up all local context
385func StopMetricGenerator() {
386 logger.Debugf("StopMetricGenerator invoked")
387
388 StopGeneratingMetrics()
389
390 dmiMG.access.Lock()
391 // reset it to an empty map
392 dmiMG.configuredMetrics = make(map[dmi.MetricNames]MetricTriggerConfig)
393 dmiMG.access.Unlock()
394}