Sangsik Yoon | f0b3ad8 | 2016-08-19 18:47:59 +0900 | [diff] [blame] | 1 | /* |
Thomas Vachuska | 52f2cd1 | 2018-11-08 21:20:04 -0800 | [diff] [blame] | 2 | * Copyright 2018-present Open Networking Foundation |
Sangsik Yoon | f0b3ad8 | 2016-08-19 18:47:59 +0900 | [diff] [blame] | 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 | */ |
| 16 | |
Thomas Vachuska | 52f2cd1 | 2018-11-08 21:20:04 -0800 | [diff] [blame] | 17 | package org.onosproject.incubator.net.dpi.impl; |
Sangsik Yoon | f0b3ad8 | 2016-08-19 18:47:59 +0900 | [diff] [blame] | 18 | |
Ray Milkey | d84f89b | 2018-08-17 14:54:17 -0700 | [diff] [blame] | 19 | import org.apache.karaf.shell.api.action.Argument; |
| 20 | import org.apache.karaf.shell.api.action.Command; |
| 21 | import org.apache.karaf.shell.api.action.lifecycle.Service; |
| 22 | import org.apache.karaf.shell.api.action.Option; |
Sangsik Yoon | f0b3ad8 | 2016-08-19 18:47:59 +0900 | [diff] [blame] | 23 | import org.onosproject.cli.AbstractShellCommand; |
| 24 | import org.onosproject.incubator.net.dpi.DpiStatInfo; |
| 25 | import org.onosproject.incubator.net.dpi.DpiStatistics; |
| 26 | import org.onosproject.incubator.net.dpi.DpiStatisticsManagerService; |
| 27 | import org.onosproject.incubator.net.dpi.FlowStatInfo; |
| 28 | import org.onosproject.incubator.net.dpi.ProtocolStatInfo; |
| 29 | import org.onosproject.incubator.net.dpi.TrafficStatInfo; |
| 30 | |
| 31 | import java.util.List; |
| 32 | |
| 33 | import static java.lang.Thread.sleep; |
| 34 | |
| 35 | /** |
| 36 | * Fetches DPI statistics list. |
| 37 | */ |
Ray Milkey | d84f89b | 2018-08-17 14:54:17 -0700 | [diff] [blame] | 38 | @Service |
Sangsik Yoon | f0b3ad8 | 2016-08-19 18:47:59 +0900 | [diff] [blame] | 39 | @Command(scope = "onos", name = "dpis", |
| 40 | description = "Fetches the DPI result entries that is received from DPI engine server") |
| 41 | public class DpisListCommand extends AbstractShellCommand { |
| 42 | @Argument(index = 0, name = "receivedTime", description = "received time: format 'yyyy-MM-dd HH:mm:ss', " |
| 43 | + "ex:'2016-08-30 10:31:20', default = null(latest time)", |
| 44 | required = false, multiValued = false) |
| 45 | String receivedTime = null; // default is latest time |
| 46 | |
| 47 | @Option(name = "-l", aliases = "--latest", |
| 48 | description = "Show the latest dpi stats result entry", |
| 49 | required = false, multiValued = false) |
| 50 | boolean latest = true; // default |
| 51 | |
| 52 | @Option(name = "-d", aliases = "--detectedProtocols", |
| 53 | description = "Show the detected protocols only for each statistic entry", |
| 54 | required = false, multiValued = false) |
| 55 | boolean dProtocols = false; // default |
| 56 | |
| 57 | @Option(name = "-k", aliases = "--knownFlows", |
| 58 | description = "Show the known flows only for each statistic entry", |
| 59 | required = false, multiValued = false) |
| 60 | boolean kFlows = false; // default |
| 61 | |
| 62 | @Option(name = "-u", aliases = "--unknownFlows", |
| 63 | description = "Show the unknown flows only for each statistic entry", |
| 64 | required = false, multiValued = false) |
| 65 | boolean uFlows = false; // default |
| 66 | |
| 67 | @Option(name = "-a", aliases = "--all", |
| 68 | description = "Show the all statistics information in detail for each statistic entry", |
| 69 | required = false, multiValued = false) |
| 70 | boolean all = false; // default is traffic statistics only display |
| 71 | |
| 72 | @Option(name = "-p", aliases = "--permanent", |
| 73 | description = "Show the latest dpi stats result entry permanently at 5 second, use Ctrl+C for quitting", |
| 74 | required = false, multiValued = false) |
| 75 | boolean permanent = false; |
| 76 | |
| 77 | @Option(name = "-n", aliases = "--lastn", |
| 78 | description = "Show the last N Dpi stats result entries, MAX_REQUEST_ENTRY = 100", |
| 79 | required = false, multiValued = false) |
| 80 | String lastn = null; |
| 81 | |
| 82 | @Option(name = "-P", aliases = "--topnProtocols", |
| 83 | description = "Show the topn detected Protocol result entries, MAX_PROTOCOLS_TOPN = 100", |
| 84 | required = false, multiValued = false) |
| 85 | String topnProtocols = null; |
| 86 | |
| 87 | @Option(name = "-F", aliases = "--topnFlows", |
| 88 | description = "Show the topn known and unknown Flows result entries, MAX_FLOWS_TOPN = 100", |
| 89 | required = false, multiValued = false) |
| 90 | String topnFlows = null; |
| 91 | |
| 92 | private static final int DEFAULT_LASTN = 100; |
| 93 | private static final int DEFAULT_TOPNP = -1; |
| 94 | private static final int DEFAULT_TOPNF = -1; |
| 95 | private static final String NO_DPI_ENTRY_ERROR_MSG = "No DPI statistic entry," |
| 96 | + " please check remote DPI engine is running"; |
| 97 | private static final String RECEIVED_TIME_ERROR_MSG = NO_DPI_ENTRY_ERROR_MSG + "\n" |
| 98 | + " or correct receivedTime format: 'yyyy-MM-dd HH:mm:ss', ex:'2016-08-30 10:31:20'"; |
| 99 | |
| 100 | @Override |
Ray Milkey | d84f89b | 2018-08-17 14:54:17 -0700 | [diff] [blame] | 101 | protected void doExecute() { |
Sangsik Yoon | f0b3ad8 | 2016-08-19 18:47:59 +0900 | [diff] [blame] | 102 | DpiStatisticsManagerService dsms = get(DpiStatisticsManagerService.class); |
| 103 | |
| 104 | DpiStatistics ds; |
| 105 | |
| 106 | int topnP = DEFAULT_TOPNP; |
| 107 | int topnF = DEFAULT_TOPNF; |
| 108 | |
| 109 | if (topnProtocols != null) { |
| 110 | topnP = parseIntWithDefault(topnProtocols, DEFAULT_TOPNP); |
| 111 | if (topnP <= 0) { |
| 112 | print("Invalid detected protocol topn number: 0 < valid number <= 100"); |
| 113 | return; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | if (topnFlows != null) { |
| 118 | topnF = parseIntWithDefault(topnFlows, DEFAULT_TOPNF); |
| 119 | if (topnF <= 0) { |
| 120 | print("Invalid known or unknown flows topn number: 0 < valid number <= 100"); |
| 121 | return; |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | boolean isTopn = (topnP > 0 || topnF > 0); |
| 126 | |
| 127 | if (all) { |
| 128 | dProtocols = true; |
| 129 | kFlows = true; |
| 130 | uFlows = true; |
| 131 | } |
| 132 | |
| 133 | if (receivedTime != null) { |
| 134 | if (isTopn) { |
| 135 | ds = dsms.getDpiStatistics(receivedTime, topnP, topnF); |
| 136 | } else { |
| 137 | ds = dsms.getDpiStatistics(receivedTime); |
| 138 | } |
| 139 | if (ds == null) { |
| 140 | print(RECEIVED_TIME_ERROR_MSG); |
| 141 | return; |
| 142 | } |
| 143 | |
| 144 | printDpiStatistics(0, ds); |
| 145 | } else if (lastn != null) { |
| 146 | int lastN = parseIntWithDefault(lastn, DEFAULT_LASTN); |
| 147 | |
| 148 | List<DpiStatistics> dsList; |
| 149 | if (isTopn) { |
| 150 | dsList = dsms.getDpiStatistics(lastN, topnP, topnF); |
| 151 | |
| 152 | } else { |
| 153 | dsList = dsms.getDpiStatistics(lastN); |
| 154 | } |
| 155 | |
| 156 | printDpiStatisticsList(dsList); |
| 157 | } else if (permanent) { |
| 158 | int i = 0; |
| 159 | while (true) { |
| 160 | try { |
| 161 | if (isTopn) { |
| 162 | ds = dsms.getDpiStatisticsLatest(topnP, topnF); |
| 163 | } else { |
| 164 | ds = dsms.getDpiStatisticsLatest(); |
| 165 | } |
| 166 | if (ds == null) { |
| 167 | print(NO_DPI_ENTRY_ERROR_MSG); |
| 168 | return; |
| 169 | } |
| 170 | |
| 171 | printDpiStatistics(i++, ds); |
| 172 | sleep(5000); |
| 173 | } catch (Exception e) { |
| 174 | return; |
| 175 | } |
| 176 | } |
| 177 | } else { // latest == true |
| 178 | if (isTopn) { |
| 179 | ds = dsms.getDpiStatisticsLatest(topnP, topnF); |
| 180 | } else { |
| 181 | ds = dsms.getDpiStatisticsLatest(); |
| 182 | } |
| 183 | if (ds == null) { |
| 184 | print(NO_DPI_ENTRY_ERROR_MSG); |
| 185 | return; |
| 186 | } |
| 187 | |
| 188 | printDpiStatistics(0, ds); |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | |
| 193 | /** |
| 194 | * Parse unsigned integer from input lastn string. |
| 195 | * |
| 196 | * @param lastN string lastn number |
| 197 | * @param defaultN integer default lastn number = 100 |
| 198 | * @return integer lastN number, defaultN if input format is not a number |
| 199 | */ |
| 200 | private int parseIntWithDefault(String lastN, int defaultN) { |
| 201 | try { |
| 202 | lastN = lastN.trim(); |
| 203 | return Integer.parseUnsignedInt(lastN); |
| 204 | } catch (NumberFormatException e) { |
| 205 | return defaultN; |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | private void printDpiStatistics(int number, DpiStatistics ds) { |
| 210 | if (outputJson()) { |
| 211 | printDpiStatisticsJson(number, ds); |
| 212 | } else { |
| 213 | printDpiStatisticsClass(number, ds); |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | private void printDpiStatisticsJson(int number, DpiStatistics ds) { |
Ray Milkey | 42b6203 | 2018-02-01 11:15:31 -0800 | [diff] [blame] | 218 | String index = number < 0 ? " - " : String.format("%5d", number); |
Jon Hall | a3fcf67 | 2017-03-28 16:53:22 -0700 | [diff] [blame] | 219 | if ("".equals(ds.receivedTime())) { |
Sangsik Yoon | f0b3ad8 | 2016-08-19 18:47:59 +0900 | [diff] [blame] | 220 | print("ReceivedTime is null, No valid DPI Statistics!"); |
| 221 | return; |
| 222 | } |
| 223 | |
| 224 | print("<--- (%s) DPI Statistics Time [%s] --->", index, ds.receivedTime()); |
| 225 | print(" %s", ds.toString()); |
| 226 | print("<--------------------------------------------------------->"); |
| 227 | } |
| 228 | |
| 229 | private void printDpiStatisticsClass(int number, DpiStatistics ds) { |
| 230 | String printLine = ""; |
Ray Milkey | 42b6203 | 2018-02-01 11:15:31 -0800 | [diff] [blame] | 231 | String index = number < 0 ? " - " : String.format("%5d", number); |
Sangsik Yoon | f0b3ad8 | 2016-08-19 18:47:59 +0900 | [diff] [blame] | 232 | |
| 233 | DpiStatInfo dsi = ds.dpiStatInfo(); |
| 234 | if (dsi == null) { |
| 235 | return; |
| 236 | } |
| 237 | |
Jon Hall | a3fcf67 | 2017-03-28 16:53:22 -0700 | [diff] [blame] | 238 | if ("".equals(ds.receivedTime())) { |
Sangsik Yoon | f0b3ad8 | 2016-08-19 18:47:59 +0900 | [diff] [blame] | 239 | print("ReceivedTime is null, No valid DPI Statistics!"); |
| 240 | return; |
| 241 | } |
| 242 | |
| 243 | print("<--- (%s) DPI Statistics Time [%s] --->", index, ds.receivedTime()); |
| 244 | |
| 245 | print("Traffic Statistics:"); |
| 246 | TrafficStatInfo tsi = dsi.trafficStatistics(); |
| 247 | |
| 248 | printLine = String.format(" %-30s %-30s", "ethernet.bytes:" + ":", tsi.ethernetBytes()); |
| 249 | print("%s", printLine); |
| 250 | printLine = String.format(" %-30s %-30s", "discarded.bytes" + ":", tsi.discardedBytes()); |
| 251 | print("%s", printLine); |
| 252 | printLine = String.format(" %-30s %-30s", "ip.packets" + ":", tsi.ipPackets()); |
| 253 | print("%s", printLine); |
| 254 | printLine = String.format(" %-30s %-30s", "total.packets" + ":", tsi.totalPackets()); |
| 255 | print("%s", printLine); |
| 256 | printLine = String.format(" %-30s %-30s", "ip.bytes" + ":", tsi.ipBytes()); |
| 257 | print("%s", printLine); |
| 258 | printLine = String.format(" %-30s %-30s", "avg.pkt.size" + ":", tsi.avgPktSize()); |
| 259 | print("%s", printLine); |
| 260 | printLine = String.format(" %-30s %-30s", "unique.flows" + ":", tsi.uniqueFlows()); |
| 261 | print("%s", printLine); |
| 262 | printLine = String.format(" %-30s %-30s", "tcp.packets" + ":", tsi.tcpPackets()); |
| 263 | print("%s", printLine); |
| 264 | printLine = String.format(" %-30s %-30s", "udp.packets" + ":", tsi.tcpPackets()); |
| 265 | print("%s", printLine); |
| 266 | printLine = String.format(" %-30s %-30s", "dpi.throughput.pps" + ":", |
| 267 | tsi.dpiThroughputPps() + " pps"); |
| 268 | print("%s", printLine); |
| 269 | printLine = String.format(" %-30s %-30s", "dpi.throughput.bps" + ":", |
| 270 | tsi.dpiThroughputBps() + " bps"); |
| 271 | print("%s", printLine); |
| 272 | printLine = String.format(" %-30s %-30s", "traffic.throughput.pps" + ":", |
| 273 | tsi.trafficThroughputPps() + " pps"); |
| 274 | print("%s", printLine); |
| 275 | printLine = String.format(" %-30s %-30s", "traffic.throughput.bps" + ":", |
| 276 | tsi.trafficThroughputBps() + " bps"); |
| 277 | print("%s", printLine); |
| 278 | printLine = String.format(" %-30s %-30s", "traffic.duration.sec" + ":", |
| 279 | tsi.trafficDurationSec() + " sec"); |
| 280 | print("%s", printLine); |
| 281 | printLine = String.format(" %-30s %-30s", "guessed.flow.protos" + ":", tsi.guessedFlowProtos()); |
| 282 | print("%s", printLine); |
| 283 | |
| 284 | if (dProtocols || topnProtocols != null) { |
| 285 | print(""); |
| 286 | print("Detected Protocols:"); |
| 287 | List<ProtocolStatInfo> psiList = dsi.detectedProtos(); |
| 288 | if (psiList != null) { |
| 289 | psiList.forEach(psi -> print(makeProtocolString(psi))); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | List<FlowStatInfo> fsiList; |
| 294 | if (kFlows || topnFlows != null) { |
| 295 | print(""); |
| 296 | print("Known Flows:"); |
| 297 | fsiList = dsi.knownFlows(); |
| 298 | if (fsiList != null) { |
| 299 | for (int i = 0; i < fsiList.size(); i++) { |
| 300 | print(makeFlowString(fsiList.get(i), i)); |
| 301 | } |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | if (uFlows || topnFlows != null) { |
| 306 | print(""); |
| 307 | print("Unknown Flows:"); |
| 308 | fsiList = dsi.unknownFlows(); |
| 309 | if (fsiList != null) { |
| 310 | for (int i = 0; i < fsiList.size(); i++) { |
| 311 | print(makeFlowString(fsiList.get(i), i)); |
| 312 | } |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | print("<--------------------------------------------------------->"); |
| 317 | } |
| 318 | |
| 319 | private void printDpiStatisticsList(List<DpiStatistics> dsList) { |
| 320 | if (outputJson()) { |
| 321 | printDpiStatisticsListJson(dsList); |
| 322 | } else { |
| 323 | printDpiStatisticsListClass(dsList); |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | private void printDpiStatisticsListJson(List<DpiStatistics> dsList) { |
| 328 | for (int i = 0; i < dsList.size(); i++) { |
| 329 | printDpiStatisticsJson(i, dsList.get(i)); |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | private void printDpiStatisticsListClass(List<DpiStatistics> dsList) { |
| 334 | for (int i = 0; i < dsList.size(); i++) { |
| 335 | printDpiStatisticsClass(i, dsList.get(i)); |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | private String makeProtocolString(ProtocolStatInfo psi) { |
| 340 | StringBuffer sb = new StringBuffer(" "); |
| 341 | |
| 342 | sb.append(String.format("%-20s", psi.name())); |
| 343 | sb.append(String.format(" %s: %-20s", "packets", psi.packets())); |
| 344 | sb.append(String.format(" %s: %-20s", "bytes", psi.bytes())); |
| 345 | sb.append(String.format(" %s: %-20s", "flows", psi.flows())); |
| 346 | |
| 347 | return sb.toString(); |
| 348 | } |
| 349 | |
| 350 | private String makeFlowString(FlowStatInfo fsi, int index) { |
| 351 | StringBuffer sb = new StringBuffer(" "); |
| 352 | |
| 353 | sb.append(String.format("%-8d ", index)); |
| 354 | sb.append(String.format("%s ", fsi.protocol())); |
| 355 | sb.append(String.format("%s", fsi.hostAName())); |
| 356 | sb.append(String.format(":%s", fsi.hostAPort())); |
| 357 | sb.append(String.format(" <-> %s", fsi.hostBName())); |
| 358 | sb.append(String.format(":%s", fsi.hostBPort())); |
| 359 | sb.append(String.format(" [proto: %d", fsi.detectedProtocol())); |
| 360 | sb.append(String.format("/%s]", fsi.detectedProtocolName())); |
| 361 | sb.append(String.format(" [%s pkts/", fsi.packets())); |
| 362 | sb.append(String.format("%s bytes]", fsi.bytes())); |
| 363 | String serverHostName = fsi.hostServerName(); |
Jon Hall | a3fcf67 | 2017-03-28 16:53:22 -0700 | [diff] [blame] | 364 | if (serverHostName != null && !"".equals(serverHostName)) { |
Sangsik Yoon | f0b3ad8 | 2016-08-19 18:47:59 +0900 | [diff] [blame] | 365 | sb.append(String.format("[Host: %s]", serverHostName)); |
| 366 | } |
| 367 | |
| 368 | return sb.toString(); |
| 369 | } |
| 370 | } |