e2c9f25cb5bf297daae8985afda9e9f8a6f8ba8a
[ric-plt/vespamgr.git] / cmd / vesmgr / config.go
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.
16  */
17
18 package main
19
20 import (
21         "encoding/json"
22         "gopkg.in/yaml.v2"
23         "io"
24         "os"
25         "strconv"
26         "time"
27 )
28
29 func basicVespaConf() VESAgentConfiguration {
30         var vespaconf = VESAgentConfiguration{
31                 DataDir: "/tmp/data",
32                 Debug:   false,
33                 Event: EventConfiguration{
34                         VNFName:           "vespa-demo",                          // XXX
35                         ReportingEntityID: "1af5bfa9-40b4-4522-b045-40e54f0310f", // XXX
36                         MaxSize:           2000000,
37                         NfNamingCode:      "hsxp",
38                         NfcNamingCodes: []NfcNamingCode{
39                                 NfcNamingCode{
40                                         Type:  "oam",
41                                         Vnfcs: []string{"lr-ope-0", "lr-ope-1", "lr-ope-2"},
42                                 },
43                                 NfcNamingCode{
44                                         Type:  "etl",
45                                         Vnfcs: []string{"lr-pro-0", "lr-pro-1"},
46                                 },
47                         },
48                         RetryInterval: time.Second * 5,
49                         MaxMissed:     2,
50                 },
51                 Measurement: MeasurementConfiguration{
52                         DomainAbbreviation:   "Mvfs",
53                         MaxBufferingDuration: time.Hour,
54                         Prometheus: PrometheusConfig{
55                                 Timeout:   time.Second * 30,
56                                 KeepAlive: time.Second * 30,
57                                 Rules: MetricRules{
58                                         DefaultValues: &MetricRule{
59                                                 VMIDLabel: "'{{.labels.instance}}'",
60                                         },
61                                 },
62                         },
63                 },
64         }
65         return vespaconf
66 }
67
68 type AppMetricsStruct struct {
69         ObjectName     string
70         ObjectInstance string
71         // xxx add labels here
72 }
73
74 type AppMetrics map[string]AppMetricsStruct
75
76 // Parses the metrics data from an array of bytes, which is expected to contain a JSON
77 // array with structs of the following format:
78 //
79 // { ...
80 //   "config" : {
81 //     "metrics": [
82 //       { "name": "...", "objectName": "...", "objectInstamce": "..." },
83 //       ...
84 //     ]
85 //   }
86 // }
87 func parseMetricsFromXAppDescriptor(descriptor []byte, appMetrics AppMetrics) AppMetrics {
88         var desc []map[string]interface{}
89         json.Unmarshal(descriptor, &desc)
90
91         for _, app := range desc {
92                 config, config_ok := app["config"]
93                 if config_ok {
94                         metrics, metrics_ok := config.(map[string]interface{})["metrics"]
95                         if metrics_ok {
96                                 parseMetricsRules(metrics.([]interface{}), appMetrics)
97                         }
98                 }
99         }
100         return appMetrics
101 }
102
103 // Parses the metrics data from an array of interfaces, which are expected to be maps
104 // of the following format:
105 //    { "name": xxx, "objectName": yyy, "objectInstance": zzz }
106 // Entries, which do not have all the necessary fields, are ignored.
107 func parseMetricsRules(metricsMap []interface{}, appMetrics AppMetrics) AppMetrics {
108         for _, element := range metricsMap {
109                 name, name_ok := element.(map[string]interface{})["name"].(string)
110                 if name_ok {
111                         _, already_found := appMetrics[name]
112                         objectName, objectName_ok := element.(map[string]interface{})["objectName"].(string)
113                         objectInstance, objectInstance_ok := element.(map[string]interface{})["objectInstance"].(string)
114                         if !already_found && objectName_ok && objectInstance_ok {
115                                 appMetrics[name] = AppMetricsStruct{objectName, objectInstance}
116                                 logger.Info("parsed counter %s %s %s", name, objectName, objectInstance)
117                         }
118                         if already_found {
119                                 logger.Info("skipped duplicate counter %s", name)
120                         }
121                 }
122         }
123         return appMetrics
124 }
125
126 func getRules(vespaconf *VESAgentConfiguration, xAppConfig []byte) {
127         appMetrics := make(AppMetrics)
128         parseMetricsFromXAppDescriptor(xAppConfig, appMetrics)
129
130         makeRule := func(expr string, obj_name string, obj_instance string) MetricRule {
131                 return MetricRule{
132                         Target:         "AdditionalObjects",
133                         Expr:           expr,
134                         ObjectInstance: obj_instance,
135                         ObjectName:     obj_name,
136                         ObjectKeys: []Label{
137                                 Label{
138                                         Name: "ricComponentName",
139                                         Expr: "'{{.labels.kubernetes_name}}'",
140                                 },
141                         },
142                 }
143         }
144         var metricsMap map[string][]interface{}
145         json.Unmarshal(xAppConfig, &metricsMap)
146         metrics := parseMetricsRules(metricsMap["metrics"], appMetrics)
147
148         vespaconf.Measurement.Prometheus.Rules.Metrics = make([]MetricRule, 0, len(metrics))
149         for key, value := range metrics {
150                 vespaconf.Measurement.Prometheus.Rules.Metrics = append(vespaconf.Measurement.Prometheus.Rules.Metrics, makeRule(key, value.ObjectName, value.ObjectInstance))
151         }
152         if len(vespaconf.Measurement.Prometheus.Rules.Metrics) == 0 {
153                 logger.Info("vespa config with empty metrics")
154         }
155 }
156
157 func getCollectorConfiguration(vespaconf *VESAgentConfiguration) {
158         vespaconf.PrimaryCollector.User = os.Getenv("VESMGR_PRICOLLECTOR_USER")
159         vespaconf.PrimaryCollector.Password = os.Getenv("VESMGR_PRICOLLECTOR_PASSWORD")
160         vespaconf.PrimaryCollector.PassPhrase = os.Getenv("VESMGR_PRICOLLECTOR_PASSPHRASE")
161         vespaconf.PrimaryCollector.FQDN = os.Getenv("VESMGR_PRICOLLECTOR_ADDR")
162         vespaconf.PrimaryCollector.ServerRoot = os.Getenv("VESMGR_PRICOLLECTOR_SERVERROOT")
163         vespaconf.PrimaryCollector.Topic = os.Getenv("VESMGR_PRICOLLECTOR_TOPIC")
164         port_str := os.Getenv("VESMGR_PRICOLLECTOR_PORT")
165         if port_str == "" {
166                 vespaconf.PrimaryCollector.Port = 8443
167         } else {
168                 port, _ := strconv.Atoi(port_str)
169                 vespaconf.PrimaryCollector.Port = port
170         }
171         secure_str := os.Getenv("VESMGR_PRICOLLECTOR_SECURE")
172         if secure_str == "true" {
173                 vespaconf.PrimaryCollector.Secure = true
174         } else {
175                 vespaconf.PrimaryCollector.Secure = false
176         }
177 }
178
179 func createVespaConfig(writer io.Writer, xAppStatus []byte) {
180         vespaconf := basicVespaConf()
181         getRules(&vespaconf, xAppStatus)
182         getCollectorConfiguration(&vespaconf)
183         err := yaml.NewEncoder(writer).Encode(vespaconf)
184         if err != nil {
185                 logger.Error("Cannot write vespa conf file: %s", err.Error())
186                 return
187         }
188 }