blob: 80eba7a75888c90c60d9bfcbfdfdfa95a3ea4bdc [file] [log] [blame]
Jian Liecb3c0f2015-12-15 10:07:49 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Jian Liecb3c0f2015-12-15 10:07:49 -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 */
16package org.onosproject.rest.resources;
17
18import com.fasterxml.jackson.databind.JsonNode;
19import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
Ray Milkey957bb372018-04-03 15:39:46 -070021import com.google.common.collect.ImmutableList;
22import org.onlab.util.HexString;
23import org.onosproject.codec.JsonCodec;
Jian Liecb3c0f2015-12-15 10:07:49 -080024import org.onosproject.net.Device;
25import org.onosproject.net.DeviceId;
26import org.onosproject.net.device.DeviceService;
27import org.onosproject.net.group.DefaultGroupDescription;
28import org.onosproject.net.group.DefaultGroupKey;
29import org.onosproject.net.group.Group;
Ray Milkey957bb372018-04-03 15:39:46 -070030import org.onosproject.net.group.GroupBucket;
31import org.onosproject.net.group.GroupBuckets;
Jian Liecb3c0f2015-12-15 10:07:49 -080032import org.onosproject.net.group.GroupDescription;
33import org.onosproject.net.group.GroupKey;
34import org.onosproject.net.group.GroupService;
35import org.onosproject.rest.AbstractWebResource;
36
37import javax.ws.rs.Consumes;
38import javax.ws.rs.DELETE;
39import javax.ws.rs.GET;
40import javax.ws.rs.POST;
41import javax.ws.rs.Path;
42import javax.ws.rs.PathParam;
43import javax.ws.rs.Produces;
Jian Li9d616492016-03-09 10:52:49 -080044import javax.ws.rs.core.Context;
Jian Liecb3c0f2015-12-15 10:07:49 -080045import javax.ws.rs.core.MediaType;
46import javax.ws.rs.core.Response;
Jian Li9d616492016-03-09 10:52:49 -080047import javax.ws.rs.core.UriBuilder;
48import javax.ws.rs.core.UriInfo;
Jian Liecb3c0f2015-12-15 10:07:49 -080049import java.io.IOException;
50import java.io.InputStream;
Ray Milkey957bb372018-04-03 15:39:46 -070051import java.util.ArrayList;
52import java.util.List;
53import java.util.stream.IntStream;
Jian Liecb3c0f2015-12-15 10:07:49 -080054
Jian Lia7f86ce2015-12-20 13:42:10 -080055import static org.onlab.util.Tools.nullIsNotFound;
Ray Milkey86ee5e82018-04-02 15:33:07 -070056import static org.onlab.util.Tools.readTreeFromStream;
Jian Lia7f86ce2015-12-20 13:42:10 -080057
Jian Liecb3c0f2015-12-15 10:07:49 -080058/**
59 * Query and program group rules.
60 */
61
62@Path("groups")
63public class GroupsWebResource extends AbstractWebResource {
Jian Li9d616492016-03-09 10:52:49 -080064
65 @Context
Jian Licc730a62016-05-10 16:36:16 -070066 private UriInfo uriInfo;
Jian Li9d616492016-03-09 10:52:49 -080067
Jian Licc730a62016-05-10 16:36:16 -070068 private static final String DEVICE_INVALID = "Invalid deviceId in group creation request";
69 private static final String GROUP_NOT_FOUND = "Group was not found";
Jian Licc730a62016-05-10 16:36:16 -070070 private final ObjectNode root = mapper().createObjectNode();
71 private final ArrayNode groupsNode = root.putArray("groups");
Jian Liecb3c0f2015-12-15 10:07:49 -080072
Ray Milkey957bb372018-04-03 15:39:46 -070073 private GroupKey createKey(String appCookieString) {
74 if (!appCookieString.startsWith("0x")) {
75 throw new IllegalArgumentException("APP_COOKIE must be a hex string starts with 0x");
76 }
77 return new DefaultGroupKey(HexString.fromHexString(
78 appCookieString.split("0x")[1], ""));
79 }
80
Jian Liecb3c0f2015-12-15 10:07:49 -080081 /**
82 * Returns all groups of all devices.
Jian Lia7f86ce2015-12-20 13:42:10 -080083 *
Jian Licc730a62016-05-10 16:36:16 -070084 * @return 200 OK with array of all the groups in the system
Jian Lia7f86ce2015-12-20 13:42:10 -080085 * @onos.rsModel Groups
Jian Liecb3c0f2015-12-15 10:07:49 -080086 */
87 @GET
88 @Produces(MediaType.APPLICATION_JSON)
89 public Response getGroups() {
Harshada Chaundkar4f62a1e2019-02-18 12:27:34 -050090 GroupService groupService = get(GroupService.class);
Jian Liecb3c0f2015-12-15 10:07:49 -080091 final Iterable<Device> devices = get(DeviceService.class).getDevices();
92 devices.forEach(device -> {
93 final Iterable<Group> groups = groupService.getGroups(device.id());
94 if (groups != null) {
95 groups.forEach(group -> groupsNode.add(codec(Group.class).encode(group, this)));
96 }
97 });
98
99 return ok(root).build();
100 }
101
102 /**
103 * Returns all groups associated with the given device.
104 *
105 * @param deviceId device identifier
Jian Licc730a62016-05-10 16:36:16 -0700106 * @return 200 OK with array of all the groups in the system
Jian Lia7f86ce2015-12-20 13:42:10 -0800107 * @onos.rsModel Groups
Jian Liecb3c0f2015-12-15 10:07:49 -0800108 */
109 @GET
110 @Produces(MediaType.APPLICATION_JSON)
111 @Path("{deviceId}")
112 public Response getGroupsByDeviceId(@PathParam("deviceId") String deviceId) {
Harshada Chaundkar4f62a1e2019-02-18 12:27:34 -0500113 GroupService groupService = get(GroupService.class);
Jian Liecb3c0f2015-12-15 10:07:49 -0800114 final Iterable<Group> groups = groupService.getGroups(DeviceId.deviceId(deviceId));
115
116 groups.forEach(group -> groupsNode.add(codec(Group.class).encode(group, this)));
117
118 return ok(root).build();
119 }
120
121 /**
Jian Lia7f86ce2015-12-20 13:42:10 -0800122 * Returns a group with the given deviceId and appCookie.
123 *
124 * @param deviceId device identifier
125 * @param appCookie group key
Jian Licc730a62016-05-10 16:36:16 -0700126 * @return 200 OK with a group entry in the system
Jian Lia7f86ce2015-12-20 13:42:10 -0800127 * @onos.rsModel Group
128 */
129 @GET
130 @Produces(MediaType.APPLICATION_JSON)
131 @Path("{deviceId}/{appCookie}")
132 public Response getGroupByDeviceIdAndAppCookie(@PathParam("deviceId") String deviceId,
133 @PathParam("appCookie") String appCookie) {
Harshada Chaundkar4f62a1e2019-02-18 12:27:34 -0500134 GroupService groupService = get(GroupService.class);
Jian Lia7f86ce2015-12-20 13:42:10 -0800135 final DeviceId deviceIdInstance = DeviceId.deviceId(deviceId);
Varun Sharma1853b3f2016-07-18 14:37:13 +0530136
Ray Milkey957bb372018-04-03 15:39:46 -0700137 final GroupKey appCookieInstance = createKey(appCookie);
Jian Lia7f86ce2015-12-20 13:42:10 -0800138
139 Group group = nullIsNotFound(groupService.getGroup(deviceIdInstance, appCookieInstance),
140 GROUP_NOT_FOUND);
141
142 groupsNode.add(codec(Group.class).encode(group, this));
143 return ok(root).build();
144 }
145
146 /**
Jian Liecb3c0f2015-12-15 10:07:49 -0800147 * Create new group rule. Creates and installs a new group rule for the
148 * specified device.
149 *
150 * @param deviceId device identifier
151 * @param stream group rule JSON
Jian Liecb3c0f2015-12-15 10:07:49 -0800152 * @return status of the request - CREATED if the JSON is correct,
153 * BAD_REQUEST if the JSON is invalid
Jian Lia7f86ce2015-12-20 13:42:10 -0800154 * @onos.rsModel GroupsPost
Jian Liecb3c0f2015-12-15 10:07:49 -0800155 */
156 @POST
157 @Path("{deviceId}")
158 @Consumes(MediaType.APPLICATION_JSON)
159 @Produces(MediaType.APPLICATION_JSON)
160 public Response createGroup(@PathParam("deviceId") String deviceId,
161 InputStream stream) {
Harshada Chaundkar4f62a1e2019-02-18 12:27:34 -0500162 GroupService groupService = get(GroupService.class);
Jian Liecb3c0f2015-12-15 10:07:49 -0800163 try {
Jian Li9d616492016-03-09 10:52:49 -0800164
Ray Milkey86ee5e82018-04-02 15:33:07 -0700165 ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
Jian Liecb3c0f2015-12-15 10:07:49 -0800166 JsonNode specifiedDeviceId = jsonTree.get("deviceId");
167
168 if (specifiedDeviceId != null &&
169 !specifiedDeviceId.asText().equals(deviceId)) {
170 throw new IllegalArgumentException(DEVICE_INVALID);
171 }
172 jsonTree.put("deviceId", deviceId);
173 Group group = codec(Group.class).decode(jsonTree, this);
174 GroupDescription description = new DefaultGroupDescription(
175 group.deviceId(), group.type(), group.buckets(),
176 group.appCookie(), group.id().id(), group.appId());
177 groupService.addGroup(description);
Jian Li9d616492016-03-09 10:52:49 -0800178 UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
179 .path("groups")
180 .path(deviceId)
181 .path(Long.toString(group.id().id()));
182 return Response
183 .created(locationBuilder.build())
184 .build();
185 } catch (IOException ex) {
Jian Liecb3c0f2015-12-15 10:07:49 -0800186 throw new IllegalArgumentException(ex);
187 }
Jian Liecb3c0f2015-12-15 10:07:49 -0800188 }
189
190 /**
191 * Removes the specified group.
192 *
193 * @param deviceId device identifier
194 * @param appCookie application cookie to be used for lookup
Jian Lic2a542b2016-05-10 11:48:19 -0700195 * @return 204 NO CONTENT
Jian Liecb3c0f2015-12-15 10:07:49 -0800196 */
197 @DELETE
Jian Liecb3c0f2015-12-15 10:07:49 -0800198 @Path("{deviceId}/{appCookie}")
Jian Lic2a542b2016-05-10 11:48:19 -0700199 public Response deleteGroupByDeviceIdAndAppCookie(@PathParam("deviceId") String deviceId,
200 @PathParam("appCookie") String appCookie) {
Harshada Chaundkar4f62a1e2019-02-18 12:27:34 -0500201 GroupService groupService = get(GroupService.class);
Jian Liecb3c0f2015-12-15 10:07:49 -0800202 DeviceId deviceIdInstance = DeviceId.deviceId(deviceId);
Varun Sharma1853b3f2016-07-18 14:37:13 +0530203
Ray Milkey957bb372018-04-03 15:39:46 -0700204 final GroupKey appCookieInstance = createKey(appCookie);
Jian Liecb3c0f2015-12-15 10:07:49 -0800205
206 groupService.removeGroup(deviceIdInstance, appCookieInstance, null);
Jian Lic2a542b2016-05-10 11:48:19 -0700207 return Response.noContent().build();
Jian Liecb3c0f2015-12-15 10:07:49 -0800208 }
Ray Milkey957bb372018-04-03 15:39:46 -0700209
210 /**
211 * Adds buckets to a group using the group service.
212 *
213 * @param deviceIdString device Id
214 * @param appCookieString application cookie
215 * @param stream JSON stream
216 */
217 private void updateGroupBuckets(String deviceIdString, String appCookieString, InputStream stream)
218 throws IOException {
Harshada Chaundkar4f62a1e2019-02-18 12:27:34 -0500219 GroupService groupService = get(GroupService.class);
Ray Milkey957bb372018-04-03 15:39:46 -0700220 DeviceId deviceId = DeviceId.deviceId(deviceIdString);
221 final GroupKey groupKey = createKey(appCookieString);
222
223 Group group = nullIsNotFound(groupService.getGroup(deviceId, groupKey), GROUP_NOT_FOUND);
224
225 ObjectNode jsonTree = readTreeFromStream(mapper(), stream);
226
227 GroupBuckets buckets = null;
228 List<GroupBucket> groupBucketList = new ArrayList<>();
229 JsonNode bucketsJson = jsonTree.get("buckets");
230 final JsonCodec<GroupBucket> groupBucketCodec = codec(GroupBucket.class);
231 if (bucketsJson != null) {
232 IntStream.range(0, bucketsJson.size())
233 .forEach(i -> {
234 ObjectNode bucketJson = (ObjectNode) bucketsJson.get(i);
235 groupBucketList.add(groupBucketCodec.decode(bucketJson, this));
236 });
237 buckets = new GroupBuckets(groupBucketList);
238 }
239 groupService.addBucketsToGroup(deviceId, groupKey, buckets, groupKey, group.appId());
240 }
241
242 /**
243 * Adds buckets to an existing group.
244 *
245 * @param deviceIdString device identifier
246 * @param appCookieString application cookie
247 * @param stream buckets JSON
248 * @return status of the request - NO_CONTENT if the JSON is correct,
249 * BAD_REQUEST if the JSON is invalid
250 * @onos.rsModel GroupsBucketsPost
251 */
252 @POST
253 @Path("{deviceId}/{appCookie}/buckets")
254 @Consumes(MediaType.APPLICATION_JSON)
255 @Produces(MediaType.APPLICATION_JSON)
256 public Response addBucket(@PathParam("deviceId") String deviceIdString,
257 @PathParam("appCookie") String appCookieString,
258 InputStream stream) {
259 try {
260 updateGroupBuckets(deviceIdString, appCookieString, stream);
261
262 return Response
263 .noContent()
264 .build();
265 } catch (IOException ex) {
266 throw new IllegalArgumentException(ex);
267 }
268 }
269
270 /**
271 * Removes buckets from a group using the group service.
272 *
273 * @param deviceIdString device Id
274 * @param appCookieString application cookie
275 * @param bucketIds comma separated list of bucket Ids to remove
276 */
277 private void removeGroupBuckets(String deviceIdString, String appCookieString, String bucketIds) {
278 DeviceId deviceId = DeviceId.deviceId(deviceIdString);
279 final GroupKey groupKey = createKey(appCookieString);
Harshada Chaundkar4f62a1e2019-02-18 12:27:34 -0500280 GroupService groupService = get(GroupService.class);
Ray Milkey957bb372018-04-03 15:39:46 -0700281
282 Group group = nullIsNotFound(groupService.getGroup(deviceId, groupKey), GROUP_NOT_FOUND);
283
284 List<GroupBucket> groupBucketList = new ArrayList<>();
285
286 List<String> bucketsToRemove = ImmutableList.copyOf(bucketIds.split(","));
287
288 bucketsToRemove.forEach(
289 bucketIdToRemove -> {
290 group.buckets().buckets().stream()
291 .filter(bucket -> Integer.toString(bucket.hashCode()).equals(bucketIdToRemove))
292 .forEach(groupBucketList::add);
293 }
294 );
295 groupService.removeBucketsFromGroup(deviceId, groupKey,
296 new GroupBuckets(groupBucketList), groupKey,
297 group.appId());
298 }
299
300 /**
301 * Removes buckets from an existing group.
302 *
303 * @param deviceIdString device identifier
304 * @param appCookieString application cookie
305 * @param bucketIds comma separated list of identifiers of buckets to remove from this group
306 * @return status of the request - NO_CONTENT if the JSON is correct,
307 * BAD_REQUEST if the JSON is invalid
308 */
309 @DELETE
310 @Path("{deviceId}/{appCookie}/buckets/{bucketIds}")
311 @Consumes(MediaType.APPLICATION_JSON)
312 @Produces(MediaType.APPLICATION_JSON)
313 public Response deleteBuckets(@PathParam("deviceId") String deviceIdString,
314 @PathParam("appCookie") String appCookieString,
315 @PathParam("bucketIds") String bucketIds) {
316 removeGroupBuckets(deviceIdString, appCookieString, bucketIds);
317
318 return Response
319 .noContent()
320 .build();
321 }
Jian Liecb3c0f2015-12-15 10:07:49 -0800322}