blob: 6d9ff674a974e2a76d38a2179aaefe014f946def [file] [log] [blame]
Madan Jampanic27b6b22016-02-05 11:36:31 -08001/*
Brian O'Connor0a4e6742016-09-15 23:03:10 -07002 * Copyright 2016-present Open Networking Laboratory
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
19import org.apache.karaf.shell.commands.Argument;
20import org.apache.karaf.shell.commands.Command;
21import org.apache.karaf.shell.commands.Option;
22import org.onosproject.cli.AbstractShellCommand;
23import org.onosproject.net.ConnectPoint;
24import org.onosproject.net.Device;
25import org.onosproject.net.DeviceId;
26import org.onosproject.net.Port;
27import org.onosproject.net.PortNumber;
28import org.onosproject.net.device.DeviceService;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090029import org.onosproject.net.flow.FlowEntry;
30import org.onosproject.net.flow.StoredFlowEntry;
Madan Jampanic27b6b22016-02-05 11:36:31 -080031import org.onosproject.net.flow.instructions.Instruction;
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090032import org.onosproject.net.statistic.FlowEntryWithLoad;
Madan Jampanic27b6b22016-02-05 11:36:31 -080033import org.onosproject.net.statistic.FlowStatisticService;
34import org.onosproject.net.statistic.SummaryFlowEntryWithLoad;
Madan Jampanic27b6b22016-02-05 11:36:31 -080035
36import java.util.List;
37import java.util.Map;
38
39import static org.onosproject.net.DeviceId.deviceId;
40import static org.onosproject.net.PortNumber.portNumber;
41
42/**
43 * Fetches flow statistics with a flow type and instruction type.
44 */
45@Command(scope = "onos", name = "get-flow-stats",
46 description = "Fetches flow stats for a connection point with given flow type and instruction type")
sangyun-han483731c2016-06-06 12:03:16 +090047public class GetFlowStatisticsCommand extends AbstractShellCommand {
Madan Jampanic27b6b22016-02-05 11:36:31 -080048 @Argument(index = 0, name = "devicePort",
49 description = "Device[/Port] connectPoint Description",
50 required = true, multiValued = false)
51 String devicePort = null;
52
53 @Option(name = "-s", aliases = "--summary",
54 description = "Show flow stats summary",
55 required = false, multiValued = false)
56 boolean showSummary = true; // default summary
57
58 @Option(name = "-a", aliases = "--all",
59 description = "Show flow stats all",
60 required = false, multiValued = false)
61 boolean showAll = false;
62
63 @Option(name = "-t", aliases = "--topn",
Sangsik Yoonb1b823f2016-05-16 18:55:39 +090064 description = "Show flow stats topn entry",
Madan Jampanic27b6b22016-02-05 11:36:31 -080065 required = false, multiValued = false)
66 String showTopn = null;
67
68 @Option(name = "-f", aliases = "--flowType",
69 description = "Flow live type, It includes IMMEDIATE, SHORT, MID, LONG, UNKNOWN"
70 + ", and is valid with -a or -t option only",
71 required = false, multiValued = false)
72 String flowLiveType = null;
73
74 @Option(name = "-i", aliases = "--instructionType",
75 description = "Flow instruction type, It includes DROP, OUTPUT, GROUP, L0MODIFICATION, L2MODIFICATION,"
76 + " TABLE, L3MODIFICATION, METADATA"
77 + ", and is valid with -a or -t option only",
78 required = false, multiValued = false)
79 String instructionType = null;
80
81 @Override
82 protected void execute() {
83 DeviceService deviceService = get(DeviceService.class);
84 FlowStatisticService flowStatsService = get(FlowStatisticService.class);
85
86 String deviceUri = getDeviceId(devicePort);
87 String portUri = getPortNumber(devicePort);
88
89 DeviceId ingressDeviceId = deviceId(deviceUri);
90 PortNumber ingressPortNumber;
91 if (portUri.length() == 0) {
92 ingressPortNumber = null;
93 } else {
94 ingressPortNumber = portNumber(portUri);
95 }
96
97 Device device = deviceService.getDevice(ingressDeviceId);
98 if (device == null) {
99 error("No such device %s", ingressDeviceId.uri());
100 return;
101 }
102
103 if (ingressPortNumber != null) {
104 Port port = deviceService.getPort(ingressDeviceId, ingressPortNumber);
105 if (port == null) {
106 error("No such port %s on device %s", portUri, ingressDeviceId.uri());
107 return;
108 }
109 }
110
111 if (flowLiveType != null) {
112 flowLiveType = flowLiveType.toUpperCase();
113 }
114 if (instructionType != null) {
115 instructionType = instructionType.toUpperCase();
116 }
117
118 // convert String to FlowLiveType and check validity
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900119 FlowEntry.FlowLiveType inLiveType;
Madan Jampanic27b6b22016-02-05 11:36:31 -0800120 if (flowLiveType == null) {
121 inLiveType = null;
122 } else {
123 inLiveType = getFlowLiveType(flowLiveType);
124 if (inLiveType == null) {
125 error("Invalid flow live type [%s] error", flowLiveType);
126 return;
127 }
128 }
129 // convert String to InstructionType and check validity
130 Instruction.Type inInstructionType;
131 if (instructionType == null) {
132 inInstructionType = null;
133 } else {
134 inInstructionType = getInstructionType(instructionType);
135 if (inInstructionType == null) {
136 error("Invalid instruction type [%s] error", instructionType);
137 return;
138 }
139 }
140
141 if (showTopn != null) {
142 int topn = Integer.parseInt(showTopn);
143
144 if (topn <= 0) {
145 topn = 100; //default value
146 } else if (topn > 1000) {
147 topn = 1000; //max value
148 }
149
150 // print show topn head line with type
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900151 print("deviceId=%s, show TOPN=%s flows, liveType=%s, instruction type=%s",
Madan Jampanic27b6b22016-02-05 11:36:31 -0800152 deviceUri,
153 Integer.toString(topn),
154 flowLiveType == null ? "ALL" : flowLiveType,
155 instructionType == null ? "ALL" : instructionType);
156 if (ingressPortNumber == null) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900157 Map<ConnectPoint, List<FlowEntryWithLoad>> typedFlowLoadMap =
Madan Jampanic27b6b22016-02-05 11:36:31 -0800158 flowStatsService.loadTopnByType(device, inLiveType, inInstructionType, topn);
159 // print all ports topn flows load for a given device
160 for (ConnectPoint cp : typedFlowLoadMap.keySet()) {
161 printPortFlowsLoad(cp, typedFlowLoadMap.get(cp));
162 }
163 } else {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900164 List<FlowEntryWithLoad> typedFlowLoad =
Madan Jampanic27b6b22016-02-05 11:36:31 -0800165 flowStatsService.loadTopnByType(device, ingressPortNumber, inLiveType, inInstructionType, topn);
166 // print device/port topn flows load
167 ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);
168 printPortFlowsLoad(cp, typedFlowLoad);
169 }
170 } else if (showAll) { // is true?
171 // print show all head line with type
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900172 print("deviceId=%s, show ALL flows, liveType=%s, instruction type=%s",
Madan Jampanic27b6b22016-02-05 11:36:31 -0800173 deviceUri,
174 flowLiveType == null ? "ALL" : flowLiveType,
175 instructionType == null ? "ALL" : instructionType);
176 if (ingressPortNumber == null) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900177 Map<ConnectPoint, List<FlowEntryWithLoad>> typedFlowLoadMap =
Madan Jampanic27b6b22016-02-05 11:36:31 -0800178 flowStatsService.loadAllByType(device, inLiveType, inInstructionType);
179 // print all ports all flows load for a given device
180 for (ConnectPoint cp : typedFlowLoadMap.keySet()) {
181 printPortFlowsLoad(cp, typedFlowLoadMap.get(cp));
182 }
183 } else {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900184 List<FlowEntryWithLoad> typedFlowLoad =
Madan Jampanic27b6b22016-02-05 11:36:31 -0800185 flowStatsService.loadAllByType(device, ingressPortNumber, inLiveType, inInstructionType);
186 // print device/port all flows load
187 ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);
188 printPortFlowsLoad(cp, typedFlowLoad);
189 }
190 } else { // if (showSummary == true) //always is true
191 // print show summary head line
192 print("deviceId=%s, show SUMMARY flows", deviceUri);
193 if (ingressPortNumber == null) {
194 Map<ConnectPoint, SummaryFlowEntryWithLoad> summaryFlowLoadMap =
195 flowStatsService.loadSummary(device);
196 // print all ports flow load summary for a given device
197 for (ConnectPoint cp : summaryFlowLoadMap.keySet()) {
198 printPortSummaryLoad(cp, summaryFlowLoadMap.get(cp));
199 }
200 } else {
201 SummaryFlowEntryWithLoad summaryFlowLoad =
202 flowStatsService.loadSummary(device, ingressPortNumber);
203 // print device/port flow load summary
204 ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);
205 printPortSummaryLoad(cp, summaryFlowLoad);
206 }
207 }
208 }
209
210 /**
211 * Extracts the port number portion of the ConnectPoint.
212 *
213 * @param deviceString string representing the device/port
214 * @return port number as a string, empty string if the port is not found
215 */
216 private String getPortNumber(String deviceString) {
217 if (deviceString == null) {
218 return "";
219 }
220
221 int slash = deviceString.indexOf('/');
222 if (slash <= 0) {
223 return ""; // return when no port number
224 }
225 return deviceString.substring(slash + 1, deviceString.length());
226 }
227
228 /**
229 * Extracts the device ID portion of the ConnectPoint.
230 *
231 * @param deviceString string representing the device/port
232 * @return device ID string
233 */
234 private String getDeviceId(String deviceString) {
235 if (deviceString == null) {
236 return "";
237 }
238
239 int slash = deviceString.indexOf('/');
240 if (slash <= 0) {
241 return deviceString; // return only included device ID
242 }
243 return deviceString.substring(0, slash);
244 }
245
246 /**
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900247 * converts string of flow live type to FlowLiveType enum.
Madan Jampanic27b6b22016-02-05 11:36:31 -0800248 *
249 * @param liveType string representing the flow live type
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900250 * @return FlowEntry.FlowLiveType
Madan Jampanic27b6b22016-02-05 11:36:31 -0800251 */
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900252 private FlowEntry.FlowLiveType getFlowLiveType(String liveType) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800253 String liveTypeUC = liveType.toUpperCase();
254
255 if (liveTypeUC.equals("IMMEDIATE")) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900256 return FlowEntry.FlowLiveType.IMMEDIATE;
Madan Jampanic27b6b22016-02-05 11:36:31 -0800257 } else if (liveTypeUC.equals("SHORT")) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900258 return FlowEntry.FlowLiveType.SHORT;
Madan Jampanic27b6b22016-02-05 11:36:31 -0800259 } else if (liveTypeUC.equals("MID")) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900260 return FlowEntry.FlowLiveType.MID;
Madan Jampanic27b6b22016-02-05 11:36:31 -0800261 } else if (liveTypeUC.equals("LONG")) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900262 return FlowEntry.FlowLiveType.LONG;
Madan Jampanic27b6b22016-02-05 11:36:31 -0800263 } else if (liveTypeUC.equals("UNKNOWN")) {
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900264 return FlowEntry.FlowLiveType.UNKNOWN;
Madan Jampanic27b6b22016-02-05 11:36:31 -0800265 } else {
266 return null; // flow live type error
267 }
268 }
269
270 /**
271 * converts string of instruction type to Instruction type enum.
272 *
273 * @param instType string representing the instruction type
274 * @return Instruction.Type
275 */
276 private Instruction.Type getInstructionType(String instType) {
277 String instTypeUC = instType.toUpperCase();
278
Sho SHIMIZU57f2efd2016-02-24 12:20:05 -0800279 if (instTypeUC.equals("OUTPUT")) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800280 return Instruction.Type.OUTPUT;
281 } else if (instTypeUC.equals("GROUP")) {
282 return Instruction.Type.GROUP;
283 } else if (instTypeUC.equals("L0MODIFICATION")) {
284 return Instruction.Type.L0MODIFICATION;
285 } else if (instTypeUC.equals("L2MODIFICATION")) {
286 return Instruction.Type.L2MODIFICATION;
287 } else if (instTypeUC.equals("TABLE")) {
288 return Instruction.Type.TABLE;
289 } else if (instTypeUC.equals("L3MODIFICATION")) {
290 return Instruction.Type.L3MODIFICATION;
291 } else if (instTypeUC.equals("METADATA")) {
292 return Instruction.Type.METADATA;
293 } else {
294 return null; // instruction type error
295 }
296 }
297
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900298 private void printPortFlowsLoad(ConnectPoint cp, List<FlowEntryWithLoad> typedFlowLoad) {
Madan Jampanic27b6b22016-02-05 11:36:31 -0800299 print(" deviceId/Port=%s/%s, %s flows", cp.elementId(), cp.port(), typedFlowLoad.size());
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900300 for (FlowEntryWithLoad fel: typedFlowLoad) {
301 StoredFlowEntry sfe = fel.storedFlowEntry();
Madan Jampanic27b6b22016-02-05 11:36:31 -0800302 print(" flowId=%s, state=%s, liveType=%s, life=%s -> %s",
Sangsik Yoonb1b823f2016-05-16 18:55:39 +0900303 Long.toHexString(sfe.id().value()),
304 sfe.state(),
305 sfe.liveType(),
306 sfe.life(),
307 fel.load().isValid() ? fel.load() : "Load{rate=0, NOT VALID}");
Madan Jampanic27b6b22016-02-05 11:36:31 -0800308 }
309 }
310
311 private void printPortSummaryLoad(ConnectPoint cp, SummaryFlowEntryWithLoad summaryFlowLoad) {
312 print(" deviceId/Port=%s/%s, Total=%s, Immediate=%s, Short=%s, Mid=%s, Long=%s, Unknown=%s",
313 cp.elementId(),
314 cp.port(),
315 summaryFlowLoad.totalLoad().isValid() ? summaryFlowLoad.totalLoad() : "Load{rate=0, NOT VALID}",
316 summaryFlowLoad.immediateLoad().isValid() ? summaryFlowLoad.immediateLoad() : "Load{rate=0, NOT VALID}",
317 summaryFlowLoad.shortLoad().isValid() ? summaryFlowLoad.shortLoad() : "Load{rate=0, NOT VALID}",
318 summaryFlowLoad.midLoad().isValid() ? summaryFlowLoad.midLoad() : "Load{rate=0, NOT VALID}",
319 summaryFlowLoad.longLoad().isValid() ? summaryFlowLoad.longLoad() : "Load{rate=0, NOT VALID}",
320 summaryFlowLoad.unknownLoad().isValid() ? summaryFlowLoad.unknownLoad() : "Load{rate=0, NOT VALID}");
321 }
322}