RIC-710: Adding E2T counters to Vespa-Manager
[ric-plt/vespamgr.git] / cmd / vespamgr / config.go
1 /*
2  *  Copyright (c) 2019 AT&T Intellectual Property.
3  *  Copyright (c) 2018-2019 Nokia.
4  *
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
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  *  This source code is part of the near-RT RIC (RAN Intelligent Controller)
18  *  platform project (RICP).
19  *
20  */
21
22 package main
23
24 import (
25         "encoding/json"
26         "fmt"
27         "io"
28         "io/ioutil"
29         "os"
30         "strings"
31         "time"
32
33         app "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
34         "gopkg.in/yaml.v2"
35 )
36
37 const defaultReportingEntityID = "00000000-0000-0000-0000-000000000000"
38 const defaultVNFName = "Vespa"
39 const defaultNFNamingCode = "ricp"
40
41 func (v *VespaMgr) readSystemUUID() string {
42         data, err := ioutil.ReadFile("/sys/class/dmi/id/product_uuid")
43         if err != nil {
44                 return defaultReportingEntityID
45         }
46         return strings.TrimSpace(string(data))
47 }
48
49 func (v *VespaMgr) getVNFName() string {
50         VNFName := os.Getenv("VESMGR_VNFNAME")
51         if VNFName == "" {
52                 return defaultVNFName
53         }
54         return VNFName
55 }
56
57 func (v *VespaMgr) getNFNamingCode() string {
58         NFNamingCode := os.Getenv("VESMGR_NFNAMINGCODE")
59         if NFNamingCode == "" {
60                 return defaultNFNamingCode
61         }
62         return NFNamingCode
63 }
64
65 func (v *VespaMgr) BasicVespaConf() VESAgentConfiguration {
66         var vespaconf = VESAgentConfiguration{
67                 DataDir: "/tmp/data",
68                 Debug:   false,
69                 Event: EventConfiguration{
70                         VNFName:             v.getVNFName(),
71                         ReportingEntityName: "Vespa",
72                         ReportingEntityID:   v.readSystemUUID(),
73                         MaxSize:             2000000,
74                         NfNamingCode:        v.getNFNamingCode(),
75                         NfcNamingCodes:      []NfcNamingCode{},
76                         RetryInterval:       time.Second * 5,
77                         MaxMissed:           2,
78                 },
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,
87                                 Rules: MetricRules{
88                                         DefaultValues: &MetricRule{
89                                                 VMIDLabel: "'{{.labels.instance}}'",
90                                         },
91                                 },
92                         },
93                 },
94         }
95         return vespaconf
96 }
97
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:
100 //
101 // { ...
102 //   "config" : {
103 //         "measurements": [
104 //      {
105 //        "metrics": [
106 //          { "name": "...", "objectName": "...", "objectInstamce": "..." },
107 //           ...
108 //         ]
109 //       }
110 //       ...
111 //     ]
112 //    }
113 // }
114 func (v *VespaMgr) ParseMetricsFromDescriptor(descriptor []byte, appMetrics AppMetrics) AppMetrics {
115         var desc []map[string]interface{}
116         json.Unmarshal(descriptor, &desc)
117
118         for _, appl := range desc {
119                 config, configOk := appl["config"]
120                 if !configOk {
121                         app.Logger.Info("No xApp config found!")
122                         continue
123                 }
124                 measurements, measurementsOk := config.(map[string]interface{})["measurements"]
125                 if !measurementsOk {
126                         app.Logger.Info("No xApp metrics found!")
127                         continue
128                 }
129
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)
138                                 continue
139                         }
140                         app.Logger.Info("Parsed measurement: moId=%s type=%s id=%s interval=%s", moId, measType, measId, measInterval)
141
142                         v.ParseMetricsRules(metrics.([]interface{}), appMetrics, moId, measType, measId, measInterval)
143                 }
144         }
145         return appMetrics
146 }
147
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)
155                 if nameOk {
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)
163                         }
164                         if alreadyFound {
165                                 app.Logger.Info("skipped duplicate counter %s", name)
166                         }
167                 }
168         }
169         return appMetrics
170 }
171
172 func (v *VespaMgr) GetRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) bool {
173         makeRule := func(expr string, value AppMetricsStruct) MetricRule {
174                 return MetricRule{
175                         Target:         "AdditionalObjects",
176                         Expr:           expr,
177                         ObjectInstance: fmt.Sprintf("%s:%s", value.ObjectInstance, value.CounterId),
178                         ObjectName:     value.ObjectName,
179                         ObjectKeys: []Label{
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},
185                         },
186                 }
187         }
188         appMetrics := make(AppMetrics)
189         metrics := v.ParseMetricsFromDescriptor(xAppConfig, appMetrics)
190
191         if v.pltFileCreated {
192                 pltConfig, err := ioutil.ReadFile(app.Config.GetString("controls.pltFile"))
193                 if err != nil {
194                         app.Logger.Error("Unable to read platform config file: %v", err)
195                 } else {
196                         metrics = v.ParseMetricsFromDescriptor(pltConfig, metrics)
197                 }
198         }
199     
200         // Adding Platform Counters
201         pltCounterFile :=  app.Config.GetString("controls.pltCounterFile")
202         bytes, err := ioutil.ReadFile(pltCounterFile)
203         if err != nil{
204                 app.Logger.Error("Platform Matrices Configuration File not found")
205         } else {
206
207                 metrics = v.ParseMetricsFromDescriptor(bytes,metrics)
208         }
209         
210
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))
214         }
215         if len(vespaconf.Measurement.Prometheus.Rules.Metrics) == 0 {
216                 app.Logger.Info("vespa config with empty metrics")
217         }
218
219         return len(vespaconf.Measurement.Prometheus.Rules.Metrics) > 0
220 }
221
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")
231 }
232
233 func (v *VespaMgr) CreateConfig(writer io.Writer, xAppStatus []byte) {
234
235         
236         vespaconf := v.BasicVespaConf()
237         v.GetRules(&vespaconf, xAppStatus)
238         v.GetCollectorConfiguration(&vespaconf)
239     
240         err := yaml.NewEncoder(writer).Encode(vespaconf)
241         if err != nil {
242                 app.Logger.Error("Cannot write vespa conf file: %s", err.Error())
243                 return
244         }
245         app.Logger.Info("Config file written to: %s", app.Config.GetString("controls.vesagent.configFile"))
246 }
247