blob: fdeb944309cf4a173c2b2d440689aeae24b4f4ad [file] [log] [blame]
Andrea Campanella01e886e2017-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 Campanella01e886e2017-12-15 15:27:31 +010021import org.onlab.packet.IpAddress;
22import org.onlab.packet.MacAddress;
23import org.onlab.packet.TpPort;
24import org.onlab.packet.VlanId;
25import org.onosproject.cli.AbstractShellCommand;
26import org.onosproject.net.ConnectPoint;
27import org.onosproject.net.flow.DefaultTrafficSelector;
28import org.onosproject.net.flow.TrafficSelector;
29import org.onosproject.net.flow.TrafficTreatment;
30import org.onosproject.net.group.GroupBucket;
31import org.onosproject.t3.api.StaticPacketTrace;
32import org.onosproject.t3.api.TroubleshootService;
33
34import java.util.List;
35
Andrea Campanella8be1af92018-01-24 15:14:03 +010036import static org.onlab.packet.EthType.EtherType;
37
Andrea Campanella01e886e2017-12-15 15:27:31 +010038/**
39 * Starts a Static Packet Trace for a given input and prints the result.
40 */
41@Command(scope = "onos", name = "troubleshoot",
42 description = "troubleshoots flows and groups between source and destination")
43public class TroubleshootTraceCommand extends AbstractShellCommand {
44
45
46 private static final String FLOW_SHORT_FORMAT = " %s, bytes=%s, packets=%s, "
47 + "table=%s, priority=%s, selector=%s, treatment=%s";
48
49 private static final String GROUP_FORMAT =
50 " id=0x%s, state=%s, type=%s, bytes=%s, packets=%s, appId=%s, referenceCount=%s";
51 private static final String GROUP_BUCKET_FORMAT =
52 " id=0x%s, bucket=%s, bytes=%s, packets=%s, actions=%s";
53
54 @Option(name = "-v", aliases = "--verbose", description = "Outputs complete path")
55 private boolean verbosity1 = false;
56
57 @Option(name = "-vv", aliases = "--veryverbose", description = "Outputs flows and groups for every device")
58 private boolean verbosity2 = false;
59
60 @Option(name = "-s", aliases = "--srcIp", description = "Source IP")
61 String srcIp = null;
62
63 @Option(name = "-sp", aliases = "--srcPort", description = "Source Port", required = true)
64 String srcPort = null;
65
66 @Option(name = "-sm", aliases = "--srcMac", description = "Source MAC")
67 String srcMac = null;
68
69 @Option(name = "-et", aliases = "--ethType", description = "ETH Type", valueToShowInHelp = "ipv4")
70 String ethType = "ipv4";
71
72 @Option(name = "-stp", aliases = "--srcTcpPort", description = "Source TCP Port")
73 String srcTcpPort = null;
74
Andrea Campanella8be1af92018-01-24 15:14:03 +010075 @Option(name = "-d", aliases = "--dstIp", description = "Destination IP")
76 String dstIp = null;
Andrea Campanella01e886e2017-12-15 15:27:31 +010077
78 @Option(name = "-dm", aliases = "--dstMac", description = "Destination MAC")
79 String dstMac = null;
80
81 @Option(name = "-dtp", aliases = "dstTcpPort", description = "destination TCP Port")
82 String dstTcpPort = null;
83
84 @Option(name = "-vid", aliases = "--vlanId", description = "Vlan of incoming packet", valueToShowInHelp = "None")
85 String vlan = "None";
86
87 @Option(name = "-mb", aliases = "--mplsBos", description = "MPLS BOS", valueToShowInHelp = "True")
88 String mplsBos = "true";
89
90 @Override
91 protected void execute() {
92 TroubleshootService service = get(TroubleshootService.class);
93 ConnectPoint cp = ConnectPoint.deviceConnectPoint(srcPort);
Andrea Campanella8be1af92018-01-24 15:14:03 +010094 EtherType type = EtherType.valueOf(ethType.toUpperCase());
Andrea Campanella01e886e2017-12-15 15:27:31 +010095
96 //Input Port must be specified
97 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
98 .matchInPort(cp.port());
99
100 if (srcIp != null) {
Andrea Campanella8be1af92018-01-24 15:14:03 +0100101 if (type.equals(EtherType.IPV6)) {
102 selectorBuilder.matchIPv6Src(IpAddress.valueOf(srcIp).toIpPrefix());
103 } else {
104 selectorBuilder.matchIPSrc(IpAddress.valueOf(srcIp).toIpPrefix());
105 }
Andrea Campanella01e886e2017-12-15 15:27:31 +0100106 }
107
108 if (srcMac != null) {
109 selectorBuilder.matchEthSrc(MacAddress.valueOf(srcMac));
110 }
111
112 //if EthType option is not specified using IPv4
Andrea Campanella8be1af92018-01-24 15:14:03 +0100113 selectorBuilder.matchEthType(type.ethType().toShort());
Andrea Campanella01e886e2017-12-15 15:27:31 +0100114
115 if (srcTcpPort != null) {
116 selectorBuilder.matchTcpSrc(TpPort.tpPort(Integer.parseInt(srcTcpPort)));
117 }
118
Andrea Campanella8be1af92018-01-24 15:14:03 +0100119 if (dstIp != null) {
120 if (type.equals(EtherType.IPV6)) {
121 selectorBuilder.matchIPv6Dst(IpAddress.valueOf(dstIp).toIpPrefix());
122 } else {
123 selectorBuilder.matchIPDst(IpAddress.valueOf(dstIp).toIpPrefix());
124 }
125 }
Andrea Campanella01e886e2017-12-15 15:27:31 +0100126
127 if (dstMac != null) {
128 selectorBuilder.matchEthDst(MacAddress.valueOf(dstMac));
129 }
130 if (dstTcpPort != null) {
131 selectorBuilder.matchTcpDst(TpPort.tpPort(Integer.parseInt(dstTcpPort)));
132 }
133
134 //if vlan option is not specified using NONE
135 selectorBuilder.matchVlanId(VlanId.vlanId(vlan));
136
137 //if mplsBos option is not specified using True
138 selectorBuilder.matchMplsBos(Boolean.valueOf(mplsBos));
139
140 TrafficSelector packet = selectorBuilder.build();
141
142 //Printing the created packet
143 print("Tracing packet: %s", packet.criteria());
144
145 //Build the trace
146 StaticPacketTrace trace = service.trace(packet, cp);
147
148 //Print based on verbosity
149 if (verbosity1) {
150 printTrace(trace, false);
151 } else if (verbosity2) {
152 printTrace(trace, true);
153 } else {
154 print("Paths");
155 List<List<ConnectPoint>> paths = trace.getCompletePaths();
156 paths.forEach(path -> print("%s", path));
157 }
158 print("Result: \n%s", trace.resultMessage());
159 }
160
161 //prints the trace
162 private void printTrace(StaticPacketTrace trace, boolean verbose) {
163 List<List<ConnectPoint>> paths = trace.getCompletePaths();
164 paths.forEach(path -> {
165 print("Path %s", path);
166 ConnectPoint previous = null;
167 for (ConnectPoint connectPoint : path) {
168 if (previous == null || !previous.deviceId().equals(connectPoint.deviceId())) {
169 print("Device %s", connectPoint.deviceId());
170 print("Input from %s", connectPoint);
171 printFlows(trace, verbose, connectPoint);
172 } else {
173 printGroups(trace, verbose, connectPoint);
174 print("Output through %s", connectPoint);
175 print("");
176 }
177 previous = connectPoint;
178 }
179 });
180 }
181
182 //Prints the flows for a given trace and a specified level of verbosity
183 private void printFlows(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint) {
184 print("Flows");
185 trace.getFlowsForDevice(connectPoint.deviceId()).forEach(f -> {
186 if (verbose) {
187 print(FLOW_SHORT_FORMAT, f.state(), f.bytes(), f.packets(),
188 f.table(), f.priority(), f.selector().criteria(),
189 printTreatment(f.treatment()));
190 } else {
191 print(" flowId=%s, selector=%s ", f.id(), f.selector().criteria());
192 }
193 });
194 }
195
196 //Prints the groups for a given trace and a specified level of verbosity
197 private void printGroups(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint) {
198 print("Groups");
199 trace.getGroupOuputs(connectPoint.deviceId()).forEach(output -> {
200 if (output.getOutput().equals(connectPoint)) {
201 output.getGroups().forEach(group -> {
202 if (verbose) {
203 print(GROUP_FORMAT, Integer.toHexString(group.id().id()), group.state(), group.type(),
204 group.bytes(), group.packets(), group.appId().name(), group.referenceCount());
205 int i = 0;
206 for (GroupBucket bucket : group.buckets().buckets()) {
207 print(GROUP_BUCKET_FORMAT, Integer.toHexString(group.id().id()), ++i,
208 bucket.bytes(), bucket.packets(),
209 bucket.treatment().allInstructions());
210 }
211 } else {
212 print(" groupId=%s", group.id());
213 }
214 });
215 print("Outgoing Packet %s", output.getFinalPacket());
216 }
217 });
218 }
219
220 private String printTreatment(TrafficTreatment treatment) {
221 final String delimiter = ", ";
222 StringBuilder builder = new StringBuilder("[");
223 if (!treatment.immediate().isEmpty()) {
224 builder.append("immediate=" + treatment.immediate() + delimiter);
225 }
226 if (!treatment.deferred().isEmpty()) {
227 builder.append("deferred=" + treatment.deferred() + delimiter);
228 }
229 if (treatment.clearedDeferred()) {
230 builder.append("clearDeferred" + delimiter);
231 }
232 if (treatment.tableTransition() != null) {
233 builder.append("transition=" + treatment.tableTransition() + delimiter);
234 }
235 if (treatment.metered() != null) {
236 builder.append("meter=" + treatment.metered() + delimiter);
237 }
238 if (treatment.writeMetadata() != null) {
239 builder.append("metadata=" + treatment.writeMetadata() + delimiter);
240 }
241 // Chop off last delimiter
242 builder.replace(builder.length() - delimiter.length(), builder.length(), "");
243 builder.append("]");
244 return builder.toString();
245 }
246}