blob: d382526b3320c9cad93f5d80cd9335741f35906e [file] [log] [blame]
Jian Li69600e02018-12-24 13:21:18 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.openstacktelemetry.impl;
17
18import com.google.common.collect.ImmutableMap;
19import com.google.common.collect.Lists;
20import com.google.common.collect.Maps;
21import org.apache.commons.configuration.ConfigurationException;
22import org.apache.commons.configuration.HierarchicalConfiguration;
23import org.apache.commons.configuration.XMLConfiguration;
Jian Li7fe7eaf2018-12-31 17:00:33 +090024import org.onosproject.openstacktelemetry.api.DefaultTelemetryConfig;
Jian Li69600e02018-12-24 13:21:18 +090025import org.onosproject.openstacktelemetry.api.config.TelemetryConfig;
26
27import java.io.IOException;
28import java.io.InputStream;
29import java.util.Arrays;
30import java.util.List;
31import java.util.Map;
32import java.util.stream.Collectors;
33
34import static org.onosproject.openstacktelemetry.api.config.TelemetryConfig.ConfigType.GRPC;
35import static org.onosproject.openstacktelemetry.api.config.TelemetryConfig.ConfigType.INFLUXDB;
36import static org.onosproject.openstacktelemetry.api.config.TelemetryConfig.ConfigType.KAFKA;
37import static org.onosproject.openstacktelemetry.api.config.TelemetryConfig.ConfigType.PROMETHEUS;
38import static org.onosproject.openstacktelemetry.api.config.TelemetryConfig.ConfigType.REST;
Jian Li69600e02018-12-24 13:21:18 +090039
40/**
41 * Utility capable of reading telemetry configuration XML resources and producing
42 * a telemetry config as a result.
43 * <p>
44 * The telemetry configurations stream structure is as follows:
45 * </p>
46 * <pre>
47 * &lt;configs&gt;
Jian Li7fe7eaf2018-12-31 17:00:33 +090048 * &lt;config name="..." [manufacturer="..." swVersion="..."]&gt;
49 * [&lt;property name="key"&gt;value&lt;/key&gt;]
Jian Li69600e02018-12-24 13:21:18 +090050 * ...
51 * &lt;/config&gt;
52 * ...
53 * &lt;/configs&gt;
54 * </pre>
55 */
56public class XmlTelemetryConfigLoader {
57
58 private static final String CONFIGS = "configs";
59 private static final String CONFIG = "config";
60
61 private static final String PROPERTY = "property";
62
Jian Li69600e02018-12-24 13:21:18 +090063 private static final String NAME = "[@name]";
64 private static final String TYPE = "[@type]";
65 private static final String EXTENDS = "[@extends]";
66 private static final String MFG = "[@manufacturer]";
67 private static final String SW = "[@swVersion]";
Jian Li667c6eb2019-01-07 23:01:12 +090068 private static final String STATUS = "[@status]";
Jian Li69600e02018-12-24 13:21:18 +090069
70 private Map<String, TelemetryConfig> configs = Maps.newHashMap();
71
72 /**
73 * Creates a new config loader capable of loading configs from the supplied
74 * class loader.
75 */
76 public XmlTelemetryConfigLoader() {
77 }
78
79 /**
80 * Loads the specified telemetry configs resource as an XML stream and parses
81 * it to produce a ready-to-register config provider.
82 *
83 * @param configsStream stream containing the telemetry configs definition
84 * @return telemetry configuration provider
85 * @throws IOException if issues are encountered reading the stream
86 * or parsing the telemetry config definition within
87 */
88 public DefaultTelemetryConfigProvider
89 loadTelemetryConfigs(InputStream configsStream) throws IOException {
90 try {
91 XMLConfiguration cfg = new XMLConfiguration();
92 cfg.setRootElementName(CONFIGS);
93 cfg.setAttributeSplittingDisabled(true);
94
95 cfg.load(configsStream);
96 return loadTelemetryConfigs(cfg);
97 } catch (ConfigurationException e) {
98 throw new IOException("Unable to load telemetry configs", e);
99 }
100 }
101
102 /**
103 * Loads a telemetry config provider from the supplied hierarchical configuration.
104 *
105 * @param telemetryConfig hierarchical configuration containing the configs definition
106 * @return telemetry configuration provider
107 */
108 public DefaultTelemetryConfigProvider
109 loadTelemetryConfigs(HierarchicalConfiguration telemetryConfig) {
110 DefaultTelemetryConfigProvider provider = new DefaultTelemetryConfigProvider();
111 for (HierarchicalConfiguration cfg : telemetryConfig.configurationsAt(CONFIG)) {
112 DefaultTelemetryConfig config = loadTelemetryConfig(cfg);
113 configs.put(config.name(), config);
114 provider.addConfig(config);
115 }
116 configs.clear();
117 return provider;
118 }
119
120 /**
121 * Loads a telemetry configuration from the supplied hierarchical configuration.
122 *
123 * @param telemetryCfg hierarchical configuration containing the telemetry config definition
124 * @return telemetry configuration
125 */
126 public DefaultTelemetryConfig loadTelemetryConfig(HierarchicalConfiguration telemetryCfg) {
127 String name = telemetryCfg.getString(NAME);
128 String parentsString = telemetryCfg.getString(EXTENDS, "");
129 List<TelemetryConfig> parents = Lists.newArrayList();
130
131 if (!"".equals(parentsString)) {
132 List<String> parentsNames;
133 if (parentsString.contains(",")) {
134 parentsNames = Arrays.asList(
135 parentsString.replace(" ", "").split(","));
136 } else {
137 parentsNames = Lists.newArrayList(parentsString);
138 }
139 parents = parentsNames.stream().map(parent -> (parent != null) ?
140 configs.get(parent) : null).collect(Collectors.toList());
141 }
142
143 String typeStr = telemetryCfg.getString(TYPE, getParentAttribute(parents, TYPE));
144 String manufacturer = telemetryCfg.getString(MFG, getParentAttribute(parents, MFG));
145 String swVersion = telemetryCfg.getString(SW, getParentAttribute(parents, SW));
146
147 // note that we do not inherits enabled property from parent
Jian Li667c6eb2019-01-07 23:01:12 +0900148 String statusStr = telemetryCfg.getString(STATUS);
149 TelemetryConfig.Status status =
150 statusStr == null ? TelemetryConfig.Status.UNKNOWN : status(statusStr);
Jian Li69600e02018-12-24 13:21:18 +0900151
152 TelemetryConfig.ConfigType type = type(typeStr);
153
154 if (type == null) {
155 return null;
156 }
157
158 return new DefaultTelemetryConfig(name, type, parents, manufacturer,
Jian Li667c6eb2019-01-07 23:01:12 +0900159 swVersion, status, parseProperties(parents, telemetryCfg));
Jian Li69600e02018-12-24 13:21:18 +0900160 }
161
162 private TelemetryConfig.ConfigType type(String typeStr) {
163 switch (typeStr.toUpperCase()) {
164 case "GRPC" :
165 return GRPC;
166 case "KAFKA":
167 return KAFKA;
168 case "REST":
169 return REST;
170 case "INFLUXDB":
171 return INFLUXDB;
172 case "PROMETHEUS":
173 return PROMETHEUS;
174 case "UNKNOWN":
175 default:
Jian Li667c6eb2019-01-07 23:01:12 +0900176 return TelemetryConfig.ConfigType.UNKNOWN;
Jian Li69600e02018-12-24 13:21:18 +0900177 }
178 }
179
180 // Returns the specified property from the highest priority parent
181 private String getParentAttribute(List<TelemetryConfig> parents, String attribute) {
182 if (!parents.isEmpty()) {
183 TelemetryConfig parent = parents.get(0);
184 switch (attribute) {
185 case TYPE:
186 return parent.type().name().toLowerCase();
187 case MFG:
188 return parent.manufacturer();
189 case SW:
190 return parent.swVersion();
191 default:
192 throw new IllegalArgumentException("Unsupported attribute");
193 }
194 }
195 return "";
196 }
197
198 // Parses the properties section.
199 private Map<String, String> parseProperties(List<TelemetryConfig> parents,
200 HierarchicalConfiguration config) {
201 ImmutableMap.Builder<String, String> properties = ImmutableMap.builder();
202
203 // note that, we only allow the inheritance from single source
Jian Li7fe7eaf2018-12-31 17:00:33 +0900204 final Map<String, String> parentConfigs = Maps.newHashMap();
Jian Li69600e02018-12-24 13:21:18 +0900205 if (!parents.isEmpty()) {
206 TelemetryConfig parent = parents.get(0);
Jian Li7fe7eaf2018-12-31 17:00:33 +0900207 parent.properties().forEach(parentConfigs::put);
208 }
209
210 for (HierarchicalConfiguration b : config.configurationsAt(PROPERTY)) {
211 if (parentConfigs.keySet().contains(b.getString(NAME))) {
212 parentConfigs.remove(b.getString(NAME));
213 }
Jian Li69600e02018-12-24 13:21:18 +0900214 }
215
216 properties.putAll(parentConfigs);
217
218 for (HierarchicalConfiguration b : config.configurationsAt(PROPERTY)) {
219 properties.put(b.getString(NAME), (String) b.getRootNode().getValue());
220 }
Jian Li7fe7eaf2018-12-31 17:00:33 +0900221
Jian Li69600e02018-12-24 13:21:18 +0900222 return properties.build();
223 }
Jian Li667c6eb2019-01-07 23:01:12 +0900224
225 private TelemetryConfig.Status status(String status) {
226 switch (status.toUpperCase()) {
227 case "ENABLED" :
228 return TelemetryConfig.Status.ENABLED;
229 case "DISABLED" :
230 return TelemetryConfig.Status.DISABLED;
231 case "PENDING" :
232 return TelemetryConfig.Status.PENDING;
233 default:
234 return TelemetryConfig.Status.UNKNOWN;
235 }
236 }
Jian Li69600e02018-12-24 13:21:18 +0900237}