| /* |
| * Copyright 2018-present Open Networking Foundation |
| |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package dmiserver |
| |
| import ( |
| "context" |
| log "github.com/sirupsen/logrus" |
| "math/rand" |
| "sync" |
| "time" |
| |
| "github.com/golang/protobuf/ptypes" |
| dmi "github.com/opencord/device-management-interface/go/dmi" |
| ) |
| |
| //MetricGenerationFunc to generate the metrics to the kafka bus |
| type MetricGenerationFunc func(*dmi.Component, *DmiAPIServer) *dmi.Metric |
| |
| // MetricTriggerConfig is the configuration of a metric and the time at which it will be exported |
| type MetricTriggerConfig struct { |
| cfg dmi.MetricConfig |
| t time.Time |
| } |
| |
| //DmiMetricsGenerator has the attributes for generating metrics |
| type DmiMetricsGenerator struct { |
| apiSrv *DmiAPIServer |
| configuredMetrics map[dmi.MetricNames]MetricTriggerConfig |
| access sync.Mutex |
| mgCancelFunc context.CancelFunc |
| } |
| |
| var dmiMG DmiMetricsGenerator |
| |
| //StartMetricGenerator starts the metric generator |
| func StartMetricGenerator(apiSrv *DmiAPIServer) { |
| log.Debugf("StartMetricGenerator invoked") |
| // Seed the rand for use later on |
| rand.Seed(time.Now().UnixNano()) |
| |
| dmiMG = DmiMetricsGenerator{ |
| apiSrv: apiSrv, |
| } |
| dmiMG.configuredMetrics = make(map[dmi.MetricNames]MetricTriggerConfig) |
| |
| // Add CPU usage as a default Metric reported every 60 secs |
| cpuMetricConfig := dmi.MetricConfig{ |
| MetricId: dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE, |
| IsConfigured: true, |
| PollInterval: 60, |
| } |
| dmiMG.configuredMetrics[dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE] = MetricTriggerConfig{ |
| cfg: cpuMetricConfig, |
| t: time.Unix(0, 0), |
| } |
| |
| // Add FAN speed metric as a default Metric reported every 120 secs |
| fanSpeedMetricConfig := dmi.MetricConfig{ |
| MetricId: dmi.MetricNames_METRIC_FAN_SPEED, |
| IsConfigured: true, |
| PollInterval: 120, |
| } |
| dmiMG.configuredMetrics[dmi.MetricNames_METRIC_FAN_SPEED] = MetricTriggerConfig{ |
| cfg: fanSpeedMetricConfig, |
| t: time.Unix(0, 0), |
| } |
| |
| // Add RAM usage percentage metric reported every 60 seconds |
| ramUsageMetricConfig := dmi.MetricConfig{ |
| MetricId: dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE, |
| IsConfigured: false, |
| PollInterval: 60, |
| } |
| dmiMG.configuredMetrics[dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE] = MetricTriggerConfig{ |
| cfg: ramUsageMetricConfig, |
| t: time.Unix(0, 0), |
| } |
| |
| // Add DISK usage percentage metric reported every 60 seconds |
| diskUsageMetricConfig := dmi.MetricConfig{ |
| MetricId: dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE, |
| IsConfigured: false, |
| PollInterval: 60, |
| } |
| dmiMG.configuredMetrics[dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE] = MetricTriggerConfig{ |
| cfg: diskUsageMetricConfig, |
| t: time.Unix(0, 0), |
| } |
| // Add Inner Surrounding TEMP usage percentage metric reported every 120 seconds |
| innerTempUsageMetricConfig := dmi.MetricConfig{ |
| MetricId: dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP, |
| IsConfigured: true, |
| PollInterval: 120, |
| } |
| dmiMG.configuredMetrics[dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP] = MetricTriggerConfig{ |
| cfg: innerTempUsageMetricConfig, |
| t: time.Unix(0, 0), |
| } |
| |
| StartGeneratingMetrics() |
| } |
| |
| // StartGeneratingMetrics starts the goroutine which submits metrics to the metrics channel |
| func StartGeneratingMetrics() { |
| if dmiMG.apiSrv == nil { |
| // Metric Generator is not yet initialized/started. |
| // Means that the device is not managed on the DMI interface |
| return |
| } |
| |
| // initialize a new context |
| var mgCtx context.Context |
| mgCtx, dmiMG.mgCancelFunc = context.WithCancel(context.Background()) |
| |
| go generateMetrics(mgCtx) |
| } |
| |
| func generateMetrics(ctx context.Context) { |
| loop: |
| for { |
| select { |
| case <-ctx.Done(): |
| log.Infof("Stopping generation of metrics ") |
| break loop |
| default: |
| c := make(map[dmi.MetricNames]MetricTriggerConfig) |
| |
| dmiMG.access.Lock() |
| for k, v := range dmiMG.configuredMetrics { |
| c[k] = v |
| } |
| dmiMG.access.Unlock() |
| |
| now := time.Now() |
| // For all the supported metrics |
| for k, v := range c { |
| if dmiMG.apiSrv.root == nil || dmiMG.apiSrv.root.Children == nil { |
| // inventory might not yet be created or somehow disappeared |
| break |
| } |
| |
| if k == dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE && v.cfg.IsConfigured { |
| if now.Before(v.t) { |
| continue |
| } |
| updateConfiguredMetrics(now, dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE, &v) |
| |
| // Get the CPUs |
| for _, cpu := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_CPU) { |
| m := generateCPUUsageMetric(cpu, dmiMG.apiSrv) |
| logger.Debugf("Got metric %v", m) |
| sendOutMetric(m, dmiMG.apiSrv) |
| } |
| } else if k == dmi.MetricNames_METRIC_FAN_SPEED && v.cfg.IsConfigured { |
| if now.Before(v.t) { |
| continue |
| } |
| updateConfiguredMetrics(now, dmi.MetricNames_METRIC_FAN_SPEED, &v) |
| |
| // Get the FANs |
| for _, fan := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_FAN) { |
| m := generateFanSpeedMetric(fan, dmiMG.apiSrv) |
| logger.Debugf("Got metric %v", m) |
| sendOutMetric(m, dmiMG.apiSrv) |
| } |
| } else if k == dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE && v.cfg.IsConfigured { |
| if now.Before(v.t) { |
| continue |
| } |
| updateConfiguredMetrics(now, dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE, &v) |
| // Get the RAM |
| for _, ram := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_MEMORY) { |
| m := generateRAMUsageMetric(ram, dmiMG.apiSrv) |
| logger.Debugf("Got metric for ram usage percentage %v", m) |
| sendOutMetric(m, dmiMG.apiSrv) |
| } |
| } else if k == dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE && v.cfg.IsConfigured { |
| if now.Before(v.t) { |
| continue |
| } |
| updateConfiguredMetrics(now, dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE, &v) |
| // Get the DISK |
| for _, disk := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_STORAGE) { |
| m := generateDiskUsageMetric(disk, dmiMG.apiSrv) |
| logger.Debugf("Got metric for disk usage percentage %v", m) |
| sendOutMetric(m, dmiMG.apiSrv) |
| } |
| } else if k == dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP && v.cfg.IsConfigured { |
| if now.Before(v.t) { |
| continue |
| } |
| updateConfiguredMetrics(now, dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP, &v) |
| // Get the INNER SURROUNDING TEMPERATURE |
| for _, isTemp := range findComponentsOfType(dmiMG.apiSrv.root.Children, dmi.ComponentType_COMPONENT_TYPE_SENSOR) { |
| m := generateInnerSurroundingTempMetric(isTemp, dmiMG.apiSrv) |
| logger.Debugf("Got metric for inner surrounding temperature %v", m) |
| sendOutMetric(m, dmiMG.apiSrv) |
| } |
| } |
| } |
| time.Sleep(1 * time.Second) |
| } |
| } |
| } |
| |
| func sendOutMetric(metric interface{}, apiSrv *DmiAPIServer) { |
| select { |
| case apiSrv.metricChannel <- metric: |
| default: |
| logger.Debugf("Channel not ready dropping Metric") |
| } |
| } |
| |
| func updateConfiguredMetrics(curr time.Time, typ dmi.MetricNames, old *MetricTriggerConfig) { |
| dmiMG.access.Lock() |
| dmiMG.configuredMetrics[typ] = MetricTriggerConfig{ |
| cfg: old.cfg, |
| t: curr.Add(time.Second * time.Duration(old.cfg.PollInterval)), |
| } |
| dmiMG.access.Unlock() |
| } |
| |
| func updateMetricIDAndMetaData(id dmi.MetricNames, c *dmi.Component, apiSrv *DmiAPIServer, m *dmi.Metric) *dmi.Metric { |
| m.MetricId = id |
| m.MetricMetadata = &dmi.MetricMetaData{ |
| DeviceUuid: &dmi.Uuid{ |
| Uuid: apiSrv.uuid, |
| }, |
| ComponentUuid: c.Uuid, |
| ComponentName: c.Name, |
| } |
| return m |
| } |
| |
| func generateCPUUsageMetric(cpu *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric { |
| var met dmi.Metric |
| met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE, cpu, apiSrv, &met) |
| met.Value = &dmi.ComponentSensorData{ |
| Value: generateRand(1, 20), |
| Type: dmi.DataValueType_VALUE_TYPE_OTHER, |
| Scale: dmi.ValueScale_VALUE_SCALE_UNITS, |
| Timestamp: ptypes.TimestampNow(), |
| } |
| return &met |
| } |
| |
| func generateFanSpeedMetric(fan *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric { |
| var met dmi.Metric |
| met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_FAN_SPEED, fan, apiSrv, &met) |
| met.Value = &dmi.ComponentSensorData{ |
| Value: generateRand(3000, 4000), |
| Type: dmi.DataValueType_VALUE_TYPE_RPM, |
| Scale: dmi.ValueScale_VALUE_SCALE_UNITS, |
| Timestamp: ptypes.TimestampNow(), |
| } |
| return &met |
| } |
| |
| // return a random number RAND which is: lValue < RAND < hValue |
| func generateRand(lValue, hValue int32) int32 { |
| if lValue >= hValue { |
| return 0 |
| } |
| |
| diff := hValue - lValue |
| |
| randVal := rand.Int31n(diff) |
| |
| return lValue + randVal |
| } |
| |
| //UpdateMetricConfig Adds/Updates the passed metric configuration |
| func UpdateMetricConfig(newCfg *dmi.MetricConfig) { |
| dmiMG.access.Lock() |
| dmiMG.configuredMetrics[newCfg.GetMetricId()] = MetricTriggerConfig{ |
| cfg: *newCfg, |
| t: time.Unix(0, 0), |
| } |
| dmiMG.access.Unlock() |
| logger.Infof("Metric updated %v", newCfg) |
| } |
| |
| func generateRAMUsageMetric(ram *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric { |
| var met dmi.Metric |
| met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE, ram, apiSrv, &met) |
| met.Value = &dmi.ComponentSensorData{ |
| Value: generateRand(1, 8), |
| Type: dmi.DataValueType_VALUE_TYPE_OTHER, |
| Scale: dmi.ValueScale_VALUE_SCALE_GIGA, |
| Timestamp: ptypes.TimestampNow(), |
| } |
| return &met |
| } |
| |
| func generateDiskUsageMetric(disk *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric { |
| var met dmi.Metric |
| met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE, disk, apiSrv, &met) |
| met.Value = &dmi.ComponentSensorData{ |
| Value: generateRand(50, 500), |
| Type: dmi.DataValueType_VALUE_TYPE_OTHER, |
| Scale: dmi.ValueScale_VALUE_SCALE_GIGA, |
| Timestamp: ptypes.TimestampNow(), |
| } |
| return &met |
| } |
| |
| func generateInnerSurroundingTempMetric(istemp *dmi.Component, apiSrv *DmiAPIServer) *dmi.Metric { |
| var met dmi.Metric |
| met = *updateMetricIDAndMetaData(dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP, istemp, apiSrv, &met) |
| met.Value = &dmi.ComponentSensorData{ |
| Value: generateRand(30, 40), |
| Type: dmi.DataValueType_VALUE_TYPE_CELSIUS, |
| Scale: dmi.ValueScale_VALUE_SCALE_UNITS, |
| Timestamp: ptypes.TimestampNow(), |
| } |
| return &met |
| } |
| |
| // get the metrics list |
| func getMetricsList() []*dmi.MetricConfig { |
| components := make(map[dmi.MetricNames]MetricTriggerConfig) |
| dmiMG.access.Lock() |
| |
| for key, value := range dmiMG.configuredMetrics { |
| components[key] = value |
| } |
| |
| dmiMG.access.Unlock() |
| |
| var toRet []*dmi.MetricConfig |
| for _, v := range components { |
| metricConfig := v.cfg |
| toRet = append(toRet, &metricConfig) |
| } |
| logger.Debugf("Metrics list %+v", toRet) |
| return toRet |
| } |
| |
| func getMetric(comp *dmi.Component, metricID dmi.MetricNames) *dmi.Metric { |
| switch metricID { |
| case dmi.MetricNames_METRIC_FAN_SPEED: |
| metric := generateFanSpeedMetric(comp, dmiMG.apiSrv) |
| return metric |
| |
| case dmi.MetricNames_METRIC_CPU_USAGE_PERCENTAGE: |
| metric := generateCPUUsageMetric(comp, dmiMG.apiSrv) |
| return metric |
| |
| case dmi.MetricNames_METRIC_RAM_USAGE_PERCENTAGE: |
| metric := generateRAMUsageMetric(comp, dmiMG.apiSrv) |
| return metric |
| |
| case dmi.MetricNames_METRIC_DISK_USAGE_PERCENTAGE: |
| metric := generateDiskUsageMetric(comp, dmiMG.apiSrv) |
| return metric |
| |
| case dmi.MetricNames_METRIC_INNER_SURROUNDING_TEMP: |
| metric := generateInnerSurroundingTempMetric(comp, dmiMG.apiSrv) |
| return metric |
| } |
| return nil |
| } |
| |
| // StopGeneratingMetrics stops the goroutine which submits metrics to the metrics channel |
| func StopGeneratingMetrics() { |
| if dmiMG.mgCancelFunc != nil { |
| dmiMG.mgCancelFunc() |
| } |
| } |
| |
| // StopMetricGenerator stops the generation of metrics and cleans up all local context |
| func StopMetricGenerator() { |
| logger.Debugf("StopMetricGenerator invoked") |
| |
| StopGeneratingMetrics() |
| |
| dmiMG.access.Lock() |
| // reset it to an empty map |
| dmiMG.configuredMetrics = make(map[dmi.MetricNames]MetricTriggerConfig) |
| dmiMG.access.Unlock() |
| } |