[ONOS-6547] Add method to obtain intent's installables in REST API.
Change-Id: I80bbb25ecda292bb4f658d0d76fd49d7fabe8732
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/IntentCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/IntentCodec.java
index 7686a55..b21ca4e 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/IntentCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/IntentCodec.java
@@ -18,7 +18,7 @@
import org.onosproject.codec.CodecContext;
import org.onosproject.codec.JsonCodec;
import org.onosproject.core.CoreService;
-import org.onosproject.net.NetworkResource;
+import org.onosproject.net.Link;
import org.onosproject.net.ResourceGroup;
import org.onosproject.net.intent.PointToPointIntent;
import org.onosproject.net.intent.Intent;
@@ -67,9 +67,16 @@
final ArrayNode jsonResources = result.putArray(RESOURCES);
- for (final NetworkResource resource : intent.resources()) {
- jsonResources.add(resource.toString());
- }
+ intent.resources()
+ .parallelStream()
+ .forEach(
+ resource -> {
+ if (resource instanceof Link) {
+ jsonResources.add(context.codec(Link.class).encode((Link) resource, context));
+ } else {
+ jsonResources.add(resource.toString());
+ }
+ });
IntentService service = context.getService(IntentService.class);
IntentState state = service.getIntentState(intent.key());
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java
index 20b59f7..34b6864 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java
@@ -98,6 +98,35 @@
}
/**
+ * Gets intent intallables by application ID and key.
+ * @param appId application identifier
+ * @param key intent key
+ *
+ * @return 200 OK with array of the intent installables
+ * @onos.rsModel Intents
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("installables/{appId}/{key}")
+ public Response getIntentWithInstallable(@PathParam("appId") String appId,
+ @PathParam("key") String key) {
+ final IntentService intentService = get(IntentService.class);
+ final ApplicationId app = get(CoreService.class).getAppId(appId);
+ nullIsNotFound(app, APP_ID_NOT_FOUND);
+
+ Intent intent = intentService.getIntent(Key.of(key, app));
+ if (intent == null) {
+ long numericalKey = Long.decode(key);
+ intent = intentService.getIntent(Key.of(numericalKey, app));
+ }
+ nullIsNotFound(intent, INTENT_NOT_FOUND);
+
+ final Iterable<Intent> installables = intentService.getInstallableIntents(intent.key());
+ final ObjectNode root = encodeArray(Intent.class, "installables", installables);
+ return ok(root).build();
+ }
+
+ /**
* Gets intent by application and key.
* Returns details of the specified intent.
*
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/IntentsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/IntentsResourceTest.java
index e041962..ba4b3d7 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/IntentsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/IntentsResourceTest.java
@@ -19,6 +19,7 @@
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import org.hamcrest.Description;
import org.hamcrest.Matchers;
import org.hamcrest.TypeSafeMatcher;
@@ -31,12 +32,17 @@
import org.onlab.rest.BaseResource;
import org.onosproject.codec.CodecService;
import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.codec.impl.MockCodecContext;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.core.GroupId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
import org.onosproject.net.NetworkResource;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowEntry;
@@ -56,18 +62,21 @@
import org.onosproject.net.intent.IntentState;
import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.MockIdGenerator;
+import org.onosproject.net.provider.ProviderId;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -89,6 +98,12 @@
private static final String ID = "id";
private static final String INSTRUCTIONS = "instructions";
private static final String PATHS = "paths";
+ private static final String INSTALLABLES = "installables";
+ private static final String RESOURCES = "resources";
+ private static final String DEVICE = "device";
+ private static final String PORT = "port";
+ private static final String SRC = "src";
+ private static final String DST = "dst";
private static final String SELECTOR = "selector";
private static final String SPACE = " ";
private static final String TREATMENT = "treatment";
@@ -102,6 +117,13 @@
private static final ApplicationId APP_ID = new DefaultApplicationId(1, "test");
final DeviceId deviceId1 = DeviceId.deviceId("1");
+ final DeviceId deviceId2 = DeviceId.deviceId("2");
+ final DeviceId deviceId3 = DeviceId.deviceId("3");
+
+ final ConnectPoint connectPoint1 = new ConnectPoint(deviceId1, PortNumber.portNumber(1L));
+ final ConnectPoint connectPoint2 = new ConnectPoint(deviceId2, PortNumber.portNumber(1L));
+ final ConnectPoint connectPoint3 = new ConnectPoint(deviceId2, PortNumber.portNumber(2L));
+ final ConnectPoint connectPoint4 = new ConnectPoint(deviceId3, PortNumber.portNumber(1L));
final TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
.setEthDst(MacAddress.BROADCAST)
@@ -367,7 +389,7 @@
// check intent type
final String jsonType = jsonIntent.get("type").asString();
- if (!"MockIntent".equals(jsonType)) {
+ if (!intent.getClass().getSimpleName().equals(jsonType)) {
reason = "type MockIntent";
return false;
}
@@ -389,10 +411,32 @@
for (final NetworkResource resource : intent.resources()) {
boolean resourceFound = false;
final String resourceString = resource.toString();
- for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
- final JsonValue value = jsonResources.get(resourceIndex);
- if (value.asString().equals(resourceString)) {
- resourceFound = true;
+
+ if (resource instanceof Link) {
+ final Link resourceLink = (Link) resource;
+ MockCodecContext codecContext = new MockCodecContext();
+
+ for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
+ final ObjectNode value;
+ try {
+ value = (ObjectNode) codecContext.mapper()
+ .readTree(jsonResources.get(resourceIndex).toString());
+ } catch (IOException e) {
+ reason = "bad json";
+ return false;
+ }
+ final Link link = codecContext.codec(Link.class).decode(value, codecContext);
+ if (resourceLink.equals(link)) {
+ resourceFound = true;
+ }
+ }
+ } else {
+
+ for (int resourceIndex = 0; resourceIndex < jsonResources.size(); resourceIndex++) {
+ final JsonValue value = jsonResources.get(resourceIndex);
+ if (value.asString().equals(resourceString)) {
+ resourceFound = true;
+ }
}
}
if (!resourceFound) {
@@ -860,6 +904,80 @@
}
/**
+ * Tests the result of a rest api GET for intent installables.
+ */
+ @Test
+ public void testIntentInstallables() {
+
+ Link link1 = DefaultLink.builder()
+ .type(Link.Type.DIRECT)
+ .providerId(ProviderId.NONE)
+ .src(connectPoint1)
+ .dst(connectPoint2)
+ .build();
+
+ Link link2 = DefaultLink.builder()
+ .type(Link.Type.DIRECT)
+ .providerId(ProviderId.NONE)
+ .src(connectPoint3)
+ .dst(connectPoint4)
+ .build();
+
+ Set<NetworkResource> resources = new HashSet<>();
+ resources.add(link1);
+ resources.add(link2);
+
+ FlowRuleIntent flowRuleIntent = new FlowRuleIntent(
+ APP_ID,
+ new ArrayList<>(),
+ resources);
+
+ Intent intent = new MockIntent(MockIntent.nextId());
+ Long intentId = intent.id().id();
+ installableIntents.add(flowRuleIntent);
+ intents.add(intent);
+
+ expect(mockIntentService.getIntent(Key.of(intentId, APP_ID)))
+ .andReturn(intent)
+ .anyTimes();
+ expect(mockIntentService.getIntent(Key.of(intentId.toString(), APP_ID)))
+ .andReturn(intent)
+ .anyTimes();
+ expect(mockIntentService.getIntent(Key.of(intentId, APP_ID)))
+ .andReturn(intent)
+ .anyTimes();
+ expect(mockIntentService.getIntent(Key.of(Long.toHexString(intentId), APP_ID)))
+ .andReturn(null)
+ .anyTimes();
+ expect(mockIntentService.getInstallableIntents(intent.key()))
+ .andReturn(installableIntents)
+ .anyTimes();
+ replay(mockIntentService);
+
+ replay(mockFlowService);
+
+ expect(mockCoreService.getAppId(APP_ID.name()))
+ .andReturn(APP_ID).anyTimes();
+ expect(mockCoreService.getAppId(APP_ID.id()))
+ .andReturn(APP_ID).anyTimes();
+ replay(mockCoreService);
+
+ final WebTarget wt = target();
+
+ // Test get using key string
+ final String response = wt.path("intents/installables/" + APP_ID.name()
+ + "/" + intentId).request().get(String.class);
+ final JsonObject result = Json.parse(response).asObject();
+ assertThat(result.get(INSTALLABLES).asArray(), hasIntent(flowRuleIntent));
+
+ // Test get using numeric value
+ final String responseNumeric = wt.path("intents/installables/" + APP_ID.name()
+ + "/" + Long.toHexString(intentId)).request().get(String.class);
+ final JsonObject resultNumeric = Json.parse(responseNumeric).asObject();
+ assertThat(resultNumeric.get(INSTALLABLES).asArray(), hasIntent(flowRuleIntent));
+ }
+
+ /**
* Tests that a fetch of a non-existent intent object throws an exception.
*/
@Test