blob: c4b49e331335c510356b7b912826dfb4bf3857f1 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Jonathan Hartf4bd0482017-01-27 15:11:18 -080016
Jonathan Hart41349e92015-02-09 14:14:02 -080017package org.onosproject.routing.cli;
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -080018
19import com.fasterxml.jackson.databind.JsonNode;
20import com.fasterxml.jackson.databind.ObjectMapper;
21import com.fasterxml.jackson.databind.node.ArrayNode;
22import com.fasterxml.jackson.databind.node.ObjectNode;
Ray Milkey86ad7bb2018-09-27 12:32:28 -070023import org.apache.karaf.shell.api.action.Command;
24import org.apache.karaf.shell.api.action.Option;
Ray Milkey7a2dee52018-09-28 10:58:28 -070025import org.apache.karaf.shell.api.action.lifecycle.Service;
Brian O'Connorabafb502014-12-02 22:26:20 -080026import org.onosproject.cli.AbstractShellCommand;
Jonathan Hartf4bd0482017-01-27 15:11:18 -080027import org.onosproject.routing.bgp.BgpConstants;
Jonathan Hart41349e92015-02-09 14:14:02 -080028import org.onosproject.routing.bgp.BgpInfoService;
29import org.onosproject.routing.bgp.BgpRouteEntry;
30import org.onosproject.routing.bgp.BgpSession;
31
32import java.util.ArrayList;
33import java.util.Collection;
Jonathan Hart0b04bed2014-10-16 16:39:19 -070034
35/**
36 * Command to show the routes learned through BGP.
37 */
Ray Milkey7a2dee52018-09-28 10:58:28 -070038@Service
Jonathan Hart0b04bed2014-10-16 16:39:19 -070039@Command(scope = "onos", name = "bgp-routes",
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -080040 description = "Lists all BGP best routes")
Jonathan Hart0b04bed2014-10-16 16:39:19 -070041public class BgpRoutesListCommand extends AbstractShellCommand {
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -080042 @Option(name = "-s", aliases = "--summary",
43 description = "BGP routes summary",
44 required = false, multiValued = false)
45 private boolean routesSummary = false;
Jonathan Hart0b04bed2014-10-16 16:39:19 -070046
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -080047 @Option(name = "-n", aliases = "--neighbor",
48 description = "Routes from a BGP neighbor",
49 required = false, multiValued = false)
50 private String bgpNeighbor;
51
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080052 private static final String FORMAT_SUMMARY_V4 =
53 "Total BGP IPv4 routes = %d";
54 private static final String FORMAT_SUMMARY_V6 =
55 "Total BGP IPv6 routes = %d";
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -080056 private static final String FORMAT_HEADER =
57 " Network Next Hop Origin LocalPref MED BGP-ID";
58 private static final String FORMAT_ROUTE_LINE1 =
59 " %-18s %-15s %6s %9s %9s %-15s";
60 private static final String FORMAT_ROUTE_LINE2 =
61 " AsPath %s";
Jonathan Hart0b04bed2014-10-16 16:39:19 -070062
63 @Override
Ray Milkey86ad7bb2018-09-27 12:32:28 -070064 protected void doExecute() {
Jonathan Hart41349e92015-02-09 14:14:02 -080065 BgpInfoService service = AbstractShellCommand.get(BgpInfoService.class);
Jonathan Hart0b04bed2014-10-16 16:39:19 -070066
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -080067 // Print summary of the routes
68 if (routesSummary) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080069 printSummary(service.getBgpRoutes4(), service.getBgpRoutes6());
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -080070 return;
71 }
72
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -080073 BgpSession foundBgpSession = null;
74 if (bgpNeighbor != null) {
75 // Print the routes from a single neighbor (if found)
76 for (BgpSession bgpSession : service.getBgpSessions()) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080077 if (bgpSession.remoteInfo().bgpId().toString().equals(bgpNeighbor)) {
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -080078 foundBgpSession = bgpSession;
79 break;
80 }
81 }
82 if (foundBgpSession == null) {
83 print("BGP neighbor %s not found", bgpNeighbor);
84 return;
85 }
86 }
87
88 // Print the routes
89 if (foundBgpSession != null) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080090 printRoutes(foundBgpSession.getBgpRibIn4(),
91 foundBgpSession.getBgpRibIn6());
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -080092 } else {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080093 printRoutes(service.getBgpRoutes4(), service.getBgpRoutes6());
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -080094 }
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -080095 }
96
97 /**
98 * Prints summary of the routes.
99 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800100 * @param routes4 the IPv4 routes
101 * @param routes6 the IPv6 routes
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800102 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800103 private void printSummary(Collection<BgpRouteEntry> routes4,
104 Collection<BgpRouteEntry> routes6) {
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800105 if (outputJson()) {
106 ObjectMapper mapper = new ObjectMapper();
107 ObjectNode result = mapper.createObjectNode();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800108 result.put("totalRoutes4", routes4.size());
109 result.put("totalRoutes6", routes6.size());
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800110 print("%s", result);
111 } else {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800112 print(FORMAT_SUMMARY_V4, routes4.size());
113 print(FORMAT_SUMMARY_V6, routes6.size());
Jonathan Hart0b04bed2014-10-16 16:39:19 -0700114 }
115 }
116
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800117 /**
118 * Prints all routes.
119 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800120 * @param routes4 the IPv4 routes to print
121 * @param routes6 the IPv6 routes to print
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800122 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800123 private void printRoutes(Collection<BgpRouteEntry> routes4,
124 Collection<BgpRouteEntry> routes6) {
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800125 if (outputJson()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800126 ObjectMapper mapper = new ObjectMapper();
127 ObjectNode result = mapper.createObjectNode();
Ray Milkey9d810f62015-02-13 11:20:58 -0800128 result.set("routes4", json(routes4));
129 result.set("routes6", json(routes6));
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800130 print("%s", result);
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800131 } else {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800132 // The IPv4 routes
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -0800133 print(FORMAT_HEADER);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800134 for (BgpRouteEntry route : routes4) {
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800135 printRoute(route);
136 }
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800137 print(FORMAT_SUMMARY_V4, routes4.size());
138 print(""); // Empty separator line
139 // The IPv6 routes
140 print(FORMAT_HEADER);
141 for (BgpRouteEntry route : routes6) {
142 printRoute(route);
143 }
144 print(FORMAT_SUMMARY_V6, routes6.size());
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800145 }
146 }
147
148 /**
149 * Prints a BGP route.
150 *
151 * @param route the route to print
152 */
Jonathan Hart0b04bed2014-10-16 16:39:19 -0700153 private void printRoute(BgpRouteEntry route) {
154 if (route != null) {
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -0800155 print(FORMAT_ROUTE_LINE1, route.prefix(), route.nextHop(),
Jonathan Hart2da1e602015-02-18 19:09:24 -0800156 BgpConstants.Update.Origin.typeToString(route.getOrigin()),
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800157 route.getLocalPref(), route.getMultiExitDisc(),
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800158 route.getBgpSession().remoteInfo().bgpId());
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -0800159 print(FORMAT_ROUTE_LINE2, asPath4Cli(route.getAsPath()));
Jonathan Hart0b04bed2014-10-16 16:39:19 -0700160 }
161 }
162
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800163 /**
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -0800164 * Formats the AS Path as a string that can be shown on the CLI.
165 *
166 * @param asPath the AS Path to format
167 * @return the AS Path as a string
168 */
169 private String asPath4Cli(BgpRouteEntry.AsPath asPath) {
170 ArrayList<BgpRouteEntry.PathSegment> pathSegments =
171 asPath.getPathSegments();
172
173 if (pathSegments.isEmpty()) {
174 return "[none]";
175 }
176
177 final StringBuilder builder = new StringBuilder();
178 for (BgpRouteEntry.PathSegment pathSegment : pathSegments) {
179 String prefix = null;
180 String suffix = null;
181 switch (pathSegment.getType()) {
Jonathan Hart2da1e602015-02-18 19:09:24 -0800182 case BgpConstants.Update.AsPath.AS_SET:
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -0800183 prefix = "[AS-Set";
184 suffix = "]";
185 break;
Jonathan Hart2da1e602015-02-18 19:09:24 -0800186 case BgpConstants.Update.AsPath.AS_SEQUENCE:
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -0800187 break;
Jonathan Hart2da1e602015-02-18 19:09:24 -0800188 case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE:
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -0800189 prefix = "[AS-Confed-Seq";
190 suffix = "]";
191 break;
Jonathan Hart2da1e602015-02-18 19:09:24 -0800192 case BgpConstants.Update.AsPath.AS_CONFED_SET:
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -0800193 prefix = "[AS-Confed-Set";
194 suffix = "]";
195 break;
196 default:
197 builder.append(String.format("(type = %s)",
Jonathan Hart2da1e602015-02-18 19:09:24 -0800198 BgpConstants.Update.AsPath.typeToString(pathSegment.getType())));
Pavlin Radoslavovdfde7ab2014-12-04 14:06:39 -0800199 break;
200 }
201
202 if (prefix != null) {
203 if (builder.length() > 0) {
204 builder.append(" "); // Separator
205 }
206 builder.append(prefix);
207 }
208 // Print the AS numbers
209 for (Long asn : pathSegment.getSegmentAsNumbers()) {
210 if (builder.length() > 0) {
211 builder.append(" "); // Separator
212 }
213 builder.append(String.format("%d", asn));
214 }
215 if (suffix != null) {
216 // No need for separator
217 builder.append(prefix);
218 }
219 }
220 return builder.toString();
221 }
222
223 /**
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800224 * Produces a JSON array of routes.
225 *
226 * @param routes the routes with the data
227 * @return JSON array with the routes
228 */
229 private JsonNode json(Collection<BgpRouteEntry> routes) {
230 ObjectMapper mapper = new ObjectMapper();
231 ArrayNode result = mapper.createArrayNode();
Jonathan Hart0b04bed2014-10-16 16:39:19 -0700232
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800233 for (BgpRouteEntry route : routes) {
234 result.add(json(mapper, route));
Jonathan Hart0b04bed2014-10-16 16:39:19 -0700235 }
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800236 return result;
Jonathan Hart0b04bed2014-10-16 16:39:19 -0700237 }
238
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800239 /**
240 * Produces JSON object for a route.
241 *
242 * @param mapper the JSON object mapper to use
243 * @param route the route with the data
244 * @return JSON object for the route
245 */
246 private ObjectNode json(ObjectMapper mapper, BgpRouteEntry route) {
247 ObjectNode result = mapper.createObjectNode();
248
249 result.put("prefix", route.prefix().toString());
250 result.put("nextHop", route.nextHop().toString());
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800251 result.put("bgpId",
252 route.getBgpSession().remoteInfo().bgpId().toString());
Jonathan Hart2da1e602015-02-18 19:09:24 -0800253 result.put("origin", BgpConstants.Update.Origin.typeToString(route.getOrigin()));
Ray Milkey9d810f62015-02-13 11:20:58 -0800254 result.set("asPath", json(mapper, route.getAsPath()));
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800255 result.put("localPref", route.getLocalPref());
256 result.put("multiExitDisc", route.getMultiExitDisc());
257
258 return result;
259 }
260
261 /**
262 * Produces JSON object for an AS path.
263 *
264 * @param mapper the JSON object mapper to use
265 * @param asPath the AS path with the data
266 * @return JSON object for the AS path
267 */
268 private ObjectNode json(ObjectMapper mapper, BgpRouteEntry.AsPath asPath) {
269 ObjectNode result = mapper.createObjectNode();
270 ArrayNode pathSegmentsJson = mapper.createArrayNode();
271 for (BgpRouteEntry.PathSegment pathSegment : asPath.getPathSegments()) {
272 ObjectNode pathSegmentJson = mapper.createObjectNode();
273 pathSegmentJson.put("type",
Jonathan Hart2da1e602015-02-18 19:09:24 -0800274 BgpConstants.Update.AsPath.typeToString(pathSegment.getType()));
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800275 ArrayNode segmentAsNumbersJson = mapper.createArrayNode();
276 for (Long asNumber : pathSegment.getSegmentAsNumbers()) {
277 segmentAsNumbersJson.add(asNumber);
278 }
Ray Milkey9d810f62015-02-13 11:20:58 -0800279 pathSegmentJson.set("segmentAsNumbers", segmentAsNumbersJson);
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800280 pathSegmentsJson.add(pathSegmentJson);
281 }
Ray Milkey9d810f62015-02-13 11:20:58 -0800282 result.set("pathSegments", pathSegmentsJson);
Pavlin Radoslavov2ce1c522014-11-07 10:32:37 -0800283
284 return result;
285 }
Jonathan Hart0b04bed2014-10-16 16:39:19 -0700286}