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
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpConstants.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpConstants.java
index 92f4f07..596720c 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpConstants.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpConstants.java
@@ -119,6 +119,31 @@
/** BGP UPDATE ORIGIN: INCOMPLETE. */
public static final int INCOMPLETE = 2;
+
+ /**
+ * Gets the BGP UPDATE origin type as a string.
+ *
+ * @param type the BGP UPDATE origin type
+ * @return the BGP UPDATE origin type as a string
+ */
+ public static String typeToString(int type) {
+ String typeString = "UNKNOWN";
+
+ switch (type) {
+ case IGP:
+ typeString = "IGP";
+ break;
+ case EGP:
+ typeString = "EGP";
+ break;
+ case INCOMPLETE:
+ typeString = "INCOMPLETE";
+ break;
+ default:
+ break;
+ }
+ return typeString;
+ }
}
/**
@@ -142,6 +167,28 @@
/** BGP UPDATE AS_PATH Type: AS_SEQUENCE. */
public static final int AS_SEQUENCE = 2;
+
+ /**
+ * Gets the BGP AS_PATH type as a string.
+ *
+ * @param type the BGP AS_PATH type
+ * @return the BGP AS_PATH type as a string
+ */
+ public static String typeToString(int type) {
+ String typeString = "UNKNOWN";
+
+ switch (type) {
+ case AS_SET:
+ typeString = "AS_SET";
+ break;
+ case AS_SEQUENCE:
+ typeString = "AS_SEQUENCE";
+ break;
+ default:
+ break;
+ }
+ return typeString;
+ }
}
/**
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java
index cd36f72..203e03c 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java
@@ -309,7 +309,7 @@
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
- .add("type", this.type)
+ .add("type", BgpConstants.Update.AsPath.typeToString(type))
.add("segmentAsNumbers", this.segmentAsNumbers)
.toString();
}
@@ -444,7 +444,7 @@
.add("prefix", prefix())
.add("nextHop", nextHop())
.add("bgpId", bgpSession.getRemoteBgpId())
- .add("origin", origin)
+ .add("origin", BgpConstants.Update.Origin.typeToString(origin))
.add("asPath", asPath)
.add("localPref", localPref)
.add("multiExitDisc", multiExitDisc)
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/cli/BgpRoutesListCommand.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/cli/BgpRoutesListCommand.java
index 260cfe6..15cb554 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/cli/BgpRoutesListCommand.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/cli/BgpRoutesListCommand.java
@@ -15,10 +15,18 @@
*/
package org.onlab.onos.sdnip.cli;
+import java.util.Collection;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.sdnip.SdnIpService;
-import org.onlab.onos.sdnip.bgp.BgpConstants;
+import org.onlab.onos.sdnip.bgp.BgpConstants.Update.AsPath;
+import org.onlab.onos.sdnip.bgp.BgpConstants.Update.Origin;
import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
/**
@@ -27,46 +35,134 @@
@Command(scope = "onos", name = "bgp-routes",
description = "Lists all routes received from BGP")
public class BgpRoutesListCommand extends AbstractShellCommand {
+ @Option(name = "-s", aliases = "--summary",
+ description = "BGP routes summary",
+ required = false, multiValued = false)
+ private boolean routesSummary = false;
- private static final String FORMAT =
+ private static final String FORMAT_SUMMARY = "Total BGP routes = %d";
+ private static final String FORMAT_ROUTE =
"prefix=%s, nexthop=%s, origin=%s, localpref=%s, med=%s, aspath=%s, bgpid=%s";
@Override
protected void execute() {
SdnIpService service = get(SdnIpService.class);
- for (BgpRouteEntry route : service.getBgpRoutes()) {
- printRoute(route);
+ // Print summary of the routes
+ if (routesSummary) {
+ printSummary(service.getBgpRoutes());
+ return;
+ }
+
+ // Print all routes
+ printRoutes(service.getBgpRoutes());
+ }
+
+ /**
+ * Prints summary of the routes.
+ *
+ * @param routes the routes
+ */
+ private void printSummary(Collection<BgpRouteEntry> routes) {
+ if (outputJson()) {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode result = mapper.createObjectNode();
+ result.put("totalRoutes", routes.size());
+ print("%s", result);
+ } else {
+ print(FORMAT_SUMMARY, routes.size());
}
}
+ /**
+ * Prints all routes.
+ *
+ * @param routes the routes to print
+ */
+ private void printRoutes(Collection<BgpRouteEntry> routes) {
+ if (outputJson()) {
+ print("%s", json(routes));
+ } else {
+ for (BgpRouteEntry route : routes) {
+ printRoute(route);
+ }
+ }
+ }
+
+ /**
+ * Prints a BGP route.
+ *
+ * @param route the route to print
+ */
private void printRoute(BgpRouteEntry route) {
if (route != null) {
- print(FORMAT, route.prefix(), route.nextHop(),
- originToString(route.getOrigin()), route.getLocalPref(),
- route.getMultiExitDisc(), route.getAsPath(),
- route.getBgpSession().getRemoteBgpId());
+ print(FORMAT_ROUTE, route.prefix(), route.nextHop(),
+ Origin.typeToString(route.getOrigin()),
+ route.getLocalPref(), route.getMultiExitDisc(),
+ route.getAsPath(), route.getBgpSession().getRemoteBgpId());
}
}
- private static String originToString(int origin) {
- String originString = "UNKNOWN";
+ /**
+ * Produces a JSON array of routes.
+ *
+ * @param routes the routes with the data
+ * @return JSON array with the routes
+ */
+ private JsonNode json(Collection<BgpRouteEntry> routes) {
+ ObjectMapper mapper = new ObjectMapper();
+ ArrayNode result = mapper.createArrayNode();
- switch (origin) {
- case BgpConstants.Update.Origin.IGP:
- originString = "IGP";
- break;
- case BgpConstants.Update.Origin.EGP:
- originString = "EGP";
- break;
- case BgpConstants.Update.Origin.INCOMPLETE:
- originString = "INCOMPLETE";
- break;
- default:
- break;
+ for (BgpRouteEntry route : routes) {
+ result.add(json(mapper, route));
}
-
- return originString;
+ return result;
}
+ /**
+ * Produces JSON object for a route.
+ *
+ * @param mapper the JSON object mapper to use
+ * @param route the route with the data
+ * @return JSON object for the route
+ */
+ private ObjectNode json(ObjectMapper mapper, BgpRouteEntry route) {
+ ObjectNode result = mapper.createObjectNode();
+
+ result.put("prefix", route.prefix().toString());
+ result.put("nextHop", route.nextHop().toString());
+ result.put("bgpId", route.getBgpSession().getRemoteBgpId().toString());
+ result.put("origin", Origin.typeToString(route.getOrigin()));
+ result.put("asPath", json(mapper, route.getAsPath()));
+ result.put("localPref", route.getLocalPref());
+ result.put("multiExitDisc", route.getMultiExitDisc());
+
+ return result;
+ }
+
+ /**
+ * Produces JSON object for an AS path.
+ *
+ * @param mapper the JSON object mapper to use
+ * @param asPath the AS path with the data
+ * @return JSON object for the AS path
+ */
+ private ObjectNode json(ObjectMapper mapper, BgpRouteEntry.AsPath asPath) {
+ ObjectNode result = mapper.createObjectNode();
+ ArrayNode pathSegmentsJson = mapper.createArrayNode();
+ for (BgpRouteEntry.PathSegment pathSegment : asPath.getPathSegments()) {
+ ObjectNode pathSegmentJson = mapper.createObjectNode();
+ pathSegmentJson.put("type",
+ AsPath.typeToString(pathSegment.getType()));
+ ArrayNode segmentAsNumbersJson = mapper.createArrayNode();
+ for (Long asNumber : pathSegment.getSegmentAsNumbers()) {
+ segmentAsNumbersJson.add(asNumber);
+ }
+ pathSegmentJson.put("segmentAsNumbers", segmentAsNumbersJson);
+ pathSegmentsJson.add(pathSegmentJson);
+ }
+ result.put("pathSegments", pathSegmentsJson);
+
+ return result;
+ }
}
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/cli/RoutesListCommand.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/cli/RoutesListCommand.java
index 0c44453..e9c25c4 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/cli/RoutesListCommand.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/cli/RoutesListCommand.java
@@ -15,7 +15,14 @@
*/
package org.onlab.onos.sdnip.cli;
+import java.util.Collection;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.sdnip.RouteEntry;
import org.onlab.onos.sdnip.SdnIpService;
@@ -26,22 +33,100 @@
@Command(scope = "onos", name = "routes",
description = "Lists all routes known to SDN-IP")
public class RoutesListCommand extends AbstractShellCommand {
+ @Option(name = "-s", aliases = "--summary",
+ description = "SDN-IP routes summary",
+ required = false, multiValued = false)
+ private boolean routesSummary = false;
- private static final String FORMAT =
+ private static final String FORMAT_SUMMARY = "Total SDN-IP routes = %d";
+ private static final String FORMAT_ROUTE =
"prefix=%s, nexthop=%s";
@Override
protected void execute() {
SdnIpService service = get(SdnIpService.class);
- for (RouteEntry route : service.getRoutes()) {
- printRoute(route);
+ // Print summary of the routes
+ if (routesSummary) {
+ printSummary(service.getRoutes());
+ return;
+ }
+
+ // Print all routes
+ printRoutes(service.getRoutes());
+ }
+
+ /**
+ * Prints summary of the routes.
+ *
+ * @param routes the routes
+ */
+ private void printSummary(Collection<RouteEntry> routes) {
+ if (outputJson()) {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode result = mapper.createObjectNode();
+ result.put("totalRoutes", routes.size());
+ print("%s", result);
+ } else {
+ print(FORMAT_SUMMARY, routes.size());
}
}
+ /**
+ * Prints all routes.
+ *
+ * @param routes the routes to print
+ */
+ private void printRoutes(Collection<RouteEntry> routes) {
+ if (outputJson()) {
+ print("%s", json(routes));
+ } else {
+ for (RouteEntry route : routes) {
+ printRoute(route);
+ }
+ }
+ }
+
+ /**
+ * Prints a route.
+ *
+ * @param route the route to print
+ */
private void printRoute(RouteEntry route) {
if (route != null) {
- print(FORMAT, route.prefix(), route.nextHop());
+ print(FORMAT_ROUTE, route.prefix(), route.nextHop());
}
}
+
+ /**
+ * Produces a JSON array of routes.
+ *
+ * @param routes the routes with the data
+ * @return JSON array with the routes
+ */
+ private JsonNode json(Collection<RouteEntry> routes) {
+ ObjectMapper mapper = new ObjectMapper();
+ ArrayNode result = mapper.createArrayNode();
+
+ for (RouteEntry route : routes) {
+ result.add(json(mapper, route));
+ }
+ return result;
+ }
+
+ /**
+ * Produces JSON object for a route.
+ *
+ * @param mapper the JSON object mapper to use
+ * @param route the route with the data
+ * @return JSON object for the route
+ */
+ private ObjectNode json(ObjectMapper mapper, RouteEntry route) {
+ ObjectNode result = mapper.createObjectNode();
+
+ result.put("prefix", route.prefix().toString());
+ result.put("nextHop", route.nextHop().toString());
+
+ return result;
+ }
}