Additions to measurements
[ric-plt/vespamgr.git] / cmd / vesmgr / 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         "strconv"
31         "strings"
32         "time"
33
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 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 getVNFName() string {
50         VNFName := os.Getenv("VESMGR_VNFNAME")
51         if VNFName == "" {
52                 return defaultVNFName
53         }
54         return VNFName
55 }
56
57 func getNFNamingCode() string {
58         NFNamingCode := os.Getenv("VESMGR_NFNAMINGCODE")
59         if NFNamingCode == "" {
60                 return defaultNFNamingCode
61         }
62         return NFNamingCode
63 }
64
65 func basicVespaConf() VESAgentConfiguration {
66         var vespaconf = VESAgentConfiguration{
67                 DataDir: "/tmp/data",
68                 Debug:   false,
69                 Event: EventConfiguration{
70                         VNFName:             getVNFName(),
71                         ReportingEntityName: "Vespa",
72                         ReportingEntityID:   readSystemUUID(),
73                         MaxSize:             2000000,
74                         NfNamingCode:        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 // AppMetricsStruct contains xapplication metrics definition
99 type AppMetricsStruct struct {
100         MoId           string
101         MeasType       string
102         MeasId         string
103         MeasInterval   string
104         ObjectName     string
105         ObjectInstance string
106         CounterId      string
107 }
108
109 // AppMetrics contains metrics definitions for all Xapps
110 type AppMetrics map[string]AppMetricsStruct
111
112 // Parses the metrics data from an array of bytes, which is expected to contain a JSON
113 // array with structs of the following format:
114 //
115 // { ...
116 //   "config" : {
117 //         "measurements": [
118 //      {
119 //        "metrics": [
120 //          { "name": "...", "objectName": "...", "objectInstamce": "..." },
121 //           ...
122 //         ]
123 //       }
124 //       ...
125 //     ]
126 //    }
127 // }
128 func parseMetricsFromXAppDescriptor(descriptor []byte, appMetrics AppMetrics) AppMetrics {
129         var desc []map[string]interface{}
130         json.Unmarshal(descriptor, &desc)
131
132         for _, app := range desc {
133                 config, configOk := app["config"]
134                 if !configOk {
135                         logger.Info("No xApp config found!")
136                         continue
137                 }
138                 measurements, measurementsOk := config.(map[string]interface{})["measurements"]
139                 if !measurementsOk {
140                         logger.Info("No xApp metrics found!")
141                         continue
142                 }
143
144                 for _, m := range measurements.([]interface{}) {
145                         moId, moIdOk := m.(map[string]interface{})["moId"].(string)
146                         measType, measTypeOk := m.(map[string]interface{})["measType"].(string)
147                         measId, measIdOk := m.(map[string]interface{})["measId"].(string)
148                         measInterval, measIntervalOk := m.(map[string]interface{})["measInterval"].(string)
149                         metrics, metricsOk := m.(map[string]interface{})["metrics"]
150                         if !metricsOk || !measTypeOk || !measIdOk || !moIdOk || !measIntervalOk {
151                                 logger.Info("No metrics found for moId=%s measType=%s measId=%s measInterval=%s", moId, measId, measType, measInterval)
152                                 continue
153                         }
154                         logger.Info("Parsed measurement: moId=%s type=%s id=%s interval=%s", moId, measType, measId, measInterval)
155
156                         parseMetricsRules(metrics.([]interface{}), appMetrics, moId, measType, measId, measInterval)
157                 }
158         }
159         return appMetrics
160 }
161
162 // Parses the metrics data from an array of interfaces, which are expected to be maps
163 // of the following format:
164 //    { "name": xxx, "objectName": yyy, "objectInstance": zzz }
165 // Entries, which do not have all the necessary fields, are ignored.
166 func parseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics, moId, measType, measId, measInterval string) AppMetrics {
167         for _, element := range metricsMap {
168                 name, nameOk := element.(map[string]interface{})["name"].(string)
169                 if nameOk {
170                         _, alreadyFound := appMetrics[name]
171                         objectName, objectNameOk := element.(map[string]interface{})["objectName"].(string)
172                         objectInstance, objectInstanceOk := element.(map[string]interface{})["objectInstance"].(string)
173                         counterId, counterIdOk := element.(map[string]interface{})["counterId"].(string)
174                         if !alreadyFound && objectNameOk && objectInstanceOk && counterIdOk {
175                                 appMetrics[name] = AppMetricsStruct{moId, measType, measId, measInterval, objectName, objectInstance, counterId}
176                                 logger.Info("Parsed counter name=%s %s/%s  M%sC%s", name, objectName, objectInstance, measId, counterId)
177                         }
178                         if alreadyFound {
179                                 logger.Info("skipped duplicate counter %s", name)
180                         }
181                 }
182         }
183         return appMetrics
184 }
185
186 func getRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) bool {
187         makeRule := func(expr string, value AppMetricsStruct) MetricRule {
188                 return MetricRule{
189                         Target:         "AdditionalObjects",
190                         Expr:           expr,
191                         ObjectInstance: fmt.Sprintf("%s:%s", value.ObjectInstance, value.CounterId),
192                         ObjectName:     value.ObjectName,
193                         ObjectKeys: []Label{
194                                 {Name: "ricComponentName", Expr: "'{{.labels.kubernetes_name}}'"},
195                                 {Name: "moId", Expr: value.MoId},
196                                 {Name: "measType", Expr: value.MeasType},
197                                 {Name: "measId", Expr: value.MeasId},
198                                 {Name: "measInterval", Expr: value.MeasInterval},
199                         },
200                 }
201         }
202         appMetrics := make(AppMetrics)
203         metrics := parseMetricsFromXAppDescriptor(xAppConfig, appMetrics)
204
205         vespaconf.Measurement.Prometheus.Rules.Metrics = make([]MetricRule, 0, len(metrics))
206         for key, value := range metrics {
207                 vespaconf.Measurement.Prometheus.Rules.Metrics = append(vespaconf.Measurement.Prometheus.Rules.Metrics, makeRule(key, value))
208         }
209         if len(vespaconf.Measurement.Prometheus.Rules.Metrics) == 0 {
210                 logger.Info("vespa config with empty metrics")
211         }
212
213         return len(vespaconf.Measurement.Prometheus.Rules.Metrics) > 0
214 }
215
216 func getCollectorConfiguration(vespaconf *VESAgentConfiguration) {
217         vespaconf.PrimaryCollector.User = os.Getenv("VESMGR_PRICOLLECTOR_USER")
218         vespaconf.PrimaryCollector.Password = os.Getenv("VESMGR_PRICOLLECTOR_PASSWORD")
219         vespaconf.PrimaryCollector.PassPhrase = os.Getenv("VESMGR_PRICOLLECTOR_PASSPHRASE")
220         vespaconf.PrimaryCollector.FQDN = os.Getenv("VESMGR_PRICOLLECTOR_ADDR")
221         vespaconf.PrimaryCollector.ServerRoot = os.Getenv("VESMGR_PRICOLLECTOR_SERVERROOT")
222         vespaconf.PrimaryCollector.Topic = os.Getenv("VESMGR_PRICOLLECTOR_TOPIC")
223         portStr := os.Getenv("VESMGR_PRICOLLECTOR_PORT")
224
225         if portStr == "" {
226                 vespaconf.PrimaryCollector.Port = 8443
227         } else {
228                 port, _ := strconv.Atoi(portStr)
229                 vespaconf.PrimaryCollector.Port = port
230         }
231
232         secureStr := os.Getenv("VESMGR_PRICOLLECTOR_SECURE")
233         if secureStr == "true" {
234                 vespaconf.PrimaryCollector.Secure = true
235         } else {
236                 vespaconf.PrimaryCollector.Secure = false
237         }
238 }
239
240 func createVespaConfig(writer io.Writer, xAppStatus []byte) {
241         vespaconf := basicVespaConf()
242
243         getRules(&vespaconf, xAppStatus)
244
245         getCollectorConfiguration(&vespaconf)
246
247         err := yaml.NewEncoder(writer).Encode(vespaconf)
248         if err != nil {
249                 logger.Error("Cannot write vespa conf file: %s", err.Error())
250                 return
251         }
252 }