e6e48d99b7125b658b33d0c9d9eda889a5a15fb7
[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         "io"
27         "io/ioutil"
28         "os"
29         "strconv"
30         "strings"
31         "time"
32
33         "gopkg.in/yaml.v2"
34 )
35
36 const defaultReportingEntityID = "00000000-0000-0000-0000-000000000000"
37 const defaultVNFName = "Vespa"
38 const defaultNFNamingCode = "ricp"
39
40 func readSystemUUID() string {
41         data, err := ioutil.ReadFile("/sys/class/dmi/id/product_uuid")
42         if err != nil {
43                 return defaultReportingEntityID
44         }
45         return strings.TrimSpace(string(data))
46 }
47
48 func getVNFName() string {
49         VNFName := os.Getenv("VESMGR_VNFNAME")
50         if VNFName == "" {
51                 return defaultVNFName
52         }
53         return VNFName
54 }
55
56 func getNFNamingCode() string {
57         NFNamingCode := os.Getenv("VESMGR_NFNAMINGCODE")
58         if NFNamingCode == "" {
59                 return defaultNFNamingCode
60         }
61         return NFNamingCode
62 }
63
64 func basicVespaConf() VESAgentConfiguration {
65         var vespaconf = VESAgentConfiguration{
66                 DataDir: "/tmp/data",
67                 Debug:   false,
68                 Event: EventConfiguration{
69                         VNFName:             getVNFName(),
70                         ReportingEntityName: "Vespa",
71                         ReportingEntityID:   readSystemUUID(),
72                         MaxSize:             2000000,
73                         NfNamingCode:        getNFNamingCode(),
74                         NfcNamingCodes: []NfcNamingCode{},
75                         RetryInterval: time.Second * 5,
76                         MaxMissed:     2,
77                 },
78                 Measurement: MeasurementConfiguration{
79                         // Domain abbreviation has to be set to “Mvfs” for VES 5.3,
80                         // and to “Measurement” for later VES interface versions.
81                         DomainAbbreviation:   "Mvfs",
82                         MaxBufferingDuration: time.Hour,
83                         Prometheus: PrometheusConfig{
84                                 Timeout:   time.Second * 30,
85                                 KeepAlive: time.Second * 30,
86                                 Rules: MetricRules{
87                                         DefaultValues: &MetricRule{
88                                                 VMIDLabel: "'{{.labels.instance}}'",
89                                         },
90                                 },
91                         },
92                 },
93         }
94         return vespaconf
95 }
96
97 // AppMetricsStruct contains xapplication metrics definition
98 type AppMetricsStruct struct {
99         ObjectName     string
100         ObjectInstance string
101         MeasId         string
102         MeasObject     string
103 }
104
105 // AppMetrics contains metrics definitions for all Xapps
106 type AppMetrics map[string]AppMetricsStruct
107
108 // Parses the metrics data from an array of bytes, which is expected to contain a JSON
109 // array with structs of the following format:
110 //
111 // { ...
112 //   "config" : {
113 //         "measurements": [
114 //      {
115 //        "metrics": [
116 //          { "name": "...", "objectName": "...", "objectInstamce": "..." },
117 //           ...
118 //         ]
119 //       }
120 //       ...
121 //     ]
122 //    }
123 // }
124 func parseMetricsFromXAppDescriptor(descriptor []byte, appMetrics AppMetrics) AppMetrics {
125         var desc []map[string]interface{}
126         json.Unmarshal(descriptor, &desc)
127
128         for _, app := range desc {
129                 config, configOk := app["config"]
130                 if !configOk {
131                         logger.Info("No xApp config found!")
132                         continue
133                 }
134                 measurements, measurementsOk := config.(map[string]interface{})["measurements"]
135                 if !measurementsOk {
136                         logger.Info("No xApp metrics found!")
137                         continue
138                 }
139
140                 for _, m := range measurements.([]interface{}) {
141                         measId, measIdOk := m.(map[string]interface{})["measId"].(string)
142                         measObject, objectOk := m.(map[string]interface{})["object"].(string)
143                         metrics, metricsOk := m.(map[string]interface{})["metrics"]
144                         if !metricsOk || !measIdOk || !objectOk {
145                                 logger.Info("No metrics found for measId=%s Object=%s", measId, measObject)
146                                 continue
147                         }
148                         parseMetricsRules(metrics.([]interface{}), appMetrics, measId, measObject)
149                 }
150         }
151         return appMetrics
152 }
153
154 // Parses the metrics data from an array of interfaces, which are expected to be maps
155 // of the following format:
156 //    { "name": xxx, "objectName": yyy, "objectInstance": zzz }
157 // Entries, which do not have all the necessary fields, are ignored.
158 func parseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics, measId, measObject string) AppMetrics {
159         for _, element := range metricsMap {
160                 name, nameOk := element.(map[string]interface{})["name"].(string)
161                 if nameOk {
162                         _, alreadyFound := appMetrics[name]
163                         objectName, objectNameOk := element.(map[string]interface{})["objectName"].(string)
164                         objectInstance, objectInstanceOk := element.(map[string]interface{})["objectInstance"].(string)
165                         if !alreadyFound && objectNameOk && objectInstanceOk {
166                                 appMetrics[name] = AppMetricsStruct{objectName, objectInstance, measId, measObject}
167                                 logger.Info("parsed counter %s %s %s", name, objectName, objectInstance)
168                         }
169                         if alreadyFound {
170                                 logger.Info("skipped duplicate counter %s", name)
171                         }
172                 }
173         }
174         return appMetrics
175 }
176
177 func getRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) {
178         makeRule := func(expr string, value AppMetricsStruct) MetricRule {
179                 return MetricRule{
180                         Target:         "AdditionalObjects",
181                         Expr:           expr,
182                         ObjectInstance: value.ObjectInstance,
183                         ObjectName:     value.ObjectName,
184                         ObjectKeys: []Label{
185                                 Label{
186                                         Name: "ricComponentName",
187                                         Expr: "'{{.labels.kubernetes_name}}'",
188                                 },
189                                 Label{
190                                         Name: "measObject",
191                                         Expr: value.MeasObject,
192                                 },
193                                 Label{
194                                         Name: "measId",
195                                         Expr: value.MeasId,
196                                 },
197                         },
198                 }
199         }
200         appMetrics := make(AppMetrics)
201         metrics := parseMetricsFromXAppDescriptor(xAppConfig, appMetrics)
202
203         vespaconf.Measurement.Prometheus.Rules.Metrics = make([]MetricRule, 0, len(metrics))
204         for key, value := range metrics {
205                 vespaconf.Measurement.Prometheus.Rules.Metrics = append(vespaconf.Measurement.Prometheus.Rules.Metrics, makeRule(key, value))
206         }
207         if len(vespaconf.Measurement.Prometheus.Rules.Metrics) == 0 {
208                 logger.Info("vespa config with empty metrics")
209         }
210 }
211
212 func getCollectorConfiguration(vespaconf *VESAgentConfiguration) {
213         vespaconf.PrimaryCollector.User = os.Getenv("VESMGR_PRICOLLECTOR_USER")
214         vespaconf.PrimaryCollector.Password = os.Getenv("VESMGR_PRICOLLECTOR_PASSWORD")
215         vespaconf.PrimaryCollector.PassPhrase = os.Getenv("VESMGR_PRICOLLECTOR_PASSPHRASE")
216         vespaconf.PrimaryCollector.FQDN = os.Getenv("VESMGR_PRICOLLECTOR_ADDR")
217         vespaconf.PrimaryCollector.ServerRoot = os.Getenv("VESMGR_PRICOLLECTOR_SERVERROOT")
218         vespaconf.PrimaryCollector.Topic = os.Getenv("VESMGR_PRICOLLECTOR_TOPIC")
219         portStr := os.Getenv("VESMGR_PRICOLLECTOR_PORT")
220         if portStr == "" {
221                 vespaconf.PrimaryCollector.Port = 8443
222         } else {
223                 port, _ := strconv.Atoi(portStr)
224                 vespaconf.PrimaryCollector.Port = port
225         }
226         secureStr := os.Getenv("VESMGR_PRICOLLECTOR_SECURE")
227         if secureStr == "true" {
228                 vespaconf.PrimaryCollector.Secure = true
229         } else {
230                 vespaconf.PrimaryCollector.Secure = false
231         }
232 }
233
234 func createVespaConfig(writer io.Writer, xAppStatus []byte) {
235         vespaconf := basicVespaConf()
236         getRules(&vespaconf, xAppStatus)
237         getCollectorConfiguration(&vespaconf)
238         err := yaml.NewEncoder(writer).Encode(vespaconf)
239         if err != nil {
240                 logger.Error("Cannot write vespa conf file: %s", err.Error())
241                 return
242         }
243 }