blob: b49c9cb4d01c4f611d3fdc17cc4b795ee5e261c2 [file] [log] [blame]
Sangsik Yoonf0b3ad82016-08-19 18:47:59 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Sangsik Yoonf0b3ad82016-08-19 18:47:59 +09003 *
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 */
16
17package org.onosproject.incubator.net.dpi.impl;
18
19import com.fasterxml.jackson.databind.ObjectMapper;
20
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
27
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.incubator.net.dpi.DpiStatInfo;
31import org.onosproject.incubator.net.dpi.DpiStatistics;
32import org.onosproject.incubator.net.dpi.DpiStatisticsManagerService;
33import org.onosproject.incubator.net.dpi.FlowStatInfo;
34import org.onosproject.incubator.net.dpi.ProtocolStatInfo;
35import org.onosproject.incubator.net.dpi.TrafficStatInfo;
36import org.osgi.service.component.ComponentContext;
37import org.slf4j.Logger;
38
39import java.io.BufferedReader;
40import java.io.IOException;
41import java.io.InputStreamReader;
42import java.io.PrintWriter;
43import java.net.ServerSocket;
44import java.net.Socket;
45import java.text.ParseException;
46import java.text.SimpleDateFormat;
47import java.util.ArrayList;
48import java.util.Collections;
49import java.util.Comparator;
50import java.util.Date;
51import java.util.List;
52import java.util.Locale;
53import java.util.SortedMap;
54import java.util.TimeZone;
55import java.util.TreeMap;
56import java.util.concurrent.ExecutorService;
57import java.util.concurrent.Executors;
58
59import static java.lang.Thread.sleep;
60import static org.onlab.util.Tools.groupedThreads;
61import static org.slf4j.LoggerFactory.getLogger;
62
63/**
64 * DPI Statistics Manager.
65 */
66@Component(immediate = true)
67@Service
68public class DpiStatisticsManager implements DpiStatisticsManagerService {
69
Ray Milkey06297ed2018-01-22 17:13:41 -080070 private ServerSocket serverSocket;
Sangsik Yoonf0b3ad82016-08-19 18:47:59 +090071 private static int port = 11990; // socket server listening port
72
73 private final Logger log = getLogger(getClass());
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected CoreService coreService;
77
78 private ApplicationId appId;
79
80 private final ExecutorService dpiListenerThread =
81 Executors.newSingleThreadExecutor(groupedThreads("onos/apps/dpi", "dpi-listener"));
82
83 DpiStatisticsListener dpiStatisticsListener = null;
84
85 // 31*2(month)*24(hour)*3600(second)/5(second)
86 private static final int MAX_DPI_STATISTICS_ENTRY = 1071360;
87
88 private SortedMap<String, DpiStatistics> dpiStatisticsMap =
89 new TreeMap<>(new MapComparator());
90
91 private long convertTimeToLong(String timeString) {
92 long timeLong = 0;
93
94 try {
95 // Time format: yyyy-MM-dd HH:mm:ss, Time Zone: GMT
96 SimpleDateFormat df = new SimpleDateFormat(DATE_FMT, Locale.KOREA);
97 df.setTimeZone(TimeZone.getTimeZone(TIME_ZONE));
98
99 timeLong = df.parse(timeString).getTime();
100 } catch (ParseException e) {
101 log.error("Time parse error! Exception={}", e.toString());
102 }
103
104 return timeLong;
105 }
106
107 private static final String DATE_FMT = "yyyy-MM-dd HH:mm:ss";
108 private static final String TIME_ZONE = "GMT";
109
110 public static final int MAX_DPI_STATISTICS_REQUEST = 100;
111 public static final int MAX_DPI_STATISTICS_TOPN = 100;
112
113 @Activate
114 public void activate(ComponentContext context) {
115 appId = coreService.registerApplication("org.onosproject.dpi");
116
117 dpiStatisticsListener = new DpiStatisticsListener();
118 dpiListenerThread.execute(dpiStatisticsListener);
119
120 log.info("Started", appId.id());
121 }
122
123 @Deactivate
124 public void deactivate() {
125 log.info("Deactivated...");
Kavitha Alagesan8c9e4102017-02-28 17:11:45 +0530126 dpiListenerThread.shutdownNow();
Sangsik Yoonf0b3ad82016-08-19 18:47:59 +0900127 log.info("Stopped");
128 }
129
130 @Override
131 public DpiStatistics getDpiStatisticsLatest() {
132 if (dpiStatisticsMap.size() > 0) {
133 return dpiStatisticsMap.get(dpiStatisticsMap.firstKey());
134 } else {
135 return null;
136 }
137 }
138
139 @Override
140 public DpiStatistics getDpiStatisticsLatest(int topnProtocols, int topnFlows) {
141 DpiStatistics ds, topnDs;
142
143 ds = getDpiStatisticsLatest();
144 topnDs = processTopn(ds, topnProtocols, topnFlows);
145
146 return topnDs;
147 }
148
149 @Override
150 public List<DpiStatistics> getDpiStatistics(int lastN) {
151 List<DpiStatistics> dsList = new ArrayList<>();
152 DpiStatistics ds;
153
154 if (lastN > MAX_DPI_STATISTICS_REQUEST) {
155 lastN = MAX_DPI_STATISTICS_REQUEST;
156 }
157
158 SortedMap tempMap = new TreeMap(new MapComparator());
159 tempMap.putAll(dpiStatisticsMap);
160
161 for (int i = 0; i < lastN && i < tempMap.size(); i++) {
162 ds = (DpiStatistics) tempMap.get(tempMap.firstKey());
163 dsList.add(i, new DpiStatistics(ds.receivedTime(), ds.dpiStatInfo()));
164
165 tempMap.remove(tempMap.firstKey());
166 }
167
168 return dsList;
169 }
170
171 @Override
172 public List<DpiStatistics> getDpiStatistics(int lastN, int topnProtocols, int topnFlows) {
173 List<DpiStatistics> dsList;
174 List<DpiStatistics> topnDsList = new ArrayList<>();
175 DpiStatistics ds, topnDs;
176
177 dsList = getDpiStatistics(lastN);
178 for (int i = 0; i < dsList.size(); i++) {
179 ds = dsList.get(i);
180 topnDs = processTopn(ds, topnProtocols, topnFlows);
181 topnDsList.add(i, topnDs);
182 }
183
184 return topnDsList;
185 }
186
187 @Override
188 public DpiStatistics getDpiStatistics(String receivedTime) {
189 DpiStatistics ds;
190
191 if (receivedTime == null) {
192 return null;
193 }
194
195 if (!dpiStatisticsMap.containsKey(receivedTime)) {
196 return null;
197 }
198
199 ds = dpiStatisticsMap.get(receivedTime);
200
201 return ds;
202 }
203
204 @Override
205 public DpiStatistics getDpiStatistics(String receivedTime, int topnProtocols, int topnFlows) {
206 DpiStatistics ds, topnDs;
207
208 ds = getDpiStatistics(receivedTime);
209
210 topnDs = processTopn(ds, topnProtocols, topnFlows);
211
212 return topnDs;
213 }
214
215 @Override
216 public DpiStatistics addDpiStatistics(DpiStatistics ds) {
217 if (ds == null) {
218 return ds;
219 }
220
221 // check the time. The firstKey is lastTime because of descending sorted order
222 if (dpiStatisticsMap.size() > 0) {
223 String lastTime = dpiStatisticsMap.get(dpiStatisticsMap.firstKey()).receivedTime();
224 String inputTime = ds.receivedTime();
225
226 long lastTimeLong = convertTimeToLong(lastTime);
227 long inputTimeLong = convertTimeToLong(inputTime);
228
229 if (lastTimeLong >= inputTimeLong) {
230 return null;
231 }
232 }
233
234 if (dpiStatisticsMap.size() >= MAX_DPI_STATISTICS_ENTRY) {
235 // remove the last (oldest) entry
236 dpiStatisticsMap.remove(dpiStatisticsMap.lastKey());
237 }
238
239 if (dpiStatisticsMap.containsKey(ds.receivedTime())) {
240 log.warn("addDpiStatistics(), {} dpiStatistics is already existing!",
241 ds.receivedTime());
242 return null;
243 }
244
245 dpiStatisticsMap.put(ds.receivedTime(), ds);
246 log.debug("addDpiStatistics: dpiResultJson data[time={}] is added " +
247 "into DpiStatisticsMap size={}.",
248 ds.receivedTime(), dpiStatisticsMap.size());
249
250 return ds;
251 }
252
253 private class MapComparator implements Comparator<String> {
254 @Override
255 public int compare(String rt1, String rt2) {
256 long rt1Long = convertTimeToLong(rt1);
257 long rt2Long = convertTimeToLong(rt2);
258
259 // Descending order
260 if (rt1Long > rt2Long) {
261 return -1;
262 } else if (rt1Long < rt2Long) {
263 return 1;
264 } else {
265 return 0;
266 }
267 }
268 }
269
270 private class ProtocolComparator implements Comparator<ProtocolStatInfo> {
271 @Override
272 public int compare(ProtocolStatInfo p1, ProtocolStatInfo p2) {
273 //Descending order
274 if (p1.bytes() > p2.bytes()) {
275 return -1;
276 } else if (p1.bytes() < p2.bytes()) {
277 return 1;
278 } else {
279 return 0;
280 }
281 }
282 }
283
284 private class FlowComparator implements Comparator<FlowStatInfo> {
285 @Override
286 public int compare(FlowStatInfo f1, FlowStatInfo f2) {
287 // Descending order
288 if (f1.bytes() > f2.bytes()) {
289 return -1;
290 } else if (f1.bytes() < f2.bytes()) {
291 return 1;
292 } else {
293 return 0;
294 }
295 }
296 }
297 private DpiStatistics processTopn(DpiStatistics ds, int topnProtocols, int topnFlows) {
298 if (ds == null) {
299 return null;
300 }
301
302 if (topnProtocols <= 0) {
303 // displays all entries
304 topnProtocols = 0;
305 } else if (topnProtocols > MAX_DPI_STATISTICS_TOPN) {
306 topnProtocols = MAX_DPI_STATISTICS_TOPN;
307 }
308
309 if (topnFlows <= 0) {
310 // displays all entries
311 topnFlows = 0;
312 } else if (topnFlows > MAX_DPI_STATISTICS_TOPN) {
313 topnFlows = MAX_DPI_STATISTICS_TOPN;
314 }
315
316 if (topnProtocols == 0 && topnFlows == 0) {
317 return ds;
318 }
319
320 TrafficStatInfo tsi = ds.dpiStatInfo().trafficStatistics();
321 List<ProtocolStatInfo> psiList;
322 List<FlowStatInfo> kfList;
323 List<FlowStatInfo> ufList;
324
325 List<ProtocolStatInfo> pList = ds.dpiStatInfo().detectedProtos();
326 Collections.sort(pList, new ProtocolComparator());
327 if (topnProtocols > 0 && topnProtocols < pList.size()) {
328 psiList = pList.subList(0, topnProtocols);
329 } else {
330 psiList = pList;
331 }
332
333
334 List<FlowStatInfo> fList = ds.dpiStatInfo().knownFlows();
335 Collections.sort(fList, new FlowComparator());
336 if (topnFlows > 0 && topnFlows < fList.size()) {
337 kfList = fList.subList(0, topnFlows);
338 } else {
339 kfList = fList;
340 }
341
342 fList = ds.dpiStatInfo().unknownFlows();
343 Collections.sort(fList, new FlowComparator());
344 if (topnFlows > 0 && topnFlows < fList.size()) {
345 ufList = fList.subList(0, topnFlows);
346 } else {
347 ufList = fList;
348 }
349
350 DpiStatInfo dsi = new DpiStatInfo();
351 dsi.setTrafficStatistics(tsi);
352 dsi.setDetectedProtos(psiList);
353 dsi.setKnownFlows(kfList);
354 dsi.setUnknownFlows(ufList);
355
356 DpiStatistics retDs = new DpiStatistics(ds.receivedTime(), dsi);
357 return retDs;
358 }
359
360 /**
361 * Receiving DPI Statistics result thread.
362 */
363 private class DpiStatisticsListener implements Runnable {
364 Socket clientSocket = null;
365 BufferedReader in = null;
366 PrintWriter out = null;
367
368 String resultJsonString = null;
369
370 static final int MAX_SLEEP_COUNT = 10;
371 int sleepCount = 0;
372
373 @Override
374 public void run() {
375 log.info("DpiStatisticsListener: Receiving thread started...");
376 receiveDpiResult();
377 }
378
Sangsik Yoonf0b3ad82016-08-19 18:47:59 +0900379 private void receiveDpiResult() {
380 try {
381 serverSocket = new ServerSocket(port);
382 } catch (Exception e) {
383 log.error("DpiStatisticsListener: ServerSocket listening error from port={} in localhost, exception={}",
384 port, e.toString());
385 return;
386 }
387
388 try {
Kavitha Alagesan8c9e4102017-02-28 17:11:45 +0530389 while (!Thread.currentThread().isInterrupted()) {
Sangsik Yoonf0b3ad82016-08-19 18:47:59 +0900390 if (clientSocket == null) {
391 log.info("DpiStatisticsListener: Waiting for accepting from dpi client...");
392 clientSocket = serverSocket.accept();
393 log.info("DpiStatisticsListener: Accepted from dpi client={}",
394 clientSocket.getRemoteSocketAddress().toString());
395
396 in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
397 out = new PrintWriter(clientSocket.getOutputStream(), true); // For disconnecting check!
398
399 resultJsonString = null;
400 }
401
402 sleepCount = 0;
403 while (!in.ready()) {
404 sleep(1000); // sleep one second.
405 if (out.checkError() || ++sleepCount >= MAX_SLEEP_COUNT) {
406 log.debug("DpiStatisticsListener: server and socket connect is lost...");
407 in.close();
408 in = null;
409 out.close();
410 out = null;
411 clientSocket.close();
412 clientSocket = null;
413
414 break;
415 }
416 }
417
418 if (in != null) {
419 resultJsonString = in.readLine();
420
421 // process the result
422 log.trace("DpiStatisticsListener: resultJsonString={}", resultJsonString);
423 processResultJson(resultJsonString);
424 }
425 }
426 } catch (Exception e) {
427 log.error("DpiStatisticsListener: Exception = {}", e.toString());
428 return;
Kavitha Alagesan8c9e4102017-02-28 17:11:45 +0530429 } finally {
430 try {
431 if (serverSocket != null) {
432 if (clientSocket != null) {
433 if (in != null) {
434 in.close();
435 }
436 if (out != null) {
437 out.close();
438 }
439 clientSocket.close();
440 //log.debug("DpiResultListener: stop(): Socket close() is done...");
441 }
442 serverSocket.close();
443 //log.debug("DpiResultListener: stop(): Server close() is done...");
444 }
445 } catch (Exception e) {
446 log.error("DpiStatisticsListener: stop(): Server Socket closing error, exception={}",
447 e.toString());
448 }
Sangsik Yoonf0b3ad82016-08-19 18:47:59 +0900449 }
450 }
451
452 private void processResultJson(String resultJsonString) {
453 Date tr = new Date(System.currentTimeMillis());
454 SimpleDateFormat df = new SimpleDateFormat(DATE_FMT, Locale.KOREA);
455 df.setTimeZone(TimeZone.getTimeZone(TIME_ZONE));
456
457 String curReceivedTime = new String(df.format(tr));
458 String curResultJson = new String(resultJsonString);
459
460 DpiStatInfo dpiStatInfo;
461 ObjectMapper mapper = new ObjectMapper();
462 try {
463 dpiStatInfo = mapper.readValue(curResultJson, DpiStatInfo.class);
464 } catch (IOException e) {
465 log.error("DpiStatisticsListener: ObjectMapper Exception = {}", e.toString());
466 return;
467 }
468
469 DpiStatistics dpiStatistics = new DpiStatistics(curReceivedTime, dpiStatInfo);
470
471 addDpiStatistics(dpiStatistics);
472 }
473 }
474}