blob: 09deacefd8c9d6c1934b55a7352937c5ff05bd1e [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"
21 log "github.com/sirupsen/logrus"
Humera Kousera4442952020-11-23 23:51:19 +053022 "math/rand"
23 "sync"
24 "time"
25
26 "github.com/golang/protobuf/ptypes"
27 dmi "github.com/opencord/device-management-interface/go/dmi"
28)
29
Humera Kouser18b275c2020-11-30 11:30:36 +053030//MetricGenerationFunc to generate the metrics to the kafka bus
31type MetricGenerationFunc func(*dmi.Component, *DmiAPIServer) *dmi.Metric
32
Humera Kousera4442952020-11-23 23:51:19 +053033// MetricTriggerConfig is the configuration of a metric and the time at which it will be exported
34type MetricTriggerConfig struct {
35 cfg dmi.MetricConfig
36 t time.Time
37}
38
39//DmiMetricsGenerator has the attributes for generating metrics
40type DmiMetricsGenerator struct {
41 apiSrv *DmiAPIServer
42 configuredMetrics map[dmi.MetricNames]MetricTriggerConfig
43 access sync.Mutex
Humera Kouser18b275c2020-11-30 11:30:36 +053044 mgCancelFunc context.CancelFunc
Humera Kousera4442952020-11-23 23:51:19 +053045}
46
47var dmiMG DmiMetricsGenerator
48
49//StartMetricGenerator starts the metric generator
50func StartMetricGenerator(apiSrv *DmiAPIServer) {
Humera Kouser18b275c2020-11-30 11:30:36 +053051 log.Debugf("StartMetricGenerator invoked")
Humera Kousera4442952020-11-23 23:51:19 +053052 // Seed the rand for use later on
53 rand.Seed(time.Now().UnixNano())
54
55 dmiMG = DmiMetricsGenerator{
56 apiSrv: apiSrv,
57 }
58 dmiMG.configuredMetrics = make(map[dmi.MetricNames]MetricTriggerConfig)
59
60 // Add CPU usage as a default Metric reported every 60 secs
61 cpuMetricConfig := dmi.MetricConfig{
62 MetricId: dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE,
63 IsConfigured: true,
64 PollInterval: 60,
65 }
66 dmiMG.configuredMetrics[dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE] = MetricTriggerConfig{
67 cfg: cpuMetricConfig,
68 t: time.Unix(0, 0),
69 }
70
71 // Add FAN speed metric as a default Metric reported every 120 secs
72 fanSpeedMetricConfig := dmi.MetricConfig{
73 MetricId: dmi.MetricNames_METRIC_FAN_SPEED,
74 IsConfigured: true,
75 PollInterval: 120,
76 }
77 dmiMG.configuredMetrics[dmi.MetricNames_METRIC_FAN_SPEED] = MetricTriggerConfig{
78 cfg: fanSpeedMetricConfig,
79 t: time.Unix(0, 0),
80 }
81
82 // Add RAM usage percentage metric reported every 60 seconds
83 ramUsageMetricConfig := dmi.MetricConfig{
84 MetricId: dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE,
85 IsConfigured: false,
86 PollInterval: 60,
87 }
88 dmiMG.configuredMetrics[dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE] = MetricTriggerConfig{
89 cfg: ramUsageMetricConfig,
90 t: time.Unix(0, 0),
91 }
92
93 // Add DISK usage percentage metric reported every 60 seconds
94 diskUsageMetricConfig := dmi.MetricConfig{
95 MetricId: dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE,
96 IsConfigured: false,
97 PollInterval: 60,
98 }
99 dmiMG.configuredMetrics[dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE] = MetricTriggerConfig{
100 cfg: diskUsageMetricConfig,
101 t: time.Unix(0, 0),
102 }
103 // Add Inner Surrounding TEMP usage percentage metric reported every 120 seconds
104 innerTempUsageMetricConfig := dmi.MetricConfig{
105 MetricId: dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP,
106 IsConfigured: true,
107 PollInterval: 120,
108 }
109 dmiMG.configuredMetrics[dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP] = MetricTriggerConfig{
110 cfg: innerTempUsageMetricConfig,
111 t: time.Unix(0, 0),
112 }
113
Humera Kouser18b275c2020-11-30 11:30:36 +0530114 StartGeneratingMetrics()
115}
116
117// StartGeneratingMetrics starts the goroutine which submits metrics to the metrics channel
118func StartGeneratingMetrics() {
119 if dmiMG.apiSrv == nil {
120 // Metric Generator is not yet initialized/started.
121 // Means that the device is not managed on the DMI interface
122 return
123 }
124
125 // initialize a new context
126 var mgCtx context.Context
127 mgCtx, dmiMG.mgCancelFunc = context.WithCancel(context.Background())
128
129 go generateMetrics(mgCtx)
130}
131
132func generateMetrics(ctx context.Context) {
133loop:
134 for {
135 select {
136 case <-ctx.Done():
137 log.Infof("Stopping generation of metrics ")
138 break loop
139 default:
Humera Kousera4442952020-11-23 23:51:19 +0530140 c := make(map[dmi.MetricNames]MetricTriggerConfig)
141
142 dmiMG.access.Lock()
143 for k, v := range dmiMG.configuredMetrics {
144 c[k] = v
145 }
146 dmiMG.access.Unlock()
147
148 now := time.Now()
149 // For all the supported metrics
150 for k, v := range c {
151 if dmiMG.apiSrv.root == nil || dmiMG.apiSrv.root.Children == nil {
152 // inventory might not yet be created or somehow disappeared
153 break
154 }
155
156 if k == dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE && v.cfg.IsConfigured {
157 if now.Before(v.t) {
158 continue
159 }
160 updateConfiguredMetrics(now, dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE, &v)
161
162 // Get the CPUs
163 for _, cpu := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_CPU) {
164 m := generateCPUUsageMetric(cpu, dmiMG.apiSrv)
165 logger.Debugf("Got metric %v", m)
166 sendOutMetric(m, dmiMG.apiSrv)
167 }
168 } else if k == dmi.MetricNames_METRIC_FAN_SPEED && v.cfg.IsConfigured {
169 if now.Before(v.t) {
170 continue
171 }
172 updateConfiguredMetrics(now, dmi.MetricNames_METRIC_FAN_SPEED, &v)
173
174 // Get the FANs
175 for _, fan := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_FAN) {
176 m := generateFanSpeedMetric(fan, dmiMG.apiSrv)
177 logger.Debugf("Got metric %v", m)
178 sendOutMetric(m, dmiMG.apiSrv)
179 }
180 } else if k == dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE && v.cfg.IsConfigured {
181 if now.Before(v.t) {
182 continue
183 }
184 updateConfiguredMetrics(now, dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE, &v)
185 // Get the RAM
186 for _, ram := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_MEMORY) {
187 m := generateRAMUsageMetric(ram, dmiMG.apiSrv)
188 logger.Debugf("Got metric for ram usage percentage %v", m)
189 sendOutMetric(m, dmiMG.apiSrv)
190 }
191 } else if k == dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE && v.cfg.IsConfigured {
192 if now.Before(v.t) {
193 continue
194 }
195 updateConfiguredMetrics(now, dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE, &v)
196 // Get the DISK
197 for _, disk := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_STORAGE) {
198 m := generateDiskUsageMetric(disk, dmiMG.apiSrv)
199 logger.Debugf("Got metric for disk usage percentage %v", m)
200 sendOutMetric(m, dmiMG.apiSrv)
201 }
202 } else if k == dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP && v.cfg.IsConfigured {
203 if now.Before(v.t) {
204 continue
205 }
206 updateConfiguredMetrics(now, dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP, &v)
207 // Get the INNER SURROUNDING TEMPERATURE
208 for _, isTemp := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_SENSOR) {
209 m := generateInnerSurroundingTempMetric(isTemp, dmiMG.apiSrv)
210 logger.Debugf("Got metric for inner surrounding temperature %v", m)
211 sendOutMetric(m, dmiMG.apiSrv)
212 }
213 }
214 }
215 time.Sleep(1 * time.Second)
216 }
Humera Kouser18b275c2020-11-30 11:30:36 +0530217 }
Humera Kousera4442952020-11-23 23:51:19 +0530218}
219
220func sendOutMetric(metric interface{}, apiSrv *DmiAPIServer) {
221 select {
222 case apiSrv.metricChannel <- metric:
223 default:
224 logger.Debugf("Channel not ready dropping Metric")
225 }
226}
227
228func updateConfiguredMetrics(curr time.Time, typ dmi.MetricNames, old *MetricTriggerConfig) {
229 dmiMG.access.Lock()
230 dmiMG.configuredMetrics[typ] = MetricTriggerConfig{
231 cfg: old.cfg,
232 t: curr.Add(time.Second * time.Duration(old.cfg.PollInterval)),
233 }
234 dmiMG.access.Unlock()
235}
236
237func updateMetricIDAndMetaData(id dmi.MetricNames, c *dmi.Component, apiSrv *DmiAPIServer, m *dmi.Metric) *dmi.Metric {
238 m.MetricId = id
239 m.MetricMetadata = &dmi.MetricMetaData{
240 DeviceUuid: &dmi.Uuid{
241 Uuid: apiSrv.uuid,
242 },
243 ComponentUuid: c.Uuid,
244 ComponentName: c.Name,
245 }
246 return m
247}
248
249func generateCPUUsageMetric(cpu *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
250 var met dmi.Metric
251 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE, cpu, apiSrv, &met)
252 met.Value = &dmi.ComponentSensorData{
253 Value: generateRand(1, 20),
254 Type: dmi.SensorValueType_SENSOR_VALUE_TYPE_OTHER,
255 Scale: dmi.SensorValueScale_SENSOR_VALUE_SCALE_UNITS,
256 Timestamp: ptypes.TimestampNow(),
257 }
258 return &met
259}
260
261func generateFanSpeedMetric(fan *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
262 var met dmi.Metric
263 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_FAN_SPEED, fan, apiSrv, &met)
264 met.Value = &dmi.ComponentSensorData{
265 Value: generateRand(3000, 4000),
266 Type: dmi.SensorValueType_SENSOR_VALUE_TYPE_RPM,
267 Scale: dmi.SensorValueScale_SENSOR_VALUE_SCALE_UNITS,
268 Timestamp: ptypes.TimestampNow(),
269 }
270 return &met
271}
272
273// return a random number RAND which is: lValue < RAND < hValue
274func generateRand(lValue, hValue int32) int32 {
275 if lValue >= hValue {
276 return 0
277 }
278
279 diff := hValue - lValue
280
281 randVal := rand.Int31n(diff)
282
283 return lValue + randVal
284}
285
286//UpdateMetricConfig Adds/Updates the passed metric configuration
287func UpdateMetricConfig(newCfg *dmi.MetricConfig) {
288 dmiMG.access.Lock()
289 dmiMG.configuredMetrics[newCfg.GetMetricId()] = MetricTriggerConfig{
290 cfg: *newCfg,
291 t: time.Unix(0, 0),
292 }
293 dmiMG.access.Unlock()
294 logger.Infof("Metric updated %v", newCfg)
295}
296
297func generateRAMUsageMetric(ram *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
298 var met dmi.Metric
299 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE, ram, apiSrv, &met)
300 met.Value = &dmi.ComponentSensorData{
301 Value: generateRand(1, 8),
302 Type: dmi.SensorValueType_SENSOR_VALUE_TYPE_OTHER,
303 Scale: dmi.SensorValueScale_SENSOR_VALUE_SCALE_GIGA,
304 Timestamp: ptypes.TimestampNow(),
305 }
306 return &met
307}
308
309func generateDiskUsageMetric(disk *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
310 var met dmi.Metric
311 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE, disk, apiSrv, &met)
312 met.Value = &dmi.ComponentSensorData{
313 Value: generateRand(50, 500),
314 Type: dmi.SensorValueType_SENSOR_VALUE_TYPE_OTHER,
315 Scale: dmi.SensorValueScale_SENSOR_VALUE_SCALE_GIGA,
316 Timestamp: ptypes.TimestampNow(),
317 }
318 return &met
319}
320
321func generateInnerSurroundingTempMetric(istemp *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric {
322 var met dmi.Metric
323 met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP, istemp, apiSrv, &met)
324 met.Value = &dmi.ComponentSensorData{
325 Value: generateRand(30, 40),
326 Type: dmi.SensorValueType_SENSOR_VALUE_TYPE_CELSIUS,
327 Scale: dmi.SensorValueScale_SENSOR_VALUE_SCALE_UNITS,
328 Timestamp: ptypes.TimestampNow(),
329 }
330 return &met
331}
332
333// get the metrics list
334func getMetricsList() []*dmi.MetricConfig {
335 components := make(map[dmi.MetricNames]MetricTriggerConfig)
336 dmiMG.access.Lock()
337
338 for key, value := range dmiMG.configuredMetrics {
339 components[key] = value
340 }
341
342 dmiMG.access.Unlock()
343
344 var toRet []*dmi.MetricConfig
345 for _, v := range components {
346 metricConfig := v.cfg
347 toRet = append(toRet, &metricConfig)
348 }
349 logger.Debugf("Metrics list %+v", toRet)
350 return toRet
351}
352
353func getMetric(comp *dmi.Component, metricID dmi.MetricNames) *dmi.Metric {
354 switch metricID {
355 case dmi.MetricNames_METRIC_FAN_SPEED:
356 metric := generateFanSpeedMetric(comp, dmiMG.apiSrv)
357 return metric
358
359 case dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE:
360 metric := generateCPUUsageMetric(comp, dmiMG.apiSrv)
361 return metric
362
363 case dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE:
364 metric := generateRAMUsageMetric(comp, dmiMG.apiSrv)
365 return metric
366
367 case dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE:
368 metric := generateDiskUsageMetric(comp, dmiMG.apiSrv)
369 return metric
370
371 case dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP:
372 metric := generateInnerSurroundingTempMetric(comp, dmiMG.apiSrv)
373 return metric
374 }
375 return nil
376}
Humera Kouser18b275c2020-11-30 11:30:36 +0530377
378// StopGeneratingMetrics stops the goroutine which submits metrics to the metrics channel
379func StopGeneratingMetrics() {
380 if dmiMG.mgCancelFunc != nil {
381 dmiMG.mgCancelFunc()
382 }
383}
384
385// StopMetricGenerator stops the generation of metrics and cleans up all local context
386func StopMetricGenerator() {
387 logger.Debugf("StopMetricGenerator invoked")
388
389 StopGeneratingMetrics()
390
391 dmiMG.access.Lock()
392 // reset it to an empty map
393 dmiMG.configuredMetrics = make(map[dmi.MetricNames]MetricTriggerConfig)
394 dmiMG.access.Unlock()
395}