blob: e6e48d99b7125b658b33d0c9d9eda889a5a15fb7 [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.
Roni Riska6ffba082019-11-27 10:59:54 +020016 *
17 * This source code is part of the near-RT RIC (RAN Intelligent Controller)
18 * platform project (RICP).
19 *
Katri Turunen4b74f012019-08-15 10:49:36 +030020 */
21
22package main
23
24import (
Katri Turunen412df962019-09-16 08:48:18 +030025 "encoding/json"
Katri Turunen4b74f012019-08-15 10:49:36 +030026 "io"
Katri Turunen3e79fbe2019-09-26 12:46:07 +030027 "io/ioutil"
Katri Turunen66b78132019-09-02 10:28:52 +030028 "os"
29 "strconv"
Katri Turunen3e79fbe2019-09-26 12:46:07 +030030 "strings"
Katri Turunen412df962019-09-16 08:48:18 +030031 "time"
Roni Riskafc77ebb2019-09-26 08:20:44 +030032
33 "gopkg.in/yaml.v2"
Katri Turunen4b74f012019-08-15 10:49:36 +030034)
35
Katri Turunen3e79fbe2019-09-26 12:46:07 +030036const defaultReportingEntityID = "00000000-0000-0000-0000-000000000000"
37const defaultVNFName = "Vespa"
Roni Riska364295f2019-09-30 09:39:12 +030038const defaultNFNamingCode = "ricp"
Katri Turunen3e79fbe2019-09-26 12:46:07 +030039
40func 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
48func getVNFName() string {
49 VNFName := os.Getenv("VESMGR_VNFNAME")
50 if VNFName == "" {
51 return defaultVNFName
52 }
53 return VNFName
54}
55
Roni Riska364295f2019-09-30 09:39:12 +030056func getNFNamingCode() string {
57 NFNamingCode := os.Getenv("VESMGR_NFNAMINGCODE")
58 if NFNamingCode == "" {
59 return defaultNFNamingCode
60 }
61 return NFNamingCode
62}
63
Katri Turunen4b74f012019-08-15 10:49:36 +030064func basicVespaConf() VESAgentConfiguration {
Katri Turunen412df962019-09-16 08:48:18 +030065 var vespaconf = VESAgentConfiguration{
Katri Turunen4b74f012019-08-15 10:49:36 +030066 DataDir: "/tmp/data",
67 Debug: false,
Katri Turunen412df962019-09-16 08:48:18 +030068 Event: EventConfiguration{
Katri Turunen3e79fbe2019-09-26 12:46:07 +030069 VNFName: getVNFName(),
70 ReportingEntityName: "Vespa",
71 ReportingEntityID: readSystemUUID(),
72 MaxSize: 2000000,
Roni Riska364295f2019-09-30 09:39:12 +030073 NfNamingCode: getNFNamingCode(),
74 NfcNamingCodes: []NfcNamingCode{},
Katri Turunen4b74f012019-08-15 10:49:36 +030075 RetryInterval: time.Second * 5,
Katri Turunen412df962019-09-16 08:48:18 +030076 MaxMissed: 2,
Katri Turunen4b74f012019-08-15 10:49:36 +030077 },
Katri Turunen412df962019-09-16 08:48:18 +030078 Measurement: MeasurementConfiguration{
Katri Turunen3e79fbe2019-09-26 12:46:07 +030079 // Domain abbreviation has to be set to “Mvfs” for VES 5.3,
80 // and to “Measurement” for later VES interface versions.
Katri Turunen412df962019-09-16 08:48:18 +030081 DomainAbbreviation: "Mvfs",
Katri Turunen4b74f012019-08-15 10:49:36 +030082 MaxBufferingDuration: time.Hour,
Katri Turunen412df962019-09-16 08:48:18 +030083 Prometheus: PrometheusConfig{
84 Timeout: time.Second * 30,
Katri Turunen4b74f012019-08-15 10:49:36 +030085 KeepAlive: time.Second * 30,
Katri Turunen412df962019-09-16 08:48:18 +030086 Rules: MetricRules{
87 DefaultValues: &MetricRule{
Katri Turunen4b74f012019-08-15 10:49:36 +030088 VMIDLabel: "'{{.labels.instance}}'",
89 },
90 },
91 },
92 },
93 }
94 return vespaconf
95}
96
Roni Riskafc77ebb2019-09-26 08:20:44 +030097// AppMetricsStruct contains xapplication metrics definition
Katri Turunen412df962019-09-16 08:48:18 +030098type AppMetricsStruct struct {
99 ObjectName string
100 ObjectInstance string
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300101 MeasId string
102 MeasObject string
Katri Turunen412df962019-09-16 08:48:18 +0300103}
104
Roni Riskafc77ebb2019-09-26 08:20:44 +0300105// AppMetrics contains metrics definitions for all Xapps
Katri Turunen412df962019-09-16 08:48:18 +0300106type 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" : {
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300113// "measurements": [
114// {
115// "metrics": [
116// { "name": "...", "objectName": "...", "objectInstamce": "..." },
117// ...
118// ]
119// }
Katri Turunen412df962019-09-16 08:48:18 +0300120// ...
121// ]
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300122// }
Katri Turunen412df962019-09-16 08:48:18 +0300123// }
124func parseMetricsFromXAppDescriptor(descriptor []byte, appMetrics AppMetrics) AppMetrics {
125 var desc []map[string]interface{}
126 json.Unmarshal(descriptor, &desc)
127
128 for _, app := range desc {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300129 config, configOk := app["config"]
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300130 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
Katri Turunen412df962019-09-16 08:48:18 +0300147 }
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300148 parseMetricsRules(metrics.([]interface{}), appMetrics, measId, measObject)
Katri Turunen412df962019-09-16 08:48:18 +0300149 }
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.
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300158func parseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics, measId, measObject string) AppMetrics {
Katri Turunen412df962019-09-16 08:48:18 +0300159 for _, element := range metricsMap {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300160 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 {
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300166 appMetrics[name] = AppMetricsStruct{objectName, objectInstance, measId, measObject}
Katri Turunen412df962019-09-16 08:48:18 +0300167 logger.Info("parsed counter %s %s %s", name, objectName, objectInstance)
168 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300169 if alreadyFound {
Katri Turunen412df962019-09-16 08:48:18 +0300170 logger.Info("skipped duplicate counter %s", name)
171 }
172 }
173 }
174 return appMetrics
175}
176
177func getRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) {
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300178 makeRule := func(expr string, value AppMetricsStruct) MetricRule {
Katri Turunen412df962019-09-16 08:48:18 +0300179 return MetricRule{
180 Target: "AdditionalObjects",
181 Expr: expr,
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300182 ObjectInstance: value.ObjectInstance,
183 ObjectName: value.ObjectName,
Katri Turunen412df962019-09-16 08:48:18 +0300184 ObjectKeys: []Label{
185 Label{
Katri Turunen4b74f012019-08-15 10:49:36 +0300186 Name: "ricComponentName",
Katri Turunen412df962019-09-16 08:48:18 +0300187 Expr: "'{{.labels.kubernetes_name}}'",
Katri Turunen4b74f012019-08-15 10:49:36 +0300188 },
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300189 Label{
190 Name: "measObject",
191 Expr: value.MeasObject,
192 },
193 Label{
194 Name: "measId",
195 Expr: value.MeasId,
196 },
Katri Turunen4b74f012019-08-15 10:49:36 +0300197 },
198 }
199 }
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300200 appMetrics := make(AppMetrics)
201 metrics := parseMetricsFromXAppDescriptor(xAppConfig, appMetrics)
Katri Turunen4b74f012019-08-15 10:49:36 +0300202
Katri Turunen412df962019-09-16 08:48:18 +0300203 vespaconf.Measurement.Prometheus.Rules.Metrics = make([]MetricRule, 0, len(metrics))
204 for key, value := range metrics {
Mohamed Abukarcbfed3c2020-10-08 09:37:21 +0300205 vespaconf.Measurement.Prometheus.Rules.Metrics = append(vespaconf.Measurement.Prometheus.Rules.Metrics, makeRule(key, value))
Katri Turunen412df962019-09-16 08:48:18 +0300206 }
207 if len(vespaconf.Measurement.Prometheus.Rules.Metrics) == 0 {
208 logger.Info("vespa config with empty metrics")
209 }
Katri Turunen4b74f012019-08-15 10:49:36 +0300210}
211
Katri Turunen66b78132019-09-02 10:28:52 +0300212func 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")
Roni Riskafc77ebb2019-09-26 08:20:44 +0300219 portStr := os.Getenv("VESMGR_PRICOLLECTOR_PORT")
220 if portStr == "" {
Katri Turunen66b78132019-09-02 10:28:52 +0300221 vespaconf.PrimaryCollector.Port = 8443
222 } else {
Roni Riskafc77ebb2019-09-26 08:20:44 +0300223 port, _ := strconv.Atoi(portStr)
Katri Turunen66b78132019-09-02 10:28:52 +0300224 vespaconf.PrimaryCollector.Port = port
225 }
Roni Riskafc77ebb2019-09-26 08:20:44 +0300226 secureStr := os.Getenv("VESMGR_PRICOLLECTOR_SECURE")
227 if secureStr == "true" {
Katri Turunen66b78132019-09-02 10:28:52 +0300228 vespaconf.PrimaryCollector.Secure = true
229 } else {
230 vespaconf.PrimaryCollector.Secure = false
231 }
232}
233
Katri Turunen412df962019-09-16 08:48:18 +0300234func createVespaConfig(writer io.Writer, xAppStatus []byte) {
Katri Turunen4b74f012019-08-15 10:49:36 +0300235 vespaconf := basicVespaConf()
Katri Turunen412df962019-09-16 08:48:18 +0300236 getRules(&vespaconf, xAppStatus)
Katri Turunen66b78132019-09-02 10:28:52 +0300237 getCollectorConfiguration(&vespaconf)
Katri Turunen4b74f012019-08-15 10:49:36 +0300238 err := yaml.NewEncoder(writer).Encode(vespaconf)
239 if err != nil {
240 logger.Error("Cannot write vespa conf file: %s", err.Error())
241 return
242 }
Katri Turunen66b78132019-09-02 10:28:52 +0300243}