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