Pavlin Radoslavov

Updates to the SDN-IP CLI:

 * Added command options to show summary of the routes:
   - "onos:routes -s" or "onos:routes --summary"
     shows summary of the SDN-IP routes
   - "onos:bgp-routes -s" or "onos:bgp-routes --summary"
     shows summary of the BGP routes

 * Implemented displaying JSON output for the "onos:routes" and
   "onos:bgp-routes" commands (and the routes summary)

Also, added static methods BgpConstants.Update.AsPath.typeToString()
and BgpConstants.Update.Origin.typeToString() to return the
BGP AS_PATH type and BGP UPDATE ORIGIN type as a string.

Change-Id: I505c55a924721838bbbaf4ffccc30ffd61e90120
...@@ -119,6 +119,31 @@ public final class BgpConstants { ...@@ -119,6 +119,31 @@ public final class BgpConstants {
119 119
120 /** BGP UPDATE ORIGIN: INCOMPLETE. */ 120 /** BGP UPDATE ORIGIN: INCOMPLETE. */
121 public static final int INCOMPLETE = 2; 121 public static final int INCOMPLETE = 2;
122 +
123 + /**
124 + * Gets the BGP UPDATE origin type as a string.
125 + *
126 + * @param type the BGP UPDATE origin type
127 + * @return the BGP UPDATE origin type as a string
128 + */
129 + public static String typeToString(int type) {
130 + String typeString = "UNKNOWN";
131 +
132 + switch (type) {
133 + case IGP:
134 + typeString = "IGP";
135 + break;
136 + case EGP:
137 + typeString = "EGP";
138 + break;
139 + case INCOMPLETE:
140 + typeString = "INCOMPLETE";
141 + break;
142 + default:
143 + break;
144 + }
145 + return typeString;
146 + }
122 } 147 }
123 148
124 /** 149 /**
...@@ -142,6 +167,28 @@ public final class BgpConstants { ...@@ -142,6 +167,28 @@ public final class BgpConstants {
142 167
143 /** BGP UPDATE AS_PATH Type: AS_SEQUENCE. */ 168 /** BGP UPDATE AS_PATH Type: AS_SEQUENCE. */
144 public static final int AS_SEQUENCE = 2; 169 public static final int AS_SEQUENCE = 2;
170 +
171 + /**
172 + * Gets the BGP AS_PATH type as a string.
173 + *
174 + * @param type the BGP AS_PATH type
175 + * @return the BGP AS_PATH type as a string
176 + */
177 + public static String typeToString(int type) {
178 + String typeString = "UNKNOWN";
179 +
180 + switch (type) {
181 + case AS_SET:
182 + typeString = "AS_SET";
183 + break;
184 + case AS_SEQUENCE:
185 + typeString = "AS_SEQUENCE";
186 + break;
187 + default:
188 + break;
189 + }
190 + return typeString;
191 + }
145 } 192 }
146 193
147 /** 194 /**
......
...@@ -309,7 +309,7 @@ public class BgpRouteEntry extends RouteEntry { ...@@ -309,7 +309,7 @@ public class BgpRouteEntry extends RouteEntry {
309 @Override 309 @Override
310 public String toString() { 310 public String toString() {
311 return MoreObjects.toStringHelper(getClass()) 311 return MoreObjects.toStringHelper(getClass())
312 - .add("type", this.type) 312 + .add("type", BgpConstants.Update.AsPath.typeToString(type))
313 .add("segmentAsNumbers", this.segmentAsNumbers) 313 .add("segmentAsNumbers", this.segmentAsNumbers)
314 .toString(); 314 .toString();
315 } 315 }
...@@ -444,7 +444,7 @@ public class BgpRouteEntry extends RouteEntry { ...@@ -444,7 +444,7 @@ public class BgpRouteEntry extends RouteEntry {
444 .add("prefix", prefix()) 444 .add("prefix", prefix())
445 .add("nextHop", nextHop()) 445 .add("nextHop", nextHop())
446 .add("bgpId", bgpSession.getRemoteBgpId()) 446 .add("bgpId", bgpSession.getRemoteBgpId())
447 - .add("origin", origin) 447 + .add("origin", BgpConstants.Update.Origin.typeToString(origin))
448 .add("asPath", asPath) 448 .add("asPath", asPath)
449 .add("localPref", localPref) 449 .add("localPref", localPref)
450 .add("multiExitDisc", multiExitDisc) 450 .add("multiExitDisc", multiExitDisc)
......
...@@ -15,10 +15,18 @@ ...@@ -15,10 +15,18 @@
15 */ 15 */
16 package org.onlab.onos.sdnip.cli; 16 package org.onlab.onos.sdnip.cli;
17 17
18 +import java.util.Collection;
19 +
20 +import com.fasterxml.jackson.databind.JsonNode;
21 +import com.fasterxml.jackson.databind.ObjectMapper;
22 +import com.fasterxml.jackson.databind.node.ArrayNode;
23 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import org.apache.karaf.shell.commands.Command; 24 import org.apache.karaf.shell.commands.Command;
25 +import org.apache.karaf.shell.commands.Option;
19 import org.onlab.onos.cli.AbstractShellCommand; 26 import org.onlab.onos.cli.AbstractShellCommand;
20 import org.onlab.onos.sdnip.SdnIpService; 27 import org.onlab.onos.sdnip.SdnIpService;
21 -import org.onlab.onos.sdnip.bgp.BgpConstants; 28 +import org.onlab.onos.sdnip.bgp.BgpConstants.Update.AsPath;
29 +import org.onlab.onos.sdnip.bgp.BgpConstants.Update.Origin;
22 import org.onlab.onos.sdnip.bgp.BgpRouteEntry; 30 import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
23 31
24 /** 32 /**
...@@ -27,46 +35,134 @@ import org.onlab.onos.sdnip.bgp.BgpRouteEntry; ...@@ -27,46 +35,134 @@ import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
27 @Command(scope = "onos", name = "bgp-routes", 35 @Command(scope = "onos", name = "bgp-routes",
28 description = "Lists all routes received from BGP") 36 description = "Lists all routes received from BGP")
29 public class BgpRoutesListCommand extends AbstractShellCommand { 37 public class BgpRoutesListCommand extends AbstractShellCommand {
38 + @Option(name = "-s", aliases = "--summary",
39 + description = "BGP routes summary",
40 + required = false, multiValued = false)
41 + private boolean routesSummary = false;
30 42
31 - private static final String FORMAT = 43 + private static final String FORMAT_SUMMARY = "Total BGP routes = %d";
44 + private static final String FORMAT_ROUTE =
32 "prefix=%s, nexthop=%s, origin=%s, localpref=%s, med=%s, aspath=%s, bgpid=%s"; 45 "prefix=%s, nexthop=%s, origin=%s, localpref=%s, med=%s, aspath=%s, bgpid=%s";
33 46
34 @Override 47 @Override
35 protected void execute() { 48 protected void execute() {
36 SdnIpService service = get(SdnIpService.class); 49 SdnIpService service = get(SdnIpService.class);
37 50
38 - for (BgpRouteEntry route : service.getBgpRoutes()) { 51 + // Print summary of the routes
39 - printRoute(route); 52 + if (routesSummary) {
53 + printSummary(service.getBgpRoutes());
54 + return;
55 + }
56 +
57 + // Print all routes
58 + printRoutes(service.getBgpRoutes());
59 + }
60 +
61 + /**
62 + * Prints summary of the routes.
63 + *
64 + * @param routes the routes
65 + */
66 + private void printSummary(Collection<BgpRouteEntry> routes) {
67 + if (outputJson()) {
68 + ObjectMapper mapper = new ObjectMapper();
69 + ObjectNode result = mapper.createObjectNode();
70 + result.put("totalRoutes", routes.size());
71 + print("%s", result);
72 + } else {
73 + print(FORMAT_SUMMARY, routes.size());
74 + }
75 + }
76 +
77 + /**
78 + * Prints all routes.
79 + *
80 + * @param routes the routes to print
81 + */
82 + private void printRoutes(Collection<BgpRouteEntry> routes) {
83 + if (outputJson()) {
84 + print("%s", json(routes));
85 + } else {
86 + for (BgpRouteEntry route : routes) {
87 + printRoute(route);
88 + }
40 } 89 }
41 } 90 }
42 91
92 + /**
93 + * Prints a BGP route.
94 + *
95 + * @param route the route to print
96 + */
43 private void printRoute(BgpRouteEntry route) { 97 private void printRoute(BgpRouteEntry route) {
44 if (route != null) { 98 if (route != null) {
45 - print(FORMAT, route.prefix(), route.nextHop(), 99 + print(FORMAT_ROUTE, route.prefix(), route.nextHop(),
46 - originToString(route.getOrigin()), route.getLocalPref(), 100 + Origin.typeToString(route.getOrigin()),
47 - route.getMultiExitDisc(), route.getAsPath(), 101 + route.getLocalPref(), route.getMultiExitDisc(),
48 - route.getBgpSession().getRemoteBgpId()); 102 + route.getAsPath(), route.getBgpSession().getRemoteBgpId());
49 } 103 }
50 } 104 }
51 105
52 - private static String originToString(int origin) { 106 + /**
53 - String originString = "UNKNOWN"; 107 + * Produces a JSON array of routes.
54 - 108 + *
55 - switch (origin) { 109 + * @param routes the routes with the data
56 - case BgpConstants.Update.Origin.IGP: 110 + * @return JSON array with the routes
57 - originString = "IGP"; 111 + */
58 - break; 112 + private JsonNode json(Collection<BgpRouteEntry> routes) {
59 - case BgpConstants.Update.Origin.EGP: 113 + ObjectMapper mapper = new ObjectMapper();
60 - originString = "EGP"; 114 + ArrayNode result = mapper.createArrayNode();
61 - break; 115 +
62 - case BgpConstants.Update.Origin.INCOMPLETE: 116 + for (BgpRouteEntry route : routes) {
63 - originString = "INCOMPLETE"; 117 + result.add(json(mapper, route));
64 - break;
65 - default:
66 - break;
67 } 118 }
119 + return result;
120 + }
121 +
122 + /**
123 + * Produces JSON object for a route.
124 + *
125 + * @param mapper the JSON object mapper to use
126 + * @param route the route with the data
127 + * @return JSON object for the route
128 + */
129 + private ObjectNode json(ObjectMapper mapper, BgpRouteEntry route) {
130 + ObjectNode result = mapper.createObjectNode();
131 +
132 + result.put("prefix", route.prefix().toString());
133 + result.put("nextHop", route.nextHop().toString());
134 + result.put("bgpId", route.getBgpSession().getRemoteBgpId().toString());
135 + result.put("origin", Origin.typeToString(route.getOrigin()));
136 + result.put("asPath", json(mapper, route.getAsPath()));
137 + result.put("localPref", route.getLocalPref());
138 + result.put("multiExitDisc", route.getMultiExitDisc());
68 139
69 - return originString; 140 + return result;
70 } 141 }
71 142
143 + /**
144 + * Produces JSON object for an AS path.
145 + *
146 + * @param mapper the JSON object mapper to use
147 + * @param asPath the AS path with the data
148 + * @return JSON object for the AS path
149 + */
150 + private ObjectNode json(ObjectMapper mapper, BgpRouteEntry.AsPath asPath) {
151 + ObjectNode result = mapper.createObjectNode();
152 + ArrayNode pathSegmentsJson = mapper.createArrayNode();
153 + for (BgpRouteEntry.PathSegment pathSegment : asPath.getPathSegments()) {
154 + ObjectNode pathSegmentJson = mapper.createObjectNode();
155 + pathSegmentJson.put("type",
156 + AsPath.typeToString(pathSegment.getType()));
157 + ArrayNode segmentAsNumbersJson = mapper.createArrayNode();
158 + for (Long asNumber : pathSegment.getSegmentAsNumbers()) {
159 + segmentAsNumbersJson.add(asNumber);
160 + }
161 + pathSegmentJson.put("segmentAsNumbers", segmentAsNumbersJson);
162 + pathSegmentsJson.add(pathSegmentJson);
163 + }
164 + result.put("pathSegments", pathSegmentsJson);
165 +
166 + return result;
167 + }
72 } 168 }
......
...@@ -15,7 +15,14 @@ ...@@ -15,7 +15,14 @@
15 */ 15 */
16 package org.onlab.onos.sdnip.cli; 16 package org.onlab.onos.sdnip.cli;
17 17
18 +import java.util.Collection;
19 +
20 +import com.fasterxml.jackson.databind.JsonNode;
21 +import com.fasterxml.jackson.databind.ObjectMapper;
22 +import com.fasterxml.jackson.databind.node.ArrayNode;
23 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import org.apache.karaf.shell.commands.Command; 24 import org.apache.karaf.shell.commands.Command;
25 +import org.apache.karaf.shell.commands.Option;
19 import org.onlab.onos.cli.AbstractShellCommand; 26 import org.onlab.onos.cli.AbstractShellCommand;
20 import org.onlab.onos.sdnip.RouteEntry; 27 import org.onlab.onos.sdnip.RouteEntry;
21 import org.onlab.onos.sdnip.SdnIpService; 28 import org.onlab.onos.sdnip.SdnIpService;
...@@ -26,22 +33,100 @@ import org.onlab.onos.sdnip.SdnIpService; ...@@ -26,22 +33,100 @@ import org.onlab.onos.sdnip.SdnIpService;
26 @Command(scope = "onos", name = "routes", 33 @Command(scope = "onos", name = "routes",
27 description = "Lists all routes known to SDN-IP") 34 description = "Lists all routes known to SDN-IP")
28 public class RoutesListCommand extends AbstractShellCommand { 35 public class RoutesListCommand extends AbstractShellCommand {
36 + @Option(name = "-s", aliases = "--summary",
37 + description = "SDN-IP routes summary",
38 + required = false, multiValued = false)
39 + private boolean routesSummary = false;
29 40
30 - private static final String FORMAT = 41 + private static final String FORMAT_SUMMARY = "Total SDN-IP routes = %d";
42 + private static final String FORMAT_ROUTE =
31 "prefix=%s, nexthop=%s"; 43 "prefix=%s, nexthop=%s";
32 44
33 @Override 45 @Override
34 protected void execute() { 46 protected void execute() {
35 SdnIpService service = get(SdnIpService.class); 47 SdnIpService service = get(SdnIpService.class);
36 48
37 - for (RouteEntry route : service.getRoutes()) { 49 + // Print summary of the routes
38 - printRoute(route); 50 + if (routesSummary) {
51 + printSummary(service.getRoutes());
52 + return;
53 + }
54 +
55 + // Print all routes
56 + printRoutes(service.getRoutes());
57 + }
58 +
59 + /**
60 + * Prints summary of the routes.
61 + *
62 + * @param routes the routes
63 + */
64 + private void printSummary(Collection<RouteEntry> routes) {
65 + if (outputJson()) {
66 + ObjectMapper mapper = new ObjectMapper();
67 + ObjectNode result = mapper.createObjectNode();
68 + result.put("totalRoutes", routes.size());
69 + print("%s", result);
70 + } else {
71 + print(FORMAT_SUMMARY, routes.size());
72 + }
73 + }
74 +
75 + /**
76 + * Prints all routes.
77 + *
78 + * @param routes the routes to print
79 + */
80 + private void printRoutes(Collection<RouteEntry> routes) {
81 + if (outputJson()) {
82 + print("%s", json(routes));
83 + } else {
84 + for (RouteEntry route : routes) {
85 + printRoute(route);
86 + }
39 } 87 }
40 } 88 }
41 89
90 + /**
91 + * Prints a route.
92 + *
93 + * @param route the route to print
94 + */
42 private void printRoute(RouteEntry route) { 95 private void printRoute(RouteEntry route) {
43 if (route != null) { 96 if (route != null) {
44 - print(FORMAT, route.prefix(), route.nextHop()); 97 + print(FORMAT_ROUTE, route.prefix(), route.nextHop());
45 } 98 }
46 } 99 }
100 +
101 + /**
102 + * Produces a JSON array of routes.
103 + *
104 + * @param routes the routes with the data
105 + * @return JSON array with the routes
106 + */
107 + private JsonNode json(Collection<RouteEntry> routes) {
108 + ObjectMapper mapper = new ObjectMapper();
109 + ArrayNode result = mapper.createArrayNode();
110 +
111 + for (RouteEntry route : routes) {
112 + result.add(json(mapper, route));
113 + }
114 + return result;
115 + }
116 +
117 + /**
118 + * Produces JSON object for a route.
119 + *
120 + * @param mapper the JSON object mapper to use
121 + * @param route the route with the data
122 + * @return JSON object for the route
123 + */
124 + private ObjectNode json(ObjectMapper mapper, RouteEntry route) {
125 + ObjectNode result = mapper.createObjectNode();
126 +
127 + result.put("prefix", route.prefix().toString());
128 + result.put("nextHop", route.nextHop().toString());
129 +
130 + return result;
131 + }
47 } 132 }
......