blob: 587c748aafd483b1b96615483db237af089f110e [file] [log] [blame]
Andrea Campanellae4084402017-12-15 15:27:31 +01001/*
2 * Copyright 2015-present Open Networking Foundation
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
17package org.onosproject.t3.cli;
18
19import org.apache.karaf.shell.commands.Command;
20import org.apache.karaf.shell.commands.Option;
Andrea Campanellae4084402017-12-15 15:27:31 +010021import org.onlab.packet.IpAddress;
22import org.onlab.packet.MacAddress;
Andrea Campanella3970e472018-01-25 16:44:04 +010023import org.onlab.packet.MplsLabel;
Andrea Campanellae4084402017-12-15 15:27:31 +010024import org.onlab.packet.TpPort;
25import org.onlab.packet.VlanId;
26import org.onosproject.cli.AbstractShellCommand;
27import org.onosproject.net.ConnectPoint;
28import org.onosproject.net.flow.DefaultTrafficSelector;
29import org.onosproject.net.flow.TrafficSelector;
30import org.onosproject.net.flow.TrafficTreatment;
31import org.onosproject.net.group.GroupBucket;
Andrea Campanella94c594a2018-02-06 18:58:40 +010032import org.onosproject.t3.api.GroupsInDevice;
Andrea Campanellae4084402017-12-15 15:27:31 +010033import org.onosproject.t3.api.StaticPacketTrace;
34import org.onosproject.t3.api.TroubleshootService;
35
36import java.util.List;
37
Andrea Campanella7db72fc2018-01-24 15:14:03 +010038import static org.onlab.packet.EthType.EtherType;
39
Andrea Campanellae4084402017-12-15 15:27:31 +010040/**
41 * Starts a Static Packet Trace for a given input and prints the result.
42 */
43@Command(scope = "onos", name = "troubleshoot",
44 description = "troubleshoots flows and groups between source and destination")
45public class TroubleshootTraceCommand extends AbstractShellCommand {
46
47
48 private static final String FLOW_SHORT_FORMAT = " %s, bytes=%s, packets=%s, "
49 + "table=%s, priority=%s, selector=%s, treatment=%s";
50
51 private static final String GROUP_FORMAT =
52 " id=0x%s, state=%s, type=%s, bytes=%s, packets=%s, appId=%s, referenceCount=%s";
53 private static final String GROUP_BUCKET_FORMAT =
54 " id=0x%s, bucket=%s, bytes=%s, packets=%s, actions=%s";
55
56 @Option(name = "-v", aliases = "--verbose", description = "Outputs complete path")
57 private boolean verbosity1 = false;
58
59 @Option(name = "-vv", aliases = "--veryverbose", description = "Outputs flows and groups for every device")
60 private boolean verbosity2 = false;
61
62 @Option(name = "-s", aliases = "--srcIp", description = "Source IP")
63 String srcIp = null;
64
65 @Option(name = "-sp", aliases = "--srcPort", description = "Source Port", required = true)
66 String srcPort = null;
67
68 @Option(name = "-sm", aliases = "--srcMac", description = "Source MAC")
69 String srcMac = null;
70
71 @Option(name = "-et", aliases = "--ethType", description = "ETH Type", valueToShowInHelp = "ipv4")
72 String ethType = "ipv4";
73
74 @Option(name = "-stp", aliases = "--srcTcpPort", description = "Source TCP Port")
75 String srcTcpPort = null;
76
Andrea Campanella7db72fc2018-01-24 15:14:03 +010077 @Option(name = "-d", aliases = "--dstIp", description = "Destination IP")
78 String dstIp = null;
Andrea Campanellae4084402017-12-15 15:27:31 +010079
80 @Option(name = "-dm", aliases = "--dstMac", description = "Destination MAC")
81 String dstMac = null;
82
83 @Option(name = "-dtp", aliases = "dstTcpPort", description = "destination TCP Port")
84 String dstTcpPort = null;
85
86 @Option(name = "-vid", aliases = "--vlanId", description = "Vlan of incoming packet", valueToShowInHelp = "None")
87 String vlan = "None";
88
Andrea Campanella3970e472018-01-25 16:44:04 +010089 @Option(name = "-ml", aliases = "--mplsLabel", description = "Mpls label of incoming packet")
90 String mplsLabel = null;
91
Andrea Campanella8292ba62018-01-31 16:43:23 +010092 @Option(name = "-mb", aliases = "--mplsBos", description = "MPLS BOS")
Andrea Campanella3970e472018-01-25 16:44:04 +010093 String mplsBos = null;
Andrea Campanellae4084402017-12-15 15:27:31 +010094
Andrea Campanella8292ba62018-01-31 16:43:23 +010095 @Option(name = "-ipp", aliases = "--ipProto", description = "IP Proto")
96 String ipProto = null;
97
98 @Option(name = "-udps", aliases = "--udpSrc", description = "UDP Source")
99 String udpSrc = null;
100
101 @Option(name = "-udpd", aliases = "--udpDst", description = "UDP Destination")
102 String udpDst = null;
103
Andrea Campanellae4084402017-12-15 15:27:31 +0100104 @Override
105 protected void execute() {
106 TroubleshootService service = get(TroubleshootService.class);
107 ConnectPoint cp = ConnectPoint.deviceConnectPoint(srcPort);
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100108 EtherType type = EtherType.valueOf(ethType.toUpperCase());
Andrea Campanellae4084402017-12-15 15:27:31 +0100109
110 //Input Port must be specified
111 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
112 .matchInPort(cp.port());
113
114 if (srcIp != null) {
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100115 if (type.equals(EtherType.IPV6)) {
116 selectorBuilder.matchIPv6Src(IpAddress.valueOf(srcIp).toIpPrefix());
117 } else {
118 selectorBuilder.matchIPSrc(IpAddress.valueOf(srcIp).toIpPrefix());
119 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100120 }
121
122 if (srcMac != null) {
123 selectorBuilder.matchEthSrc(MacAddress.valueOf(srcMac));
124 }
125
126 //if EthType option is not specified using IPv4
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100127 selectorBuilder.matchEthType(type.ethType().toShort());
Andrea Campanellae4084402017-12-15 15:27:31 +0100128
129 if (srcTcpPort != null) {
130 selectorBuilder.matchTcpSrc(TpPort.tpPort(Integer.parseInt(srcTcpPort)));
131 }
132
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100133 if (dstIp != null) {
134 if (type.equals(EtherType.IPV6)) {
135 selectorBuilder.matchIPv6Dst(IpAddress.valueOf(dstIp).toIpPrefix());
136 } else {
137 selectorBuilder.matchIPDst(IpAddress.valueOf(dstIp).toIpPrefix());
138 }
139 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100140
141 if (dstMac != null) {
142 selectorBuilder.matchEthDst(MacAddress.valueOf(dstMac));
143 }
144 if (dstTcpPort != null) {
145 selectorBuilder.matchTcpDst(TpPort.tpPort(Integer.parseInt(dstTcpPort)));
146 }
147
148 //if vlan option is not specified using NONE
149 selectorBuilder.matchVlanId(VlanId.vlanId(vlan));
150
Andrea Campanella3970e472018-01-25 16:44:04 +0100151 if (mplsLabel != null) {
152 selectorBuilder.matchMplsLabel(MplsLabel.mplsLabel(Integer.parseInt(mplsLabel)));
153 }
154
155 if (mplsBos != null) {
156 selectorBuilder.matchMplsBos(Boolean.valueOf(mplsBos));
157 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100158
Andrea Campanella8292ba62018-01-31 16:43:23 +0100159 if (ipProto != null) {
160 selectorBuilder.matchIPProtocol(Byte.valueOf(ipProto));
161 }
162
163 if (udpSrc != null) {
164 selectorBuilder.matchUdpSrc(TpPort.tpPort(Integer.parseInt(udpSrc)));
165 }
166
167 if (udpDst != null) {
168 selectorBuilder.matchUdpDst(TpPort.tpPort(Integer.parseInt(udpDst)));
169 }
170
171
Andrea Campanellae4084402017-12-15 15:27:31 +0100172 TrafficSelector packet = selectorBuilder.build();
173
174 //Printing the created packet
175 print("Tracing packet: %s", packet.criteria());
176
177 //Build the trace
178 StaticPacketTrace trace = service.trace(packet, cp);
179
180 //Print based on verbosity
181 if (verbosity1) {
182 printTrace(trace, false);
183 } else if (verbosity2) {
184 printTrace(trace, true);
185 } else {
186 print("Paths");
187 List<List<ConnectPoint>> paths = trace.getCompletePaths();
188 paths.forEach(path -> print("%s", path));
189 }
190 print("Result: \n%s", trace.resultMessage());
191 }
192
193 //prints the trace
194 private void printTrace(StaticPacketTrace trace, boolean verbose) {
195 List<List<ConnectPoint>> paths = trace.getCompletePaths();
196 paths.forEach(path -> {
197 print("Path %s", path);
198 ConnectPoint previous = null;
199 for (ConnectPoint connectPoint : path) {
200 if (previous == null || !previous.deviceId().equals(connectPoint.deviceId())) {
201 print("Device %s", connectPoint.deviceId());
202 print("Input from %s", connectPoint);
203 printFlows(trace, verbose, connectPoint);
204 } else {
205 printGroups(trace, verbose, connectPoint);
206 print("Output through %s", connectPoint);
207 print("");
208 }
209 previous = connectPoint;
210 }
Andrea Campanella09eec852018-02-05 19:39:25 +0100211 print("---------------------------------------------------------------\n");
Andrea Campanellae4084402017-12-15 15:27:31 +0100212 });
213 }
214
215 //Prints the flows for a given trace and a specified level of verbosity
216 private void printFlows(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint) {
217 print("Flows");
218 trace.getFlowsForDevice(connectPoint.deviceId()).forEach(f -> {
219 if (verbose) {
220 print(FLOW_SHORT_FORMAT, f.state(), f.bytes(), f.packets(),
221 f.table(), f.priority(), f.selector().criteria(),
222 printTreatment(f.treatment()));
223 } else {
Andrea Campanella09eec852018-02-05 19:39:25 +0100224 print(" flowId=%s, table=%s, selector=%s", f.id(), f.table(), f.selector().criteria());
Andrea Campanellae4084402017-12-15 15:27:31 +0100225 }
226 });
227 }
228
229 //Prints the groups for a given trace and a specified level of verbosity
230 private void printGroups(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint) {
Andrea Campanella94c594a2018-02-06 18:58:40 +0100231 List<GroupsInDevice> groupsInDevice = trace.getGroupOuputs(connectPoint.deviceId());
232 if (groupsInDevice != null) {
233 print("Groups");
234 groupsInDevice.forEach(output -> {
235 if (output.getOutput().equals(connectPoint)) {
236 output.getGroups().forEach(group -> {
237 if (verbose) {
238 print(GROUP_FORMAT, Integer.toHexString(group.id().id()), group.state(), group.type(),
239 group.bytes(), group.packets(), group.appId().name(), group.referenceCount());
240 int i = 0;
241 for (GroupBucket bucket : group.buckets().buckets()) {
242 print(GROUP_BUCKET_FORMAT, Integer.toHexString(group.id().id()), ++i,
243 bucket.bytes(), bucket.packets(),
244 bucket.treatment().allInstructions());
245 }
246 } else {
247 print(" groupId=%s", group.id());
Andrea Campanellae4084402017-12-15 15:27:31 +0100248 }
Andrea Campanella94c594a2018-02-06 18:58:40 +0100249 });
250 print("Outgoing Packet %s", output.getFinalPacket());
251 }
252 });
253 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100254 }
255
256 private String printTreatment(TrafficTreatment treatment) {
257 final String delimiter = ", ";
258 StringBuilder builder = new StringBuilder("[");
259 if (!treatment.immediate().isEmpty()) {
260 builder.append("immediate=" + treatment.immediate() + delimiter);
261 }
262 if (!treatment.deferred().isEmpty()) {
263 builder.append("deferred=" + treatment.deferred() + delimiter);
264 }
265 if (treatment.clearedDeferred()) {
266 builder.append("clearDeferred" + delimiter);
267 }
268 if (treatment.tableTransition() != null) {
269 builder.append("transition=" + treatment.tableTransition() + delimiter);
270 }
271 if (treatment.metered() != null) {
272 builder.append("meter=" + treatment.metered() + delimiter);
273 }
274 if (treatment.writeMetadata() != null) {
275 builder.append("metadata=" + treatment.writeMetadata() + delimiter);
276 }
277 // Chop off last delimiter
278 builder.replace(builder.length() - delimiter.length(), builder.length(), "");
279 builder.append("]");
280 return builder.toString();
281 }
282}