blob: 42e9590b7a5a8bcc6bcca9fe6b819b20185fe3c3 [file] [log] [blame]
Ray Milkey4f5de002014-12-17 19:26:11 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Ray Milkey4f5de002014-12-17 19:26:11 -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 Milkey4f5de002014-12-17 19:26:11 -080017
Jian Li9d616492016-03-09 10:52:49 -080018import com.fasterxml.jackson.databind.JsonNode;
19import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
Ray Milkey460f9b02016-03-29 11:56:19 -070021import com.google.common.collect.ArrayListMultimap;
22import com.google.common.collect.ListMultimap;
Ray Milkeyd43fe452015-05-29 09:35:12 -070023import org.onlab.util.ItemNotFoundException;
Jian Li2907ad22016-05-12 23:08:54 -070024import org.onosproject.app.ApplicationService;
25import org.onosproject.core.ApplicationId;
Ray Milkeyd43fe452015-05-29 09:35:12 -070026import org.onosproject.net.Device;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.device.DeviceService;
29import org.onosproject.net.flow.FlowEntry;
30import org.onosproject.net.flow.FlowRule;
31import org.onosproject.net.flow.FlowRuleService;
32import org.onosproject.rest.AbstractWebResource;
33
Jian Li9d616492016-03-09 10:52:49 -080034import javax.ws.rs.Consumes;
35import javax.ws.rs.DELETE;
36import javax.ws.rs.GET;
37import javax.ws.rs.POST;
38import javax.ws.rs.Path;
39import javax.ws.rs.PathParam;
40import javax.ws.rs.Produces;
Jian Li2907ad22016-05-12 23:08:54 -070041import javax.ws.rs.QueryParam;
Jian Li9d616492016-03-09 10:52:49 -080042import javax.ws.rs.core.Context;
43import javax.ws.rs.core.MediaType;
44import javax.ws.rs.core.Response;
45import javax.ws.rs.core.UriBuilder;
46import javax.ws.rs.core.UriInfo;
47import java.io.IOException;
48import java.io.InputStream;
Ray Milkey460f9b02016-03-29 11:56:19 -070049import java.util.ArrayList;
Thomas Vachuskaa1ae5e12016-03-28 10:36:30 -070050import java.util.List;
Jian Li9d616492016-03-09 10:52:49 -080051import java.util.stream.StreamSupport;
Ray Milkeyd43fe452015-05-29 09:35:12 -070052
Ray Milkey460f9b02016-03-29 11:56:19 -070053import static org.onlab.util.Tools.nullIsNotFound;
54
Ray Milkey4f5de002014-12-17 19:26:11 -080055/**
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -070056 * Query and program flow rules.
Ray Milkey4f5de002014-12-17 19:26:11 -080057 */
58
59@Path("flows")
60public class FlowsWebResource extends AbstractWebResource {
Jian Li9d616492016-03-09 10:52:49 -080061
62 @Context
Jian Licc730a62016-05-10 16:36:16 -070063 private UriInfo uriInfo;
Jian Li9d616492016-03-09 10:52:49 -080064
Jian Licc730a62016-05-10 16:36:16 -070065 private static final String DEVICE_NOT_FOUND = "Device is not found";
66 private static final String FLOW_NOT_FOUND = "Flow is not found";
Jian Li2907ad22016-05-12 23:08:54 -070067 private static final String APP_ID_NOT_FOUND = "Application Id is not found";
Jian Licc730a62016-05-10 16:36:16 -070068 private static final String FLOWS = "flows";
69 private static final String DEVICE_ID = "deviceId";
70 private static final String FLOW_ID = "flowId";
Ray Milkey4f5de002014-12-17 19:26:11 -080071
Jian Licc730a62016-05-10 16:36:16 -070072 private final FlowRuleService service = get(FlowRuleService.class);
73 private final ObjectNode root = mapper().createObjectNode();
74 private final ArrayNode flowsNode = root.putArray(FLOWS);
Ray Milkey4f5de002014-12-17 19:26:11 -080075
76 /**
Jian Licc730a62016-05-10 16:36:16 -070077 * Gets all flow entries. Returns array of all flow rules in the system.
78 *
79 * @return 200 OK with a collection of flows
Jian Li2907ad22016-05-12 23:08:54 -070080 * @onos.rsModel FlowEntries
Ray Milkey4f5de002014-12-17 19:26:11 -080081 */
82 @GET
83 @Produces(MediaType.APPLICATION_JSON)
84 public Response getFlows() {
Ray Milkey4f5de002014-12-17 19:26:11 -080085 final Iterable<Device> devices = get(DeviceService.class).getDevices();
86 for (final Device device : devices) {
Phaneendra Mandaaec654c2015-09-23 19:02:23 +053087 final Iterable<FlowEntry> flowEntries = service.getFlowEntries(device.id());
88 if (flowEntries != null) {
89 for (final FlowEntry entry : flowEntries) {
Thomas Vachuska8683e012015-03-18 18:03:33 -070090 flowsNode.add(codec(FlowEntry.class).encode(entry, this));
Ray Milkey4f5de002014-12-17 19:26:11 -080091 }
92 }
93 }
94
Ray Milkey3f025692015-01-26 11:15:41 -080095 return ok(root).build();
Ray Milkey4f5de002014-12-17 19:26:11 -080096 }
97
98 /**
Jian Licc730a62016-05-10 16:36:16 -070099 * Creates new flow rules. Creates and installs a new flow rules.<br>
Thomas Vachuskaa1ae5e12016-03-28 10:36:30 -0700100 * Instructions description:
101 * https://wiki.onosproject.org/display/ONOS/Flow+Rule+Instructions
102 * <br>
103 * Criteria description:
104 * https://wiki.onosproject.org/display/ONOS/Flow+Rule+Criteria
105 *
Jian Licc730a62016-05-10 16:36:16 -0700106 * @param stream flow rules JSON
Thomas Vachuskaa1ae5e12016-03-28 10:36:30 -0700107 * @return status of the request - CREATED if the JSON is correct,
108 * BAD_REQUEST if the JSON is invalid
Jian Licc730a62016-05-10 16:36:16 -0700109 * @onos.rsModel FlowsBatchPost
Thomas Vachuskaa1ae5e12016-03-28 10:36:30 -0700110 */
111 @POST
112 @Consumes(MediaType.APPLICATION_JSON)
113 @Produces(MediaType.APPLICATION_JSON)
Jian Li2907ad22016-05-12 23:08:54 -0700114 public Response createFlows(@QueryParam("appId") String appId, InputStream stream) {
Thomas Vachuskaa1ae5e12016-03-28 10:36:30 -0700115 try {
116 ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
117 ArrayNode flowsArray = (ArrayNode) jsonTree.get(FLOWS);
Jian Li2907ad22016-05-12 23:08:54 -0700118
119 if (appId != null) {
120 flowsArray.forEach(flowJson -> ((ObjectNode) flowJson).put("appId", appId));
121 }
122
Thomas Vachuskaa1ae5e12016-03-28 10:36:30 -0700123 List<FlowRule> rules = codec(FlowRule.class).decode(flowsArray, this);
124 service.applyFlowRules(rules.toArray(new FlowRule[rules.size()]));
Ray Milkey460f9b02016-03-29 11:56:19 -0700125 rules.forEach(flowRule -> {
126 ObjectNode flowNode = mapper().createObjectNode();
127 flowNode.put(DEVICE_ID, flowRule.deviceId().toString())
128 .put(FLOW_ID, flowRule.id().value());
129 flowsNode.add(flowNode);
130 });
Thomas Vachuskaa1ae5e12016-03-28 10:36:30 -0700131 } catch (IOException ex) {
132 throw new IllegalArgumentException(ex);
133 }
Ray Milkey460f9b02016-03-29 11:56:19 -0700134 return Response.ok(root).build();
Thomas Vachuskaa1ae5e12016-03-28 10:36:30 -0700135 }
136
137 /**
Jian Licc730a62016-05-10 16:36:16 -0700138 * Gets flow entries of a device. Returns array of all flow rules for the
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700139 * specified device.
Jian Licc730a62016-05-10 16:36:16 -0700140 *
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700141 * @param deviceId device identifier
Jian Licc730a62016-05-10 16:36:16 -0700142 * @return 200 OK with a collection of flows of given device
Jian Li2907ad22016-05-12 23:08:54 -0700143 * @onos.rsModel FlowEntries
Ray Milkey4f5de002014-12-17 19:26:11 -0800144 */
145 @GET
146 @Produces(MediaType.APPLICATION_JSON)
Jian Li2907ad22016-05-12 23:08:54 -0700147 // TODO: we need to add "/device" suffix to the path to differentiate with appId
Ray Milkey4f5de002014-12-17 19:26:11 -0800148 @Path("{deviceId}")
149 public Response getFlowByDeviceId(@PathParam("deviceId") String deviceId) {
Phaneendra Mandaaec654c2015-09-23 19:02:23 +0530150 final Iterable<FlowEntry> flowEntries =
Ray Milkey4f5de002014-12-17 19:26:11 -0800151 service.getFlowEntries(DeviceId.deviceId(deviceId));
152
Jian Li9d616492016-03-09 10:52:49 -0800153 if (flowEntries == null || !flowEntries.iterator().hasNext()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800154 throw new ItemNotFoundException(DEVICE_NOT_FOUND);
155 }
Phaneendra Mandaaec654c2015-09-23 19:02:23 +0530156 for (final FlowEntry entry : flowEntries) {
Thomas Vachuska8683e012015-03-18 18:03:33 -0700157 flowsNode.add(codec(FlowEntry.class).encode(entry, this));
Ray Milkey4f5de002014-12-17 19:26:11 -0800158 }
Ray Milkey3f025692015-01-26 11:15:41 -0800159 return ok(root).build();
Ray Milkey4f5de002014-12-17 19:26:11 -0800160 }
161
162 /**
Jian Li2907ad22016-05-12 23:08:54 -0700163 * Gets flow rules. Returns the flow entry specified by the device id and
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700164 * flow rule id.
Jian Licc730a62016-05-10 16:36:16 -0700165 *
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700166 * @param deviceId device identifier
167 * @param flowId flow rule identifier
Jian Li2907ad22016-05-12 23:08:54 -0700168 * @return 200 OK with a collection of flows of given device and flow
169 * @onos.rsModel FlowEntries
Ray Milkey4f5de002014-12-17 19:26:11 -0800170 */
171 @GET
172 @Produces(MediaType.APPLICATION_JSON)
173 @Path("{deviceId}/{flowId}")
174 public Response getFlowByDeviceIdAndFlowId(@PathParam("deviceId") String deviceId,
175 @PathParam("flowId") long flowId) {
Phaneendra Mandaaec654c2015-09-23 19:02:23 +0530176 final Iterable<FlowEntry> flowEntries =
Ray Milkey4f5de002014-12-17 19:26:11 -0800177 service.getFlowEntries(DeviceId.deviceId(deviceId));
178
Jian Li9d616492016-03-09 10:52:49 -0800179 if (flowEntries == null || !flowEntries.iterator().hasNext()) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800180 throw new ItemNotFoundException(DEVICE_NOT_FOUND);
181 }
Phaneendra Mandaaec654c2015-09-23 19:02:23 +0530182 for (final FlowEntry entry : flowEntries) {
Ray Milkey4f5de002014-12-17 19:26:11 -0800183 if (entry.id().value() == flowId) {
Thomas Vachuska8683e012015-03-18 18:03:33 -0700184 flowsNode.add(codec(FlowEntry.class).encode(entry, this));
Ray Milkey4f5de002014-12-17 19:26:11 -0800185 }
186 }
Ray Milkey3f025692015-01-26 11:15:41 -0800187 return ok(root).build();
Ray Milkey4f5de002014-12-17 19:26:11 -0800188 }
Ray Milkeyd43fe452015-05-29 09:35:12 -0700189
190 /**
Jian Li2907ad22016-05-12 23:08:54 -0700191 * Gets flow rules generated by an application.
192 * Returns the flow rule specified by the application id.
193 *
194 * @param appId application identifier
195 * @return 200 OK with a collection of flows of given application id
196 * @onos.rsModel FlowRules
197 */
198 @GET
199 @Produces(MediaType.APPLICATION_JSON)
200 @Path("application/{appId}")
201 public Response getFlowByAppId(@PathParam("appId") String appId) {
202 final ApplicationService appService = get(ApplicationService.class);
203 final ApplicationId idInstant = nullIsNotFound(appService.getId(appId), APP_ID_NOT_FOUND);
204 final Iterable<FlowRule> flowRules = service.getFlowRulesById(idInstant);
205
206 flowRules.forEach(flow -> flowsNode.add(codec(FlowRule.class).encode(flow, this)));
207 return ok(root).build();
208 }
209
210 /**
211 * Removes flow rules by application ID.
212 * Removes a collection of flow rules generated by the given application.
213 *
214 * @param appId application identifier
215 * @return 204 NO CONTENT
216 */
217 @DELETE
218 @Produces(MediaType.APPLICATION_JSON)
219 @Path("application/{appId}")
220 public Response removeFlowByAppId(@PathParam("appId") String appId) {
221 final ApplicationService appService = get(ApplicationService.class);
222 final ApplicationId idInstant = nullIsNotFound(appService.getId(appId), APP_ID_NOT_FOUND);
223 service.removeFlowRulesById(idInstant);
224 return Response.noContent().build();
225 }
226
227 /**
Jian Licc730a62016-05-10 16:36:16 -0700228 * Creates new flow rule. Creates and installs a new flow rule for the
Andrea Campanella881f29f2016-03-03 19:18:42 -0800229 * specified device. <br>
230 * Instructions description:
231 * https://wiki.onosproject.org/display/ONOS/Flow+Rule+Instructions
232 * <br>
233 * Criteria description:
234 * https://wiki.onosproject.org/display/ONOS/Flow+Rule+Criteria
235 *
Madan Jampani0dbac7a2015-06-25 10:37:45 -0700236 * @param deviceId device identifier
Jian Li2907ad22016-05-12 23:08:54 -0700237 * @param appId application identifier
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700238 * @param stream flow rule JSON
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700239 * @return status of the request - CREATED if the JSON is correct,
Ray Milkeyd43fe452015-05-29 09:35:12 -0700240 * BAD_REQUEST if the JSON is invalid
Jian Licc730a62016-05-10 16:36:16 -0700241 * @onos.rsModel FlowsPost
Ray Milkeyd43fe452015-05-29 09:35:12 -0700242 */
243 @POST
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700244 @Path("{deviceId}")
Ray Milkeyd43fe452015-05-29 09:35:12 -0700245 @Consumes(MediaType.APPLICATION_JSON)
246 @Produces(MediaType.APPLICATION_JSON)
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700247 public Response createFlow(@PathParam("deviceId") String deviceId,
Jian Li2907ad22016-05-12 23:08:54 -0700248 @QueryParam("appId") String appId,
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700249 InputStream stream) {
Ray Milkeyd43fe452015-05-29 09:35:12 -0700250 try {
Ray Milkey5d915f42015-08-13 10:27:53 -0700251 ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
252 JsonNode specifiedDeviceId = jsonTree.get("deviceId");
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700253 if (specifiedDeviceId != null &&
254 !specifiedDeviceId.asText().equals(deviceId)) {
255 throw new IllegalArgumentException(
256 "Invalid deviceId in flow creation request");
257 }
Ray Milkey5d915f42015-08-13 10:27:53 -0700258 jsonTree.put("deviceId", deviceId);
Jian Li2907ad22016-05-12 23:08:54 -0700259
260 if (appId != null) {
261 jsonTree.put("appId", appId);
262 }
263
Ray Milkey5d915f42015-08-13 10:27:53 -0700264 FlowRule rule = codec(FlowRule.class).decode(jsonTree, this);
Ray Milkeyd43fe452015-05-29 09:35:12 -0700265 service.applyFlowRules(rule);
Jian Li9d616492016-03-09 10:52:49 -0800266 UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
267 .path("flows")
268 .path(deviceId)
Ray Milkey90289b02016-03-31 15:23:28 -0700269 .path(Long.toString(rule.id().value()));
Jian Li9d616492016-03-09 10:52:49 -0800270
271 return Response
272 .created(locationBuilder.build())
273 .build();
274 } catch (IOException ex) {
Ray Milkey5d915f42015-08-13 10:27:53 -0700275 throw new IllegalArgumentException(ex);
Ray Milkeyd43fe452015-05-29 09:35:12 -0700276 }
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700277 }
278
279 /**
Jian Li2907ad22016-05-12 23:08:54 -0700280 * Removes flow rule. Removes the specified flow rule.
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700281 *
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -0700282 * @param deviceId device identifier
283 * @param flowId flow rule identifier
Jian Lic2a542b2016-05-10 11:48:19 -0700284 * @return 204 NO CONTENT
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700285 */
286 @DELETE
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700287 @Path("{deviceId}/{flowId}")
Jian Lic2a542b2016-05-10 11:48:19 -0700288 public Response deleteFlowByDeviceIdAndFlowId(@PathParam("deviceId") String deviceId,
Jian Licc730a62016-05-10 16:36:16 -0700289 @PathParam("flowId") long flowId) {
Phaneendra Mandaaec654c2015-09-23 19:02:23 +0530290 final Iterable<FlowEntry> flowEntries =
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700291 service.getFlowEntries(DeviceId.deviceId(deviceId));
292
Phaneendra Mandaaec654c2015-09-23 19:02:23 +0530293 if (!flowEntries.iterator().hasNext()) {
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700294 throw new ItemNotFoundException(DEVICE_NOT_FOUND);
295 }
296
Phaneendra Mandaaec654c2015-09-23 19:02:23 +0530297 StreamSupport.stream(flowEntries.spliterator(), false)
Ray Milkeyeb5c7172015-06-23 14:59:27 -0700298 .filter(entry -> entry.id().value() == flowId)
299 .forEach(service::removeFlowRules);
Jian Lic2a542b2016-05-10 11:48:19 -0700300 return Response.noContent().build();
Ray Milkeyd43fe452015-05-29 09:35:12 -0700301 }
302
Ray Milkey460f9b02016-03-29 11:56:19 -0700303 /**
304 * Removes a batch of flow rules.
Ray Milkeybee35092016-04-12 10:01:26 -0700305 *
306 * @param stream stream for posted JSON
Jian Licc730a62016-05-10 16:36:16 -0700307 * @return 204 NO CONTENT
Ray Milkey460f9b02016-03-29 11:56:19 -0700308 */
309 @DELETE
Jian Lic2a542b2016-05-10 11:48:19 -0700310 public Response deleteFlows(InputStream stream) {
Ray Milkey460f9b02016-03-29 11:56:19 -0700311 ListMultimap<DeviceId, Long> deviceMap = ArrayListMultimap.create();
312 List<FlowEntry> rulesToRemove = new ArrayList<>();
313
314 try {
315 ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
316
317 JsonNode jsonFlows = jsonTree.get("flows");
318
319 jsonFlows.forEach(node -> {
320 DeviceId deviceId =
321 DeviceId.deviceId(
322 nullIsNotFound(node.get(DEVICE_ID),
Jian Licc730a62016-05-10 16:36:16 -0700323 DEVICE_NOT_FOUND).asText());
Ray Milkey460f9b02016-03-29 11:56:19 -0700324 long flowId = nullIsNotFound(node.get(FLOW_ID),
Jian Licc730a62016-05-10 16:36:16 -0700325 FLOW_NOT_FOUND).asLong();
Ray Milkey460f9b02016-03-29 11:56:19 -0700326 deviceMap.put(deviceId, flowId);
327
328 });
329 } catch (IOException ex) {
330 throw new IllegalArgumentException(ex);
331 }
332
333 deviceMap.keySet().forEach(deviceId -> {
334 List<Long> flowIds = deviceMap.get(deviceId);
335 Iterable<FlowEntry> entries = service.getFlowEntries(deviceId);
336 flowIds.forEach(flowId -> {
337 StreamSupport.stream(entries.spliterator(), false)
338 .filter(entry -> flowId == entry.id().value())
339 .forEach(rulesToRemove::add);
340 });
341 });
342
343 service.removeFlowRules(rulesToRemove.toArray(new FlowEntry[0]));
Jian Lic2a542b2016-05-10 11:48:19 -0700344 return Response.noContent().build();
Ray Milkey460f9b02016-03-29 11:56:19 -0700345 }
Ray Milkey4f5de002014-12-17 19:26:11 -0800346}