blob: e6e48d99b7125b658b33d0c9d9eda889a5a15fb7 [file] [log] [blame]
/*
* Copyright (c) 2019 AT&T Intellectual Property.
* Copyright (c) 2018-2019 Nokia.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This source code is part of the near-RT RIC (RAN Intelligent Controller)
* platform project (RICP).
*
*/
package main
import (
"encoding/json"
"io"
"io/ioutil"
"os"
"strconv"
"strings"
"time"
"gopkg.in/yaml.v2"
)
const defaultReportingEntityID = "00000000-0000-0000-0000-000000000000"
const defaultVNFName = "Vespa"
const defaultNFNamingCode = "ricp"
func readSystemUUID() string {
data, err := ioutil.ReadFile("/sys/class/dmi/id/product_uuid")
if err != nil {
return defaultReportingEntityID
}
return strings.TrimSpace(string(data))
}
func getVNFName() string {
VNFName := os.Getenv("VESMGR_VNFNAME")
if VNFName == "" {
return defaultVNFName
}
return VNFName
}
func getNFNamingCode() string {
NFNamingCode := os.Getenv("VESMGR_NFNAMINGCODE")
if NFNamingCode == "" {
return defaultNFNamingCode
}
return NFNamingCode
}
func basicVespaConf() VESAgentConfiguration {
var vespaconf = VESAgentConfiguration{
DataDir: "/tmp/data",
Debug: false,
Event: EventConfiguration{
VNFName: getVNFName(),
ReportingEntityName: "Vespa",
ReportingEntityID: readSystemUUID(),
MaxSize: 2000000,
NfNamingCode: getNFNamingCode(),
NfcNamingCodes: []NfcNamingCode{},
RetryInterval: time.Second * 5,
MaxMissed: 2,
},
Measurement: MeasurementConfiguration{
// Domain abbreviation has to be set to “Mvfs” for VES 5.3,
// and to “Measurement” for later VES interface versions.
DomainAbbreviation: "Mvfs",
MaxBufferingDuration: time.Hour,
Prometheus: PrometheusConfig{
Timeout: time.Second * 30,
KeepAlive: time.Second * 30,
Rules: MetricRules{
DefaultValues: &MetricRule{
VMIDLabel: "'{{.labels.instance}}'",
},
},
},
},
}
return vespaconf
}
// AppMetricsStruct contains xapplication metrics definition
type AppMetricsStruct struct {
ObjectName string
ObjectInstance string
MeasId string
MeasObject string
}
// AppMetrics contains metrics definitions for all Xapps
type AppMetrics map[string]AppMetricsStruct
// Parses the metrics data from an array of bytes, which is expected to contain a JSON
// array with structs of the following format:
//
// { ...
// "config" : {
// "measurements": [
// {
// "metrics": [
// { "name": "...", "objectName": "...", "objectInstamce": "..." },
// ...
// ]
// }
// ...
// ]
// }
// }
func parseMetricsFromXAppDescriptor(descriptor []byte, appMetrics AppMetrics) AppMetrics {
var desc []map[string]interface{}
json.Unmarshal(descriptor, &desc)
for _, app := range desc {
config, configOk := app["config"]
if !configOk {
logger.Info("No xApp config found!")
continue
}
measurements, measurementsOk := config.(map[string]interface{})["measurements"]
if !measurementsOk {
logger.Info("No xApp metrics found!")
continue
}
for _, m := range measurements.([]interface{}) {
measId, measIdOk := m.(map[string]interface{})["measId"].(string)
measObject, objectOk := m.(map[string]interface{})["object"].(string)
metrics, metricsOk := m.(map[string]interface{})["metrics"]
if !metricsOk || !measIdOk || !objectOk {
logger.Info("No metrics found for measId=%s Object=%s", measId, measObject)
continue
}
parseMetricsRules(metrics.([]interface{}), appMetrics, measId, measObject)
}
}
return appMetrics
}
// Parses the metrics data from an array of interfaces, which are expected to be maps
// of the following format:
// { "name": xxx, "objectName": yyy, "objectInstance": zzz }
// Entries, which do not have all the necessary fields, are ignored.
func parseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics, measId, measObject string) AppMetrics {
for _, element := range metricsMap {
name, nameOk := element.(map[string]interface{})["name"].(string)
if nameOk {
_, alreadyFound := appMetrics[name]
objectName, objectNameOk := element.(map[string]interface{})["objectName"].(string)
objectInstance, objectInstanceOk := element.(map[string]interface{})["objectInstance"].(string)
if !alreadyFound && objectNameOk && objectInstanceOk {
appMetrics[name] = AppMetricsStruct{objectName, objectInstance, measId, measObject}
logger.Info("parsed counter %s %s %s", name, objectName, objectInstance)
}
if alreadyFound {
logger.Info("skipped duplicate counter %s", name)
}
}
}
return appMetrics
}
func getRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) {
makeRule := func(expr string, value AppMetricsStruct) MetricRule {
return MetricRule{
Target: "AdditionalObjects",
Expr: expr,
ObjectInstance: value.ObjectInstance,
ObjectName: value.ObjectName,
ObjectKeys: []Label{
Label{
Name: "ricComponentName",
Expr: "'{{.labels.kubernetes_name}}'",
},
Label{
Name: "measObject",
Expr: value.MeasObject,
},
Label{
Name: "measId",
Expr: value.MeasId,
},
},
}
}
appMetrics := make(AppMetrics)
metrics := parseMetricsFromXAppDescriptor(xAppConfig, appMetrics)
vespaconf.Measurement.Prometheus.Rules.Metrics = make([]MetricRule, 0, len(metrics))
for key, value := range metrics {
vespaconf.Measurement.Prometheus.Rules.Metrics = append(vespaconf.Measurement.Prometheus.Rules.Metrics, makeRule(key, value))
}
if len(vespaconf.Measurement.Prometheus.Rules.Metrics) == 0 {
logger.Info("vespa config with empty metrics")
}
}
func getCollectorConfiguration(vespaconf *VESAgentConfiguration) {
vespaconf.PrimaryCollector.User = os.Getenv("VESMGR_PRICOLLECTOR_USER")
vespaconf.PrimaryCollector.Password = os.Getenv("VESMGR_PRICOLLECTOR_PASSWORD")
vespaconf.PrimaryCollector.PassPhrase = os.Getenv("VESMGR_PRICOLLECTOR_PASSPHRASE")
vespaconf.PrimaryCollector.FQDN = os.Getenv("VESMGR_PRICOLLECTOR_ADDR")
vespaconf.PrimaryCollector.ServerRoot = os.Getenv("VESMGR_PRICOLLECTOR_SERVERROOT")
vespaconf.PrimaryCollector.Topic = os.Getenv("VESMGR_PRICOLLECTOR_TOPIC")
portStr := os.Getenv("VESMGR_PRICOLLECTOR_PORT")
if portStr == "" {
vespaconf.PrimaryCollector.Port = 8443
} else {
port, _ := strconv.Atoi(portStr)
vespaconf.PrimaryCollector.Port = port
}
secureStr := os.Getenv("VESMGR_PRICOLLECTOR_SECURE")
if secureStr == "true" {
vespaconf.PrimaryCollector.Secure = true
} else {
vespaconf.PrimaryCollector.Secure = false
}
}
func createVespaConfig(writer io.Writer, xAppStatus []byte) {
vespaconf := basicVespaConf()
getRules(&vespaconf, xAppStatus)
getCollectorConfiguration(&vespaconf)
err := yaml.NewEncoder(writer).Encode(vespaconf)
if err != nil {
logger.Error("Cannot write vespa conf file: %s", err.Error())
return
}
}