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 |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 101 | } |
| 102 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 103 | // AppMetrics contains metrics definitions for all Xapps |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 104 | type AppMetrics map[string]AppMetricsStruct |
| 105 | |
| 106 | // Parses the metrics data from an array of bytes, which is expected to contain a JSON |
| 107 | // array with structs of the following format: |
| 108 | // |
| 109 | // { ... |
| 110 | // "config" : { |
| 111 | // "metrics": [ |
| 112 | // { "name": "...", "objectName": "...", "objectInstamce": "..." }, |
| 113 | // ... |
| 114 | // ] |
| 115 | // } |
| 116 | // } |
| 117 | func parseMetricsFromXAppDescriptor(descriptor []byte, appMetrics AppMetrics) AppMetrics { |
| 118 | var desc []map[string]interface{} |
| 119 | json.Unmarshal(descriptor, &desc) |
| 120 | |
| 121 | for _, app := range desc { |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 122 | config, configOk := app["config"] |
| 123 | if configOk { |
| 124 | metrics, metricsOk := config.(map[string]interface{})["metrics"] |
| 125 | if metricsOk { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 126 | parseMetricsRules(metrics.([]interface{}), appMetrics) |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | return appMetrics |
| 131 | } |
| 132 | |
| 133 | // Parses the metrics data from an array of interfaces, which are expected to be maps |
| 134 | // of the following format: |
| 135 | // { "name": xxx, "objectName": yyy, "objectInstance": zzz } |
| 136 | // Entries, which do not have all the necessary fields, are ignored. |
| 137 | func parseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics) AppMetrics { |
| 138 | for _, element := range metricsMap { |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 139 | name, nameOk := element.(map[string]interface{})["name"].(string) |
| 140 | if nameOk { |
| 141 | _, alreadyFound := appMetrics[name] |
| 142 | objectName, objectNameOk := element.(map[string]interface{})["objectName"].(string) |
| 143 | objectInstance, objectInstanceOk := element.(map[string]interface{})["objectInstance"].(string) |
| 144 | if !alreadyFound && objectNameOk && objectInstanceOk { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 145 | appMetrics[name] = AppMetricsStruct{objectName, objectInstance} |
| 146 | logger.Info("parsed counter %s %s %s", name, objectName, objectInstance) |
| 147 | } |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 148 | if alreadyFound { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 149 | logger.Info("skipped duplicate counter %s", name) |
| 150 | } |
| 151 | } |
| 152 | } |
| 153 | return appMetrics |
| 154 | } |
| 155 | |
| 156 | func getRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) { |
| 157 | appMetrics := make(AppMetrics) |
| 158 | parseMetricsFromXAppDescriptor(xAppConfig, appMetrics) |
| 159 | |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 160 | makeRule := func(expr string, objName string, objInstance string) MetricRule { |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 161 | return MetricRule{ |
| 162 | Target: "AdditionalObjects", |
| 163 | Expr: expr, |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 164 | ObjectInstance: objInstance, |
| 165 | ObjectName: objName, |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 166 | ObjectKeys: []Label{ |
| 167 | Label{ |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 168 | Name: "ricComponentName", |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 169 | Expr: "'{{.labels.kubernetes_name}}'", |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 170 | }, |
| 171 | }, |
| 172 | } |
| 173 | } |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 174 | var metricsMap map[string][]interface{} |
| 175 | json.Unmarshal(xAppConfig, &metricsMap) |
| 176 | metrics := parseMetricsRules(metricsMap["metrics"], appMetrics) |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 177 | |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 178 | vespaconf.Measurement.Prometheus.Rules.Metrics = make([]MetricRule, 0, len(metrics)) |
| 179 | for key, value := range metrics { |
| 180 | vespaconf.Measurement.Prometheus.Rules.Metrics = append(vespaconf.Measurement.Prometheus.Rules.Metrics, makeRule(key, value.ObjectName, value.ObjectInstance)) |
| 181 | } |
| 182 | if len(vespaconf.Measurement.Prometheus.Rules.Metrics) == 0 { |
| 183 | logger.Info("vespa config with empty metrics") |
| 184 | } |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 185 | } |
| 186 | |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 187 | func getCollectorConfiguration(vespaconf *VESAgentConfiguration) { |
| 188 | vespaconf.PrimaryCollector.User = os.Getenv("VESMGR_PRICOLLECTOR_USER") |
| 189 | vespaconf.PrimaryCollector.Password = os.Getenv("VESMGR_PRICOLLECTOR_PASSWORD") |
| 190 | vespaconf.PrimaryCollector.PassPhrase = os.Getenv("VESMGR_PRICOLLECTOR_PASSPHRASE") |
| 191 | vespaconf.PrimaryCollector.FQDN = os.Getenv("VESMGR_PRICOLLECTOR_ADDR") |
| 192 | vespaconf.PrimaryCollector.ServerRoot = os.Getenv("VESMGR_PRICOLLECTOR_SERVERROOT") |
| 193 | vespaconf.PrimaryCollector.Topic = os.Getenv("VESMGR_PRICOLLECTOR_TOPIC") |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 194 | portStr := os.Getenv("VESMGR_PRICOLLECTOR_PORT") |
| 195 | if portStr == "" { |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 196 | vespaconf.PrimaryCollector.Port = 8443 |
| 197 | } else { |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 198 | port, _ := strconv.Atoi(portStr) |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 199 | vespaconf.PrimaryCollector.Port = port |
| 200 | } |
Roni Riska | fc77ebb | 2019-09-26 08:20:44 +0300 | [diff] [blame] | 201 | secureStr := os.Getenv("VESMGR_PRICOLLECTOR_SECURE") |
| 202 | if secureStr == "true" { |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 203 | vespaconf.PrimaryCollector.Secure = true |
| 204 | } else { |
| 205 | vespaconf.PrimaryCollector.Secure = false |
| 206 | } |
| 207 | } |
| 208 | |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 209 | func createVespaConfig(writer io.Writer, xAppStatus []byte) { |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 210 | vespaconf := basicVespaConf() |
Katri Turunen | 412df96 | 2019-09-16 08:48:18 +0300 | [diff] [blame] | 211 | getRules(&vespaconf, xAppStatus) |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 212 | getCollectorConfiguration(&vespaconf) |
Katri Turunen | 4b74f01 | 2019-08-15 10:49:36 +0300 | [diff] [blame] | 213 | err := yaml.NewEncoder(writer).Encode(vespaconf) |
| 214 | if err != nil { |
| 215 | logger.Error("Cannot write vespa conf file: %s", err.Error()) |
| 216 | return |
| 217 | } |
Katri Turunen | 66b7813 | 2019-09-02 10:28:52 +0300 | [diff] [blame] | 218 | } |