blob: 38465acd47e119aaa29e0f227326daad25eab75c [file] [log] [blame]
Ray Milkey2b217142014-12-15 09:24:24 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Ray Milkey2b217142014-12-15 09:24:24 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Jonathan Hart9bb32ab2015-05-05 18:17:31 -070016package org.onosproject.rest.resources;
Ray Milkey2b217142014-12-15 09:24:24 -080017
Author Namee252a002016-09-26 22:42:24 +053018import com.fasterxml.jackson.databind.node.ArrayNode;
Jian Lic2a542b2016-05-10 11:48:19 -070019import com.fasterxml.jackson.databind.node.ObjectNode;
Ayaka Koshibec06c89b2015-02-10 19:25:41 -080020import org.onosproject.core.ApplicationId;
21import org.onosproject.core.CoreService;
Author Namee252a002016-09-26 22:42:24 +053022import org.onosproject.net.flow.FlowEntry;
23import org.onosproject.net.flow.FlowRuleService;
Chiara Contolia8f69ff2016-07-28 01:06:07 +090024import org.onosproject.net.intent.SinglePointToMultiPointIntent;
nassima toumi95761312017-05-23 14:00:33 +030025import org.onosproject.net.intent.MultiPointToSinglePointIntent;
Chiara Contolia8f69ff2016-07-28 01:06:07 +090026import org.onosproject.net.intent.PointToPointIntent;
Ray Milkeyc95bb9d2015-01-06 10:28:24 -080027import org.onosproject.net.intent.HostToHostIntent;
Ray Milkey2b217142014-12-15 09:24:24 -080028import org.onosproject.net.intent.Intent;
Chiara Contolia8f69ff2016-07-28 01:06:07 +090029import org.onosproject.net.intent.IntentState;
Ray Milkey67c22722015-03-09 15:48:57 -070030import org.onosproject.net.intent.IntentEvent;
31import org.onosproject.net.intent.IntentListener;
Ray Milkey2b217142014-12-15 09:24:24 -080032import org.onosproject.net.intent.IntentService;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080033import org.onosproject.net.intent.Key;
Author Namee252a002016-09-26 22:42:24 +053034import org.onosproject.net.intent.util.IntentFilter;
dvaddire95c84ed2017-06-14 15:42:24 +053035import org.onosproject.net.intent.util.IntentMiniSummary;
Jonathan Hart9bb32ab2015-05-05 18:17:31 -070036import org.onosproject.rest.AbstractWebResource;
Ray Milkey67c22722015-03-09 15:48:57 -070037import org.slf4j.Logger;
Ray Milkey2b217142014-12-15 09:24:24 -080038
Jian Lic2a542b2016-05-10 11:48:19 -070039import javax.ws.rs.Consumes;
40import javax.ws.rs.DELETE;
41import javax.ws.rs.GET;
42import javax.ws.rs.POST;
43import javax.ws.rs.Path;
44import javax.ws.rs.PathParam;
45import javax.ws.rs.Produces;
46import javax.ws.rs.core.Context;
47import javax.ws.rs.core.MediaType;
48import javax.ws.rs.core.Response;
49import javax.ws.rs.core.UriBuilder;
50import javax.ws.rs.core.UriInfo;
51import java.io.IOException;
52import java.io.InputStream;
Author Namee252a002016-09-26 22:42:24 +053053import java.util.List;
dvaddire95c84ed2017-06-14 15:42:24 +053054import java.util.Map;
Jian Lic2a542b2016-05-10 11:48:19 -070055import java.util.Objects;
56import java.util.concurrent.CountDownLatch;
57import java.util.concurrent.TimeUnit;
Ray Milkey2b217142014-12-15 09:24:24 -080058
Thomas Vachuskaf8cac482015-04-08 19:40:12 -070059import static org.onlab.util.Tools.nullIsNotFound;
Ray Milkeyb784adb2018-04-02 15:33:07 -070060import static org.onlab.util.Tools.readTreeFromStream;
Ray Milkey67c22722015-03-09 15:48:57 -070061import static org.onosproject.net.intent.IntentState.FAILED;
62import static org.onosproject.net.intent.IntentState.WITHDRAWN;
63import static org.slf4j.LoggerFactory.getLogger;
64
Ray Milkey2b217142014-12-15 09:24:24 -080065/**
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -070066 * Query, submit and withdraw network intents.
Ray Milkey2b217142014-12-15 09:24:24 -080067 */
Ray Milkey2b217142014-12-15 09:24:24 -080068@Path("intents")
69public class IntentsWebResource extends AbstractWebResource {
Ray Milkey303e6712015-07-17 14:06:21 -070070
Ray Milkey67c22722015-03-09 15:48:57 -070071 private static final Logger log = getLogger(IntentsWebResource.class);
72 private static final int WITHDRAW_EVENT_TIMEOUT_SECONDS = 5;
73
Kavitha Alagesane6840cf2016-10-21 10:58:05 +053074 private static final String APP_ID_NOT_FOUND = "Application Id not found";
Author Namee252a002016-09-26 22:42:24 +053075 private static final String HOST_TO_HOST_INTENT = "HostToHostIntent";
76 private static final String POINT_TO_POINT_INTENT = "PointToPointIntent";
77 private static final String SINGLE_TO_MULTI_POINT_INTENT =
78 "SinglePointToMultiPointIntent";
nassima toumi95761312017-05-23 14:00:33 +030079 private static final String MULTI_TO_SINGLE_POINT_INTENT =
80 "MultiPointToSinglePointIntent";
Aris C. Risdiantoa579c8f2017-04-13 19:38:46 +090081
Author Namee252a002016-09-26 22:42:24 +053082 private static final String INTENT = "Intent";
83 private static final String APP_ID = "appId";
84 private static final String ID = "id";
85 private static final String INTENT_PATHS = "paths";
86 private static final String INTENT_TYPE = "type";
Jian Licc730a62016-05-10 16:36:16 -070087 private static final String INTENT_NOT_FOUND = "Intent is not found";
Ray Milkey2b217142014-12-15 09:24:24 -080088
Author Namee252a002016-09-26 22:42:24 +053089 @Context
90 private UriInfo uriInfo;
91
Ray Milkey2b217142014-12-15 09:24:24 -080092 /**
Jian Licc730a62016-05-10 16:36:16 -070093 * Gets all intents.
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -070094 * Returns array containing all the intents in the system.
Jian Licc730a62016-05-10 16:36:16 -070095 *
96 * @return 200 OK with array of all the intents in the system
Andrea Campanella10c4adc2015-12-03 15:27:54 -080097 * @onos.rsModel Intents
Ray Milkey2b217142014-12-15 09:24:24 -080098 */
99 @GET
100 @Produces(MediaType.APPLICATION_JSON)
101 public Response getIntents() {
102 final Iterable<Intent> intents = get(IntentService.class).getIntents();
103 final ObjectNode root = encodeArray(Intent.class, "intents", intents);
Ray Milkey3f025692015-01-26 11:15:41 -0800104 return ok(root).build();
Ray Milkey2b217142014-12-15 09:24:24 -0800105 }
106
dvaddire95c84ed2017-06-14 15:42:24 +0530107
108 /**
109 * Gets Summary of all intents.
110 * Returns Summary of the intents in the system.
111 *
112 * @return 200 OK with Summary of all the intents in the system
113 * @onos.rsModel Minisummary
114 */
115 @GET
116 @Produces(MediaType.APPLICATION_JSON)
117 @Path("minisummary")
118 public Response getIntentSummary() {
119 final Iterable<Intent> intents = get(IntentService.class).getIntents();
120 ObjectNode root = mapper().createObjectNode();
121 IntentMiniSummary intentminisummary = new IntentMiniSummary();
122 Map<String, IntentMiniSummary> map = intentminisummary.summarize(intents, get(IntentService.class));
123 map.values().stream().forEach(intentsummary -> {
124 root.put(intentsummary.getIntentType(), codec(IntentMiniSummary.class).encode(intentsummary, this));
125 });
126 return ok(root).build();
127 }
128
129
Ray Milkey2b217142014-12-15 09:24:24 -0800130 /**
Aris C. Risdiantoa579c8f2017-04-13 19:38:46 +0900131 * Gets intent installables by application ID and key.
Rafal Szaleckif97e0ed2017-06-01 13:07:18 +0200132 * @param appId application identifier
133 * @param key intent key
134 *
135 * @return 200 OK with array of the intent installables
136 * @onos.rsModel Intents
137 */
138 @GET
139 @Produces(MediaType.APPLICATION_JSON)
140 @Path("installables/{appId}/{key}")
141 public Response getIntentWithInstallable(@PathParam("appId") String appId,
142 @PathParam("key") String key) {
143 final IntentService intentService = get(IntentService.class);
144 final ApplicationId app = get(CoreService.class).getAppId(appId);
145 nullIsNotFound(app, APP_ID_NOT_FOUND);
146
147 Intent intent = intentService.getIntent(Key.of(key, app));
148 if (intent == null) {
149 long numericalKey = Long.decode(key);
150 intent = intentService.getIntent(Key.of(numericalKey, app));
151 }
152 nullIsNotFound(intent, INTENT_NOT_FOUND);
153
154 final Iterable<Intent> installables = intentService.getInstallableIntents(intent.key());
155 final ObjectNode root = encodeArray(Intent.class, "installables", installables);
156 return ok(root).build();
157 }
158
159 /**
Jian Licc730a62016-05-10 16:36:16 -0700160 * Gets intent by application and key.
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700161 * Returns details of the specified intent.
Jian Licc730a62016-05-10 16:36:16 -0700162 *
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700163 * @param appId application identifier
164 * @param key intent key
Jian Licc730a62016-05-10 16:36:16 -0700165 * @return 200 OK with intent data
166 * @onos.rsModel Intents
Ray Milkey2b217142014-12-15 09:24:24 -0800167 */
168 @GET
169 @Produces(MediaType.APPLICATION_JSON)
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800170 @Path("{appId}/{key}")
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700171 public Response getIntentById(@PathParam("appId") String appId,
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800172 @PathParam("key") String key) {
173 final ApplicationId app = get(CoreService.class).getAppId(appId);
Kavitha Alagesane6840cf2016-10-21 10:58:05 +0530174 nullIsNotFound(app, APP_ID_NOT_FOUND);
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800175 Intent intent = get(IntentService.class).getIntent(Key.of(key, app));
176 if (intent == null) {
Ray Milkeyf7cb4012015-07-20 13:01:07 -0700177 long numericalKey = Long.decode(key);
178 intent = get(IntentService.class).getIntent(Key.of(numericalKey, app));
Ayaka Koshibec06c89b2015-02-10 19:25:41 -0800179 }
180 nullIsNotFound(intent, INTENT_NOT_FOUND);
181
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800182 final ObjectNode root;
183 if (intent instanceof HostToHostIntent) {
184 root = codec(HostToHostIntent.class).encode((HostToHostIntent) intent, this);
185 } else if (intent instanceof PointToPointIntent) {
186 root = codec(PointToPointIntent.class).encode((PointToPointIntent) intent, this);
Chiara Contolia8f69ff2016-07-28 01:06:07 +0900187 } else if (intent instanceof SinglePointToMultiPointIntent) {
188 root = codec(SinglePointToMultiPointIntent.class).encode((SinglePointToMultiPointIntent) intent, this);
nassima toumi95761312017-05-23 14:00:33 +0300189 } else if (intent instanceof MultiPointToSinglePointIntent) {
190 root = codec(MultiPointToSinglePointIntent.class).encode((MultiPointToSinglePointIntent) intent, this);
Ray Milkeyc95bb9d2015-01-06 10:28:24 -0800191 } else {
192 root = codec(Intent.class).encode(intent, this);
193 }
Ray Milkey3f025692015-01-26 11:15:41 -0800194 return ok(root).build();
Ray Milkey2b217142014-12-15 09:24:24 -0800195 }
Ray Milkey67c22722015-03-09 15:48:57 -0700196
Jian Licc730a62016-05-10 16:36:16 -0700197 /**
Author Namee252a002016-09-26 22:42:24 +0530198 * Gets all related flow entries created by a particular intent.
199 * Returns all flow entries of the specified intent.
200 *
201 * @param appId application identifier
202 * @param key intent key
203 * @return 200 OK with intent data
204 * @onos.rsModel Relatedflows
205 */
206 @GET
207 @Produces(MediaType.APPLICATION_JSON)
208 @Path("relatedflows/{appId}/{key}")
209 public Response getIntentFlowsById(@PathParam("appId") String appId,
210 @PathParam("key") String key) {
211 ApplicationId applicationId = get(CoreService.class).getAppId(appId);
212 nullIsNotFound(applicationId, APP_ID_NOT_FOUND);
213 IntentService intentService = get(IntentService.class);
214 FlowRuleService flowService = get(FlowRuleService.class);
215
216 Intent intent = intentService.getIntent(Key.of(key, applicationId));
217 if (intent == null) {
218 long numericalKey = Long.decode(key);
219 intent = intentService.getIntent(
220 Key.of(numericalKey, applicationId));
221 }
222 nullIsNotFound(intent, INTENT_NOT_FOUND);
223
224 ObjectNode root = mapper().createObjectNode();
225 root.put(APP_ID, appId);
226 root.put(ID, key);
227
228 IntentFilter intentFilter = new IntentFilter(intentService, flowService);
229
230 List<Intent> installables =
231 intentService.getInstallableIntents(intent.key());
232
233 if (intent instanceof HostToHostIntent) {
234 root.put(INTENT_TYPE, HOST_TO_HOST_INTENT);
235 } else if (intent instanceof PointToPointIntent) {
236 root.put(INTENT_TYPE, POINT_TO_POINT_INTENT);
237 } else if (intent instanceof SinglePointToMultiPointIntent) {
238 root.put(INTENT_TYPE, SINGLE_TO_MULTI_POINT_INTENT);
nassima toumi95761312017-05-23 14:00:33 +0300239 } else if (intent instanceof MultiPointToSinglePointIntent) {
240 root.put(INTENT_TYPE, MULTI_TO_SINGLE_POINT_INTENT);
Author Namee252a002016-09-26 22:42:24 +0530241 } else {
242 root.put(INTENT_TYPE, INTENT);
243 }
244
245 ArrayNode pathsNode = root.putArray(INTENT_PATHS);
246
247 for (List<FlowEntry> flowEntries :
248 intentFilter.readIntentFlows(installables)) {
249 ArrayNode flowNode = pathsNode.addArray();
250
251 for (FlowEntry entry : flowEntries) {
252 flowNode.add(codec(FlowEntry.class).encode(entry, this));
253 }
254 }
255 return ok(root).build();
256 }
257
258 /**
Jian Licc730a62016-05-10 16:36:16 -0700259 * Internal listener for tracking the intent deletion events.
260 */
261 private class DeleteListener implements IntentListener {
Ray Milkey67c22722015-03-09 15:48:57 -0700262 final Key key;
263 final CountDownLatch latch;
264
Jian Licc730a62016-05-10 16:36:16 -0700265 /**
266 * Default constructor.
267 *
268 * @param key key
269 * @param latch count down latch
270 */
Ray Milkey67c22722015-03-09 15:48:57 -0700271 DeleteListener(Key key, CountDownLatch latch) {
272 this.key = key;
273 this.latch = latch;
274 }
275
276 @Override
277 public void event(IntentEvent event) {
278 if (Objects.equals(event.subject().key(), key) &&
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700279 (event.type() == IntentEvent.Type.WITHDRAWN ||
280 event.type() == IntentEvent.Type.FAILED)) {
Ray Milkey67c22722015-03-09 15:48:57 -0700281 latch.countDown();
282 }
283 }
284 }
285
286 /**
Jian Licc730a62016-05-10 16:36:16 -0700287 * Submits a new intent.
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700288 * Creates and submits intent from the JSON request.
Jian Licc730a62016-05-10 16:36:16 -0700289 *
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700290 * @param stream input JSON
291 * @return status of the request - CREATED if the JSON is correct,
292 * BAD_REQUEST if the JSON is invalid
Jian Licc730a62016-05-10 16:36:16 -0700293 * @onos.rsModel IntentHost
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700294 */
295 @POST
296 @Consumes(MediaType.APPLICATION_JSON)
297 @Produces(MediaType.APPLICATION_JSON)
298 public Response createIntent(InputStream stream) {
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700299 try {
300 IntentService service = get(IntentService.class);
Ray Milkeyb784adb2018-04-02 15:33:07 -0700301 ObjectNode root = readTreeFromStream(mapper(), stream);
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700302 Intent intent = codec(Intent.class).decode(root, this);
303 service.submit(intent);
Ray Milkey303e6712015-07-17 14:06:21 -0700304 UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
305 .path("intents")
Ray Milkey8d076402015-08-31 15:43:18 -0700306 .path(intent.appId().name())
Ray Milkey303e6712015-07-17 14:06:21 -0700307 .path(Long.toString(intent.id().fingerprint()));
308 return Response
309 .created(locationBuilder.build())
310 .build();
311 } catch (IOException ioe) {
312 throw new IllegalArgumentException(ioe);
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700313 }
Ray Milkeyb82c42b2015-06-30 09:42:20 -0700314 }
315
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700316 /**
Jian Licc730a62016-05-10 16:36:16 -0700317 * Withdraws intent.
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700318 * Withdraws the specified intent from the system.
319 *
320 * @param appId application identifier
321 * @param key intent key
Jian Lic2a542b2016-05-10 11:48:19 -0700322 * @return 204 NO CONTENT
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700323 */
324 @DELETE
325 @Path("{appId}/{key}")
Jian Lic2a542b2016-05-10 11:48:19 -0700326 public Response deleteIntentById(@PathParam("appId") String appId,
Jian Licc730a62016-05-10 16:36:16 -0700327 @PathParam("key") String key) {
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700328 final ApplicationId app = get(CoreService.class).getAppId(appId);
Kavitha Alagesane6840cf2016-10-21 10:58:05 +0530329 nullIsNotFound(app, APP_ID_NOT_FOUND);
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700330 Intent intent = get(IntentService.class).getIntent(Key.of(key, app));
331 IntentService service = get(IntentService.class);
332
333 if (intent == null) {
334 intent = service
335 .getIntent(Key.of(Long.decode(key), app));
336 }
337 if (intent == null) {
338 // No such intent. REST standards recommend a positive status code
339 // in this case.
Jian Lic2a542b2016-05-10 11:48:19 -0700340 return Response.noContent().build();
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700341 }
342
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700343 Key k = intent.key();
344
345 // set up latch and listener to track uninstall progress
346 CountDownLatch latch = new CountDownLatch(1);
347
348 IntentListener listener = new DeleteListener(k, latch);
349 service.addListener(listener);
350
351 try {
352 // request the withdraw
353 service.withdraw(intent);
354
355 try {
356 latch.await(WITHDRAW_EVENT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
357 } catch (InterruptedException e) {
358 log.info("REST Delete operation timed out waiting for intent {}", k);
359 }
360 // double check the state
361 IntentState state = service.getIntentState(k);
362 if (state == WITHDRAWN || state == FAILED) {
363 service.purge(intent);
364 }
365
366 } finally {
367 // clean up the listener
368 service.removeListener(listener);
369 }
Jian Lic2a542b2016-05-10 11:48:19 -0700370 return Response.noContent().build();
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700371 }
Ray Milkey2b217142014-12-15 09:24:24 -0800372}