blob: d156f8a0df3b54450ace57b7cf31c590b76f4f81 [file] [log] [blame]
Andrea Campanellae4084402017-12-15 15:27:31 +01001/*
Andrea Campanella55c3f422018-02-08 17:10:11 +01002 * Copyright 2018-present Open Networking Foundation
Andrea Campanellae4084402017-12-15 15:27:31 +01003 *
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;
pierventre4fd3b462020-10-15 17:31:44 +020020import com.google.common.primitives.UnsignedLongs;
Ray Milkey822567d2018-09-27 12:32:28 -070021import org.apache.karaf.shell.api.action.Command;
Seyeon Jeong83e79862020-02-28 01:17:34 -080022import org.apache.karaf.shell.api.action.Completion;
Ray Milkey822567d2018-09-27 12:32:28 -070023import org.apache.karaf.shell.api.action.Option;
Ray Milkeyac725032018-09-28 10:58:28 -070024import org.apache.karaf.shell.api.action.lifecycle.Service;
Andrea Campanellae4084402017-12-15 15:27:31 +010025import org.onlab.packet.IpAddress;
26import org.onlab.packet.MacAddress;
Andrea Campanella3970e472018-01-25 16:44:04 +010027import org.onlab.packet.MplsLabel;
Andrea Campanellae4084402017-12-15 15:27:31 +010028import org.onlab.packet.TpPort;
29import org.onlab.packet.VlanId;
30import org.onosproject.cli.AbstractShellCommand;
Seyeon Jeong83e79862020-02-28 01:17:34 -080031import org.onosproject.cli.PlaceholderCompleter;
32import org.onosproject.cli.net.ConnectPointCompleter;
33import org.onosproject.cli.net.EthTypeCompleter;
34import org.onosproject.cli.net.IpProtocolCompleter;
Andrea Campanellae4084402017-12-15 15:27:31 +010035import org.onosproject.net.ConnectPoint;
Andrea Campanella1445f7a2018-02-07 12:00:12 +010036import org.onosproject.net.DeviceId;
37import org.onosproject.net.PortNumber;
Andrea Campanellae4084402017-12-15 15:27:31 +010038import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.TrafficSelector;
Andrea Campanellae4084402017-12-15 15:27:31 +010040import org.onosproject.t3.api.StaticPacketTrace;
41import org.onosproject.t3.api.TroubleshootService;
42
pierventre4fd3b462020-10-15 17:31:44 +020043import java.util.regex.Matcher;
44import java.util.regex.Pattern;
45
Andrea Campanella7db72fc2018-01-24 15:14:03 +010046import static org.onlab.packet.EthType.EtherType;
47
Andrea Campanellae4084402017-12-15 15:27:31 +010048/**
49 * Starts a Static Packet Trace for a given input and prints the result.
50 */
Ray Milkeyac725032018-09-28 10:58:28 -070051@Service
Andrea Campanellad756ede2018-02-26 11:03:48 +010052@Command(scope = "onos", name = "t3-troubleshoot",
Andrea Campanellae4084402017-12-15 15:27:31 +010053 description = "troubleshoots flows and groups between source and destination")
54public class TroubleshootTraceCommand extends AbstractShellCommand {
55
pierventre4fd3b462020-10-15 17:31:44 +020056 private static final Pattern NAMED = Pattern.compile("^(?<deviceid>\\S*)\\/\\[(?<name>\\S*)\\]\\((?<num>\\d+)\\)$");
57
58 private static final Pattern NOT_NAMED = Pattern.compile("^(?<deviceid>\\S*)\\/(?<num>\\d+)$");
Andrea Campanellae4084402017-12-15 15:27:31 +010059
60 private static final String FLOW_SHORT_FORMAT = " %s, bytes=%s, packets=%s, "
61 + "table=%s, priority=%s, selector=%s, treatment=%s";
62
63 private static final String GROUP_FORMAT =
64 " id=0x%s, state=%s, type=%s, bytes=%s, packets=%s, appId=%s, referenceCount=%s";
65 private static final String GROUP_BUCKET_FORMAT =
66 " id=0x%s, bucket=%s, bytes=%s, packets=%s, actions=%s";
67
Andrea Campanella1445f7a2018-02-07 12:00:12 +010068 private static final String CONTROLLER = "CONTROLLER";
69
Andrea Campanellae4084402017-12-15 15:27:31 +010070 @Option(name = "-v", aliases = "--verbose", description = "Outputs complete path")
Seyeon Jeong83e79862020-02-28 01:17:34 -080071 @Completion(PlaceholderCompleter.class)
Andrea Campanellae4084402017-12-15 15:27:31 +010072 private boolean verbosity1 = false;
73
74 @Option(name = "-vv", aliases = "--veryverbose", description = "Outputs flows and groups for every device")
Seyeon Jeong83e79862020-02-28 01:17:34 -080075 @Completion(PlaceholderCompleter.class)
Andrea Campanellae4084402017-12-15 15:27:31 +010076 private boolean verbosity2 = false;
77
78 @Option(name = "-s", aliases = "--srcIp", description = "Source IP")
Seyeon Jeong83e79862020-02-28 01:17:34 -080079 @Completion(PlaceholderCompleter.class)
Andrea Campanellae4084402017-12-15 15:27:31 +010080 String srcIp = null;
81
82 @Option(name = "-sp", aliases = "--srcPort", description = "Source Port", required = true)
Seyeon Jeong83e79862020-02-28 01:17:34 -080083 @Completion(ConnectPointCompleter.class)
Andrea Campanellae4084402017-12-15 15:27:31 +010084 String srcPort = null;
85
86 @Option(name = "-sm", aliases = "--srcMac", description = "Source MAC")
Seyeon Jeong83e79862020-02-28 01:17:34 -080087 @Completion(PlaceholderCompleter.class)
Andrea Campanellae4084402017-12-15 15:27:31 +010088 String srcMac = null;
89
90 @Option(name = "-et", aliases = "--ethType", description = "ETH Type", valueToShowInHelp = "ipv4")
Seyeon Jeong83e79862020-02-28 01:17:34 -080091 @Completion(EthTypeCompleter.class)
Andrea Campanellae4084402017-12-15 15:27:31 +010092 String ethType = "ipv4";
93
94 @Option(name = "-stp", aliases = "--srcTcpPort", description = "Source TCP Port")
Seyeon Jeong83e79862020-02-28 01:17:34 -080095 @Completion(PlaceholderCompleter.class)
Andrea Campanellae4084402017-12-15 15:27:31 +010096 String srcTcpPort = null;
97
Andrea Campanella7db72fc2018-01-24 15:14:03 +010098 @Option(name = "-d", aliases = "--dstIp", description = "Destination IP")
Seyeon Jeong83e79862020-02-28 01:17:34 -080099 @Completion(PlaceholderCompleter.class)
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100100 String dstIp = null;
Andrea Campanellae4084402017-12-15 15:27:31 +0100101
102 @Option(name = "-dm", aliases = "--dstMac", description = "Destination MAC")
Seyeon Jeong83e79862020-02-28 01:17:34 -0800103 @Completion(PlaceholderCompleter.class)
Andrea Campanellae4084402017-12-15 15:27:31 +0100104 String dstMac = null;
105
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100106 @Option(name = "-dtp", aliases = "--dstTcpPort", description = "destination TCP Port")
Seyeon Jeong83e79862020-02-28 01:17:34 -0800107 @Completion(PlaceholderCompleter.class)
Andrea Campanellae4084402017-12-15 15:27:31 +0100108 String dstTcpPort = null;
109
110 @Option(name = "-vid", aliases = "--vlanId", description = "Vlan of incoming packet", valueToShowInHelp = "None")
Seyeon Jeong83e79862020-02-28 01:17:34 -0800111 @Completion(PlaceholderCompleter.class)
Andrea Campanellae4084402017-12-15 15:27:31 +0100112 String vlan = "None";
113
Andrea Campanella3970e472018-01-25 16:44:04 +0100114 @Option(name = "-ml", aliases = "--mplsLabel", description = "Mpls label of incoming packet")
Seyeon Jeong83e79862020-02-28 01:17:34 -0800115 @Completion(PlaceholderCompleter.class)
Andrea Campanella3970e472018-01-25 16:44:04 +0100116 String mplsLabel = null;
117
Andrea Campanella8292ba62018-01-31 16:43:23 +0100118 @Option(name = "-mb", aliases = "--mplsBos", description = "MPLS BOS")
Seyeon Jeong83e79862020-02-28 01:17:34 -0800119 @Completion(PlaceholderCompleter.class)
Andrea Campanella3970e472018-01-25 16:44:04 +0100120 String mplsBos = null;
Andrea Campanellae4084402017-12-15 15:27:31 +0100121
Andrea Campanella8292ba62018-01-31 16:43:23 +0100122 @Option(name = "-ipp", aliases = "--ipProto", description = "IP Proto")
Seyeon Jeong83e79862020-02-28 01:17:34 -0800123 @Completion(IpProtocolCompleter.class)
Andrea Campanella8292ba62018-01-31 16:43:23 +0100124 String ipProto = null;
125
126 @Option(name = "-udps", aliases = "--udpSrc", description = "UDP Source")
Seyeon Jeong83e79862020-02-28 01:17:34 -0800127 @Completion(PlaceholderCompleter.class)
Andrea Campanella8292ba62018-01-31 16:43:23 +0100128 String udpSrc = null;
129
130 @Option(name = "-udpd", aliases = "--udpDst", description = "UDP Destination")
Seyeon Jeong83e79862020-02-28 01:17:34 -0800131 @Completion(PlaceholderCompleter.class)
Andrea Campanella8292ba62018-01-31 16:43:23 +0100132 String udpDst = null;
133
Andrea Campanellae4084402017-12-15 15:27:31 +0100134 @Override
Ray Milkey822567d2018-09-27 12:32:28 -0700135 protected void doExecute() {
Andrea Campanellae4084402017-12-15 15:27:31 +0100136 TroubleshootService service = get(TroubleshootService.class);
Seyeon Jeong5018bdd2020-02-28 01:17:34 -0800137 if (!service.checkNibValidity()) {
138 // if the NIB is found invalid, fill it with the current network states so that this command can proceed
139 print(T3CliUtils.NIB_AUTOFILLED);
140 TroubleshootLoadSnapshotCommand cmd = new TroubleshootLoadSnapshotCommand();
141 cmd.doExecute();
142 if (!service.checkNibValidity()) {
143 // if the NIB is still invalid even after auto-filled snapshots, stop and warn
144 print(T3CliUtils.NIB_TERMINATE);
145 return;
146 }
147 } else {
148 print(service.printNibSummary());
Seyeon Jeong83e79862020-02-28 01:17:34 -0800149 }
150
pierventre4fd3b462020-10-15 17:31:44 +0200151 // Uses input port as a convenience to carry the Controller port, proper flood behaviour is handled in the
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100152 // troubleshoot manager.
pierventre4fd3b462020-10-15 17:31:44 +0200153 ConnectPoint cp = parseConnectPoint(srcPort);
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100154 EtherType type = EtherType.valueOf(ethType.toUpperCase());
Andrea Campanellae4084402017-12-15 15:27:31 +0100155
156 //Input Port must be specified
157 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
158 .matchInPort(cp.port());
159
160 if (srcIp != null) {
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100161 if (type.equals(EtherType.IPV6)) {
162 selectorBuilder.matchIPv6Src(IpAddress.valueOf(srcIp).toIpPrefix());
163 } else {
164 selectorBuilder.matchIPSrc(IpAddress.valueOf(srcIp).toIpPrefix());
165 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100166 }
167
168 if (srcMac != null) {
169 selectorBuilder.matchEthSrc(MacAddress.valueOf(srcMac));
170 }
171
172 //if EthType option is not specified using IPv4
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100173 selectorBuilder.matchEthType(type.ethType().toShort());
Andrea Campanellae4084402017-12-15 15:27:31 +0100174
175 if (srcTcpPort != null) {
176 selectorBuilder.matchTcpSrc(TpPort.tpPort(Integer.parseInt(srcTcpPort)));
177 }
178
Andrea Campanella7db72fc2018-01-24 15:14:03 +0100179 if (dstIp != null) {
180 if (type.equals(EtherType.IPV6)) {
181 selectorBuilder.matchIPv6Dst(IpAddress.valueOf(dstIp).toIpPrefix());
182 } else {
183 selectorBuilder.matchIPDst(IpAddress.valueOf(dstIp).toIpPrefix());
184 }
185 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100186
187 if (dstMac != null) {
188 selectorBuilder.matchEthDst(MacAddress.valueOf(dstMac));
189 }
190 if (dstTcpPort != null) {
191 selectorBuilder.matchTcpDst(TpPort.tpPort(Integer.parseInt(dstTcpPort)));
192 }
193
194 //if vlan option is not specified using NONE
195 selectorBuilder.matchVlanId(VlanId.vlanId(vlan));
196
Andrea Campanella3970e472018-01-25 16:44:04 +0100197 if (mplsLabel != null) {
198 selectorBuilder.matchMplsLabel(MplsLabel.mplsLabel(Integer.parseInt(mplsLabel)));
199 }
200
201 if (mplsBos != null) {
202 selectorBuilder.matchMplsBos(Boolean.valueOf(mplsBos));
203 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100204
Andrea Campanella8292ba62018-01-31 16:43:23 +0100205 if (ipProto != null) {
206 selectorBuilder.matchIPProtocol(Byte.valueOf(ipProto));
207 }
208
209 if (udpSrc != null) {
210 selectorBuilder.matchUdpSrc(TpPort.tpPort(Integer.parseInt(udpSrc)));
211 }
212
213 if (udpDst != null) {
214 selectorBuilder.matchUdpDst(TpPort.tpPort(Integer.parseInt(udpDst)));
215 }
216
217
Andrea Campanellae4084402017-12-15 15:27:31 +0100218 TrafficSelector packet = selectorBuilder.build();
219
220 //Printing the created packet
221 print("Tracing packet: %s", packet.criteria());
222
223 //Build the trace
224 StaticPacketTrace trace = service.trace(packet, cp);
Andrea Campanellae4084402017-12-15 15:27:31 +0100225
Andrea Campanella55c3f422018-02-08 17:10:11 +0100226 print("%s", T3CliUtils.printTrace(trace, verbosity1, verbosity2));
Andrea Campanellae4084402017-12-15 15:27:31 +0100227
Andrea Campanellae4084402017-12-15 15:27:31 +0100228 }
pierventre4fd3b462020-10-15 17:31:44 +0200229
230 private ConnectPoint parseConnectPoint(String cp) {
231 String deviceId;
232 String name;
233 String num;
234
235 Matcher matcher = NAMED.matcher(cp);
236 // If it is not a named port - try to parse using classic way
237 if (!matcher.matches()) {
238 matcher = NOT_NAMED.matcher(cp);
239 Preconditions.checkArgument(matcher.matches(), "wrong format of source port");
240 deviceId = matcher.group("deviceid");
241 num = matcher.group("num");
242 return new ConnectPoint(DeviceId.deviceId(deviceId),
243 PortNumber.portNumber(num));
244 }
245 // Named port - decomposes in sub components
246 deviceId = matcher.group("deviceid");
247 name = matcher.group("name");
248 num = matcher.group("num");
249 return new ConnectPoint(DeviceId.deviceId(deviceId),
250 PortNumber.portNumber(UnsignedLongs.parseUnsignedLong(num), name));
251 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100252}