Topology REST API modifications and unit tests
- added a Serializer for Topology operations
- added a REST test harness class for Topology tests
- added REST API tests for topology get of all
topology, links, switches, and devices.
Change-Id: I13e3654aad7c0f5af41f07be66da82330933d3a3
diff --git a/src/main/java/net/onrc/onos/core/topology/Topology.java b/src/main/java/net/onrc/onos/core/topology/Topology.java
index 3b90f89..844507d 100644
--- a/src/main/java/net/onrc/onos/core/topology/Topology.java
+++ b/src/main/java/net/onrc/onos/core/topology/Topology.java
@@ -1,12 +1,15 @@
package net.onrc.onos.core.topology;
import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.core.topology.serializers.TopologySerializer;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
/**
* The northbound interface to the topology. This interface
* is presented to the rest of ONOS. It is currently read-only, as we want
* only the discovery modules to be allowed to modify the topology.
*/
+@JsonSerialize(using = TopologySerializer.class)
public interface Topology {
/**
* Get the switch for a given switch DPID.
diff --git a/src/main/java/net/onrc/onos/core/topology/serializers/TopologySerializer.java b/src/main/java/net/onrc/onos/core/topology/serializers/TopologySerializer.java
new file mode 100644
index 0000000..1bc4ff2
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/topology/serializers/TopologySerializer.java
@@ -0,0 +1,68 @@
+package net.onrc.onos.core.topology.serializers;
+
+import net.onrc.onos.core.topology.Device;
+import net.onrc.onos.core.topology.Link;
+import net.onrc.onos.core.topology.Switch;
+import net.onrc.onos.core.topology.Topology;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.SerializerProvider;
+import org.codehaus.jackson.map.ser.std.SerializerBase;
+
+import java.io.IOException;
+
+/**
+ * JSON serializer for Topology objects. Used by REST implementation of the
+ * topology APIs.
+ */
+public class TopologySerializer extends SerializerBase<Topology> {
+
+ /**
+ * Default constructor. Performs basic initialization of the JSON
+ * serializer.
+ */
+ public TopologySerializer() {
+ super(Topology.class);
+ }
+
+ /**
+ * Serialize a Topology object in JSON. The resulting JSON contains the
+ * switches, links and ports provided by the Topology object.
+ *
+ * @param topology the Topology that is being converted to JSON
+ * @param jsonGenerator generator to place the serialized JSON into
+ * @param serializerProvider unused but required for method override
+ * @throws IOException if the JSON serialization process fails
+ */
+ @Override
+ public void serialize(Topology topology,
+ JsonGenerator jsonGenerator,
+ SerializerProvider serializerProvider)
+ throws IOException {
+ // Start the object
+ jsonGenerator.writeStartObject();
+
+ // Output the switches array
+ jsonGenerator.writeArrayFieldStart("switches");
+ for (final Switch swtch : topology.getSwitches()) {
+ jsonGenerator.writeObject(swtch);
+ }
+ jsonGenerator.writeEndArray();
+
+ // Output the links array
+ jsonGenerator.writeArrayFieldStart("links");
+ for (final Link link : topology.getLinks()) {
+ jsonGenerator.writeObject(link);
+ }
+ jsonGenerator.writeEndArray();
+
+ // Output the devices array
+ jsonGenerator.writeArrayFieldStart("devices");
+ for (final Device device : topology.getDevices()) {
+ jsonGenerator.writeObject(device);
+ }
+ jsonGenerator.writeEndArray();
+
+ // All done
+ jsonGenerator.writeEndObject();
+ }
+}
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestTopology.java b/src/test/java/net/onrc/onos/api/rest/TestRestTopology.java
new file mode 100644
index 0000000..e87d232
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestTopology.java
@@ -0,0 +1,109 @@
+package net.onrc.onos.api.rest;
+
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.onrc.onos.core.intent.runtime.IntentTestMocks;
+import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
+import net.onrc.onos.core.intent.runtime.web.IntentWebRoutable;
+import net.onrc.onos.core.topology.ITopologyService;
+import net.onrc.onos.core.topology.web.TopologyWebRoutable;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.restlet.resource.ClientResource;
+
+/**
+ * Test harness for Topology based REST API tests. This class maintains the
+ * web server and mocks required for testing topology APIs. REST API tests
+ * for topology should inherit from this class.
+ */
+public class TestRestTopology extends TestRest {
+
+ private IntentTestMocks mocks;
+
+ /**
+ * Fetch the Intent mocking object.
+ *
+ * @return intent mocking object
+ */
+ IntentTestMocks getMocks() {
+ return mocks;
+ }
+
+ /**
+ * Create the web server and mocks required for the topology tests.
+ */
+ @Override
+ public void setUp() {
+ mocks = new IntentTestMocks();
+ mocks.setUpIntentMocks();
+
+ addRestlet(new TopologyWebRoutable());
+ super.setUp();
+
+ final PathCalcRuntimeModule runtime = new PathCalcRuntimeModule();
+ final FloodlightModuleContext moduleContext = getMocks().getModuleContext();
+ try {
+ runtime.init(moduleContext);
+ } catch (FloodlightModuleException floodlightEx) {
+ throw new IllegalArgumentException(floodlightEx);
+ }
+ runtime.startUp(moduleContext);
+
+ getRestApiServer().addAttribute(ITopologyService.class.getCanonicalName(),
+ mocks.getTopologyService());
+ }
+
+ /**
+ * Remove anything that will interfere with the next test running correctly.
+ * Shuts down the test REST web server and removes the mocks.
+ */
+ @Override
+ public void tearDown() {
+ getMocks().tearDownIntentMocks();
+ super.tearDown();
+ }
+
+ /**
+ * Fetch the base URL for Topology REST APIs.
+ *
+ * @return base URL
+ */
+ String getBaseRestTopologyUrl() {
+ return getBaseRestUrl() + "/topology";
+ }
+
+ /**
+ * Get the JSON object representation for the top level object referred
+ * to by the given client.
+ *
+ * @param client the ClientResource that references the JSON object
+ * @return JSONObject that represents the object, null if it can't be
+ * fetched
+ */
+ JSONObject getJSONObject(final ClientResource client) {
+ try {
+ final String topologyJSONString = client.get(String.class);
+ return new JSONObject(topologyJSONString);
+ } catch (JSONException jsonException) {
+ return null;
+ }
+ }
+
+ /**
+ * Get the JSON array representation for the array referred to by
+ * the given client.
+ *
+ * @param client the ClientResource that references the JSON array
+ * @return JSONArray that represents the array, null if it can't be
+ * fetched.
+ */
+ JSONArray getJSONArray(final ClientResource client) {
+ try {
+ final String topologyJSONString = client.get(String.class);
+ return new JSONArray(topologyJSONString);
+ } catch (JSONException jsonException) {
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/net/onrc/onos/api/rest/TestRestTopologyGet.java b/src/test/java/net/onrc/onos/api/rest/TestRestTopologyGet.java
new file mode 100644
index 0000000..eb8ee2e
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/TestRestTopologyGet.java
@@ -0,0 +1,211 @@
+package net.onrc.onos.api.rest;
+
+import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.restlet.data.Status;
+import org.restlet.resource.ClientResource;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static net.onrc.onos.api.rest.ClientResourceStatusMatcher.hasStatusOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Tests for topology REST get operations.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(PathCalcRuntimeModule.class)
+public class TestRestTopologyGet extends TestRestTopology {
+
+ /**
+ * Create the web server and mocks required for
+ * all of the tests.
+ */
+ @Before
+ @SuppressWarnings("ununsed")
+ public void beforeTest() {
+ setRestPort(generateRandomPort());
+ setUp();
+ }
+
+
+ /**
+ * Remove anything that will interfere with the next test running correctly.
+ * Shuts down the test REST web server and removes the mocks.
+ */
+ @After
+ @SuppressWarnings("unused")
+ public void afterTest() {
+ tearDown();
+ }
+
+ /**
+ * Check that the JSON array returned for the switches element matches
+ * the data in the mocked topology.
+ *
+ * @param switches JSON array of switches
+ * @throws JSONException if the JSON is not properly specified
+ */
+ private void checkSwitches(final JSONArray switches) throws JSONException {
+ assertThat(switches.length(), is(equalTo(4)));
+
+ // Check that the first switch has the proper data
+ final JSONObject switch0 = switches.getJSONObject(0);
+ assertThat(switch0.length(), is(equalTo(3)));
+ assertThat(switch0.getString("dpid"), is(equalTo("00:00:00:00:00:00:00:02")));
+ assertThat(switch0.getString("state"), is(equalTo("ACTIVE")));
+
+ // Check that the ports array for the switch is correct
+ final JSONArray switch0Ports = switch0.getJSONArray("ports");
+
+ // check the length of the port array
+ assertThat(switch0Ports.length(), equalTo(4));
+
+ // check the contents of the ports array. All of the ports should be
+ // active and refer to this switch.
+ for (int portIndex = 0; portIndex < switch0Ports.length(); portIndex++) {
+ final JSONObject switchPort = switch0Ports.getJSONObject(portIndex);
+ assertThat(switchPort.getString("dpid"), is(equalTo("00:00:00:00:00:00:00:02")));
+ assertThat(switchPort.getString("state"), is(equalTo("ACTIVE")));
+ }
+ }
+
+ /**
+ * Check that the JSON array returned for the links element matches
+ * the data in the mocked topology.
+ *
+ * @param links JSON array of links
+ * @throws JSONException if the JSON is not properly specified
+ */
+ private void checkLinks(final JSONArray links) throws JSONException {
+ // Check the length of the links array
+ assertThat(links.length(), is(equalTo(10)));
+
+ final Set<String> fromLinks = new HashSet<>();
+ final Set<String> toLinks = new HashSet<>();
+
+ // Check that the source and destination of links to switch 0 are
+ // correct
+ for (int linkIndex = 0; linkIndex < links.length(); linkIndex++) {
+ final JSONObject link = links.getJSONObject(linkIndex);
+ final String src = link.getString("src-switch");
+ final String dst = link.getString("dst-switch");
+ assertThat(src, is(notNullValue()));
+ assertThat(dst, is(notNullValue()));
+
+ if (src.equals("00:00:00:00:00:00:00:02")) {
+ toLinks.add(dst);
+ }
+
+ if (dst.equals("00:00:00:00:00:00:00:02")) {
+ fromLinks.add(src);
+ }
+ }
+
+ assertThat(toLinks, hasItems("00:00:00:00:00:00:00:01",
+ "00:00:00:00:00:00:00:03",
+ "00:00:00:00:00:00:00:04"));
+
+ assertThat(fromLinks, hasItems("00:00:00:00:00:00:00:01",
+ "00:00:00:00:00:00:00:03",
+ "00:00:00:00:00:00:00:04"));
+ }
+
+ /**
+ * Check that the JSON array returned for the devices element matches
+ * the data in the mocked topology.
+ *
+ * @param devices JSON array of switches
+ */
+ private void checkDevices(final JSONArray devices) {
+ // devices array should be empty
+ assertThat(devices.length(), is(equalTo(0)));
+ }
+
+ /**
+ * Test that the GET of all Topology REST call returns the proper result.
+ * The call to get all Topology should return 3 items (switches, links,
+ * and devices), an HTTP status of OK, and the proper topology data.
+ */
+ @Test
+ public void testFetchOfAllTopology() throws Exception {
+ final ClientResource client = new ClientResource(getBaseRestTopologyUrl());
+ final JSONObject topology = getJSONObject(client);
+
+ // HTTP status should be OK
+ assertThat(client, hasStatusOf(Status.SUCCESS_OK));
+
+ // Check the number of top level members in the topology object
+ assertThat(topology.length(), is(equalTo(3)));
+
+ // Check the switches element
+ final JSONArray switches = topology.getJSONArray("switches");
+ checkSwitches(switches);
+
+ // Check the values in the links array
+ final JSONArray links = topology.getJSONArray("links");
+ checkLinks(links);
+
+ // Check the devices array
+ final JSONArray devices = topology.getJSONArray("devices");
+ checkDevices(devices);
+ }
+
+ /**
+ * Test that the GET of all switches REST call returns the proper result.
+ * The call to get all switches should return the correct switch data.
+ */
+ @Test
+ public void testFetchOfAllSwitches() throws Exception {
+ final ClientResource client = new ClientResource(getBaseRestTopologyUrl() + "/switches");
+ final JSONArray switches = getJSONArray(client);
+
+ // HTTP status should be OK
+ assertThat(client, hasStatusOf(Status.SUCCESS_OK));
+
+ checkSwitches(switches);
+ }
+
+ /**
+ * Test that the GET of all links REST call returns the proper result.
+ * The call to get all links should return the proper link data.
+ */
+ @Test
+ public void testFetchOfAllLinks() throws Exception {
+ final ClientResource client = new ClientResource(getBaseRestTopologyUrl() + "/links");
+ final JSONArray links = getJSONArray(client);
+
+ // HTTP status should be OK
+ assertThat(client, hasStatusOf(Status.SUCCESS_OK));
+
+ checkLinks(links);
+ }
+
+ /**
+ * Test that the GET of all devices REST call returns the proper result.
+ * The call to get all devices should return no devices.
+ */
+ @Test
+ public void testFetchOfAllDevices() throws Exception {
+ final ClientResource client = new ClientResource(getBaseRestTopologyUrl() + "/switches");
+ final JSONArray devices = getJSONArray(client);
+
+ // HTTP status should be OK
+ assertThat(client, hasStatusOf(Status.SUCCESS_OK));
+
+ checkSwitches(devices);
+ }
+}
diff --git a/src/test/java/net/onrc/onos/core/intent/runtime/IntentTestMocks.java b/src/test/java/net/onrc/onos/core/intent/runtime/IntentTestMocks.java
index aa5223e..3d3cdcb 100644
--- a/src/test/java/net/onrc/onos/core/intent/runtime/IntentTestMocks.java
+++ b/src/test/java/net/onrc/onos/core/intent/runtime/IntentTestMocks.java
@@ -150,4 +150,13 @@
public MockTopology getTopology() {
return topology;
}
+
+ /**
+ * Fetch the mocked topology service.
+ *
+ * @return mocked topology service
+ */
+ public ITopologyService getTopologyService() {
+ return topologyService;
+ }
}