blob: 0df132b8a17d677549d433abd0efb4fc74ffa992 [file] [log] [blame]
Katri Turunen4b74f012019-08-15 10:49:36 +03001/*
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
18package main
19
20import (
Katri Turunen412df962019-09-16 08:48:18 +030021 "encoding/json"
Katri Turunen4b74f012019-08-15 10:49:36 +030022 "io"
Katri Turunen3e79fbe2019-09-26 12:46:07 +030023 "io/ioutil"
Katri Turunen66b78132019-09-02 10:28:52 +030024 "os"
25 "strconv"
Katri Turunen3e79fbe2019-09-26 12:46:07 +030026 "strings"
Katri Turunen412df962019-09-16 08:48:18 +030027 "time"
Roni Riskafc77ebb2019-09-26 08:20:44 +030028
29 "gopkg.in/yaml.v2"
Katri Turunen4b74f012019-08-15 10:49:36 +030030)
31
Katri Turunen3e79fbe2019-09-26 12:46:07 +030032const defaultReportingEntityID = "00000000-0000-0000-0000-000000000000"
33const defaultVNFName = "Vespa"
Roni Riska364295f2019-09-30 09:39:12 +030034const defaultNFNamingCode = "ricp"
Katri Turunen3e79fbe2019-09-26 12:46:07 +030035
36func readSystemUUID() string {
37 data, err := ioutil.ReadFile("/sys/class/dmi/id/product_uuid")
38 if err != nil {
39 return defaultReportingEntityID
40 }
41 return strings.TrimSpace(string(data))
42}
43
44func getVNFName() string {
45 VNFName := os.Getenv("VESMGR_VNFNAME")
46 if VNFName == "" {
47 return defaultVNFName
48 }
49 return VNFName
50}
51
Roni Riska364295f2019-09-30 09:39:12 +030052func getNFNamingCode() string {
53 NFNamingCode := os.Getenv("VESMGR_NFNAMINGCODE")
54 if NFNamingCode == "" {
55 return defaultNFNamingCode
56 }
57 return NFNamingCode
58}
59
Katri Turunen4b74f012019-08-15 10:49:36 +030060func basicVespaConf() VESAgentConfiguration {
Katri Turunen412df962019-09-16 08:48:18 +030061 var vespaconf = VESAgentConfiguration{
Katri Turunen4b74f012019-08-15 10:49:36 +030062 DataDir: "/tmp/data",
63 Debug: false,
Katri Turunen412df962019-09-16 08:48:18 +030064 Event: EventConfiguration{
Katri Turunen3e79fbe2019-09-26 12:46:07 +030065 VNFName: getVNFName(),
66 ReportingEntityName: "Vespa",
67 ReportingEntityID: readSystemUUID(),
68 MaxSize: 2000000,
Roni Riska364295f2019-09-30 09:39:12 +030069 NfNamingCode: getNFNamingCode(),
70 NfcNamingCodes: []NfcNamingCode{},
Katri Turunen4b74f012019-08-15 10:49:36 +030071 RetryInterval: time.Second * 5,
Katri Turunen412df962019-09-16 08:48:18 +030072 MaxMissed: 2,
Katri Turunen4b74f012019-08-15 10:49:36 +030073 },
Katri Turunen412df962019-09-16 08:48:18 +030074 Measurement: MeasurementConfiguration{
Katri Turunen3e79fbe2019-09-26 12:46:07 +030075 // Domain abbreviation has to be set to “Mvfs” for VES 5.3,
76 // and to “Measurement” for later VES interface versions.
Katri Turunen412df962019-09-16 08:48:18 +030077 DomainAbbreviation: "Mvfs",
Katri Turunen4b74f012019-08-15 10:49:36 +030078 MaxBufferingDuration: time.Hour,
Katri Turunen412df962019-09-16 08:48:18 +030079 Prometheus: PrometheusConfig{
80 Timeout: time.Second * 30,
Katri Turunen4b74f012019-08-15 10:49:36 +030081 KeepAlive: time.Second * 30,
Katri Turunen412df962019-09-16 08:48:18 +030082 Rules: MetricRules{
83 DefaultValues: &MetricRule{
Katri Turunen4b74f012019-08-15 10:49:36 +030084 VMIDLabel: "'{{.labels.instance}}'",
85 },
86 },
87 },
88 },
89 }
90 return vespaconf
91}
92
Roni Riskafc77ebb2019-09-26 08:20:44 +030093// AppMetricsStruct contains xapplication metrics definition
Katri Turunen412df962019-09-16 08:48:18 +030094type AppMetricsStruct struct {
95 ObjectName string
96 ObjectInstance string
Katri Turunen412df962019-09-16 08:48:18 +030097}
98
Roni Riskafc77ebb2019-09-26 08:20:44 +030099// AppMetrics contains metrics definitions for all Xapps
Katri Turunen412df962019-09-16 08:48:18 +0300100type 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// }
113func parseMetricsFromXAppDescriptor(descriptor []byte, appMetrics AppMetrics) AppMetrics {
114 var desc []map[string]interface{}
115 json.Unmarshal(descriptor, &desc)
116
117 for _, app := range desc {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300118 config, configOk := app["config"]
119 if configOk {
120 metrics, metricsOk := config.(map[string]interface{})["metrics"]
121 if metricsOk {
Katri Turunen412df962019-09-16 08:48:18 +0300122 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.
133func parseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics) AppMetrics {
134 for _, element := range metricsMap {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300135 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 {
Katri Turunen412df962019-09-16 08:48:18 +0300141 appMetrics[name] = AppMetricsStruct{objectName, objectInstance}
142 logger.Info("parsed counter %s %s %s", name, objectName, objectInstance)
143 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300144 if alreadyFound {
Katri Turunen412df962019-09-16 08:48:18 +0300145 logger.Info("skipped duplicate counter %s", name)
146 }
147 }
148 }
149 return appMetrics
150}
151
152func getRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) {
153 appMetrics := make(AppMetrics)
154 parseMetricsFromXAppDescriptor(xAppConfig, appMetrics)
155
Roni Riskafc77ebb2019-09-26 08:20:44 +0300156 makeRule := func(expr string, objName string, objInstance string) MetricRule {
Katri Turunen412df962019-09-16 08:48:18 +0300157 return MetricRule{
158 Target: "AdditionalObjects",
159 Expr: expr,
Roni Riskafc77ebb2019-09-26 08:20:44 +0300160 ObjectInstance: objInstance,
161 ObjectName: objName,
Katri Turunen412df962019-09-16 08:48:18 +0300162 ObjectKeys: []Label{
163 Label{
Katri Turunen4b74f012019-08-15 10:49:36 +0300164 Name: "ricComponentName",
Katri Turunen412df962019-09-16 08:48:18 +0300165 Expr: "'{{.labels.kubernetes_name}}'",
Katri Turunen4b74f012019-08-15 10:49:36 +0300166 },
167 },
168 }
169 }
Katri Turunen412df962019-09-16 08:48:18 +0300170 var metricsMap map[string][]interface{}
171 json.Unmarshal(xAppConfig, &metricsMap)
172 metrics := parseMetricsRules(metricsMap["metrics"], appMetrics)
Katri Turunen4b74f012019-08-15 10:49:36 +0300173
Katri Turunen412df962019-09-16 08:48:18 +0300174 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 }
Katri Turunen4b74f012019-08-15 10:49:36 +0300181}
182
Katri Turunen66b78132019-09-02 10:28:52 +0300183func 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")
Roni Riskafc77ebb2019-09-26 08:20:44 +0300190 portStr := os.Getenv("VESMGR_PRICOLLECTOR_PORT")
191 if portStr == "" {
Katri Turunen66b78132019-09-02 10:28:52 +0300192 vespaconf.PrimaryCollector.Port = 8443
193 } else {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300194 port, _ := strconv.Atoi(portStr)
Katri Turunen66b78132019-09-02 10:28:52 +0300195 vespaconf.PrimaryCollector.Port = port
196 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300197 secureStr := os.Getenv("VESMGR_PRICOLLECTOR_SECURE")
198 if secureStr == "true" {
Katri Turunen66b78132019-09-02 10:28:52 +0300199 vespaconf.PrimaryCollector.Secure = true
200 } else {
201 vespaconf.PrimaryCollector.Secure = false
202 }
203}
204
Katri Turunen412df962019-09-16 08:48:18 +0300205func createVespaConfig(writer io.Writer, xAppStatus []byte) {
Katri Turunen4b74f012019-08-15 10:49:36 +0300206 vespaconf := basicVespaConf()
Katri Turunen412df962019-09-16 08:48:18 +0300207 getRules(&vespaconf, xAppStatus)
Katri Turunen66b78132019-09-02 10:28:52 +0300208 getCollectorConfiguration(&vespaconf)
Katri Turunen4b74f012019-08-15 10:49:36 +0300209 err := yaml.NewEncoder(writer).Encode(vespaconf)
210 if err != nil {
211 logger.Error("Cannot write vespa conf file: %s", err.Error())
212 return
213 }
Katri Turunen66b78132019-09-02 10:28:52 +0300214}