Ray Milkey

Implement REST API for Hosts objects

Change-Id: Ie1d4f7c19c47d2d357dedf81016e4d4d197e4052
......@@ -38,6 +38,10 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
</dependency>
</dependencies>
<properties>
......
......@@ -25,6 +25,8 @@ import org.onosproject.codec.JsonCodec;
import org.onosproject.net.Annotations;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.Host;
import org.onosproject.net.HostLocation;
import org.onosproject.net.Link;
import org.onosproject.net.Port;
import org.slf4j.Logger;
......@@ -53,11 +55,13 @@ public class CodecManager implements CodecService {
registerCodec(Port.class, new PortCodec());
registerCodec(ConnectPoint.class, new ConnectPointCodec());
registerCodec(Link.class, new LinkCodec());
registerCodec(Host.class, new HostCodec());
registerCodec(HostLocation.class, new HostLocationCodec());
log.info("Started");
}
@Deactivate
public void deativate() {
public void deactivate() {
codecs.clear();
log.info("Stopped");
}
......
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.codec.impl;
import org.onlab.packet.IpAddress;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.Host;
import org.onosproject.net.HostLocation;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Host JSON codec.
*/
public class HostCodec extends AnnotatedCodec<Host> {
@Override
public ObjectNode encode(Host host, CodecContext context) {
checkNotNull(host, "Host cannot be null");
final JsonCodec<HostLocation> locationCodec = new HostLocationCodec();
final ObjectNode result = context.mapper().createObjectNode()
.put("id", host.id().toString())
.put("mac", host.mac().toString())
.put("vlan", host.vlan().toString());
final ArrayNode jsonIpAddresses = result.putArray("ipAddresses");
for (final IpAddress ipAddress : host.ipAddresses()) {
jsonIpAddresses.add(ipAddress.toString());
}
result.set("ipAddresses", jsonIpAddresses);
result.set("location", locationCodec.encode(host.location(), context));
return annotate(result, host, context);
}
}
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.codec.impl;
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.net.HostLocation;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Host JSON codec.
*/
public class HostLocationCodec extends JsonCodec<HostLocation> {
@Override
public ObjectNode encode(HostLocation hostLocation, CodecContext context) {
checkNotNull(hostLocation, "Host location cannot be null");
return context.mapper().createObjectNode()
.put("elementId", hostLocation.elementId().toString())
.put("port", hostLocation.port().toString());
}
}
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.rest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.onosproject.net.Host;
import org.onosproject.net.host.HostService;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static org.onosproject.net.HostId.hostId;
/**
* REST resource for interacting with the inventory of hosts.
*/
@Path("hosts")
public class HostsWebResource extends AbstractWebResource {
public static final String HOST_NOT_FOUND = "Host is not found";
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getHosts() {
final Iterable<Host> hosts = get(HostService.class).getHosts();
final ObjectNode root = encodeArray(Host.class, "hosts", hosts);
return ok(root.toString()).build();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
public Response getHostById(@PathParam("id") String id) {
final Host host = nullIsNotFound(get(HostService.class).getHost(hostId(id)),
HOST_NOT_FOUND);
final ObjectNode root = codec(Host.class).encode(host, this);
return ok(root.toString()).build();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{mac}/{vlan}")
public Response getHostByMacAndVlan(@PathParam("mac") String mac,
@PathParam("vlan") String vlan) {
final Host host = nullIsNotFound(get(HostService.class).getHost(hostId(mac + "/" + vlan)),
HOST_NOT_FOUND);
final ObjectNode root = codec(Host.class).encode(host, this);
return ok(root.toString()).build();
}
}
......@@ -37,6 +37,7 @@
org.onosproject.rest.DevicesWebResource,
org.onosproject.rest.LinksWebResource,
org.onosproject.rest.HostsWebResource,
org.onosproject.rest.ConfigResource
</param-value>
</init-param>
......
......@@ -45,7 +45,7 @@ public class GreetResourceTest extends JerseyTest {
public void basics() {
WebResource rs = resource();
String response = rs.path("greet").get(String.class);
assertTrue("incorrect response", response.contains("Whazup "));
assertTrue("incorrect response", response.contains("greeting"));
}
}
......
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.rest;
import java.util.HashSet;
import java.util.Set;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.osgi.TestServiceDirectory;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.rest.BaseResource;
import org.onosproject.codec.CodecService;
import org.onosproject.codec.impl.CodecManager;
import org.onosproject.net.DefaultHost;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.host.HostService;
import org.onosproject.net.provider.ProviderId;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.google.common.collect.ImmutableSet;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.test.framework.JerseyTest;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.onlab.packet.MacAddress.valueOf;
import static org.onlab.packet.VlanId.vlanId;
import static org.onosproject.net.PortNumber.portNumber;
/**
* Simple example on how to write a JAX-RS unit test using Jersey test framework.
* A base class should/will be created to provide further assistance for testing.
*/
public class HostResourceTest extends JerseyTest {
final HostService mockHostService = createMock(HostService.class);
final HashSet<Host> hosts = new HashSet<>();
public HostResourceTest() {
super("org.onosproject.rest");
}
@Before
public void setUp() {
expect(mockHostService.getHosts()).andReturn(hosts).anyTimes();
// Register the services needed for the test
final CodecManager codecService = new CodecManager();
codecService.activate();
ServiceDirectory testDirectory =
new TestServiceDirectory()
.add(HostService.class, mockHostService)
.add(CodecService.class, codecService);
BaseResource.setServiceDirectory(testDirectory);
}
@After
public void tearDown() throws Exception {
super.tearDown();
verify(mockHostService);
}
/**
* Hamcrest matcher to check that a host representation in JSON matches
* the actual host.
*/
public static class HostJsonMatcher extends TypeSafeMatcher<JsonObject> {
private final Host host;
private String reason = "";
public HostJsonMatcher(Host hostValue) {
host = hostValue;
}
@Override
public boolean matchesSafely(JsonObject jsonHost) {
// Check id
final String jsonId = jsonHost.get("id").asString();
if (!jsonId.equals(host.id().toString())) {
reason = "id " + host.id().toString();
return false;
}
// Check vlan id
final String jsonVlanId = jsonHost.get("vlan").asString();
if (!jsonVlanId.equals(host.vlan().toString())) {
reason = "vlan id " + host.vlan().toString();
return false;
}
// Check mac address
final String jsonMacAddress = jsonHost.get("mac").asString();
if (!jsonMacAddress.equals(host.mac().toString())) {
reason = "mac address " + host.mac().toString();
return false;
}
// Check location element id
final JsonObject jsonLocation = jsonHost.get("location").asObject();
final String jsonLocationElementId = jsonLocation.get("elementId").asString();
if (!jsonLocationElementId.equals(host.location().elementId().toString())) {
reason = "location element id " + host.location().elementId().toString();
return false;
}
// Check location port number
final String jsonLocationPortNumber = jsonLocation.get("port").asString();
if (!jsonLocationPortNumber.equals(host.location().port().toString())) {
reason = "location portNumber " + host.location().port().toString();
return false;
}
// Check Ip Addresses
final JsonArray jsonHostIps = jsonHost.get("ipAddresses").asArray();
final Set<IpAddress> expectedHostIps = host.ipAddresses();
if (jsonHostIps.size() != expectedHostIps.size()) {
reason = "IP address arrays differ in size";
return false;
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText(reason);
}
}
/**
* Factory to allocate a host array matcher.
*
* @param host host object we are looking for
* @return matcher
*/
private static HostJsonMatcher matchesHost(Host host) {
return new HostJsonMatcher(host);
}
/**
* Hamcrest matcher to check that a host is represented properly in a JSON
* array of hosts.
*/
public static class HostJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
private final Host host;
private String reason = "";
public HostJsonArrayMatcher(Host hostValue) {
host = hostValue;
}
@Override
public boolean matchesSafely(JsonArray json) {
boolean hostFound = false;
final int expectedAttributes = 5;
for (int jsonHostIndex = 0; jsonHostIndex < json.size();
jsonHostIndex++) {
final JsonObject jsonHost = json.get(jsonHostIndex).asObject();
if (jsonHost.names().size() != expectedAttributes) {
reason = "Found a host with the wrong number of attributes";
return false;
}
final String jsonHostId = jsonHost.get("id").asString();
if (jsonHostId.equals(host.id().toString())) {
hostFound = true;
// We found the correct host, check attribute values
assertThat(jsonHost, matchesHost(host));
}
}
if (!hostFound) {
reason = "Host with id " + host.id().toString() + " not found";
return false;
} else {
return true;
}
}
@Override
public void describeTo(Description description) {
description.appendText(reason);
}
}
/**
* Factory to allocate a host array matcher.
*
* @param host host object we are looking for
* @return matcher
*/
private static HostJsonArrayMatcher hasHost(Host host) {
return new HostJsonArrayMatcher(host);
}
/**
* Tests the result of the rest api GET when there are no hosts.
*/
@Test
public void testHostsEmptyArray() {
replay(mockHostService);
WebResource rs = resource();
String response = rs.path("hosts").get(String.class);
assertThat(response, is("{\"hosts\":[]}"));
}
/**
* Tests the result of the rest api GET when hosts are defined.
*/
@Test
public void testHostsArray() {
replay(mockHostService);
final ProviderId pid = new ProviderId("of", "foo");
final MacAddress mac1 = MacAddress.valueOf("00:00:11:00:00:01");
final Set<IpAddress> ips1 = ImmutableSet.of(IpAddress.valueOf("1111:1111:1111:1::"));
final Host host1 =
new DefaultHost(pid, HostId.hostId(mac1), valueOf(1), vlanId((short) 1),
new HostLocation(DeviceId.deviceId("1"), portNumber(11), 1),
ips1);
final MacAddress mac2 = MacAddress.valueOf("00:00:11:00:00:02");
final Set<IpAddress> ips2 = ImmutableSet.of(
IpAddress.valueOf("2222:2222:2222:1::"),
IpAddress.valueOf("2222:2222:2222:2::"));
final Host host2 =
new DefaultHost(pid, HostId.hostId(mac2), valueOf(2), vlanId((short) 2),
new HostLocation(DeviceId.deviceId("2"), portNumber(22), 2),
ips2);
hosts.add(host1);
hosts.add(host2);
WebResource rs = resource();
String response = rs.path("hosts").get(String.class);
assertThat(response, containsString("{\"hosts\":["));
final JsonObject result = JsonObject.readFrom(response);
assertThat(result, notNullValue());
assertThat(result.names(), hasSize(1));
assertThat(result.names().get(0), is("hosts"));
final JsonArray hosts = result.get("hosts").asArray();
assertThat(hosts, notNullValue());
assertThat(hosts, hasHost(host1));
assertThat(hosts, hasHost(host2));
}
/**
* Tests fetch of one host by Id.
*/
@Test
public void testSingleHostByIdFetch() {
final ProviderId pid = new ProviderId("of", "foo");
final MacAddress mac1 = MacAddress.valueOf("00:00:11:00:00:01");
final Set<IpAddress> ips1 = ImmutableSet.of(IpAddress.valueOf("1111:1111:1111:1::"));
final Host host1 =
new DefaultHost(pid, HostId.hostId(mac1), valueOf(1), vlanId((short) 1),
new HostLocation(DeviceId.deviceId("1"), portNumber(11), 1),
ips1);
hosts.add(host1);
expect(mockHostService.getHost(HostId.hostId("00:00:11:00:00:01/1")))
.andReturn(host1)
.anyTimes();
replay(mockHostService);
WebResource rs = resource();
String response = rs.path("hosts/00:00:11:00:00:01%2F1").get(String.class);
final JsonObject result = JsonObject.readFrom(response);
assertThat(result, matchesHost(host1));
}
/**
* Tests fetch of one host by mac and vlan.
*/
@Test
public void testSingleHostByMacAndVlanFetch() {
final ProviderId pid = new ProviderId("of", "foo");
final MacAddress mac1 = MacAddress.valueOf("00:00:11:00:00:01");
final Set<IpAddress> ips1 = ImmutableSet.of(IpAddress.valueOf("1111:1111:1111:1::"));
final Host host1 =
new DefaultHost(pid, HostId.hostId(mac1), valueOf(1), vlanId((short) 1),
new HostLocation(DeviceId.deviceId("1"), portNumber(11), 1),
ips1);
hosts.add(host1);
expect(mockHostService.getHost(HostId.hostId("00:00:11:00:00:01/1")))
.andReturn(host1)
.anyTimes();
replay(mockHostService);
WebResource rs = resource();
String response = rs.path("hosts/00:00:11:00:00:01/1").get(String.class);
final JsonObject result = JsonObject.readFrom(response);
assertThat(result, matchesHost(host1));
}
}