blob: dcb7082ae7ca8f0169b8cdda9474b33a7b250bc1 [file] [log] [blame]
Boyoung Jeong1cca5e82018-08-01 21:00:08 +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.gui;
17
18import com.fasterxml.jackson.databind.node.ArrayNode;
19import com.fasterxml.jackson.databind.node.ObjectNode;
20import com.google.common.base.Strings;
21import com.google.common.collect.ImmutableMap;
22import com.google.common.collect.ImmutableSet;
23import com.google.common.collect.Maps;
boyoung2a8549d22018-11-23 20:42:37 +090024import org.onlab.osgi.ServiceDirectory;
25import org.onlab.packet.IpAddress;
26import org.onlab.packet.IpPrefix;
27import org.onlab.packet.TpPort;
28import org.onosproject.net.Host;
29import org.onosproject.net.host.HostService;
Boyoung Jeong1cca5e82018-08-01 21:00:08 +090030import org.onosproject.openstacktelemetry.api.FlowInfo;
boyoung2a8549d22018-11-23 20:42:37 +090031import org.onosproject.openstacktelemetry.api.StatsFlowRule;
Boyoung Jeong1cca5e82018-08-01 21:00:08 +090032import org.onosproject.openstacktelemetry.api.StatsFlowRuleAdminService;
boyoung2a8549d22018-11-23 20:42:37 +090033import org.onosproject.openstacktelemetry.impl.DefaultStatsFlowRule;
Boyoung Jeong1cca5e82018-08-01 21:00:08 +090034import org.onosproject.ui.RequestHandler;
boyoung2a8549d22018-11-23 20:42:37 +090035import org.onosproject.ui.UiConnection;
Boyoung Jeong1cca5e82018-08-01 21:00:08 +090036import org.onosproject.ui.UiMessageHandler;
37import org.onosproject.ui.chart.ChartModel;
38import org.onosproject.ui.chart.ChartRequestHandler;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
42import java.text.SimpleDateFormat;
43import java.util.Collection;
44import java.util.Date;
boyoung2a8549d22018-11-23 20:42:37 +090045import java.util.Iterator;
Boyoung Jeong1cca5e82018-08-01 21:00:08 +090046import java.util.Map;
47import java.util.Queue;
boyoung2a8549d22018-11-23 20:42:37 +090048import java.util.Set;
49
50import static org.onosproject.net.HostId.hostId;
51import static org.onosproject.openstacktelemetry.util.OpenstackTelemetryUtil.getProtocolTypeFromString;
Boyoung Jeong1cca5e82018-08-01 21:00:08 +090052
53/**
54 * Message handler for Openstack Telemetry view related messages.
55 */
56public class OpensteckTelemetryViewMessageHandler extends UiMessageHandler {
57 private final Logger log = LoggerFactory.getLogger(getClass());
58
boyoung2a8549d22018-11-23 20:42:37 +090059 // Chart View
Boyoung Jeong1cca5e82018-08-01 21:00:08 +090060 private static final String OST_DATA_REQ = "openstacktelemetryDataRequest";
61 private static final String OST_DATA_RESP = "openstacktelemetryDataResponse";
boyoung2a8549d22018-11-23 20:42:37 +090062
63 // Network Topology
64 private static final String OST_IS_ACTIVATED_REQ = "openstackFlowStatsIsActivatedRequest";
65 private static final String OST_IS_ACTIVATED_RESP = "openstackFlowStatsIsActivatedResponse";
66 private static final String OST_FLOW_STATS_CREATE_REQ = "openstackFlowStatsCreateRequest";
67 private static final String OST_FLOW_STATS_CREATE_RESP = "openstackFlowStatsCreateResponse";
68
Boyoung Jeong1cca5e82018-08-01 21:00:08 +090069 private static final String OSTS = "openstacktelemetrys";
70 private static final String ANNOT_FLOW_IDS = "flowIds";
71 private static final String ANNOT_PERIOD_OPTIONS = "periodOptions";
72
boyoung2a8549d22018-11-23 20:42:37 +090073 private static final String SOURCE = "src";
74 private static final String DESTINATION = "dst";
75 private static final String SOURCE_IP = "srcIp";
76 private static final String DESTINATION_IP = "dstIp";
77 private static final String SOURCE_TRANSPORT_PORT = "srcPort";
78 private static final String DESTINATION_TRANSPORT_PORT = "dstPort";
79 private static final String SOURCE_HOST_NAME = "srcName";
80 private static final String DESTINATION_HOST_NAME = "dstName";
81 private static final String IP_PROTOCOL = "ipProto";
82 private static final String IP_PROTOCOL_LIST = "ipProtoList";
83
84 private static final String RESULT = "result";
85 private static final String RESULT_OK = "OK";
86 private static final String VALUE = "value";
87 private static final String SUCCESS = "Success";
88 private static final String FAILED = "Failed";
89 private static final String FAILED_TO_CREATE_FLOW_STATS = "Failed to create a flow rule for statistics";
90
91 private static final String[] IP_PROTOCOL_ARRAY = {"TCP", "UDP", "ANY"};
92
Boyoung Jeong1cca5e82018-08-01 21:00:08 +090093 private static final String STAT_CURR_ACC_PACKET = "curr_acc_packet";
94 private static final String STAT_PREV_ACC_PACKET = "prev_acc_packet";
95 private static final String STAT_CURR_ACC_BYTE = "curr_acc_byte";
96 private static final String STAT_PREV_ACC_BYTE = "prev_acc_byte";
97 private static final String STAT_ERROR_PACKET = "error_packet";
98 private static final String STAT_DROP_PACKET = "drop_packet";
99
100 private static final String CHART_TIME_FORMAT = "HH:mm:ss";
101
102 // JSON node name
103 private static final String JSON_NODE_FLOW = "flowOpt";
104 private static final String JSON_NODE_PERIOD = "periodOpt";
105
106 private static final String DEFAULT_PERIOD_OPTION_1MIN = "1 MIN";
107
108 // Statistics period of statistics chart
109 private static final Map<String, Integer> PERIOD_OPTION_MAP =
110 ImmutableMap.<String, Integer>builder()
111 .put(DEFAULT_PERIOD_OPTION_1MIN, 12)
112 .put("3 MIN", 36)
113 .put("5 MIN", 60)
114 .put("30 MIN", 360)
115 .put("1 HOUR", 720)
116 .put("2 HOUR", 1440)
117 .put("6 HOUR", 4320)
118 .put("24 HOUR", 17280)
119 .build();
120
boyoung2a8549d22018-11-23 20:42:37 +0900121 private HostService hostService;
122 private StatsFlowRuleAdminService statsFlowRuleService;
123
124 @Override
125 public void init(UiConnection connection, ServiceDirectory directory) {
126 super.init(connection, directory);
127
128 hostService = directory.get(HostService.class);
129 statsFlowRuleService = directory.get(StatsFlowRuleAdminService.class);
130 }
131
Boyoung Jeong1cca5e82018-08-01 21:00:08 +0900132 @Override
133 protected Collection<RequestHandler> createRequestHandlers() {
134 return ImmutableSet.of(
boyoung2a8549d22018-11-23 20:42:37 +0900135 new ChartControlMessageRequest(),
136 new FlowStatsIsActivatedRequestHandler(),
137 new FlowStatsCreateRequestHandler()
Boyoung Jeong1cca5e82018-08-01 21:00:08 +0900138 );
139 }
140
boyoung2a8549d22018-11-23 20:42:37 +0900141 /* Handler for Line-Chart View */
142 private final class ChartControlMessageRequest extends ChartRequestHandler {
Boyoung Jeong1cca5e82018-08-01 21:00:08 +0900143 private Map<String, Queue<FlowInfo>> flowInfoMap = null;
144 private String currentFlowKey = null;
145 private String currentPeriod = DEFAULT_PERIOD_OPTION_1MIN;
146
boyoung2a8549d22018-11-23 20:42:37 +0900147 private ChartControlMessageRequest() {
Boyoung Jeong1cca5e82018-08-01 21:00:08 +0900148 super(OST_DATA_REQ, OST_DATA_RESP, OSTS);
149 }
150
151 @Override
152 protected String[] getSeries() {
153 String[] series = {STAT_CURR_ACC_PACKET, STAT_PREV_ACC_PACKET,
154 STAT_CURR_ACC_BYTE, STAT_PREV_ACC_BYTE, STAT_ERROR_PACKET, STAT_DROP_PACKET};
155 return series;
156 }
157
158 @Override
159 protected void populateChart(ChartModel cm, ObjectNode payload) {
160 if (statsFlowRuleService == null) {
161 statsFlowRuleService = get(StatsFlowRuleAdminService.class);
162 }
163 if (flowInfoMap == null) {
164 flowInfoMap = statsFlowRuleService.getFlowInfoMap();
165 }
166
167 String flowKey = string(payload, JSON_NODE_FLOW);
168 String period = string(payload, JSON_NODE_PERIOD);
169
170 if (!Strings.isNullOrEmpty(flowKey) || !Strings.isNullOrEmpty(period)) {
171 if (!Strings.isNullOrEmpty(flowKey)) {
172 currentFlowKey = flowKey;
173 }
174 if (!Strings.isNullOrEmpty(period)) {
175 currentPeriod = period;
176 }
177
178 Queue<FlowInfo> flowInfoQ = flowInfoMap.get(currentFlowKey);
179
180 if (flowInfoQ == null) {
181 log.warn("No such flow key {}", currentFlowKey);
182 return;
183 } else {
184 populateMetrics(cm, flowInfoQ);
185 attachFlowList(cm);
186 attachPeriodList(cm);
187 }
188 } else {
189 flowInfoMap.keySet().forEach(key -> {
190 Queue<FlowInfo> flowInfoQ = flowInfoMap.get(key);
191 if (flowInfoQ == null) {
192 log.warn("Key {} is not found in FlowInfoMap", key);
193 return;
194 }
195 FlowInfo flowInfo = getLatestFlowInfo(flowInfoQ);
196
197 Map<String, Object> local = Maps.newHashMap();
198 local.put(LABEL, key);
199 local.put(STAT_CURR_ACC_PACKET, flowInfo.statsInfo().currAccPkts());
200 local.put(STAT_PREV_ACC_PACKET, flowInfo.statsInfo().prevAccPkts());
201 local.put(STAT_CURR_ACC_BYTE, flowInfo.statsInfo().currAccBytes());
202 local.put(STAT_PREV_ACC_BYTE, flowInfo.statsInfo().prevAccBytes());
203 local.put(STAT_ERROR_PACKET, flowInfo.statsInfo().errorPkts());
204 local.put(STAT_DROP_PACKET, flowInfo.statsInfo().dropPkts());
205
206 populateMetric(cm.addDataPoint(key), local);
207 });
208 }
209 }
210
211 private void populateMetrics(ChartModel cm, Queue<FlowInfo> flowInfoQ) {
212 FlowInfo[] flowInfos = flowInfoQ.toArray(new FlowInfo[flowInfoQ.size()]);
213 SimpleDateFormat form = new SimpleDateFormat(CHART_TIME_FORMAT);
214 int timeOffset = 0;
215 Integer dataPointCount = PERIOD_OPTION_MAP.get(currentPeriod);
216 if (dataPointCount != null) {
217 timeOffset = flowInfos.length - (int) dataPointCount;
218 if (timeOffset < 0) {
219 timeOffset = 0;
220 }
221 }
222
223 for (int idx = timeOffset; idx < flowInfos.length; idx++) {
224 Map<String, Object> local = Maps.newHashMap();
225 local.put(LABEL, form.format(new Date(flowInfos[idx].statsInfo().fstPktArrTime())));
226 local.put(STAT_CURR_ACC_PACKET, flowInfos[idx].statsInfo().currAccPkts());
227 local.put(STAT_PREV_ACC_PACKET, flowInfos[idx].statsInfo().prevAccPkts());
228 local.put(STAT_CURR_ACC_BYTE, flowInfos[idx].statsInfo().currAccBytes());
229 local.put(STAT_PREV_ACC_BYTE, flowInfos[idx].statsInfo().prevAccBytes());
230 local.put(STAT_ERROR_PACKET, flowInfos[idx].statsInfo().errorPkts());
231 local.put(STAT_DROP_PACKET, flowInfos[idx].statsInfo().dropPkts());
232 populateMetric(cm.addDataPoint(flowInfos[idx].uniqueFlowInfoKey()), local);
233 }
234 }
235
236 private void populateMetric(ChartModel.DataPoint dataPoint,
237 Map<String, Object> data) {
238 data.forEach(dataPoint::data);
239 }
240
241 private void attachFlowList(ChartModel cm) {
242 ArrayNode array = arrayNode();
243 flowInfoMap.keySet().forEach(key -> {
244 array.add(key);
245 });
246 cm.addAnnotation(ANNOT_FLOW_IDS, array);
247 }
248
249 private void attachPeriodList(ChartModel cm) {
250 ArrayNode array = arrayNode();
251 PERIOD_OPTION_MAP.keySet().forEach(period -> {
252 array.add(period);
253 });
254 cm.addAnnotation(ANNOT_PERIOD_OPTIONS, array);
255 }
256
257 private FlowInfo getLatestFlowInfo(Queue<FlowInfo> flowInfoQ) {
258 FlowInfo[] flowInfos = flowInfoQ.toArray(new FlowInfo[flowInfoQ.size()]);
259 return flowInfos[flowInfos.length - 1];
260 }
261 }
boyoung2a8549d22018-11-23 20:42:37 +0900262
263 /* Handler for Network Topology View */
264 private final class FlowStatsIsActivatedRequestHandler extends RequestHandler {
265
266 private FlowStatsIsActivatedRequestHandler() {
267 super(OST_IS_ACTIVATED_REQ);
268 }
269
270 @Override
271 public void process(ObjectNode payload) {
272 String srcId = string(payload, SOURCE, null);
273 String dstId = string(payload, DESTINATION, null);
274
275 log.debug("Input from GUI: {}", payload.toString());
276 if (srcId != null && dstId != null) {
277 Host srcHost = hostService.getHost(hostId(srcId));
278 Host dstHost = hostService.getHost(hostId(dstId));
279
280 if (srcHost != null && dstHost != null) {
281 ArrayNode ipProtos = arrayNode();
282 String srcHostName = ipForHost(srcHost);
283 String dstHostName = ipForHost(dstHost);
284
285 for (String proto : IP_PROTOCOL_ARRAY) {
286 ipProtos.add(proto);
287 }
288
289 payload.put(SOURCE_HOST_NAME, srcHostName);
290 payload.put(DESTINATION_HOST_NAME, dstHostName);
291 payload.put(IP_PROTOCOL_LIST, ipProtos);
292
293 log.debug("Output to GUI: {}", payload.toString());
294
295 sendMessage(OST_IS_ACTIVATED_RESP, payload);
296 }
297 }
298 }
299
300 // Returns the first of the given host's set of IP addresses as a string.
301 private String ipForHost(Host host) {
302 Set<IpAddress> ipAddresses = host.ipAddresses();
303 Iterator<IpAddress> it = ipAddresses.iterator();
304 return it.hasNext() ? it.next().toString() + "/32" : "unknown";
305 }
306 }
307
308 private final class FlowStatsCreateRequestHandler extends RequestHandler {
309 private String srcIp;
310 private String dstIp;
311 private String ipProto;
312 private String srcTpPort;
313 private String dstTpPort;
314 private ObjectNode result = objectNode();
315
316 private FlowStatsCreateRequestHandler() {
317 super(OST_FLOW_STATS_CREATE_REQ);
318 }
319
320 @Override
321 public void process(ObjectNode payload) {
322 srcIp = string(payload, SOURCE_IP, null);
323 dstIp = string(payload, DESTINATION_IP, null);
324 ipProto = string(payload, IP_PROTOCOL, null);
325 srcTpPort = string(payload, SOURCE_TRANSPORT_PORT, null);
326 dstTpPort = string(payload, DESTINATION_TRANSPORT_PORT, null);
327
328 log.debug("[GUI input] srcIp:{} dstIp:{} ipPro:{} srcTpPort:{} dstTpPort:{}",
329 srcIp, dstIp, ipProto, srcTpPort, dstTpPort);
330
331 try {
332 StatsFlowRule statsFlowRule = DefaultStatsFlowRule.builder()
333 .srcIpPrefix(IpPrefix.valueOf(srcIp))
334 .dstIpPrefix(IpPrefix.valueOf(dstIp))
335 .srcTpPort(TpPort.tpPort(Integer.valueOf(srcTpPort)))
336 .dstTpPort(TpPort.tpPort(Integer.valueOf(dstTpPort)))
337 .ipProtocol(getProtocolTypeFromString(ipProto))
338 .build();
339 statsFlowRuleService.createStatFlowRule(statsFlowRule);
340 result.put(RESULT, SUCCESS);
341 result.put(VALUE, RESULT_OK);
342 } catch (Exception ex) {
343 log.warn(FAILED_TO_CREATE_FLOW_STATS + " : " + ex.toString());
344 result.put(RESULT, FAILED);
345 result.put(VALUE, FAILED_TO_CREATE_FLOW_STATS);
346 }
347 sendMessage(OST_FLOW_STATS_CREATE_RESP, result);
348 }
349 }
Boyoung Jeong1cca5e82018-08-01 21:00:08 +0900350}