blob: bc2fca5b2c9b6cb0e55eeaaa6a7c07075754329f [file] [log] [blame]
Madan Jampanic27b6b22016-02-05 11:36:31 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Madan Jampanic27b6b22016-02-05 11:36:31 -08003 *
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.cli.net;
18
Ray Milkeyd84f89b2018-08-17 14:54:17 -070019import org.apache.karaf.shell.api.action.Argument;
20import org.apache.karaf.shell.api.action.Command;
Ray Milkey0068fd02018-10-11 15:45:39 -070021import org.apache.karaf.shell.api.action.Completion;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070022import org.apache.karaf.shell.api.action.lifecycle.Service;
23import org.apache.karaf.shell.api.action.Option;
Madan Jampanic27b6b22016-02-05 11:36:31 -080024import org.onosproject.cli.AbstractShellCommand;
25import org.onosproject.net.ConnectPoint;
26import org.onosproject.net.Device;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.Port;
29import org.onosproject.net.PortNumber;
30import org.onosproject.net.device.DeviceService;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090031import org.onosproject.net.flow.FlowEntry;
32import org.onosproject.net.flow.StoredFlowEntry;
Madan Jampanic27b6b22016-02-05 11:36:31 -080033import org.onosproject.net.flow.instructions.Instruction;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090034import org.onosproject.net.statistic.FlowEntryWithLoad;
Madan Jampanic27b6b22016-02-05 11:36:31 -080035import org.onosproject.net.statistic.FlowStatisticService;
36import org.onosproject.net.statistic.SummaryFlowEntryWithLoad;
Madan Jampanic27b6b22016-02-05 11:36:31 -080037
38import java.util.List;
39import java.util.Map;
40
41import static org.onosproject.net.DeviceId.deviceId;
42import static org.onosproject.net.PortNumber.portNumber;
43
44/**
45 * Fetches flow statistics with a flow type and instruction type.
46 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070047@Service
Madan Jampanic27b6b22016-02-05 11:36:31 -080048@Command(scope = "onos", name = "get-flow-stats",
49 description = "Fetches flow stats for a connection point with given flow type and instruction type")
sangyun-han483731c2016-06-06 12:03:16 +090050public class GetFlowStatisticsCommand extends AbstractShellCommand {
Madan Jampanic27b6b22016-02-05 11:36:31 -080051 @Argument(index = 0, name = "devicePort",
52 description = "Device[/Port] connectPoint Description",
53 required = true, multiValued = false)
Ray Milkey0068fd02018-10-11 15:45:39 -070054 @Completion(ConnectPointCompleter.class)
Madan Jampanic27b6b22016-02-05 11:36:31 -080055 String devicePort = null;
56
57 @Option(name = "-s", aliases = "--summary",
58 description = "Show flow stats summary",
59 required = false, multiValued = false)
60 boolean showSummary = true; // default summary
61
62 @Option(name = "-a", aliases = "--all",
63 description = "Show flow stats all",
64 required = false, multiValued = false)
65 boolean showAll = false;
66
67 @Option(name = "-t", aliases = "--topn",
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090068 description = "Show flow stats topn entry",
Madan Jampanic27b6b22016-02-05 11:36:31 -080069 required = false, multiValued = false)
70 String showTopn = null;
71
72 @Option(name = "-f", aliases = "--flowType",
73 description = "Flow live type, It includes IMMEDIATE, SHORT, MID, LONG, UNKNOWN"
74 + ", and is valid with -a or -t option only",
75 required = false, multiValued = false)
76 String flowLiveType = null;
77
78 @Option(name = "-i", aliases = "--instructionType",
79 description = "Flow instruction type, It includes DROP, OUTPUT, GROUP, L0MODIFICATION, L2MODIFICATION,"
80 + " TABLE, L3MODIFICATION, METADATA"
81 + ", and is valid with -a or -t option only",
82 required = false, multiValued = false)
83 String instructionType = null;
84
85 @Override
Ray Milkeyd84f89b2018-08-17 14:54:17 -070086 protected void doExecute() {
Madan Jampanic27b6b22016-02-05 11:36:31 -080087 DeviceService deviceService = get(DeviceService.class);
88 FlowStatisticService flowStatsService = get(FlowStatisticService.class);
89
90 String deviceUri = getDeviceId(devicePort);
91 String portUri = getPortNumber(devicePort);
92
93 DeviceId ingressDeviceId = deviceId(deviceUri);
94 PortNumber ingressPortNumber;
95 if (portUri.length() == 0) {
96 ingressPortNumber = null;
97 } else {
98 ingressPortNumber = portNumber(portUri);
99 }
100
101 Device device = deviceService.getDevice(ingressDeviceId);
102 if (device == null) {
103 error("No such device %s", ingressDeviceId.uri());
104 return;
105 }
106
107 if (ingressPortNumber != null) {
108 Port port = deviceService.getPort(ingressDeviceId, ingressPortNumber);
109 if (port == null) {
110 error("No such port %s on device %s", portUri, ingressDeviceId.uri());
111 return;
112 }
113 }
114
115 if (flowLiveType != null) {
116 flowLiveType = flowLiveType.toUpperCase();
117 }
118 if (instructionType != null) {
119 instructionType = instructionType.toUpperCase();
120 }
121
122 // convert String to FlowLiveType and check validity
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900123 FlowEntry.FlowLiveType inLiveType;
Madan Jampanic27b6b22016-02-05 11:36:31 -0800124 if (flowLiveType == null) {
125 inLiveType = null;
126 } else {
127 inLiveType = getFlowLiveType(flowLiveType);
128 if (inLiveType == null) {
129 error("Invalid flow live type [%s] error", flowLiveType);
130 return;
131 }
132 }
133 // convert String to InstructionType and check validity
134 Instruction.Type inInstructionType;
135 if (instructionType == null) {
136 inInstructionType = null;
137 } else {
138 inInstructionType = getInstructionType(instructionType);
139 if (inInstructionType == null) {
140 error("Invalid instruction type [%s] error", instructionType);
141 return;
142 }
143 }
144
145 if (showTopn != null) {
146 int topn = Integer.parseInt(showTopn);
147
148 if (topn <= 0) {
149 topn = 100; //default value
150 } else if (topn > 1000) {
151 topn = 1000; //max value
152 }
153
154 // print show topn head line with type
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900155 print("deviceId=%s, show TOPN=%s flows, liveType=%s, instruction type=%s",
Madan Jampanic27b6b22016-02-05 11:36:31 -0800156 deviceUri,
157 Integer.toString(topn),
158 flowLiveType == null ? "ALL" : flowLiveType,
159 instructionType == null ? "ALL" : instructionType);
160 if (ingressPortNumber == null) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900161 Map<ConnectPoint, List<FlowEntryWithLoad>> typedFlowLoadMap =
Madan Jampanic27b6b22016-02-05 11:36:31 -0800162 flowStatsService.loadTopnByType(device, inLiveType, inInstructionType, topn);
163 // print all ports topn flows load for a given device
164 for (ConnectPoint cp : typedFlowLoadMap.keySet()) {
165 printPortFlowsLoad(cp, typedFlowLoadMap.get(cp));
166 }
167 } else {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900168 List<FlowEntryWithLoad> typedFlowLoad =
Madan Jampanic27b6b22016-02-05 11:36:31 -0800169 flowStatsService.loadTopnByType(device, ingressPortNumber, inLiveType, inInstructionType, topn);
170 // print device/port topn flows load
171 ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);
172 printPortFlowsLoad(cp, typedFlowLoad);
173 }
174 } else if (showAll) { // is true?
175 // print show all head line with type
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900176 print("deviceId=%s, show ALL flows, liveType=%s, instruction type=%s",
Madan Jampanic27b6b22016-02-05 11:36:31 -0800177 deviceUri,
178 flowLiveType == null ? "ALL" : flowLiveType,
179 instructionType == null ? "ALL" : instructionType);
180 if (ingressPortNumber == null) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900181 Map<ConnectPoint, List<FlowEntryWithLoad>> typedFlowLoadMap =
Madan Jampanic27b6b22016-02-05 11:36:31 -0800182 flowStatsService.loadAllByType(device, inLiveType, inInstructionType);
183 // print all ports all flows load for a given device
184 for (ConnectPoint cp : typedFlowLoadMap.keySet()) {
185 printPortFlowsLoad(cp, typedFlowLoadMap.get(cp));
186 }
187 } else {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900188 List<FlowEntryWithLoad> typedFlowLoad =
Madan Jampanic27b6b22016-02-05 11:36:31 -0800189 flowStatsService.loadAllByType(device, ingressPortNumber, inLiveType, inInstructionType);
190 // print device/port all flows load
191 ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);
192 printPortFlowsLoad(cp, typedFlowLoad);
193 }
194 } else { // if (showSummary == true) //always is true
195 // print show summary head line
196 print("deviceId=%s, show SUMMARY flows", deviceUri);
197 if (ingressPortNumber == null) {
198 Map<ConnectPoint, SummaryFlowEntryWithLoad> summaryFlowLoadMap =
199 flowStatsService.loadSummary(device);
200 // print all ports flow load summary for a given device
201 for (ConnectPoint cp : summaryFlowLoadMap.keySet()) {
202 printPortSummaryLoad(cp, summaryFlowLoadMap.get(cp));
203 }
204 } else {
205 SummaryFlowEntryWithLoad summaryFlowLoad =
206 flowStatsService.loadSummary(device, ingressPortNumber);
207 // print device/port flow load summary
208 ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);
209 printPortSummaryLoad(cp, summaryFlowLoad);
210 }
211 }
212 }
213
214 /**
215 * Extracts the port number portion of the ConnectPoint.
216 *
217 * @param deviceString string representing the device/port
218 * @return port number as a string, empty string if the port is not found
219 */
220 private String getPortNumber(String deviceString) {
221 if (deviceString == null) {
222 return "";
223 }
224
225 int slash = deviceString.indexOf('/');
226 if (slash <= 0) {
227 return ""; // return when no port number
228 }
229 return deviceString.substring(slash + 1, deviceString.length());
230 }
231
232 /**
233 * Extracts the device ID portion of the ConnectPoint.
234 *
235 * @param deviceString string representing the device/port
236 * @return device ID string
237 */
238 private String getDeviceId(String deviceString) {
239 if (deviceString == null) {
240 return "";
241 }
242
243 int slash = deviceString.indexOf('/');
244 if (slash <= 0) {
245 return deviceString; // return only included device ID
246 }
247 return deviceString.substring(0, slash);
248 }
249
250 /**
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900251 * converts string of flow live type to FlowLiveType enum.
Madan Jampanic27b6b22016-02-05 11:36:31 -0800252 *
253 * @param liveType string representing the flow live type
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900254 * @return FlowEntry.FlowLiveType
Madan Jampanic27b6b22016-02-05 11:36:31 -0800255 */
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900256 private FlowEntry.FlowLiveType getFlowLiveType(String liveType) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800257 String liveTypeUC = liveType.toUpperCase();
258
Jon Halla3fcf672017-03-28 16:53:22 -0700259 if ("IMMEDIATE".equals(liveTypeUC)) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900260 return FlowEntry.FlowLiveType.IMMEDIATE;
Jon Halla3fcf672017-03-28 16:53:22 -0700261 } else if ("SHORT".equals(liveTypeUC)) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900262 return FlowEntry.FlowLiveType.SHORT;
Jon Halla3fcf672017-03-28 16:53:22 -0700263 } else if ("MID".equals(liveTypeUC)) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900264 return FlowEntry.FlowLiveType.MID;
Jon Halla3fcf672017-03-28 16:53:22 -0700265 } else if ("LONG".equals(liveTypeUC)) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900266 return FlowEntry.FlowLiveType.LONG;
Jon Halla3fcf672017-03-28 16:53:22 -0700267 } else if ("UNKNOWN".equals(liveTypeUC)) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900268 return FlowEntry.FlowLiveType.UNKNOWN;
Madan Jampanic27b6b22016-02-05 11:36:31 -0800269 } else {
270 return null; // flow live type error
271 }
272 }
273
274 /**
275 * converts string of instruction type to Instruction type enum.
276 *
277 * @param instType string representing the instruction type
278 * @return Instruction.Type
279 */
280 private Instruction.Type getInstructionType(String instType) {
281 String instTypeUC = instType.toUpperCase();
282
Jon Halla3fcf672017-03-28 16:53:22 -0700283 if ("OUTPUT".equals(instTypeUC)) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800284 return Instruction.Type.OUTPUT;
Jon Halla3fcf672017-03-28 16:53:22 -0700285 } else if ("GROUP".equals(instTypeUC)) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800286 return Instruction.Type.GROUP;
Jon Halla3fcf672017-03-28 16:53:22 -0700287 } else if ("L0MODIFICATION".equals(instTypeUC)) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800288 return Instruction.Type.L0MODIFICATION;
Jon Halla3fcf672017-03-28 16:53:22 -0700289 } else if ("L2MODIFICATION".equals(instTypeUC)) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800290 return Instruction.Type.L2MODIFICATION;
Jon Halla3fcf672017-03-28 16:53:22 -0700291 } else if ("TABLE".equals(instTypeUC)) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800292 return Instruction.Type.TABLE;
Jon Halla3fcf672017-03-28 16:53:22 -0700293 } else if ("L3MODIFICATION".equals(instTypeUC)) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800294 return Instruction.Type.L3MODIFICATION;
Jon Halla3fcf672017-03-28 16:53:22 -0700295 } else if ("METADATA".equals(instTypeUC)) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800296 return Instruction.Type.METADATA;
297 } else {
298 return null; // instruction type error
299 }
300 }
301
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900302 private void printPortFlowsLoad(ConnectPoint cp, List<FlowEntryWithLoad> typedFlowLoad) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800303 print(" deviceId/Port=%s/%s, %s flows", cp.elementId(), cp.port(), typedFlowLoad.size());
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900304 for (FlowEntryWithLoad fel: typedFlowLoad) {
305 StoredFlowEntry sfe = fel.storedFlowEntry();
Madan Jampanic27b6b22016-02-05 11:36:31 -0800306 print(" flowId=%s, state=%s, liveType=%s, life=%s -> %s",
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900307 Long.toHexString(sfe.id().value()),
308 sfe.state(),
309 sfe.liveType(),
310 sfe.life(),
311 fel.load().isValid() ? fel.load() : "Load{rate=0, NOT VALID}");
Madan Jampanic27b6b22016-02-05 11:36:31 -0800312 }
313 }
314
315 private void printPortSummaryLoad(ConnectPoint cp, SummaryFlowEntryWithLoad summaryFlowLoad) {
316 print(" deviceId/Port=%s/%s, Total=%s, Immediate=%s, Short=%s, Mid=%s, Long=%s, Unknown=%s",
317 cp.elementId(),
318 cp.port(),
319 summaryFlowLoad.totalLoad().isValid() ? summaryFlowLoad.totalLoad() : "Load{rate=0, NOT VALID}",
320 summaryFlowLoad.immediateLoad().isValid() ? summaryFlowLoad.immediateLoad() : "Load{rate=0, NOT VALID}",
321 summaryFlowLoad.shortLoad().isValid() ? summaryFlowLoad.shortLoad() : "Load{rate=0, NOT VALID}",
322 summaryFlowLoad.midLoad().isValid() ? summaryFlowLoad.midLoad() : "Load{rate=0, NOT VALID}",
323 summaryFlowLoad.longLoad().isValid() ? summaryFlowLoad.longLoad() : "Load{rate=0, NOT VALID}",
324 summaryFlowLoad.unknownLoad().isValid() ? summaryFlowLoad.unknownLoad() : "Load{rate=0, NOT VALID}");
325 }
326}