Jian Li
Committed by Ray Milkey

[ONOS-3603] Implement REST API for Group query, insert, delete

* Implement decoding feature for GroupBucketCodec and GroupCodec
* Implement GroupsWebResource
* Add unit test for GroupBucketCodec and GroupCodec
* Add unit test for GroupsWebResource
* Add group insertion json example
* Add Swagger doc

Change-Id: Ie58cba2e1af996c7b8652a55d9ef0c27207beafc
...@@ -17,7 +17,6 @@ package org.onosproject.codec.impl; ...@@ -17,7 +17,6 @@ package org.onosproject.codec.impl;
17 17
18 import com.codahale.metrics.Metric; 18 import com.codahale.metrics.Metric;
19 import com.google.common.collect.ImmutableSet; 19 import com.google.common.collect.ImmutableSet;
20 -
21 import org.apache.felix.scr.annotations.Activate; 20 import org.apache.felix.scr.annotations.Activate;
22 import org.apache.felix.scr.annotations.Component; 21 import org.apache.felix.scr.annotations.Component;
23 import org.apache.felix.scr.annotations.Deactivate; 22 import org.apache.felix.scr.annotations.Deactivate;
...@@ -35,11 +34,11 @@ import org.onosproject.net.HostLocation; ...@@ -35,11 +34,11 @@ import org.onosproject.net.HostLocation;
35 import org.onosproject.net.Link; 34 import org.onosproject.net.Link;
36 import org.onosproject.net.Path; 35 import org.onosproject.net.Path;
37 import org.onosproject.net.Port; 36 import org.onosproject.net.Port;
37 +import org.onosproject.net.device.PortStatistics;
38 import org.onosproject.net.driver.Driver; 38 import org.onosproject.net.driver.Driver;
39 import org.onosproject.net.flow.FlowEntry; 39 import org.onosproject.net.flow.FlowEntry;
40 import org.onosproject.net.flow.FlowRule; 40 import org.onosproject.net.flow.FlowRule;
41 import org.onosproject.net.flow.TableStatisticsEntry; 41 import org.onosproject.net.flow.TableStatisticsEntry;
42 -import org.onosproject.net.device.PortStatistics;
43 import org.onosproject.net.flow.TrafficSelector; 42 import org.onosproject.net.flow.TrafficSelector;
44 import org.onosproject.net.flow.TrafficTreatment; 43 import org.onosproject.net.flow.TrafficTreatment;
45 import org.onosproject.net.flow.criteria.Criterion; 44 import org.onosproject.net.flow.criteria.Criterion;
......
...@@ -15,14 +15,18 @@ ...@@ -15,14 +15,18 @@
15 */ 15 */
16 package org.onosproject.codec.impl; 16 package org.onosproject.codec.impl;
17 17
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import org.onosproject.codec.CodecContext; 19 import org.onosproject.codec.CodecContext;
19 import org.onosproject.codec.JsonCodec; 20 import org.onosproject.codec.JsonCodec;
21 +import org.onosproject.core.DefaultGroupId;
22 +import org.onosproject.core.GroupId;
23 +import org.onosproject.net.PortNumber;
20 import org.onosproject.net.flow.TrafficTreatment; 24 import org.onosproject.net.flow.TrafficTreatment;
25 +import org.onosproject.net.group.DefaultGroupBucket;
21 import org.onosproject.net.group.GroupBucket; 26 import org.onosproject.net.group.GroupBucket;
22 27
23 -import com.fasterxml.jackson.databind.node.ObjectNode;
24 -
25 import static com.google.common.base.Preconditions.checkNotNull; 28 import static com.google.common.base.Preconditions.checkNotNull;
29 +import static org.onlab.util.Tools.nullIsIllegal;
26 30
27 /** 31 /**
28 * Group bucket JSON codec. 32 * Group bucket JSON codec.
...@@ -36,6 +40,8 @@ public class GroupBucketCodec extends JsonCodec<GroupBucket> { ...@@ -36,6 +40,8 @@ public class GroupBucketCodec extends JsonCodec<GroupBucket> {
36 private static final String WATCH_GROUP = "watchGroup"; 40 private static final String WATCH_GROUP = "watchGroup";
37 private static final String PACKETS = "packets"; 41 private static final String PACKETS = "packets";
38 private static final String BYTES = "bytes"; 42 private static final String BYTES = "bytes";
43 + private static final String MISSING_MEMBER_MESSAGE =
44 + " member is required in Group";
39 45
40 @Override 46 @Override
41 public ObjectNode encode(GroupBucket bucket, CodecContext context) { 47 public ObjectNode encode(GroupBucket bucket, CodecContext context) {
...@@ -61,4 +67,59 @@ public class GroupBucketCodec extends JsonCodec<GroupBucket> { ...@@ -61,4 +67,59 @@ public class GroupBucketCodec extends JsonCodec<GroupBucket> {
61 67
62 return result; 68 return result;
63 } 69 }
70 +
71 + @Override
72 + public GroupBucket decode(ObjectNode json, CodecContext context) {
73 + if (json == null || !json.isObject()) {
74 + return null;
75 + }
76 +
77 + // build traffic treatment
78 + ObjectNode treatmentJson = get(json, TREATMENT);
79 + TrafficTreatment trafficTreatment = null;
80 + if (treatmentJson != null) {
81 + JsonCodec<TrafficTreatment> treatmentCodec =
82 + context.codec(TrafficTreatment.class);
83 + trafficTreatment = treatmentCodec.decode(treatmentJson, context);
84 + }
85 +
86 + // parse group type
87 + String type = nullIsIllegal(json.get(TYPE), TYPE + MISSING_MEMBER_MESSAGE).asText();
88 + GroupBucket groupBucket = null;
89 +
90 + switch (type) {
91 + case "SELECT":
92 + // parse weight
93 + int weightInt = nullIsIllegal(json.get(WEIGHT), WEIGHT + MISSING_MEMBER_MESSAGE).asInt();
94 +
95 + groupBucket =
96 + DefaultGroupBucket.createSelectGroupBucket(trafficTreatment, (short) weightInt);
97 + break;
98 + case "INDIRECT":
99 + groupBucket =
100 + DefaultGroupBucket.createIndirectGroupBucket(trafficTreatment);
101 + break;
102 + case "ALL":
103 + groupBucket =
104 + DefaultGroupBucket.createAllGroupBucket(trafficTreatment);
105 + break;
106 + case "FAILOVER":
107 + // parse watchPort
108 + PortNumber watchPort = PortNumber.portNumber(nullIsIllegal(json.get(WATCH_PORT),
109 + WATCH_PORT + MISSING_MEMBER_MESSAGE).asText());
110 +
111 + // parse watchGroup
112 + int groupIdInt = nullIsIllegal(json.get(WATCH_GROUP),
113 + WATCH_GROUP + MISSING_MEMBER_MESSAGE).asInt();
114 + GroupId watchGroup = new DefaultGroupId((short) groupIdInt);
115 +
116 + groupBucket =
117 + DefaultGroupBucket.createFailoverGroupBucket(trafficTreatment, watchPort, watchGroup);
118 + break;
119 + default:
120 + DefaultGroupBucket.createAllGroupBucket(trafficTreatment);
121 + }
122 +
123 + return groupBucket;
124 + }
64 } 125 }
......
...@@ -15,20 +15,40 @@ ...@@ -15,20 +15,40 @@
15 */ 15 */
16 package org.onosproject.codec.impl; 16 package org.onosproject.codec.impl;
17 17
18 +import com.fasterxml.jackson.databind.JsonNode;
19 +import com.fasterxml.jackson.databind.node.ArrayNode;
20 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import org.onosproject.codec.CodecContext; 21 import org.onosproject.codec.CodecContext;
19 import org.onosproject.codec.JsonCodec; 22 import org.onosproject.codec.JsonCodec;
23 +import org.onosproject.core.ApplicationId;
24 +import org.onosproject.core.CoreService;
25 +import org.onosproject.core.DefaultGroupId;
26 +import org.onosproject.core.GroupId;
27 +import org.onosproject.net.DeviceId;
28 +import org.onosproject.net.group.DefaultGroup;
29 +import org.onosproject.net.group.DefaultGroupDescription;
30 +import org.onosproject.net.group.DefaultGroupKey;
20 import org.onosproject.net.group.Group; 31 import org.onosproject.net.group.Group;
21 import org.onosproject.net.group.GroupBucket; 32 import org.onosproject.net.group.GroupBucket;
33 +import org.onosproject.net.group.GroupBuckets;
34 +import org.onosproject.net.group.GroupDescription;
35 +import org.onosproject.net.group.GroupKey;
36 +import org.slf4j.Logger;
22 37
23 -import com.fasterxml.jackson.databind.node.ArrayNode; 38 +import java.util.ArrayList;
24 -import com.fasterxml.jackson.databind.node.ObjectNode; 39 +import java.util.List;
40 +import java.util.stream.IntStream;
25 41
26 import static com.google.common.base.Preconditions.checkNotNull; 42 import static com.google.common.base.Preconditions.checkNotNull;
43 +import static org.onlab.util.Tools.nullIsIllegal;
44 +import static org.slf4j.LoggerFactory.getLogger;
27 45
28 /** 46 /**
29 * Group JSON codec. 47 * Group JSON codec.
30 */ 48 */
31 public final class GroupCodec extends JsonCodec<Group> { 49 public final class GroupCodec extends JsonCodec<Group> {
50 + private final Logger log = getLogger(getClass());
51 +
32 // JSON field names 52 // JSON field names
33 private static final String ID = "id"; 53 private static final String ID = "id";
34 private static final String STATE = "state"; 54 private static final String STATE = "state";
...@@ -37,11 +57,15 @@ public final class GroupCodec extends JsonCodec<Group> { ...@@ -37,11 +57,15 @@ public final class GroupCodec extends JsonCodec<Group> {
37 private static final String BYTES = "bytes"; 57 private static final String BYTES = "bytes";
38 private static final String REFERENCE_COUNT = "referenceCount"; 58 private static final String REFERENCE_COUNT = "referenceCount";
39 private static final String TYPE = "type"; 59 private static final String TYPE = "type";
60 + private static final String GROUP_ID = "groupId";
40 private static final String DEVICE_ID = "deviceId"; 61 private static final String DEVICE_ID = "deviceId";
41 private static final String APP_ID = "appId"; 62 private static final String APP_ID = "appId";
42 - private static final String APP_COOKIE = "appCookie"; 63 + private static final String APP_COOKIE = "appCookie";
43 - private static final String GIVEN_GROUP_ID = "givenGroupId"; 64 + private static final String GIVEN_GROUP_ID = "givenGroupId";
44 private static final String BUCKETS = "buckets"; 65 private static final String BUCKETS = "buckets";
66 + private static final String MISSING_MEMBER_MESSAGE =
67 + " member is required in Group";
68 + public static final String REST_APP_ID = "org.onosproject.rest";
45 69
46 @Override 70 @Override
47 public ObjectNode encode(Group group, CodecContext context) { 71 public ObjectNode encode(Group group, CodecContext context) {
...@@ -70,10 +94,81 @@ public final class GroupCodec extends JsonCodec<Group> { ...@@ -70,10 +94,81 @@ public final class GroupCodec extends JsonCodec<Group> {
70 94
71 ArrayNode buckets = context.mapper().createArrayNode(); 95 ArrayNode buckets = context.mapper().createArrayNode();
72 group.buckets().buckets().forEach(bucket -> { 96 group.buckets().buckets().forEach(bucket -> {
73 - ObjectNode bucketJson = context.codec(GroupBucket.class).encode(bucket, context); 97 + ObjectNode bucketJson = context.codec(GroupBucket.class).encode(bucket, context);
74 - buckets.add(bucketJson); 98 + buckets.add(bucketJson);
75 - }); 99 + });
76 result.set(BUCKETS, buckets); 100 result.set(BUCKETS, buckets);
77 return result; 101 return result;
78 } 102 }
103 +
104 + @Override
105 + public Group decode(ObjectNode json, CodecContext context) {
106 + if (json == null || !json.isObject()) {
107 + return null;
108 + }
109 +
110 + final JsonCodec<GroupBucket> groupBucketCodec = context.codec(GroupBucket.class);
111 + CoreService coreService = context.getService(CoreService.class);
112 +
113 + // parse group id
114 + int groupIdInt = nullIsIllegal(json.get(GROUP_ID),
115 + GROUP_ID + MISSING_MEMBER_MESSAGE).asInt();
116 + GroupId groupId = new DefaultGroupId((short) groupIdInt);
117 +
118 + // parse group key (appCookie)
119 + String groupKeyStr = nullIsIllegal(json.get(APP_COOKIE),
120 + APP_COOKIE + MISSING_MEMBER_MESSAGE).asText();
121 + GroupKey groupKey = new DefaultGroupKey(groupKeyStr.getBytes());
122 +
123 + // parse device id
124 + DeviceId deviceId = DeviceId.deviceId(nullIsIllegal(json.get(DEVICE_ID),
125 + DEVICE_ID + MISSING_MEMBER_MESSAGE).asText());
126 +
127 + // application id
128 + ApplicationId appId = coreService.registerApplication(REST_APP_ID);
129 +
130 + // parse group type
131 + String type = nullIsIllegal(json.get(TYPE),
132 + TYPE + MISSING_MEMBER_MESSAGE).asText();
133 + GroupDescription.Type groupType = null;
134 +
135 + switch (type) {
136 + case "SELECT":
137 + groupType = Group.Type.SELECT;
138 + break;
139 + case "INDIRECT":
140 + groupType = Group.Type.INDIRECT;
141 + break;
142 + case "ALL":
143 + groupType = Group.Type.ALL;
144 + break;
145 + case "FAILOVER":
146 + groupType = Group.Type.FAILOVER;
147 + break;
148 + default:
149 + log.warn("The requested type {} is not defined for group.", type);
150 + return null;
151 + }
152 +
153 + // parse group buckets
154 + // TODO: make sure that INDIRECT group only has one bucket
155 + GroupBuckets buckets = null;
156 + List<GroupBucket> groupBucketList = new ArrayList<>();
157 + JsonNode bucketsJson = json.get(BUCKETS);
158 + checkNotNull(bucketsJson);
159 + if (bucketsJson != null) {
160 + IntStream.range(0, bucketsJson.size())
161 + .forEach(i -> {
162 + ObjectNode bucketJson = get(bucketsJson, i);
163 + bucketJson.put("type", type);
164 + groupBucketList.add(groupBucketCodec.decode(bucketJson, context));
165 + });
166 + buckets = new GroupBuckets(groupBucketList);
167 + }
168 +
169 + GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
170 + groupType, buckets, groupKey, groupIdInt, appId);
171 +
172 + return new DefaultGroup(groupId, groupDescription);
173 + }
79 } 174 }
......
...@@ -15,21 +15,37 @@ ...@@ -15,21 +15,37 @@
15 */ 15 */
16 package org.onosproject.codec.impl; 16 package org.onosproject.codec.impl;
17 17
18 +import com.fasterxml.jackson.databind.JsonNode;
19 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 +import com.google.common.collect.ImmutableList;
21 +import org.junit.Before;
18 import org.junit.Test; 22 import org.junit.Test;
23 +import org.onosproject.codec.JsonCodec;
24 +import org.onosproject.core.CoreService;
19 import org.onosproject.core.DefaultGroupId; 25 import org.onosproject.core.DefaultGroupId;
20 import org.onosproject.net.NetTestTools; 26 import org.onosproject.net.NetTestTools;
27 +import org.onosproject.net.PortNumber;
21 import org.onosproject.net.flow.DefaultTrafficTreatment; 28 import org.onosproject.net.flow.DefaultTrafficTreatment;
29 +import org.onosproject.net.flow.instructions.Instruction;
30 +import org.onosproject.net.flow.instructions.Instructions;
22 import org.onosproject.net.group.DefaultGroup; 31 import org.onosproject.net.group.DefaultGroup;
23 import org.onosproject.net.group.DefaultGroupBucket; 32 import org.onosproject.net.group.DefaultGroupBucket;
33 +import org.onosproject.net.group.Group;
24 import org.onosproject.net.group.GroupBucket; 34 import org.onosproject.net.group.GroupBucket;
25 import org.onosproject.net.group.GroupBuckets; 35 import org.onosproject.net.group.GroupBuckets;
26 import org.onosproject.net.group.GroupDescription; 36 import org.onosproject.net.group.GroupDescription;
27 37
28 -import com.fasterxml.jackson.databind.node.ObjectNode; 38 +import java.io.IOException;
29 -import com.google.common.collect.ImmutableList; 39 +import java.io.InputStream;
30 40
41 +import static org.easymock.EasyMock.createMock;
42 +import static org.easymock.EasyMock.expect;
43 +import static org.easymock.EasyMock.replay;
31 import static org.hamcrest.MatcherAssert.assertThat; 44 import static org.hamcrest.MatcherAssert.assertThat;
45 +import static org.hamcrest.Matchers.is;
46 +import static org.hamcrest.Matchers.notNullValue;
32 import static org.onosproject.codec.impl.GroupJsonMatcher.matchesGroup; 47 import static org.onosproject.codec.impl.GroupJsonMatcher.matchesGroup;
48 +import static org.onosproject.net.NetTestTools.APP_ID;
33 49
34 /** 50 /**
35 * Group codec unit tests. 51 * Group codec unit tests.
...@@ -37,8 +53,28 @@ import static org.onosproject.codec.impl.GroupJsonMatcher.matchesGroup; ...@@ -37,8 +53,28 @@ import static org.onosproject.codec.impl.GroupJsonMatcher.matchesGroup;
37 53
38 public class GroupCodecTest { 54 public class GroupCodecTest {
39 55
56 + MockCodecContext context;
57 + JsonCodec<Group> groupCodec;
58 + final CoreService mockCoreService = createMock(CoreService.class);
59 +
60 + /**
61 + * Sets up for each test. Creates a context and fetches the flow rule
62 + * codec.
63 + */
64 + @Before
65 + public void setUp() {
66 + context = new MockCodecContext();
67 + groupCodec = context.codec(Group.class);
68 + assertThat(groupCodec, notNullValue());
69 +
70 + expect(mockCoreService.registerApplication(GroupCodec.REST_APP_ID))
71 + .andReturn(APP_ID).anyTimes();
72 + replay(mockCoreService);
73 + context.registerService(CoreService.class, mockCoreService);
74 + }
75 +
40 @Test 76 @Test
41 - public void codecTest() { 77 + public void codecEncodeTest() {
42 GroupBucket bucket1 = DefaultGroupBucket 78 GroupBucket bucket1 = DefaultGroupBucket
43 .createSelectGroupBucket(DefaultTrafficTreatment.emptyTreatment()); 79 .createSelectGroupBucket(DefaultTrafficTreatment.emptyTreatment());
44 GroupBucket bucket2 = DefaultGroupBucket 80 GroupBucket bucket2 = DefaultGroupBucket
...@@ -58,4 +94,48 @@ public class GroupCodecTest { ...@@ -58,4 +94,48 @@ public class GroupCodecTest {
58 94
59 assertThat(groupJson, matchesGroup(group)); 95 assertThat(groupJson, matchesGroup(group));
60 } 96 }
97 +
98 + @Test
99 + public void codecDecodeTest() throws IOException {
100 + Group group = getGroup("simple-group.json");
101 + checkCommonData(group);
102 +
103 + assertThat(group.buckets().buckets().size(), is(1));
104 + GroupBucket groupBucket = group.buckets().buckets().get(0);
105 + assertThat(groupBucket.type().toString(), is("ALL"));
106 + assertThat(groupBucket.treatment().allInstructions().size(), is(1));
107 + Instruction instruction1 = groupBucket.treatment().allInstructions().get(0);
108 + assertThat(instruction1.type(), is(Instruction.Type.OUTPUT));
109 + assertThat(((Instructions.OutputInstruction) instruction1).port(), is(PortNumber.portNumber(2)));
110 + }
111 +
112 + /**
113 + * Checks that the data shared by all the resource is correct for a given group.
114 + *
115 + * @param group group to check
116 + */
117 + private void checkCommonData(Group group) {
118 + assertThat(group.appId(), is(APP_ID));
119 + assertThat(group.deviceId().toString(), is("of:0000000000000001"));
120 + assertThat(group.type().toString(), is("ALL"));
121 + assertThat(group.appCookie().key(), is("1".getBytes()));
122 + assertThat(group.id().id(), is(1));
123 + }
124 +
125 + /**
126 + * Reads in a group from the given resource and decodes it.
127 + *
128 + * @param resourceName resource to use to read the JSON for the rule
129 + * @return decoded group
130 + * @throws IOException if processing the resource fails
131 + */
132 + private Group getGroup(String resourceName) throws IOException {
133 + InputStream jsonStream = GroupCodecTest.class
134 + .getResourceAsStream(resourceName);
135 + JsonNode json = context.mapper().readTree(jsonStream);
136 + assertThat(json, notNullValue());
137 + Group group = groupCodec.decode((ObjectNode) json, context);
138 + assertThat(group, notNullValue());
139 + return group;
140 + }
61 } 141 }
......
1 +{
2 + "type": "ALL",
3 + "deviceId": "of:0000000000000001",
4 + "appCookie": "1",
5 + "groupId": "1",
6 + "buckets": [
7 + {
8 + "treatment": {
9 + "instructions": [
10 + {
11 + "type": "OUTPUT",
12 + "port": 2
13 + }
14 + ]
15 + }
16 + }
17 + ]
18 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -37,6 +37,7 @@ public class CoreWebApplication extends AbstractWebApplication { ...@@ -37,6 +37,7 @@ public class CoreWebApplication extends AbstractWebApplication {
37 HostsWebResource.class, 37 HostsWebResource.class,
38 IntentsWebResource.class, 38 IntentsWebResource.class,
39 FlowsWebResource.class, 39 FlowsWebResource.class,
40 + GroupsWebResource.class,
40 TopologyWebResource.class, 41 TopologyWebResource.class,
41 ConfigWebResource.class, 42 ConfigWebResource.class,
42 PathsWebResource.class, 43 PathsWebResource.class,
......
1 +/*
2 + * Copyright 2014-2015 Open Networking Laboratory
3 + *
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 + */
16 +package org.onosproject.rest.resources;
17 +
18 +import com.fasterxml.jackson.databind.JsonNode;
19 +import com.fasterxml.jackson.databind.node.ArrayNode;
20 +import com.fasterxml.jackson.databind.node.ObjectNode;
21 +import org.onosproject.net.Device;
22 +import org.onosproject.net.DeviceId;
23 +import org.onosproject.net.device.DeviceService;
24 +import org.onosproject.net.group.DefaultGroupDescription;
25 +import org.onosproject.net.group.DefaultGroupKey;
26 +import org.onosproject.net.group.Group;
27 +import org.onosproject.net.group.GroupDescription;
28 +import org.onosproject.net.group.GroupKey;
29 +import org.onosproject.net.group.GroupService;
30 +import org.onosproject.rest.AbstractWebResource;
31 +
32 +import javax.ws.rs.Consumes;
33 +import javax.ws.rs.DELETE;
34 +import javax.ws.rs.GET;
35 +import javax.ws.rs.POST;
36 +import javax.ws.rs.Path;
37 +import javax.ws.rs.PathParam;
38 +import javax.ws.rs.Produces;
39 +import javax.ws.rs.core.MediaType;
40 +import javax.ws.rs.core.Response;
41 +import java.io.IOException;
42 +import java.io.InputStream;
43 +import java.net.URI;
44 +import java.net.URISyntaxException;
45 +
46 +/**
47 + * Query and program group rules.
48 + */
49 +
50 +@Path("groups")
51 +public class GroupsWebResource extends AbstractWebResource {
52 + public static final String DEVICE_INVALID = "Invalid deviceId in group creation request";
53 +
54 + final GroupService groupService = get(GroupService.class);
55 + final ObjectNode root = mapper().createObjectNode();
56 + final ArrayNode groupsNode = root.putArray("groups");
57 +
58 + /**
59 + * Returns all groups of all devices.
60 + * @onos.rsModel Groups
61 + * @return array of all the groups in the system
62 + */
63 + @GET
64 + @Produces(MediaType.APPLICATION_JSON)
65 + public Response getGroups() {
66 + final Iterable<Device> devices = get(DeviceService.class).getDevices();
67 + devices.forEach(device -> {
68 + final Iterable<Group> groups = groupService.getGroups(device.id());
69 + if (groups != null) {
70 + groups.forEach(group -> groupsNode.add(codec(Group.class).encode(group, this)));
71 + }
72 + });
73 +
74 + return ok(root).build();
75 + }
76 +
77 + /**
78 + * Returns all groups associated with the given device.
79 + *
80 + * @param deviceId device identifier
81 + * @onos.rsModel Groups
82 + * @return array of all the groups in the system
83 + */
84 + @GET
85 + @Produces(MediaType.APPLICATION_JSON)
86 + @Path("{deviceId}")
87 + public Response getGroupsByDeviceId(@PathParam("deviceId") String deviceId) {
88 + final Iterable<Group> groups = groupService.getGroups(DeviceId.deviceId(deviceId));
89 +
90 + groups.forEach(group -> groupsNode.add(codec(Group.class).encode(group, this)));
91 +
92 + return ok(root).build();
93 + }
94 +
95 + /**
96 + * Create new group rule. Creates and installs a new group rule for the
97 + * specified device.
98 + *
99 + * @param deviceId device identifier
100 + * @param stream group rule JSON
101 + * @onos.rsModel GroupsPost
102 + * @return status of the request - CREATED if the JSON is correct,
103 + * BAD_REQUEST if the JSON is invalid
104 + */
105 + @POST
106 + @Path("{deviceId}")
107 + @Consumes(MediaType.APPLICATION_JSON)
108 + @Produces(MediaType.APPLICATION_JSON)
109 + public Response createGroup(@PathParam("deviceId") String deviceId,
110 + InputStream stream) {
111 + URI location;
112 + try {
113 + ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
114 + JsonNode specifiedDeviceId = jsonTree.get("deviceId");
115 +
116 + if (specifiedDeviceId != null &&
117 + !specifiedDeviceId.asText().equals(deviceId)) {
118 + throw new IllegalArgumentException(DEVICE_INVALID);
119 + }
120 + jsonTree.put("deviceId", deviceId);
121 + Group group = codec(Group.class).decode(jsonTree, this);
122 + GroupDescription description = new DefaultGroupDescription(
123 + group.deviceId(), group.type(), group.buckets(),
124 + group.appCookie(), group.id().id(), group.appId());
125 + groupService.addGroup(description);
126 + location = new URI(Long.toString(group.id().id()));
127 + } catch (IOException | URISyntaxException ex) {
128 + throw new IllegalArgumentException(ex);
129 + }
130 +
131 + return Response
132 + .created(location)
133 + .build();
134 + }
135 +
136 + /**
137 + * Removes the specified group.
138 + *
139 + * @param deviceId device identifier
140 + * @param appCookie application cookie to be used for lookup
141 + */
142 + @DELETE
143 + @Produces(MediaType.APPLICATION_JSON)
144 + @Path("{deviceId}/{appCookie}")
145 + public void deleteGroupByDeviceIdAndAppCookie(@PathParam("deviceId") String deviceId,
146 + @PathParam("appCookie") String appCookie) {
147 + DeviceId deviceIdInstance = DeviceId.deviceId(deviceId);
148 + GroupKey appCookieInstance = new DefaultGroupKey(appCookie.getBytes());
149 +
150 + groupService.removeGroup(deviceIdInstance, appCookieInstance, null);
151 + }
152 +}
1 +{
2 + "type": "object",
3 + "title": "groups",
4 + "required": [
5 + "groups"
6 + ],
7 + "properties": {
8 + "groups": {
9 + "type": "array",
10 + "xml": {
11 + "name": "groups",
12 + "wrapped": true
13 + },
14 + "items": {
15 + "type": "object",
16 + "title": "group",
17 + "required": [
18 + "id",
19 + "state",
20 + "life",
21 + "packets",
22 + "bytes",
23 + "referenceCount",
24 + "type",
25 + "deviceId",
26 + "buckets"
27 + ],
28 + "properties": {
29 + "id": {
30 + "type": "string",
31 + "example": "1"
32 + },
33 + "state": {
34 + "type": "string",
35 + "example": "PENDING_ADD"
36 + },
37 + "life": {
38 + "type": "integer",
39 + "format": "int64",
40 + "example": 69889
41 + },
42 + "packets": {
43 + "type": "integer",
44 + "format": "int64",
45 + "example": 22546
46 + },
47 + "bytes": {
48 + "type": "integer",
49 + "format": "int64",
50 + "example": 1826226
51 + },
52 + "referenceCount": {
53 + "type": "integer",
54 + "format": "int64",
55 + "example": 1826226
56 + },
57 + "type": {
58 + "type": "string",
59 + "example": "ALL"
60 + },
61 + "deviceId": {
62 + "type": "string",
63 + "example": "of:0000000000000003"
64 + },
65 + "buckets": {
66 + "type": "array",
67 + "xml": {
68 + "name": "buckets",
69 + "wrapped": true
70 + },
71 + "items": {
72 + "type": "object",
73 + "title": "buckets",
74 + "required": [
75 + "treatment",
76 + "weight",
77 + "watchPort",
78 + "watchGroup"
79 + ],
80 + "properties": {
81 + "treatment": {
82 + "type": "object",
83 + "title": "treatment",
84 + "required": [
85 + "instructions",
86 + "deferred"
87 + ],
88 + "properties": {
89 + "instructions": {
90 + "type": "array",
91 + "title": "treatment",
92 + "required": [
93 + "properties",
94 + "port"
95 + ],
96 + "items": {
97 + "type": "object",
98 + "title": "instructions",
99 + "required": [
100 + "type",
101 + "port"
102 + ],
103 + "properties": {
104 + "type": {
105 + "type": "string",
106 + "example": "OUTPUT"
107 + },
108 + "port": {
109 + "type": "string",
110 + "example": "2"
111 + }
112 + }
113 + }
114 + }
115 + }
116 + }
117 + }
118 + }
119 + }
120 + }
121 + }
122 + }
123 + }
124 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "type": "object",
3 + "title": "group",
4 + "required": [
5 + "type",
6 + "deviceId",
7 + "appCookie",
8 + "groupId",
9 + "buckets"
10 + ],
11 + "properties": {
12 + "type": {
13 + "type": "string",
14 + "example": "ALL"
15 + },
16 + "deviceId": {
17 + "type": "string",
18 + "example": "of:0000000000000001"
19 + },
20 + "appCookie": {
21 + "type": "string",
22 + "example": "1"
23 + },
24 + "groupId": {
25 + "type": "string",
26 + "example": "1"
27 + },
28 + "buckets": {
29 + "type": "array",
30 + "xml": {
31 + "name": "buckets",
32 + "wrapped": true
33 + },
34 + "items": {
35 + "type": "object",
36 + "title": "buckets",
37 + "required": [
38 + "treatment",
39 + "weight",
40 + "watchPort",
41 + "watchGroup"
42 + ],
43 + "properties": {
44 + "treatment": {
45 + "type": "object",
46 + "title": "treatment",
47 + "required": [
48 + "instructions",
49 + "deferred"
50 + ],
51 + "properties": {
52 + "instructions": {
53 + "type": "array",
54 + "title": "treatment",
55 + "required": [
56 + "properties",
57 + "port"
58 + ],
59 + "items": {
60 + "type": "object",
61 + "title": "instructions",
62 + "required": [
63 + "type",
64 + "port"
65 + ],
66 + "properties": {
67 + "type": {
68 + "type": "string",
69 + "example": "OUTPUT"
70 + },
71 + "port": {
72 + "type": "string",
73 + "example": "2"
74 + }
75 + }
76 + }
77 + }
78 + }
79 + }
80 + }
81 + }
82 + }
83 + }
84 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "type": "ALL",
3 + "deviceId": "of:0000000000000001",
4 + "appCookie": "1",
5 + "groupId": "1",
6 + "buckets": [
7 + {
8 + "treatment": {
9 + "instructions": [
10 + {
11 + "type": "OUTPUT",
12 + "port": 2
13 + }
14 + ]
15 + }
16 + }
17 + ]
18 +}
...\ No newline at end of file ...\ No newline at end of file