Pavlin Radoslavov

Cleanup the SDN-IP CLI:

 * Updated/cleanup the "routes" and "bgp-rotues" commands output formatting
 * Fixed/updated the help description for the "routes" and "bgp-routes"
 * Added optional argument "-n <nbr>" or "-neighbor <nbr>" argument
   to the "bgp-routes" command to print the routes from a single BGP neighbor

Change-Id: I65afd00772bc8e097468ae2ed0dd3fd41bd27f25
...@@ -43,7 +43,7 @@ public class BgpNeighborsListCommand extends AbstractShellCommand { ...@@ -43,7 +43,7 @@ public class BgpNeighborsListCommand extends AbstractShellCommand {
43 private static final String FORMAT_NEIGHBOR_LINE2 = 43 private static final String FORMAT_NEIGHBOR_LINE2 =
44 " Remote router ID %s, IP %s, BGP version %d, Hold time %d"; 44 " Remote router ID %s, IP %s, BGP version %d, Hold time %d";
45 private static final String FORMAT_NEIGHBOR_LINE3 = 45 private static final String FORMAT_NEIGHBOR_LINE3 =
46 - " Local router ID %s, IP %s, BGP version %d, Hold time %d"; 46 + " Local router ID %s, IP %s, BGP version %d, Hold time %d";
47 47
48 @Override 48 @Override
49 protected void execute() { 49 protected void execute() {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.sdnip.cli; 16 package org.onosproject.sdnip.cli;
17 17
18 +import java.util.ArrayList;
18 import java.util.Collection; 19 import java.util.Collection;
19 20
20 import com.fasterxml.jackson.databind.JsonNode; 21 import com.fasterxml.jackson.databind.JsonNode;
...@@ -25,24 +26,33 @@ import org.apache.karaf.shell.commands.Command; ...@@ -25,24 +26,33 @@ import org.apache.karaf.shell.commands.Command;
25 import org.apache.karaf.shell.commands.Option; 26 import org.apache.karaf.shell.commands.Option;
26 import org.onosproject.cli.AbstractShellCommand; 27 import org.onosproject.cli.AbstractShellCommand;
27 import org.onosproject.sdnip.SdnIpService; 28 import org.onosproject.sdnip.SdnIpService;
28 -import org.onosproject.sdnip.bgp.BgpConstants.Update.AsPath; 29 +import org.onosproject.sdnip.bgp.BgpConstants.Update;
29 -import org.onosproject.sdnip.bgp.BgpConstants.Update.Origin;
30 import org.onosproject.sdnip.bgp.BgpRouteEntry; 30 import org.onosproject.sdnip.bgp.BgpRouteEntry;
31 +import org.onosproject.sdnip.bgp.BgpSession;
31 32
32 /** 33 /**
33 * Command to show the routes learned through BGP. 34 * Command to show the routes learned through BGP.
34 */ 35 */
35 @Command(scope = "onos", name = "bgp-routes", 36 @Command(scope = "onos", name = "bgp-routes",
36 - description = "Lists all routes received from BGP") 37 + description = "Lists all BGP best routes")
37 public class BgpRoutesListCommand extends AbstractShellCommand { 38 public class BgpRoutesListCommand extends AbstractShellCommand {
38 @Option(name = "-s", aliases = "--summary", 39 @Option(name = "-s", aliases = "--summary",
39 description = "BGP routes summary", 40 description = "BGP routes summary",
40 required = false, multiValued = false) 41 required = false, multiValued = false)
41 private boolean routesSummary = false; 42 private boolean routesSummary = false;
42 43
44 + @Option(name = "-n", aliases = "--neighbor",
45 + description = "Routes from a BGP neighbor",
46 + required = false, multiValued = false)
47 + private String bgpNeighbor;
48 +
43 private static final String FORMAT_SUMMARY = "Total BGP routes = %d"; 49 private static final String FORMAT_SUMMARY = "Total BGP routes = %d";
44 - private static final String FORMAT_ROUTE = 50 + private static final String FORMAT_HEADER =
45 - "prefix=%s, nexthop=%s, origin=%s, localpref=%s, med=%s, aspath=%s, bgpid=%s"; 51 + " Network Next Hop Origin LocalPref MED BGP-ID";
52 + private static final String FORMAT_ROUTE_LINE1 =
53 + " %-18s %-15s %6s %9s %9s %-15s";
54 + private static final String FORMAT_ROUTE_LINE2 =
55 + " AsPath %s";
46 56
47 @Override 57 @Override
48 protected void execute() { 58 protected void execute() {
...@@ -54,8 +64,27 @@ public class BgpRoutesListCommand extends AbstractShellCommand { ...@@ -54,8 +64,27 @@ public class BgpRoutesListCommand extends AbstractShellCommand {
54 return; 64 return;
55 } 65 }
56 66
57 - // Print all routes 67 + BgpSession foundBgpSession = null;
58 - printRoutes(service.getBgpRoutes()); 68 + if (bgpNeighbor != null) {
69 + // Print the routes from a single neighbor (if found)
70 + for (BgpSession bgpSession : service.getBgpSessions()) {
71 + if (bgpSession.getRemoteBgpId().toString().equals(bgpNeighbor)) {
72 + foundBgpSession = bgpSession;
73 + break;
74 + }
75 + }
76 + if (foundBgpSession == null) {
77 + print("BGP neighbor %s not found", bgpNeighbor);
78 + return;
79 + }
80 + }
81 +
82 + // Print the routes
83 + if (foundBgpSession != null) {
84 + printRoutes(foundBgpSession.getBgpRibIn());
85 + } else {
86 + printRoutes(service.getBgpRoutes());
87 + }
59 } 88 }
60 89
61 /** 90 /**
...@@ -83,9 +112,11 @@ public class BgpRoutesListCommand extends AbstractShellCommand { ...@@ -83,9 +112,11 @@ public class BgpRoutesListCommand extends AbstractShellCommand {
83 if (outputJson()) { 112 if (outputJson()) {
84 print("%s", json(routes)); 113 print("%s", json(routes));
85 } else { 114 } else {
115 + print(FORMAT_HEADER);
86 for (BgpRouteEntry route : routes) { 116 for (BgpRouteEntry route : routes) {
87 printRoute(route); 117 printRoute(route);
88 } 118 }
119 + print(FORMAT_SUMMARY, routes.size());
89 } 120 }
90 } 121 }
91 122
...@@ -96,11 +127,72 @@ public class BgpRoutesListCommand extends AbstractShellCommand { ...@@ -96,11 +127,72 @@ public class BgpRoutesListCommand extends AbstractShellCommand {
96 */ 127 */
97 private void printRoute(BgpRouteEntry route) { 128 private void printRoute(BgpRouteEntry route) {
98 if (route != null) { 129 if (route != null) {
99 - print(FORMAT_ROUTE, route.prefix(), route.nextHop(), 130 + print(FORMAT_ROUTE_LINE1, route.prefix(), route.nextHop(),
100 - Origin.typeToString(route.getOrigin()), 131 + Update.Origin.typeToString(route.getOrigin()),
101 route.getLocalPref(), route.getMultiExitDisc(), 132 route.getLocalPref(), route.getMultiExitDisc(),
102 - route.getAsPath(), route.getBgpSession().getRemoteBgpId()); 133 + route.getBgpSession().getRemoteBgpId());
134 + print(FORMAT_ROUTE_LINE2, asPath4Cli(route.getAsPath()));
135 + }
136 + }
137 +
138 + /**
139 + * Formats the AS Path as a string that can be shown on the CLI.
140 + *
141 + * @param asPath the AS Path to format
142 + * @return the AS Path as a string
143 + */
144 + private String asPath4Cli(BgpRouteEntry.AsPath asPath) {
145 + ArrayList<BgpRouteEntry.PathSegment> pathSegments =
146 + asPath.getPathSegments();
147 +
148 + if (pathSegments.isEmpty()) {
149 + return "[none]";
150 + }
151 +
152 + final StringBuilder builder = new StringBuilder();
153 + for (BgpRouteEntry.PathSegment pathSegment : pathSegments) {
154 + String prefix = null;
155 + String suffix = null;
156 + switch (pathSegment.getType()) {
157 + case Update.AsPath.AS_SET:
158 + prefix = "[AS-Set";
159 + suffix = "]";
160 + break;
161 + case Update.AsPath.AS_SEQUENCE:
162 + break;
163 + case Update.AsPath.AS_CONFED_SEQUENCE:
164 + prefix = "[AS-Confed-Seq";
165 + suffix = "]";
166 + break;
167 + case Update.AsPath.AS_CONFED_SET:
168 + prefix = "[AS-Confed-Set";
169 + suffix = "]";
170 + break;
171 + default:
172 + builder.append(String.format("(type = %s)",
173 + Update.AsPath.typeToString(pathSegment.getType())));
174 + break;
175 + }
176 +
177 + if (prefix != null) {
178 + if (builder.length() > 0) {
179 + builder.append(" "); // Separator
180 + }
181 + builder.append(prefix);
182 + }
183 + // Print the AS numbers
184 + for (Long asn : pathSegment.getSegmentAsNumbers()) {
185 + if (builder.length() > 0) {
186 + builder.append(" "); // Separator
187 + }
188 + builder.append(String.format("%d", asn));
189 + }
190 + if (suffix != null) {
191 + // No need for separator
192 + builder.append(prefix);
193 + }
103 } 194 }
195 + return builder.toString();
104 } 196 }
105 197
106 /** 198 /**
...@@ -132,7 +224,7 @@ public class BgpRoutesListCommand extends AbstractShellCommand { ...@@ -132,7 +224,7 @@ public class BgpRoutesListCommand extends AbstractShellCommand {
132 result.put("prefix", route.prefix().toString()); 224 result.put("prefix", route.prefix().toString());
133 result.put("nextHop", route.nextHop().toString()); 225 result.put("nextHop", route.nextHop().toString());
134 result.put("bgpId", route.getBgpSession().getRemoteBgpId().toString()); 226 result.put("bgpId", route.getBgpSession().getRemoteBgpId().toString());
135 - result.put("origin", Origin.typeToString(route.getOrigin())); 227 + result.put("origin", Update.Origin.typeToString(route.getOrigin()));
136 result.put("asPath", json(mapper, route.getAsPath())); 228 result.put("asPath", json(mapper, route.getAsPath()));
137 result.put("localPref", route.getLocalPref()); 229 result.put("localPref", route.getLocalPref());
138 result.put("multiExitDisc", route.getMultiExitDisc()); 230 result.put("multiExitDisc", route.getMultiExitDisc());
...@@ -153,7 +245,7 @@ public class BgpRoutesListCommand extends AbstractShellCommand { ...@@ -153,7 +245,7 @@ public class BgpRoutesListCommand extends AbstractShellCommand {
153 for (BgpRouteEntry.PathSegment pathSegment : asPath.getPathSegments()) { 245 for (BgpRouteEntry.PathSegment pathSegment : asPath.getPathSegments()) {
154 ObjectNode pathSegmentJson = mapper.createObjectNode(); 246 ObjectNode pathSegmentJson = mapper.createObjectNode();
155 pathSegmentJson.put("type", 247 pathSegmentJson.put("type",
156 - AsPath.typeToString(pathSegment.getType())); 248 + Update.AsPath.typeToString(pathSegment.getType()));
157 ArrayNode segmentAsNumbersJson = mapper.createArrayNode(); 249 ArrayNode segmentAsNumbersJson = mapper.createArrayNode();
158 for (Long asNumber : pathSegment.getSegmentAsNumbers()) { 250 for (Long asNumber : pathSegment.getSegmentAsNumbers()) {
159 segmentAsNumbersJson.add(asNumber); 251 segmentAsNumbersJson.add(asNumber);
......
...@@ -31,7 +31,7 @@ import org.onosproject.sdnip.SdnIpService; ...@@ -31,7 +31,7 @@ import org.onosproject.sdnip.SdnIpService;
31 * Command to show the list of routes in SDN-IP's routing table. 31 * Command to show the list of routes in SDN-IP's routing table.
32 */ 32 */
33 @Command(scope = "onos", name = "routes", 33 @Command(scope = "onos", name = "routes",
34 - description = "Lists all routes known to SDN-IP") 34 + description = "Lists all SDN-IP best routes")
35 public class RoutesListCommand extends AbstractShellCommand { 35 public class RoutesListCommand extends AbstractShellCommand {
36 @Option(name = "-s", aliases = "--summary", 36 @Option(name = "-s", aliases = "--summary",
37 description = "SDN-IP routes summary", 37 description = "SDN-IP routes summary",
...@@ -39,8 +39,10 @@ public class RoutesListCommand extends AbstractShellCommand { ...@@ -39,8 +39,10 @@ public class RoutesListCommand extends AbstractShellCommand {
39 private boolean routesSummary = false; 39 private boolean routesSummary = false;
40 40
41 private static final String FORMAT_SUMMARY = "Total SDN-IP routes = %d"; 41 private static final String FORMAT_SUMMARY = "Total SDN-IP routes = %d";
42 + private static final String FORMAT_HEADER =
43 + " Network Next Hop";
42 private static final String FORMAT_ROUTE = 44 private static final String FORMAT_ROUTE =
43 - "prefix=%s, nexthop=%s"; 45 + " %-18s %-15s";
44 46
45 @Override 47 @Override
46 protected void execute() { 48 protected void execute() {
...@@ -81,9 +83,11 @@ public class RoutesListCommand extends AbstractShellCommand { ...@@ -81,9 +83,11 @@ public class RoutesListCommand extends AbstractShellCommand {
81 if (outputJson()) { 83 if (outputJson()) {
82 print("%s", json(routes)); 84 print("%s", json(routes));
83 } else { 85 } else {
86 + print(FORMAT_HEADER);
84 for (RouteEntry route : routes) { 87 for (RouteEntry route : routes) {
85 printRoute(route); 88 printRoute(route);
86 } 89 }
90 + print(FORMAT_SUMMARY, routes.size());
87 } 91 }
88 } 92 }
89 93
......