Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 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. |
Roni Riska | 6ffba08 | 2019-11-27 10:59:54 +0200 | [diff] [blame] | 16 | * |
| 17 | * This source code is part of the near-RT RIC (RAN Intelligent Controller) |
| 18 | * platform project (RICP). |
| 19 | * |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 20 | */ |
| 21 | |
| 22 | package main |
| 23 | |
| 24 | import ( |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 25 | "encoding/json" |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 26 | "io" |
Katri Turunen | 3e79fbe | 2019-09-26 12:46:07 +0300 | [diff] [blame] | 27 | "io/ioutil" |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 28 | "os" |
| 29 | "strconv" |
Katri Turunen | 3e79fbe | 2019-09-26 12:46:07 +0300 | [diff] [blame] | 30 | "strings" |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 31 | "time" |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 32 | |
| 33 | "gopkg.in/yaml.v2" |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 34 | ) |
| 35 | |
Katri Turunen | 3e79fbe | 2019-09-26 12:46:07 +0300 | [diff] [blame] | 36 | const defaultReportingEntityID = "00000000-0000-0000-0000-000000000000" |
| 37 | const defaultVNFName = "Vespa" |
Roni Riska | 364295f | 2019-09-30 09:39:12 +0300 | [diff] [blame] | 38 | const defaultNFNamingCode = "ricp" |
Katri Turunen | 3e79fbe | 2019-09-26 12:46:07 +0300 | [diff] [blame] | 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 | |
Roni Riska | 364295f | 2019-09-30 09:39:12 +0300 | [diff] [blame] | 56 | func getNFNamingCode() string { |
| 57 | NFNamingCode := os.Getenv("VESMGR_NFNAMINGCODE") |
| 58 | if NFNamingCode == "" { |
| 59 | return defaultNFNamingCode |
| 60 | } |
| 61 | return NFNamingCode |
| 62 | } |
| 63 | |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 64 | func basicVespaConf() VESAgentConfiguration { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 65 | var vespaconf = VESAgentConfiguration{ |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 66 | DataDir: "/tmp/data", |
| 67 | Debug: false, |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 68 | Event: EventConfiguration{ |
Katri Turunen | 3e79fbe | 2019-09-26 12:46:07 +0300 | [diff] [blame] | 69 | VNFName: getVNFName(), |
| 70 | ReportingEntityName: "Vespa", |
| 71 | ReportingEntityID: readSystemUUID(), |
| 72 | MaxSize: 2000000, |
Roni Riska | 364295f | 2019-09-30 09:39:12 +0300 | [diff] [blame] | 73 | NfNamingCode: getNFNamingCode(), |
| 74 | NfcNamingCodes: []NfcNamingCode{}, |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 75 | RetryInterval: time.Second * 5, |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 76 | MaxMissed: 2, |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 77 | }, |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 78 | Measurement: MeasurementConfiguration{ |
Katri Turunen | 3e79fbe | 2019-09-26 12:46:07 +0300 | [diff] [blame] | 79 | // Domain abbreviation has to be set to “Mvfs” for VES 5.3, |
| 80 | // and to “Measurement” for later VES interface versions. |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 81 | DomainAbbreviation: "Mvfs", |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 82 | MaxBufferingDuration: time.Hour, |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 83 | Prometheus: PrometheusConfig{ |
| 84 | Timeout: time.Second * 30, |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 85 | KeepAlive: time.Second * 30, |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 86 | Rules: MetricRules{ |
| 87 | DefaultValues: &MetricRule{ |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 88 | VMIDLabel: "'{{.labels.instance}}'", |
| 89 | }, |
| 90 | }, |
| 91 | }, |
| 92 | }, |
| 93 | } |
| 94 | return vespaconf |
| 95 | } |
| 96 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 97 | // AppMetricsStruct contains xapplication metrics definition |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 98 | type AppMetricsStruct struct { |
| 99 | ObjectName string |
| 100 | ObjectInstance string |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 101 | MeasId string |
| 102 | MeasObject string |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 103 | } |
| 104 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 105 | // AppMetrics contains metrics definitions for all Xapps |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 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" : { |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 113 | // "measurements": [ |
| 114 | // { |
| 115 | // "metrics": [ |
| 116 | // { "name": "...", "objectName": "...", "objectInstamce": "..." }, |
| 117 | // ... |
| 118 | // ] |
| 119 | // } |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 120 | // ... |
| 121 | // ] |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 122 | // } |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 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 { |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 129 | config, configOk := app["config"] |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 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 |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 147 | } |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 148 | parseMetricsRules(metrics.([]interface{}), appMetrics, measId, measObject) |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 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. |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 158 | func parseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics, measId, measObject string) AppMetrics { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 159 | for _, element := range metricsMap { |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 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 { |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 166 | appMetrics[name] = AppMetricsStruct{objectName, objectInstance, measId, measObject} |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 167 | logger.Info("parsed counter %s %s %s", name, objectName, objectInstance) |
| 168 | } |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 169 | if alreadyFound { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 170 | logger.Info("skipped duplicate counter %s", name) |
| 171 | } |
| 172 | } |
| 173 | } |
| 174 | return appMetrics |
| 175 | } |
| 176 | |
| 177 | func getRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) { |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 178 | makeRule := func(expr string, value AppMetricsStruct) MetricRule { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 179 | return MetricRule{ |
| 180 | Target: "AdditionalObjects", |
| 181 | Expr: expr, |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 182 | ObjectInstance: value.ObjectInstance, |
| 183 | ObjectName: value.ObjectName, |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 184 | ObjectKeys: []Label{ |
| 185 | Label{ |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 186 | Name: "ricComponentName", |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 187 | Expr: "'{{.labels.kubernetes_name}}'", |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 188 | }, |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 189 | Label{ |
| 190 | Name: "measObject", |
| 191 | Expr: value.MeasObject, |
| 192 | }, |
| 193 | Label{ |
| 194 | Name: "measId", |
| 195 | Expr: value.MeasId, |
| 196 | }, |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 197 | }, |
| 198 | } |
| 199 | } |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 200 | appMetrics := make(AppMetrics) |
| 201 | metrics := parseMetricsFromXAppDescriptor(xAppConfig, appMetrics) |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 202 | |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 203 | vespaconf.Measurement.Prometheus.Rules.Metrics = make([]MetricRule, 0, len(metrics)) |
| 204 | for key, value := range metrics { |
Mohamed Abukar | cbfed3c | 2020-10-08 09:37:21 +0300 | [diff] [blame^] | 205 | vespaconf.Measurement.Prometheus.Rules.Metrics = append(vespaconf.Measurement.Prometheus.Rules.Metrics, makeRule(key, value)) |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 206 | } |
| 207 | if len(vespaconf.Measurement.Prometheus.Rules.Metrics) == 0 { |
| 208 | logger.Info("vespa config with empty metrics") |
| 209 | } |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 210 | } |
| 211 | |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 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") |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 219 | portStr := os.Getenv("VESMGR_PRICOLLECTOR_PORT") |
| 220 | if portStr == "" { |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 221 | vespaconf.PrimaryCollector.Port = 8443 |
| 222 | } else { |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 223 | port, _ := strconv.Atoi(portStr) |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 224 | vespaconf.PrimaryCollector.Port = port |
| 225 | } |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 226 | secureStr := os.Getenv("VESMGR_PRICOLLECTOR_SECURE") |
| 227 | if secureStr == "true" { |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 228 | vespaconf.PrimaryCollector.Secure = true |
| 229 | } else { |
| 230 | vespaconf.PrimaryCollector.Secure = false |
| 231 | } |
| 232 | } |
| 233 | |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 234 | func createVespaConfig(writer io.Writer, xAppStatus []byte) { |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 235 | vespaconf := basicVespaConf() |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 236 | getRules(&vespaconf, xAppStatus) |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 237 | getCollectorConfiguration(&vespaconf) |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 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 | } |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 243 | } |