blob: ae68d93b9e455b21f39ae585c0a582cad30c8e76 [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
Andrea Campanella1445f7a2018-02-07 12:00:12 +010019import com.google.common.base.Preconditions;
Andrea Campanellae4084402017-12-15 15:27:31 +010020import org.apache.karaf.shell.commands.Command;
21import org.apache.karaf.shell.commands.Option;
Andrea Campanellae4084402017-12-15 15:27:31 +010022import org.onlab.packet.IpAddress;
23import org.onlab.packet.MacAddress;
Andrea Campanella3970e472018-01-25 16:44:04 +010024import org.onlab.packet.MplsLabel;
Andrea Campanellae4084402017-12-15 15:27:31 +010025import org.onlab.packet.TpPort;
26import org.onlab.packet.VlanId;
27import org.onosproject.cli.AbstractShellCommand;
28import org.onosproject.net.ConnectPoint;
Andrea Campanella1445f7a2018-02-07 12:00:12 +010029import org.onosproject.net.DeviceId;
30import org.onosproject.net.PortNumber;
Andrea Campanellae4084402017-12-15 15:27:31 +010031import org.onosproject.net.flow.DefaultTrafficSelector;
32import org.onosproject.net.flow.TrafficSelector;
33import org.onosproject.net.flow.TrafficTreatment;
34import org.onosproject.net.group.GroupBucket;
Andrea Campanella94c594a2018-02-06 18:58:40 +010035import org.onosproject.t3.api.GroupsInDevice;
Andrea Campanellae4084402017-12-15 15:27:31 +010036import org.onosproject.t3.api.StaticPacketTrace;
37import org.onosproject.t3.api.TroubleshootService;
38
39import java.util.List;
40
Andrea Campanella7db72fc2018-01-24 15:14:03 +010041import static org.onlab.packet.EthType.EtherType;
42
Andrea Campanellae4084402017-12-15 15:27:31 +010043/**
44 * Starts a Static Packet Trace for a given input and prints the result.
45 */
46@Command(scope = "onos", name = "troubleshoot",
47 description = "troubleshoots flows and groups between source and destination")
48public class TroubleshootTraceCommand extends AbstractShellCommand {
49
50
51 private static final String FLOW_SHORT_FORMAT = " %s, bytes=%s, packets=%s, "
52 + "table=%s, priority=%s, selector=%s, treatment=%s";
53
54 private static final String GROUP_FORMAT =
55 " id=0x%s, state=%s, type=%s, bytes=%s, packets=%s, appId=%s, referenceCount=%s";
56 private static final String GROUP_BUCKET_FORMAT =
57 " id=0x%s, bucket=%s, bytes=%s, packets=%s, actions=%s";
58
Andrea Campanella1445f7a2018-02-07 12:00:12 +010059 private static final String CONTROLLER = "CONTROLLER";
60
Andrea Campanellae4084402017-12-15 15:27:31 +010061 @Option(name = "-v", aliases = "--verbose", description = "Outputs complete path")
62 private boolean verbosity1 = false;
63
64 @Option(name = "-vv", aliases = "--veryverbose", description = "Outputs flows and groups for every device")
65 private boolean verbosity2 = false;
66
67 @Option(name = "-s", aliases = "--srcIp", description = "Source IP")
68 String srcIp = null;
69
70 @Option(name = "-sp", aliases = "--srcPort", description = "Source Port", required = true)
71 String srcPort = null;
72
73 @Option(name = "-sm", aliases = "--srcMac", description = "Source MAC")
74 String srcMac = null;
75
76 @Option(name = "-et", aliases = "--ethType", description = "ETH Type", valueToShowInHelp = "ipv4")
77 String ethType = "ipv4";
78
79 @Option(name = "-stp", aliases = "--srcTcpPort", description = "Source TCP Port")
80 String srcTcpPort = null;
81
Andrea Campanella7db72fc2018-01-24 15:14:03 +010082 @Option(name = "-d", aliases = "--dstIp", description = "Destination IP")
83 String dstIp = null;
Andrea Campanellae4084402017-12-15 15:27:31 +010084
85 @Option(name = "-dm", aliases = "--dstMac", description = "Destination MAC")
86 String dstMac = null;
87
Andrea Campanella1445f7a2018-02-07 12:00:12 +010088 @Option(name = "-dtp", aliases = "--dstTcpPort", description = "destination TCP Port")
Andrea Campanellae4084402017-12-15 15:27:31 +010089 String dstTcpPort = null;
90
91 @Option(name = "-vid", aliases = "--vlanId", description = "Vlan of incoming packet", valueToShowInHelp = "None")
92 String vlan = "None";
93
Andrea Campanella3970e472018-01-25 16:44:04 +010094 @Option(name = "-ml", aliases = "--mplsLabel", description = "Mpls label of incoming packet")
95 String mplsLabel = null;
96
Andrea Campanella8292ba62018-01-31 16:43:23 +010097 @Option(name = "-mb", aliases = "--mplsBos", description = "MPLS BOS")
Andrea Campanella3970e472018-01-25 16:44:04 +010098 String mplsBos = null;
Andrea Campanellae4084402017-12-15 15:27:31 +010099
Andrea Campanella8292ba62018-01-31 16:43:23 +0100100 @Option(name = "-ipp", aliases = "--ipProto", description = "IP Proto")
101 String ipProto = null;
102
103 @Option(name = "-udps", aliases = "--udpSrc", description = "UDP Source")
104 String udpSrc = null;
105
106 @Option(name = "-udpd", aliases = "--udpDst", description = "UDP Destination")
107 String udpDst = null;
108
Andrea Campanellae4084402017-12-15 15:27:31 +0100109 @Override
110 protected void execute() {
111 TroubleshootService service = get(TroubleshootService.class);
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100112 String[] cpInfo = srcPort.split("/");
113 Preconditions.checkArgument(cpInfo.length == 2, "wrong format of source port");
114 ConnectPoint cp;
115 //Uses input port as a convenience to carry the Controller port, proper flood behaviour is handled in the
116 // troubleshoot manager.
117 if (cpInfo[1].equalsIgnoreCase(CONTROLLER)) {
118 cp = new ConnectPoint(DeviceId.deviceId(cpInfo[0]), PortNumber.CONTROLLER);
119 } else {
120 cp = ConnectPoint.deviceConnectPoint(srcPort);
121 }
122
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100123 EtherType type = EtherType.valueOf(ethType.toUpperCase());
Andrea Campanellae4084402017-12-15 15:27:31 +0100124
125 //Input Port must be specified
126 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
127 .matchInPort(cp.port());
128
129 if (srcIp != null) {
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100130 if (type.equals(EtherType.IPV6)) {
131 selectorBuilder.matchIPv6Src(IpAddress.valueOf(srcIp).toIpPrefix());
132 } else {
133 selectorBuilder.matchIPSrc(IpAddress.valueOf(srcIp).toIpPrefix());
134 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100135 }
136
137 if (srcMac != null) {
138 selectorBuilder.matchEthSrc(MacAddress.valueOf(srcMac));
139 }
140
141 //if EthType option is not specified using IPv4
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100142 selectorBuilder.matchEthType(type.ethType().toShort());
Andrea Campanellae4084402017-12-15 15:27:31 +0100143
144 if (srcTcpPort != null) {
145 selectorBuilder.matchTcpSrc(TpPort.tpPort(Integer.parseInt(srcTcpPort)));
146 }
147
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100148 if (dstIp != null) {
149 if (type.equals(EtherType.IPV6)) {
150 selectorBuilder.matchIPv6Dst(IpAddress.valueOf(dstIp).toIpPrefix());
151 } else {
152 selectorBuilder.matchIPDst(IpAddress.valueOf(dstIp).toIpPrefix());
153 }
154 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100155
156 if (dstMac != null) {
157 selectorBuilder.matchEthDst(MacAddress.valueOf(dstMac));
158 }
159 if (dstTcpPort != null) {
160 selectorBuilder.matchTcpDst(TpPort.tpPort(Integer.parseInt(dstTcpPort)));
161 }
162
163 //if vlan option is not specified using NONE
164 selectorBuilder.matchVlanId(VlanId.vlanId(vlan));
165
Andrea Campanella3970e472018-01-25 16:44:04 +0100166 if (mplsLabel != null) {
167 selectorBuilder.matchMplsLabel(MplsLabel.mplsLabel(Integer.parseInt(mplsLabel)));
168 }
169
170 if (mplsBos != null) {
171 selectorBuilder.matchMplsBos(Boolean.valueOf(mplsBos));
172 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100173
Andrea Campanella8292ba62018-01-31 16:43:23 +0100174 if (ipProto != null) {
175 selectorBuilder.matchIPProtocol(Byte.valueOf(ipProto));
176 }
177
178 if (udpSrc != null) {
179 selectorBuilder.matchUdpSrc(TpPort.tpPort(Integer.parseInt(udpSrc)));
180 }
181
182 if (udpDst != null) {
183 selectorBuilder.matchUdpDst(TpPort.tpPort(Integer.parseInt(udpDst)));
184 }
185
186
Andrea Campanellae4084402017-12-15 15:27:31 +0100187 TrafficSelector packet = selectorBuilder.build();
188
189 //Printing the created packet
190 print("Tracing packet: %s", packet.criteria());
191
192 //Build the trace
193 StaticPacketTrace trace = service.trace(packet, cp);
Andrea Campanellae4084402017-12-15 15:27:31 +0100194 //Print based on verbosity
195 if (verbosity1) {
196 printTrace(trace, false);
197 } else if (verbosity2) {
198 printTrace(trace, true);
199 } else {
200 print("Paths");
201 List<List<ConnectPoint>> paths = trace.getCompletePaths();
202 paths.forEach(path -> print("%s", path));
203 }
204 print("Result: \n%s", trace.resultMessage());
205 }
206
207 //prints the trace
208 private void printTrace(StaticPacketTrace trace, boolean verbose) {
209 List<List<ConnectPoint>> paths = trace.getCompletePaths();
210 paths.forEach(path -> {
211 print("Path %s", path);
212 ConnectPoint previous = null;
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100213 if (path.size() == 1) {
214 ConnectPoint connectPoint = path.get(0);
215 print("Device %s", connectPoint.deviceId());
216 print("Input from %s", connectPoint);
217 printFlows(trace, verbose, connectPoint);
218 printGroups(trace, verbose, connectPoint);
219 print("Output through %s", connectPoint);
220 print("");
221 } else {
222 for (ConnectPoint connectPoint : path) {
223 if (previous == null || !previous.deviceId().equals(connectPoint.deviceId())) {
224 print("Device %s", connectPoint.deviceId());
225 print("Input from %s", connectPoint);
226 printFlows(trace, verbose, connectPoint);
227 } else {
228 printGroups(trace, verbose, connectPoint);
229 print("Output through %s", connectPoint);
230 print("");
231 }
232 previous = connectPoint;
Andrea Campanellae4084402017-12-15 15:27:31 +0100233 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100234 }
Andrea Campanella09eec852018-02-05 19:39:25 +0100235 print("---------------------------------------------------------------\n");
Andrea Campanellae4084402017-12-15 15:27:31 +0100236 });
237 }
238
239 //Prints the flows for a given trace and a specified level of verbosity
240 private void printFlows(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint) {
241 print("Flows");
242 trace.getFlowsForDevice(connectPoint.deviceId()).forEach(f -> {
243 if (verbose) {
244 print(FLOW_SHORT_FORMAT, f.state(), f.bytes(), f.packets(),
245 f.table(), f.priority(), f.selector().criteria(),
246 printTreatment(f.treatment()));
247 } else {
Andrea Campanella09eec852018-02-05 19:39:25 +0100248 print(" flowId=%s, table=%s, selector=%s", f.id(), f.table(), f.selector().criteria());
Andrea Campanellae4084402017-12-15 15:27:31 +0100249 }
250 });
251 }
252
253 //Prints the groups for a given trace and a specified level of verbosity
254 private void printGroups(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint) {
Andrea Campanella94c594a2018-02-06 18:58:40 +0100255 List<GroupsInDevice> groupsInDevice = trace.getGroupOuputs(connectPoint.deviceId());
256 if (groupsInDevice != null) {
257 print("Groups");
258 groupsInDevice.forEach(output -> {
259 if (output.getOutput().equals(connectPoint)) {
260 output.getGroups().forEach(group -> {
261 if (verbose) {
262 print(GROUP_FORMAT, Integer.toHexString(group.id().id()), group.state(), group.type(),
263 group.bytes(), group.packets(), group.appId().name(), group.referenceCount());
264 int i = 0;
265 for (GroupBucket bucket : group.buckets().buckets()) {
266 print(GROUP_BUCKET_FORMAT, Integer.toHexString(group.id().id()), ++i,
267 bucket.bytes(), bucket.packets(),
268 bucket.treatment().allInstructions());
269 }
270 } else {
271 print(" groupId=%s", group.id());
Andrea Campanellae4084402017-12-15 15:27:31 +0100272 }
Andrea Campanella94c594a2018-02-06 18:58:40 +0100273 });
274 print("Outgoing Packet %s", output.getFinalPacket());
275 }
276 });
277 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100278 }
279
280 private String printTreatment(TrafficTreatment treatment) {
281 final String delimiter = ", ";
282 StringBuilder builder = new StringBuilder("[");
283 if (!treatment.immediate().isEmpty()) {
284 builder.append("immediate=" + treatment.immediate() + delimiter);
285 }
286 if (!treatment.deferred().isEmpty()) {
287 builder.append("deferred=" + treatment.deferred() + delimiter);
288 }
289 if (treatment.clearedDeferred()) {
290 builder.append("clearDeferred" + delimiter);
291 }
292 if (treatment.tableTransition() != null) {
293 builder.append("transition=" + treatment.tableTransition() + delimiter);
294 }
295 if (treatment.metered() != null) {
296 builder.append("meter=" + treatment.metered() + delimiter);
297 }
298 if (treatment.writeMetadata() != null) {
299 builder.append("metadata=" + treatment.writeMetadata() + delimiter);
300 }
301 // Chop off last delimiter
302 builder.replace(builder.length() - delimiter.length(), builder.length(), "");
303 builder.append("]");
304 return builder.toString();
305 }
306}