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