tom

Adding JSON format to the CLI. Intents and flows still need to be done.

......@@ -26,6 +26,16 @@
<groupId>org.onlab.onos</groupId>
<artifactId>onlab-osgi</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
......
package org.onlab.onos.cli;
import org.apache.karaf.shell.commands.Option;
import org.apache.karaf.shell.console.OsgiCommandSupport;
import org.onlab.osgi.DefaultServiceDirectory;
import org.onlab.osgi.ServiceNotFoundException;
......@@ -9,6 +10,10 @@ import org.onlab.osgi.ServiceNotFoundException;
*/
public abstract class AbstractShellCommand extends OsgiCommandSupport {
@Option(name = "-j", aliases = "--json", description = "Output JSON",
required = false, multiValued = false)
private boolean json = false;
/**
* Returns the reference to the implementation of the specified service.
*
......@@ -46,6 +51,15 @@ public abstract class AbstractShellCommand extends OsgiCommandSupport {
*/
protected abstract void execute();
/**
* Indicates whether JSON format should be output.
*
* @return true if JSON is requested
*/
protected boolean outputJson() {
return json;
}
@Override
protected Object doExecute() throws Exception {
try {
......
package org.onlab.onos.cli;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.Lists;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.ControllerNode;
......@@ -26,7 +28,10 @@ public class MastersListCommand extends AbstractShellCommand {
MastershipService mastershipService = get(MastershipService.class);
List<ControllerNode> nodes = newArrayList(service.getNodes());
Collections.sort(nodes, Comparators.NODE_COMPARATOR);
ControllerNode self = service.getLocalNode();
if (outputJson()) {
print("%s", json(service, mastershipService, nodes));
} else {
for (ControllerNode node : nodes) {
List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id()));
Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR);
......@@ -36,5 +41,37 @@ public class MastersListCommand extends AbstractShellCommand {
}
}
}
}
// Produces JSON structure.
private JsonNode json(ClusterService service, MastershipService mastershipService,
List<ControllerNode> nodes) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
ControllerNode self = service.getLocalNode();
for (ControllerNode node : nodes) {
List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id()));
result.add(mapper.createObjectNode()
.put("id", node.id().toString())
.put("size", ids.size())
.set("devices", json(mapper, ids)));
}
return result;
}
/**
* Produces a JSON array containing the specified device identifiers.
*
* @param mapper object mapper
* @param ids collection of device identifiers
* @return JSON array
*/
public static JsonNode json(ObjectMapper mapper, Iterable<DeviceId> ids) {
ArrayNode result = mapper.createArrayNode();
for (DeviceId deviceId : ids) {
result.add(deviceId.toString());
}
return result;
}
}
......
package org.onlab.onos.cli;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.ControllerNode;
......@@ -24,6 +27,9 @@ public class NodesListCommand extends AbstractShellCommand {
ClusterService service = get(ClusterService.class);
List<ControllerNode> nodes = newArrayList(service.getNodes());
Collections.sort(nodes, Comparators.NODE_COMPARATOR);
if (outputJson()) {
print("%s", json(service, nodes));
} else {
ControllerNode self = service.getLocalNode();
for (ControllerNode node : nodes) {
print(FMT, node.id(), node.ip(), node.tcpPort(),
......@@ -31,5 +37,22 @@ public class NodesListCommand extends AbstractShellCommand {
node.equals(self) ? "*" : "");
}
}
}
// Produces JSON structure.
private JsonNode json(ClusterService service, List<ControllerNode> nodes) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
ControllerNode self = service.getLocalNode();
for (ControllerNode node : nodes) {
result.add(mapper.createObjectNode()
.put("id", node.id().toString())
.put("ip", node.ip().toString())
.put("tcpPort", node.tcpPort())
.put("state", service.getState(node.id()).toString())
.put("self", node.equals(self)));
}
return result;
}
}
......
package org.onlab.onos.cli;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.CoreService;
import org.onlab.onos.cluster.ClusterService;
......@@ -22,6 +23,19 @@ public class SummaryCommand extends AbstractShellCommand {
protected void execute() {
TopologyService topologyService = get(TopologyService.class);
Topology topology = topologyService.currentTopology();
if (outputJson()) {
print("%s", new ObjectMapper().createObjectNode()
.put("node", get(ClusterService.class).getLocalNode().ip().toString())
.put("version", get(CoreService.class).version().toString())
.put("nodes", get(ClusterService.class).getNodes().size())
.put("devices", get(DeviceService.class).getDeviceCount())
.put("links", get(LinkService.class).getLinkCount())
.put("hosts", get(HostService.class).getHostCount())
.put("clusters", topologyService.getClusters(topology).size())
.put("paths", topology.pathCount())
.put("flows", get(FlowRuleService.class).getFlowRuleCount())
.put("intents", get(IntentService.class).getIntentCount()));
} else {
print("node=%s, version=%s",
get(ClusterService.class).getLocalNode().ip(),
get(CoreService.class).version().toString());
......@@ -35,5 +49,6 @@ public class SummaryCommand extends AbstractShellCommand {
get(FlowRuleService.class).getFlowRuleCount(),
get(IntentService.class).getIntentCount());
}
}
}
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
......@@ -10,6 +11,7 @@ import org.onlab.onos.net.topology.TopologyCluster;
import java.util.Collections;
import java.util.List;
import static org.onlab.onos.cli.MastersListCommand.json;
import static org.onlab.onos.net.topology.ClusterId.clusterId;
/**
......@@ -33,11 +35,14 @@ public class ClusterDevicesCommand extends ClustersListCommand {
} else {
List<DeviceId> ids = Lists.newArrayList(service.getClusterDevices(topology, cluster));
Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR);
if (outputJson()) {
print("%s", json(new ObjectMapper(), ids));
} else {
for (DeviceId deviceId : ids) {
print("%s", deviceId);
}
}
}
}
}
......
......@@ -5,6 +5,7 @@ import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.topology.TopologyCluster;
import static org.onlab.onos.cli.net.LinksListCommand.json;
import static org.onlab.onos.cli.net.LinksListCommand.linkString;
import static org.onlab.onos.net.topology.ClusterId.clusterId;
......@@ -26,6 +27,8 @@ public class ClusterLinksCommand extends ClustersListCommand {
TopologyCluster cluster = service.getCluster(topology, clusterId(cid));
if (cluster == null) {
error("No such cluster %s", cid);
} else if (outputJson()) {
print("%s", json(service.getClusterLinks(topology, cluster)));
} else {
for (Link link : service.getClusterLinks(topology, cluster)) {
print(linkString(link));
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.google.common.collect.Lists;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.Comparators;
......@@ -24,9 +27,26 @@ public class ClustersListCommand extends TopologyCommand {
List<TopologyCluster> clusters = Lists.newArrayList(service.getClusters(topology));
Collections.sort(clusters, Comparators.CLUSTER_COMPARATOR);
if (outputJson()) {
print("%s", json(clusters));
} else {
for (TopologyCluster cluster : clusters) {
print(FMT, cluster.id().index(), cluster.deviceCount(), cluster.linkCount());
}
}
}
// Produces a JSON result.
private JsonNode json(Iterable<TopologyCluster> clusters) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (TopologyCluster cluster : clusters) {
result.add(mapper.createObjectNode()
.put("id", cluster.id().index())
.put("deviceCount", cluster.deviceCount())
.put("linkCount", cluster.linkCount()));
}
return result;
}
}
......
package org.onlab.onos.cli.net;
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.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.Comparators;
......@@ -30,19 +34,61 @@ public class DevicePortsListCommand extends DevicesListCommand {
protected void execute() {
DeviceService service = get(DeviceService.class);
if (uri == null) {
if (outputJson()) {
print("%s", jsonPorts(service, getSortedDevices(service)));
} else {
for (Device device : getSortedDevices(service)) {
printDevice(service, device);
}
}
} else {
Device device = service.getDevice(deviceId(uri));
if (device == null) {
error("No such device %s", uri);
} else if (outputJson()) {
print("%s", jsonPorts(service, new ObjectMapper(), device));
} else {
printDevice(service, device);
}
}
}
/**
* Produces JSON array containing ports of the specified devices.
*
* @param service device service
* @param devices collection of devices
* @return JSON array
*/
public static JsonNode jsonPorts(DeviceService service, Iterable<Device> devices) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (Device device : devices) {
result.add(jsonPorts(service, mapper, device));
}
return result;
}
/**
* Produces JSON array containing ports of the specified device.
*
* @param service device service
* @param mapper object mapper
* @param device infrastructure devices
* @return JSON array
*/
public static JsonNode jsonPorts(DeviceService service, ObjectMapper mapper, Device device) {
ObjectNode result = mapper.createObjectNode();
ArrayNode ports = mapper.createArrayNode();
for (Port port : service.getPorts(device.id())) {
ports.add(mapper.createObjectNode()
.put("port", port.number().toString())
.put("isEnabled", port.isEnabled()));
}
return result.put("device", device.id().toString()).set("ports", ports);
}
@Override
protected void printDevice(DeviceService service, Device device) {
super.printDevice(service, device);
......
package org.onlab.onos.cli.net;
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.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.cli.Comparators;
......@@ -24,10 +28,53 @@ public class DevicesListCommand extends AbstractShellCommand {
@Override
protected void execute() {
DeviceService service = get(DeviceService.class);
if (outputJson()) {
print("%s", json(service, getSortedDevices(service)));
} else {
for (Device device : getSortedDevices(service)) {
printDevice(service, device);
}
}
}
/**
* Returns JSON node representing the specified devices.
*
* @param service device service
* @param devices collection of devices
* @return JSON node
*/
public static JsonNode json(DeviceService service, Iterable<Device> devices) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (Device device : devices) {
result.add(json(service, mapper, device));
}
return result;
}
/**
* Returns JSON node representing the specified device.
*
* @param service device service
* @param mapper object mapper
* @param device infrastructure device
* @return JSON node
*/
public static ObjectNode json(DeviceService service, ObjectMapper mapper,
Device device) {
ObjectNode result = mapper.createObjectNode();
if (device != null) {
result.put("id", device.id().toString())
.put("available", service.isAvailable(device.id()))
.put("role", service.getRole(device.id()).toString())
.put("mfr", device.manufacturer())
.put("hw", device.hwVersion())
.put("sw", device.swVersion())
.put("serial", device.serialNumber());
}
return result;
}
/**
* Returns the list of devices sorted using the device ID URIs.
......
package org.onlab.onos.cli.net;
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.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.cli.Comparators;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.host.HostService;
import org.onlab.packet.IpPrefix;
import java.util.Collections;
import java.util.List;
......@@ -24,10 +29,41 @@ public class HostsListCommand extends AbstractShellCommand {
@Override
protected void execute() {
HostService service = get(HostService.class);
if (outputJson()) {
print("%s", json(getSortedHosts(service)));
} else {
for (Host host : getSortedHosts(service)) {
printHost(host);
}
}
}
// Produces JSON structure.
private static JsonNode json(Iterable<Host> hosts) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (Host host : hosts) {
result.add(json(mapper, host));
}
return result;
}
// Produces JSON structure.
private static JsonNode json(ObjectMapper mapper, Host host) {
ObjectNode loc = LinksListCommand.json(mapper, host.location())
.put("time", host.location().time());
ArrayNode ips = mapper.createArrayNode();
for (IpPrefix ip : host.ipAddresses()) {
ips.add(ip.toString());
}
ObjectNode result = mapper.createObjectNode()
.put("id", host.id().toString())
.put("mac", host.mac().toString())
.put("vlan", host.vlan().toString());
result.set("location", loc);
result.set("ips", ips);
return result;
}
/**
* Returns the list of devices sorted using the device ID URIs.
......@@ -44,7 +80,7 @@ public class HostsListCommand extends AbstractShellCommand {
/**
* Prints information about a host.
*
* @param host
* @param host end-station host
*/
protected void printHost(Host host) {
if (host != null) {
......@@ -54,4 +90,4 @@ public class HostsListCommand extends AbstractShellCommand {
host.vlan(), host.ipAddresses());
}
}
}
}
......
package org.onlab.onos.cli.net;
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.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.link.LinkService;
......@@ -27,10 +32,56 @@ public class LinksListCommand extends AbstractShellCommand {
LinkService service = get(LinkService.class);
Iterable<Link> links = uri != null ?
service.getDeviceLinks(deviceId(uri)) : service.getLinks();
if (outputJson()) {
print("%s", json(links));
} else {
for (Link link : links) {
print(linkString(link));
}
}
}
/**
* Produces a JSON array containing the specified links.
*
* @param links collection of links
* @return JSON array
*/
public static JsonNode json(Iterable<Link> links) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (Link link : links) {
result.add(json(mapper, link));
}
return result;
}
/**
* Produces a JSON object for the specified link.
*
* @param mapper object mapper
* @param link link to encode
* @return JSON object
*/
public static ObjectNode json(ObjectMapper mapper, Link link) {
ObjectNode result = mapper.createObjectNode();
result.set("src", json(mapper, link.src()));
result.set("dst", json(mapper, link.src()));
return result;
}
/**
* Produces a JSON object for the specified connect point.
*
* @param mapper object mapper
* @param connectPoint connection point to encode
* @return JSON object
*/
public static ObjectNode json(ObjectMapper mapper, ConnectPoint connectPoint) {
return mapper.createObjectNode()
.put("device", connectPoint.deviceId().toString())
.put("port", connectPoint.port().toString());
}
/**
* Returns a formatted string representing the given link.
......
package org.onlab.onos.cli.net;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.net.topology.Topology;
......@@ -30,8 +31,17 @@ public class TopologyCommand extends AbstractShellCommand {
@Override
protected void execute() {
init();
if (outputJson()) {
print("%s", new ObjectMapper().createObjectNode()
.put("time", topology.time())
.put("deviceCount", topology.deviceCount())
.put("linkCount", topology.linkCount())
.put("clusterCount", topology.clusterCount())
.put("pathCount", topology.pathCount()));
} else {
print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(),
topology.clusterCount(), topology.pathCount());
}
}
}
......