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