2 * Copyright (c) 2019 AT&T Intellectual Property.
3 * Copyright (c) 2018-2019 Nokia.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * This source code is part of the near-RT RIC (RAN Intelligent Controller)
18 * platform project (RICP).
33 app "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
37 const defaultReportingEntityID = "00000000-0000-0000-0000-000000000000"
38 const defaultVNFName = "Vespa"
39 const defaultNFNamingCode = "ricp"
41 func (v *VespaMgr) readSystemUUID() string {
42 data, err := ioutil.ReadFile("/sys/class/dmi/id/product_uuid")
44 return defaultReportingEntityID
46 return strings.TrimSpace(string(data))
49 func (v *VespaMgr) getVNFName() string {
50 VNFName := os.Getenv("VESMGR_VNFNAME")
57 func (v *VespaMgr) getNFNamingCode() string {
58 NFNamingCode := os.Getenv("VESMGR_NFNAMINGCODE")
59 if NFNamingCode == "" {
60 return defaultNFNamingCode
65 func (v *VespaMgr) BasicVespaConf() VESAgentConfiguration {
66 var vespaconf = VESAgentConfiguration{
69 Event: EventConfiguration{
70 VNFName: v.getVNFName(),
71 ReportingEntityName: "Vespa",
72 ReportingEntityID: v.readSystemUUID(),
74 NfNamingCode: v.getNFNamingCode(),
75 NfcNamingCodes: []NfcNamingCode{},
76 RetryInterval: time.Second * 5,
79 Measurement: MeasurementConfiguration{
80 // Domain abbreviation has to be set to “Mvfs” for VES 5.3,
81 // and to “Measurement” for later VES interface versions.
82 DomainAbbreviation: "Mvfs",
83 MaxBufferingDuration: time.Hour,
84 Prometheus: PrometheusConfig{
85 Timeout: time.Second * 30,
86 KeepAlive: time.Second * 30,
88 DefaultValues: &MetricRule{
89 VMIDLabel: "'{{.labels.instance}}'",
98 // Parses the metrics data from an array of bytes, which is expected to contain a JSON
99 // array with structs of the following format:
106 // { "name": "...", "objectName": "...", "objectInstamce": "..." },
114 func (v *VespaMgr) ParseMetricsFromDescriptor(descriptor []byte, appMetrics AppMetrics) AppMetrics {
115 var desc []map[string]interface{}
116 json.Unmarshal(descriptor, &desc)
118 for _, appl := range desc {
119 config, configOk := appl["config"]
121 app.Logger.Info("No xApp config found!")
124 measurements, measurementsOk := config.(map[string]interface{})["measurements"]
126 app.Logger.Info("No xApp metrics found!")
130 for _, m := range measurements.([]interface{}) {
131 moId, moIdOk := m.(map[string]interface{})["moId"].(string)
132 measType, measTypeOk := m.(map[string]interface{})["measType"].(string)
133 measId, measIdOk := m.(map[string]interface{})["measId"].(string)
134 measInterval, measIntervalOk := m.(map[string]interface{})["measInterval"].(string)
135 metrics, metricsOk := m.(map[string]interface{})["metrics"]
136 if !metricsOk || !measTypeOk || !measIdOk || !moIdOk || !measIntervalOk {
137 app.Logger.Info("No metrics found for moId=%s measType=%s measId=%s measInterval=%s", moId, measId, measType, measInterval)
140 app.Logger.Info("Parsed measurement: moId=%s type=%s id=%s interval=%s", moId, measType, measId, measInterval)
142 v.ParseMetricsRules(metrics.([]interface{}), appMetrics, moId, measType, measId, measInterval)
148 // Parses the metrics data from an array of interfaces, which are expected to be maps
149 // of the following format:
150 // { "name": xxx, "objectName": yyy, "objectInstance": zzz }
151 // Entries, which do not have all the necessary fields, are ignored.
152 func (v *VespaMgr) ParseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics, moId, measType, measId, measInterval string) AppMetrics {
153 for _, element := range metricsMap {
154 name, nameOk := element.(map[string]interface{})["name"].(string)
156 _, alreadyFound := appMetrics[name]
157 objectName, objectNameOk := element.(map[string]interface{})["objectName"].(string)
158 objectInstance, objectInstanceOk := element.(map[string]interface{})["objectInstance"].(string)
159 counterId, counterIdOk := element.(map[string]interface{})["counterId"].(string)
160 if !alreadyFound && objectNameOk && objectInstanceOk && counterIdOk {
161 appMetrics[name] = AppMetricsStruct{moId, measType, measId, measInterval, objectName, objectInstance, counterId}
162 app.Logger.Info("Parsed counter name=%s %s/%s M%sC%s", name, objectName, objectInstance, measId, counterId)
165 app.Logger.Info("skipped duplicate counter %s", name)
172 func (v *VespaMgr) GetRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) bool {
173 makeRule := func(expr string, value AppMetricsStruct) MetricRule {
175 Target: "AdditionalObjects",
177 ObjectInstance: fmt.Sprintf("%s:%s", value.ObjectInstance, value.CounterId),
178 ObjectName: value.ObjectName,
180 {Name: "ricComponentName", Expr: "'{{.labels.kubernetes_name}}'"},
181 {Name: "moId", Expr: value.MoId},
182 {Name: "measType", Expr: value.MeasType},
183 {Name: "measId", Expr: value.MeasId},
184 {Name: "measInterval", Expr: value.MeasInterval},
188 appMetrics := make(AppMetrics)
189 metrics := v.ParseMetricsFromDescriptor(xAppConfig, appMetrics)
191 if v.pltFileCreated {
192 pltConfig, err := ioutil.ReadFile(app.Config.GetString("controls.pltFile"))
194 app.Logger.Error("Unable to read platform config file: %v", err)
196 metrics = v.ParseMetricsFromDescriptor(pltConfig, metrics)
200 // Adding Platform Counters
201 pltCounterFile := app.Config.GetString("controls.pltCounterFile")
202 bytes, err := ioutil.ReadFile(pltCounterFile)
204 app.Logger.Error("Platform Matrices Configuration File not found")
207 metrics = v.ParseMetricsFromDescriptor(bytes,metrics)
211 vespaconf.Measurement.Prometheus.Rules.Metrics = make([]MetricRule, 0, len(metrics))
212 for key, value := range metrics {
213 vespaconf.Measurement.Prometheus.Rules.Metrics = append(vespaconf.Measurement.Prometheus.Rules.Metrics, makeRule(key, value))
215 if len(vespaconf.Measurement.Prometheus.Rules.Metrics) == 0 {
216 app.Logger.Info("vespa config with empty metrics")
219 return len(vespaconf.Measurement.Prometheus.Rules.Metrics) > 0
222 func (v *VespaMgr) GetCollectorConfiguration(vespaconf *VESAgentConfiguration) {
223 vespaconf.PrimaryCollector.User = app.Config.GetString("controls.collector.primaryUser")
224 vespaconf.PrimaryCollector.Password = app.Config.GetString("controls.collector.primaryPassword")
225 vespaconf.PrimaryCollector.PassPhrase = ""
226 vespaconf.PrimaryCollector.FQDN = app.Config.GetString("controls.collector.primaryAddr")
227 vespaconf.PrimaryCollector.ServerRoot = app.Config.GetString("controls.collector.serverRoot")
228 vespaconf.PrimaryCollector.Topic = ""
229 vespaconf.PrimaryCollector.Port = app.Config.GetInt("controls.collector.primaryPort")
230 vespaconf.PrimaryCollector.Secure = app.Config.GetBool("controls.collector.secure")
233 func (v *VespaMgr) CreateConfig(writer io.Writer, xAppStatus []byte) {
236 vespaconf := v.BasicVespaConf()
237 v.GetRules(&vespaconf, xAppStatus)
238 v.GetCollectorConfiguration(&vespaconf)
240 err := yaml.NewEncoder(writer).Encode(vespaconf)
242 app.Logger.Error("Cannot write vespa conf file: %s", err.Error())
245 app.Logger.Info("Config file written to: %s", app.Config.GetString("controls.vesagent.configFile"))