Jian Li
Committed by Thomas Vachuska

[ONOS-4530] Allow to specify appId when insert FlowRule through REST

- Augment FlowRuleCodec to encode FlowRule
- Add unit test for encode method of FlowRuleCodec
- Add getFlowByAppId and removeFlowByAppId methods in FlowsWebResource
- Add more unit tests for FlowWebResource
- Add FlowRules.json swagger doc
- Rename Flows.json to FlowEntries.json, correct FlowEntries.json

Change-Id: Ic3ec390c13a349e51ae4208adbc478564b6724ba
...@@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode; ...@@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode;
19 import com.fasterxml.jackson.databind.node.ObjectNode; 19 import com.fasterxml.jackson.databind.node.ObjectNode;
20 import org.onosproject.codec.CodecContext; 20 import org.onosproject.codec.CodecContext;
21 import org.onosproject.codec.JsonCodec; 21 import org.onosproject.codec.JsonCodec;
22 +import org.onosproject.core.ApplicationId;
22 import org.onosproject.core.CoreService; 23 import org.onosproject.core.CoreService;
23 import org.onosproject.net.DeviceId; 24 import org.onosproject.net.DeviceId;
24 import org.onosproject.net.flow.DefaultFlowRule; 25 import org.onosproject.net.flow.DefaultFlowRule;
...@@ -26,6 +27,7 @@ import org.onosproject.net.flow.FlowRule; ...@@ -26,6 +27,7 @@ import org.onosproject.net.flow.FlowRule;
26 import org.onosproject.net.flow.TrafficSelector; 27 import org.onosproject.net.flow.TrafficSelector;
27 import org.onosproject.net.flow.TrafficTreatment; 28 import org.onosproject.net.flow.TrafficTreatment;
28 29
30 +import static com.google.common.base.Preconditions.checkNotNull;
29 import static org.onlab.util.Tools.nullIsIllegal; 31 import static org.onlab.util.Tools.nullIsIllegal;
30 32
31 /** 33 /**
...@@ -36,14 +38,46 @@ public final class FlowRuleCodec extends JsonCodec<FlowRule> { ...@@ -36,14 +38,46 @@ public final class FlowRuleCodec extends JsonCodec<FlowRule> {
36 private static final String PRIORITY = "priority"; 38 private static final String PRIORITY = "priority";
37 private static final String TIMEOUT = "timeout"; 39 private static final String TIMEOUT = "timeout";
38 private static final String IS_PERMANENT = "isPermanent"; 40 private static final String IS_PERMANENT = "isPermanent";
41 + private static final String APP_ID = "appId";
39 private static final String TABLE_ID = "tableId"; 42 private static final String TABLE_ID = "tableId";
40 private static final String DEVICE_ID = "deviceId"; 43 private static final String DEVICE_ID = "deviceId";
41 private static final String TREATMENT = "treatment"; 44 private static final String TREATMENT = "treatment";
42 private static final String SELECTOR = "selector"; 45 private static final String SELECTOR = "selector";
43 private static final String MISSING_MEMBER_MESSAGE = 46 private static final String MISSING_MEMBER_MESSAGE =
44 - " member is required in FlowRule"; 47 + " member is required in FlowRule";
45 public static final String REST_APP_ID = "org.onosproject.rest"; 48 public static final String REST_APP_ID = "org.onosproject.rest";
46 49
50 + @Override
51 + public ObjectNode encode(FlowRule flowRule, CodecContext context) {
52 + checkNotNull(flowRule, "Flow rule cannot be null");
53 +
54 + CoreService service = context.getService(CoreService.class);
55 + ApplicationId appId = service.getAppId(flowRule.appId());
56 + String strAppId = (appId == null) ? "<none>" : appId.name();
57 +
58 + final ObjectNode result = context.mapper().createObjectNode()
59 + .put("id", Long.toString(flowRule.id().value()))
60 + .put("tableId", flowRule.tableId())
61 + .put("appId", strAppId)
62 + .put("priority", flowRule.priority())
63 + .put("timeout", flowRule.timeout())
64 + .put("isPermanent", flowRule.isPermanent())
65 + .put("deviceId", flowRule.deviceId().toString());
66 +
67 + if (flowRule.treatment() != null) {
68 + final JsonCodec<TrafficTreatment> treatmentCodec =
69 + context.codec(TrafficTreatment.class);
70 + result.set("treatment", treatmentCodec.encode(flowRule.treatment(), context));
71 + }
72 +
73 + if (flowRule.selector() != null) {
74 + final JsonCodec<TrafficSelector> selectorCodec =
75 + context.codec(TrafficSelector.class);
76 + result.set("selector", selectorCodec.encode(flowRule.selector(), context));
77 + }
78 +
79 + return result;
80 + }
47 81
48 @Override 82 @Override
49 public FlowRule decode(ObjectNode json, CodecContext context) { 83 public FlowRule decode(ObjectNode json, CodecContext context) {
...@@ -54,8 +88,9 @@ public final class FlowRuleCodec extends JsonCodec<FlowRule> { ...@@ -54,8 +88,9 @@ public final class FlowRuleCodec extends JsonCodec<FlowRule> {
54 FlowRule.Builder resultBuilder = new DefaultFlowRule.Builder(); 88 FlowRule.Builder resultBuilder = new DefaultFlowRule.Builder();
55 89
56 CoreService coreService = context.getService(CoreService.class); 90 CoreService coreService = context.getService(CoreService.class);
57 - resultBuilder.fromApp(coreService 91 + JsonNode appIdJson = json.get(APP_ID);
58 - .registerApplication(REST_APP_ID)); 92 + String appId = appIdJson != null ? appIdJson.asText() : REST_APP_ID;
93 + resultBuilder.fromApp(coreService.registerApplication(appId));
59 94
60 int priority = nullIsIllegal(json.get(PRIORITY), 95 int priority = nullIsIllegal(json.get(PRIORITY),
61 PRIORITY + MISSING_MEMBER_MESSAGE).asInt(); 96 PRIORITY + MISSING_MEMBER_MESSAGE).asInt();
......
...@@ -17,6 +17,8 @@ package org.onosproject.codec.impl; ...@@ -17,6 +17,8 @@ package org.onosproject.codec.impl;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 import com.fasterxml.jackson.databind.node.ObjectNode; 19 import com.fasterxml.jackson.databind.node.ObjectNode;
20 +import org.hamcrest.Description;
21 +import org.hamcrest.TypeSafeDiagnosingMatcher;
20 import org.junit.Before; 22 import org.junit.Before;
21 import org.junit.Test; 23 import org.junit.Test;
22 import org.onlab.packet.EthType; 24 import org.onlab.packet.EthType;
...@@ -29,12 +31,14 @@ import org.onlab.packet.VlanId; ...@@ -29,12 +31,14 @@ import org.onlab.packet.VlanId;
29 import org.onosproject.codec.JsonCodec; 31 import org.onosproject.codec.JsonCodec;
30 import org.onosproject.core.CoreService; 32 import org.onosproject.core.CoreService;
31 import org.onosproject.net.ChannelSpacing; 33 import org.onosproject.net.ChannelSpacing;
34 +import org.onosproject.net.DeviceId;
32 import org.onosproject.net.GridType; 35 import org.onosproject.net.GridType;
33 import org.onosproject.net.Lambda; 36 import org.onosproject.net.Lambda;
34 import org.onosproject.net.OchSignal; 37 import org.onosproject.net.OchSignal;
35 import org.onosproject.net.OchSignalType; 38 import org.onosproject.net.OchSignalType;
36 import org.onosproject.net.OduSignalType; 39 import org.onosproject.net.OduSignalType;
37 import org.onosproject.net.PortNumber; 40 import org.onosproject.net.PortNumber;
41 +import org.onosproject.net.flow.DefaultFlowRule;
38 import org.onosproject.net.flow.FlowRule; 42 import org.onosproject.net.flow.FlowRule;
39 import org.onosproject.net.flow.criteria.Criterion; 43 import org.onosproject.net.flow.criteria.Criterion;
40 import org.onosproject.net.flow.criteria.EthCriterion; 44 import org.onosproject.net.flow.criteria.EthCriterion;
...@@ -75,6 +79,7 @@ import java.io.InputStream; ...@@ -75,6 +79,7 @@ import java.io.InputStream;
75 import java.util.SortedMap; 79 import java.util.SortedMap;
76 import java.util.TreeMap; 80 import java.util.TreeMap;
77 81
82 +import static org.easymock.EasyMock.anyShort;
78 import static org.easymock.EasyMock.createMock; 83 import static org.easymock.EasyMock.createMock;
79 import static org.easymock.EasyMock.expect; 84 import static org.easymock.EasyMock.expect;
80 import static org.easymock.EasyMock.replay; 85 import static org.easymock.EasyMock.replay;
...@@ -105,6 +110,7 @@ public class FlowRuleCodecTest { ...@@ -105,6 +110,7 @@ public class FlowRuleCodecTest {
105 110
106 expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID)) 111 expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID))
107 .andReturn(APP_ID).anyTimes(); 112 .andReturn(APP_ID).anyTimes();
113 + expect(mockCoreService.getAppId(anyShort())).andReturn(APP_ID).anyTimes();
108 replay(mockCoreService); 114 replay(mockCoreService);
109 context.registerService(CoreService.class, mockCoreService); 115 context.registerService(CoreService.class, mockCoreService);
110 } 116 }
...@@ -166,6 +172,118 @@ public class FlowRuleCodecTest { ...@@ -166,6 +172,118 @@ public class FlowRuleCodecTest {
166 SortedMap<String, Instruction> instructions = new TreeMap<>(); 172 SortedMap<String, Instruction> instructions = new TreeMap<>();
167 173
168 /** 174 /**
175 + * Checks that a simple rule encodes properly.
176 + */
177 + @Test
178 + public void testFlowRuleEncode() {
179 +
180 + DeviceId deviceId = DeviceId.deviceId("of:000000000000000a");
181 + FlowRule permFlowRule = DefaultFlowRule.builder()
182 + .withCookie(1)
183 + .forTable(1)
184 + .withPriority(1)
185 + .makePermanent()
186 + .forDevice(deviceId).build();
187 +
188 + FlowRule tempFlowRule = DefaultFlowRule.builder()
189 + .withCookie(1)
190 + .forTable(1)
191 + .withPriority(1)
192 + .makeTemporary(1000)
193 + .forDevice(deviceId).build();
194 +
195 + ObjectNode permFlowRuleJson = flowRuleCodec.encode(permFlowRule, context);
196 + ObjectNode tempFlowRuleJson = flowRuleCodec.encode(tempFlowRule, context);
197 +
198 + assertThat(permFlowRuleJson, FlowRuleJsonMatcher.matchesFlowRule(permFlowRule));
199 + assertThat(tempFlowRuleJson, FlowRuleJsonMatcher.matchesFlowRule(tempFlowRule));
200 + }
201 +
202 + private static final class FlowRuleJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
203 +
204 + private final FlowRule flowRule;
205 +
206 + private FlowRuleJsonMatcher(FlowRule flowRule) {
207 + this.flowRule = flowRule;
208 + }
209 +
210 + @Override
211 + protected boolean matchesSafely(JsonNode jsonNode, Description description) {
212 +
213 + // check id
214 + long jsonId = jsonNode.get("id").asLong();
215 + long id = flowRule.id().id();
216 + if (jsonId != id) {
217 + description.appendText("flow rule id was " + jsonId);
218 + return false;
219 + }
220 +
221 + // TODO: need to check application ID
222 +
223 + // check tableId
224 + int jsonTableId = jsonNode.get("tableId").asInt();
225 + int tableId = flowRule.tableId();
226 + if (jsonTableId != tableId) {
227 + description.appendText("table id was " + jsonId);
228 + return false;
229 + }
230 +
231 + // check priority
232 + int jsonPriority = jsonNode.get("priority").asInt();
233 + int priority = flowRule.priority();
234 + if (jsonPriority != priority) {
235 + description.appendText("priority was " + jsonPriority);
236 + return false;
237 + }
238 +
239 + // check timeout
240 + int jsonTimeout = jsonNode.get("timeout").asInt();
241 + int timeout = flowRule.timeout();
242 + if (jsonTimeout != timeout) {
243 + description.appendText("timeout was " + jsonTimeout);
244 + return false;
245 + }
246 +
247 + // check isPermanent
248 + boolean jsonIsPermanent = jsonNode.get("isPermanent").asBoolean();
249 + boolean isPermanent = flowRule.isPermanent();
250 + if (jsonIsPermanent != isPermanent) {
251 + description.appendText("isPermanent was " + jsonIsPermanent);
252 + return false;
253 + }
254 +
255 + // check deviceId
256 + String jsonDeviceId = jsonNode.get("deviceId").asText();
257 + String deviceId = flowRule.deviceId().toString();
258 + if (!jsonDeviceId.equals(deviceId)) {
259 + description.appendText("deviceId was " + jsonDeviceId);
260 + return false;
261 + }
262 +
263 + // TODO: need to check traffic treatment
264 +
265 + // TODO: need to check selector
266 +
267 + return true;
268 + }
269 +
270 + @Override
271 + public void describeTo(Description description) {
272 + description.appendText(flowRule.toString());
273 + }
274 +
275 + /**
276 + * Factory to allocate a flow rule matcher.
277 + *
278 + * @param flowRule flow rule object we are looking for
279 + * @return matcher
280 + */
281 + public static FlowRuleJsonMatcher matchesFlowRule(FlowRule flowRule) {
282 + return new FlowRuleJsonMatcher(flowRule);
283 + }
284 + }
285 +
286 + /**
169 * Looks up an instruction in the instruction map based on type and subtype. 287 * Looks up an instruction in the instruction map based on type and subtype.
170 * 288 *
171 * @param type type string 289 * @param type type string
......
...@@ -21,6 +21,8 @@ import com.fasterxml.jackson.databind.node.ObjectNode; ...@@ -21,6 +21,8 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
21 import com.google.common.collect.ArrayListMultimap; 21 import com.google.common.collect.ArrayListMultimap;
22 import com.google.common.collect.ListMultimap; 22 import com.google.common.collect.ListMultimap;
23 import org.onlab.util.ItemNotFoundException; 23 import org.onlab.util.ItemNotFoundException;
24 +import org.onosproject.app.ApplicationService;
25 +import org.onosproject.core.ApplicationId;
24 import org.onosproject.net.Device; 26 import org.onosproject.net.Device;
25 import org.onosproject.net.DeviceId; 27 import org.onosproject.net.DeviceId;
26 import org.onosproject.net.device.DeviceService; 28 import org.onosproject.net.device.DeviceService;
...@@ -36,6 +38,7 @@ import javax.ws.rs.POST; ...@@ -36,6 +38,7 @@ import javax.ws.rs.POST;
36 import javax.ws.rs.Path; 38 import javax.ws.rs.Path;
37 import javax.ws.rs.PathParam; 39 import javax.ws.rs.PathParam;
38 import javax.ws.rs.Produces; 40 import javax.ws.rs.Produces;
41 +import javax.ws.rs.QueryParam;
39 import javax.ws.rs.core.Context; 42 import javax.ws.rs.core.Context;
40 import javax.ws.rs.core.MediaType; 43 import javax.ws.rs.core.MediaType;
41 import javax.ws.rs.core.Response; 44 import javax.ws.rs.core.Response;
...@@ -61,6 +64,7 @@ public class FlowsWebResource extends AbstractWebResource { ...@@ -61,6 +64,7 @@ public class FlowsWebResource extends AbstractWebResource {
61 64
62 private static final String DEVICE_NOT_FOUND = "Device is not found"; 65 private static final String DEVICE_NOT_FOUND = "Device is not found";
63 private static final String FLOW_NOT_FOUND = "Flow is not found"; 66 private static final String FLOW_NOT_FOUND = "Flow is not found";
67 + private static final String APP_ID_NOT_FOUND = "Application Id is not found";
64 private static final String FLOWS = "flows"; 68 private static final String FLOWS = "flows";
65 private static final String DEVICE_ID = "deviceId"; 69 private static final String DEVICE_ID = "deviceId";
66 private static final String FLOW_ID = "flowId"; 70 private static final String FLOW_ID = "flowId";
...@@ -73,7 +77,7 @@ public class FlowsWebResource extends AbstractWebResource { ...@@ -73,7 +77,7 @@ public class FlowsWebResource extends AbstractWebResource {
73 * Gets all flow entries. Returns array of all flow rules in the system. 77 * Gets all flow entries. Returns array of all flow rules in the system.
74 * 78 *
75 * @return 200 OK with a collection of flows 79 * @return 200 OK with a collection of flows
76 - * @onos.rsModel Flows 80 + * @onos.rsModel FlowEntries
77 */ 81 */
78 @GET 82 @GET
79 @Produces(MediaType.APPLICATION_JSON) 83 @Produces(MediaType.APPLICATION_JSON)
...@@ -107,10 +111,15 @@ public class FlowsWebResource extends AbstractWebResource { ...@@ -107,10 +111,15 @@ public class FlowsWebResource extends AbstractWebResource {
107 @POST 111 @POST
108 @Consumes(MediaType.APPLICATION_JSON) 112 @Consumes(MediaType.APPLICATION_JSON)
109 @Produces(MediaType.APPLICATION_JSON) 113 @Produces(MediaType.APPLICATION_JSON)
110 - public Response createFlows(InputStream stream) { 114 + public Response createFlows(@QueryParam("appId") String appId, InputStream stream) {
111 try { 115 try {
112 ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream); 116 ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
113 ArrayNode flowsArray = (ArrayNode) jsonTree.get(FLOWS); 117 ArrayNode flowsArray = (ArrayNode) jsonTree.get(FLOWS);
118 +
119 + if (appId != null) {
120 + flowsArray.forEach(flowJson -> ((ObjectNode) flowJson).put("appId", appId));
121 + }
122 +
114 List<FlowRule> rules = codec(FlowRule.class).decode(flowsArray, this); 123 List<FlowRule> rules = codec(FlowRule.class).decode(flowsArray, this);
115 service.applyFlowRules(rules.toArray(new FlowRule[rules.size()])); 124 service.applyFlowRules(rules.toArray(new FlowRule[rules.size()]));
116 rules.forEach(flowRule -> { 125 rules.forEach(flowRule -> {
...@@ -131,10 +140,11 @@ public class FlowsWebResource extends AbstractWebResource { ...@@ -131,10 +140,11 @@ public class FlowsWebResource extends AbstractWebResource {
131 * 140 *
132 * @param deviceId device identifier 141 * @param deviceId device identifier
133 * @return 200 OK with a collection of flows of given device 142 * @return 200 OK with a collection of flows of given device
134 - * @onos.rsModel Flows 143 + * @onos.rsModel FlowEntries
135 */ 144 */
136 @GET 145 @GET
137 @Produces(MediaType.APPLICATION_JSON) 146 @Produces(MediaType.APPLICATION_JSON)
147 + // TODO: we need to add "/device" suffix to the path to differentiate with appId
138 @Path("{deviceId}") 148 @Path("{deviceId}")
139 public Response getFlowByDeviceId(@PathParam("deviceId") String deviceId) { 149 public Response getFlowByDeviceId(@PathParam("deviceId") String deviceId) {
140 final Iterable<FlowEntry> flowEntries = 150 final Iterable<FlowEntry> flowEntries =
...@@ -150,13 +160,13 @@ public class FlowsWebResource extends AbstractWebResource { ...@@ -150,13 +160,13 @@ public class FlowsWebResource extends AbstractWebResource {
150 } 160 }
151 161
152 /** 162 /**
153 - * Gets flow rule. Returns the flow entry specified by the device id and 163 + * Gets flow rules. Returns the flow entry specified by the device id and
154 * flow rule id. 164 * flow rule id.
155 * 165 *
156 * @param deviceId device identifier 166 * @param deviceId device identifier
157 * @param flowId flow rule identifier 167 * @param flowId flow rule identifier
158 - * @return 200 OK with a flows of given device and flow 168 + * @return 200 OK with a collection of flows of given device and flow
159 - * @onos.rsModel Flows 169 + * @onos.rsModel FlowEntries
160 */ 170 */
161 @GET 171 @GET
162 @Produces(MediaType.APPLICATION_JSON) 172 @Produces(MediaType.APPLICATION_JSON)
...@@ -178,6 +188,43 @@ public class FlowsWebResource extends AbstractWebResource { ...@@ -178,6 +188,43 @@ public class FlowsWebResource extends AbstractWebResource {
178 } 188 }
179 189
180 /** 190 /**
191 + * 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 + /**
181 * Creates new flow rule. Creates and installs a new flow rule for the 228 * Creates new flow rule. Creates and installs a new flow rule for the
182 * specified device. <br> 229 * specified device. <br>
183 * Instructions description: 230 * Instructions description:
...@@ -187,6 +234,7 @@ public class FlowsWebResource extends AbstractWebResource { ...@@ -187,6 +234,7 @@ public class FlowsWebResource extends AbstractWebResource {
187 * https://wiki.onosproject.org/display/ONOS/Flow+Rule+Criteria 234 * https://wiki.onosproject.org/display/ONOS/Flow+Rule+Criteria
188 * 235 *
189 * @param deviceId device identifier 236 * @param deviceId device identifier
237 + * @param appId application identifier
190 * @param stream flow rule JSON 238 * @param stream flow rule JSON
191 * @return status of the request - CREATED if the JSON is correct, 239 * @return status of the request - CREATED if the JSON is correct,
192 * BAD_REQUEST if the JSON is invalid 240 * BAD_REQUEST if the JSON is invalid
...@@ -197,6 +245,7 @@ public class FlowsWebResource extends AbstractWebResource { ...@@ -197,6 +245,7 @@ public class FlowsWebResource extends AbstractWebResource {
197 @Consumes(MediaType.APPLICATION_JSON) 245 @Consumes(MediaType.APPLICATION_JSON)
198 @Produces(MediaType.APPLICATION_JSON) 246 @Produces(MediaType.APPLICATION_JSON)
199 public Response createFlow(@PathParam("deviceId") String deviceId, 247 public Response createFlow(@PathParam("deviceId") String deviceId,
248 + @QueryParam("appId") String appId,
200 InputStream stream) { 249 InputStream stream) {
201 try { 250 try {
202 ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream); 251 ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
...@@ -207,6 +256,11 @@ public class FlowsWebResource extends AbstractWebResource { ...@@ -207,6 +256,11 @@ public class FlowsWebResource extends AbstractWebResource {
207 "Invalid deviceId in flow creation request"); 256 "Invalid deviceId in flow creation request");
208 } 257 }
209 jsonTree.put("deviceId", deviceId); 258 jsonTree.put("deviceId", deviceId);
259 +
260 + if (appId != null) {
261 + jsonTree.put("appId", appId);
262 + }
263 +
210 FlowRule rule = codec(FlowRule.class).decode(jsonTree, this); 264 FlowRule rule = codec(FlowRule.class).decode(jsonTree, this);
211 service.applyFlowRules(rule); 265 service.applyFlowRules(rule);
212 UriBuilder locationBuilder = uriInfo.getBaseUriBuilder() 266 UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
...@@ -223,7 +277,7 @@ public class FlowsWebResource extends AbstractWebResource { ...@@ -223,7 +277,7 @@ public class FlowsWebResource extends AbstractWebResource {
223 } 277 }
224 278
225 /** 279 /**
226 - * Remove flow rule. Removes the specified flow rule. 280 + * Removes flow rule. Removes the specified flow rule.
227 * 281 *
228 * @param deviceId device identifier 282 * @param deviceId device identifier
229 * @param flowId flow rule identifier 283 * @param flowId flow rule identifier
......
...@@ -135,234 +135,234 @@ ...@@ -135,234 +135,234 @@
135 } 135 }
136 } 136 }
137 } 137 }
138 - }
139 - }
140 - }
141 - },
142 - "selector": {
143 - "type": "object",
144 - "title": "selector",
145 - "required": [
146 - "criteria"
147 - ],
148 - "properties": {
149 - "criteria": {
150 - "type": "array",
151 - "xml": {
152 - "name": "criteria",
153 - "wrapped": true
154 }, 138 },
155 - "items": { 139 + "selector": {
156 "type": "object", 140 "type": "object",
157 - "title": "criteria", 141 + "title": "selector",
142 + "required": [
143 + "criteria"
144 + ],
158 "properties": { 145 "properties": {
159 - "type": { 146 + "criteria": {
160 - "type": "string",
161 - "description":"Ethernet field name",
162 - "example": "ETH_TYPE"
163 - },
164 - "ethType": {
165 - "type": "int64",
166 - "format": "int64",
167 - "example": "0x88cc",
168 - "description":"Ethernet frame type"
169 - },
170 - "mac": {
171 - "type": "string",
172 - "example": "00:00:11:00:00:01"
173 - },
174 - "port": {
175 - "type": "int64",
176 - "format": "int64",
177 - "example": 1,
178 - "description":"Match port"
179 - },
180 - "metadata": {
181 - "type": "Hex16",
182 - "format": "Hex16",
183 - "example": "0xabcdL",
184 - "description":"Metadata passed between tables"
185 - },
186 - "vlanId": {
187 - "type": "uint16",
188 - "format": "uint16",
189 - "example": "0x1000"
190 - },
191 - "priority": {
192 - "type": "int64",
193 - "format": "int64",
194 - "example": 1,
195 - "description":"VLAN priority."
196 - },
197 - "ipDscp": {
198 - "type": "byte",
199 - "format": "byte",
200 - "description":"IP DSCP (6 bits in ToS field)"
201 - },
202 - "ipEcn": {
203 - "type": "byte",
204 - "format": "byte",
205 - "description":"IP ECN (2 bits in ToS field)."
206 - },
207 - "protocol": {
208 - "type": "uint16",
209 - "format": "uint16",
210 - "example": 1,
211 - "description":"IP protocol"
212 - },
213 - "ip": {
214 - "type": "string",
215 - "example": "10.1.1.0/24",
216 - "description":"IP source address"
217 - },
218 - "tcpPort": {
219 - "type": "integer",
220 - "format": "uint16",
221 - "example": 1,
222 - "description":"TCP source address"
223 - },
224 - "udpPort": {
225 - "type": "uint16",
226 - "format": "uint16",
227 - "example": 1,
228 - "description":"UDP source address"
229 - },
230 - "sctpPort": {
231 - "type": "uint16",
232 - "format": "uint16",
233 - "example": 1,
234 - "description":"SCTP source address"
235 - },
236 - "icmpType": {
237 - "type": "uint16",
238 - "format": "uint16",
239 - "example": 1,
240 - "description":"Internet Control Message Protocol for IPV4 code (RFC0792)"
241 - },
242 - "icmpCode": {
243 - "type": "uint16",
244 - "format": "uint16",
245 - "example": 1,
246 - "description":"Internet Control Message Protocol for IPV4 code (RFC0792)"
247 - },
248 - "flowLabel": {
249 - "type": "Hex16",
250 - "format": "Hex16",
251 - "example": "0xffffe",
252 - "description":"IPv6 Flow Label (RFC 6437)"
253 - },
254 - "icmpv6Type": {
255 - "type": "uint16",
256 - "format": "uint16",
257 - "example": 1,
258 - "description":"Internet Control Message Protocol for IPV6 type (RFC2463)"
259 - },
260 - "icmpv6Code": {
261 - "type": "uint16",
262 - "format": "uint16",
263 - "example": 1,
264 - "description":"Internet Control Message Protocol for IPV6 code (RFC2463)"
265 - },
266 - "targetAddress": {
267 - "type": "String",
268 - "example": "10.1.1.0/24",
269 - "description":"IPv6 Neighbor discovery target address"
270 - },
271 - "label": {
272 - "type": "int32",
273 - "format": "int32",
274 - "example": 1,
275 - "description":"MPLS label"
276 - },
277 - "exthdrFlags": {
278 - "type": "int64",
279 - "format": "int64",
280 - "example": 1,
281 - "description":"IPv6 extension header pseudo-field"
282 - },
283 - "lambda": {
284 - "type": "int64",
285 - "format": "int64",
286 - "example": 1,
287 - "description":"wavelength abstraction"
288 - },
289 - "gridType": {
290 - "type": "String",
291 - "example": "DWDM",
292 - "description":"Type of wavelength grid"
293 - },
294 - "channelSpacing": {
295 - "type": "int64",
296 - "format": "int64",
297 - "example": 100,
298 - "description":"Optical channel spacing"
299 - },
300 - "spacingMultiplier": {
301 - "type": "integer",
302 - "format": "int64",
303 - "example": 4,
304 - "description":"Optical channel spacing multiplier"
305 - },
306 - "slotGranularity": {
307 - "type": "int64",
308 - "format": "int64",
309 - "example": 8
310 - },
311 - "ochSignalId": {
312 - "type": "integer",
313 - "format": "int64",
314 - "example": 1,
315 - "description":"Optical channel signal ID"
316 - },
317 - "tunnelId": {
318 - "type": "int64",
319 - "format": "int64",
320 - "example": 5,
321 - "description":"Tunnel ID"
322 - },
323 - "ochSignalType": {
324 - "type": "int64",
325 - "format": "int64",
326 - "example": 1,
327 - "description":"Optical channel signal type"
328 - },
329 - "oduSignalId": {
330 - "type": "int64",
331 - "format": "int64",
332 - "example": 1,
333 - "description":"ODU (Optical channel Data Unit) signal ID."
334 - },
335 - "tributaryPortNumber": {
336 - "type": "int64",
337 - "format": "int64",
338 - "example": 11,
339 - "description":"OPU (Optical channel Payload Unit) port number."
340 - },
341 - "tributarySlotLen": {
342 - "type": "int64",
343 - "format": "int64",
344 - "example": 80,
345 - "description":"OPU (Optical channel Payload Unit) slot length."
346 - },
347 - "tributarySlotBitmap": {
348 "type": "array", 147 "type": "array",
349 - "title": "tributarySlotBitmap", 148 + "xml": {
350 - "description":"OPU (Optical channel Payload Unit) slot bitmap.", 149 + "name": "criteria",
351 - "required": [ 150 + "wrapped": true
352 - "byte", 151 + },
353 - "port"
354 - ],
355 "items": { 152 "items": {
356 - "type": "byte", 153 + "type": "object",
357 - "title": "byte", 154 + "title": "criteria",
358 - "example": 1 155 + "properties": {
156 + "type": {
157 + "type": "string",
158 + "description": "Ethernet field name",
159 + "example": "ETH_TYPE"
160 + },
161 + "ethType": {
162 + "type": "int64",
163 + "format": "int64",
164 + "example": "0x88cc",
165 + "description": "Ethernet frame type"
166 + },
167 + "mac": {
168 + "type": "string",
169 + "example": "00:00:11:00:00:01"
170 + },
171 + "port": {
172 + "type": "int64",
173 + "format": "int64",
174 + "example": 1,
175 + "description": "Match port"
176 + },
177 + "metadata": {
178 + "type": "Hex16",
179 + "format": "Hex16",
180 + "example": "0xabcdL",
181 + "description": "Metadata passed between tables"
182 + },
183 + "vlanId": {
184 + "type": "uint16",
185 + "format": "uint16",
186 + "example": "0x1000"
187 + },
188 + "priority": {
189 + "type": "int64",
190 + "format": "int64",
191 + "example": 1,
192 + "description": "VLAN priority."
193 + },
194 + "ipDscp": {
195 + "type": "byte",
196 + "format": "byte",
197 + "description": "IP DSCP (6 bits in ToS field)"
198 + },
199 + "ipEcn": {
200 + "type": "byte",
201 + "format": "byte",
202 + "description": "IP ECN (2 bits in ToS field)."
203 + },
204 + "protocol": {
205 + "type": "uint16",
206 + "format": "uint16",
207 + "example": 1,
208 + "description": "IP protocol"
209 + },
210 + "ip": {
211 + "type": "string",
212 + "example": "10.1.1.0/24",
213 + "description": "IP source address"
214 + },
215 + "tcpPort": {
216 + "type": "integer",
217 + "format": "uint16",
218 + "example": 1,
219 + "description": "TCP source address"
220 + },
221 + "udpPort": {
222 + "type": "uint16",
223 + "format": "uint16",
224 + "example": 1,
225 + "description": "UDP source address"
226 + },
227 + "sctpPort": {
228 + "type": "uint16",
229 + "format": "uint16",
230 + "example": 1,
231 + "description": "SCTP source address"
232 + },
233 + "icmpType": {
234 + "type": "uint16",
235 + "format": "uint16",
236 + "example": 1,
237 + "description": "Internet Control Message Protocol for IPV4 code (RFC0792)"
238 + },
239 + "icmpCode": {
240 + "type": "uint16",
241 + "format": "uint16",
242 + "example": 1,
243 + "description": "Internet Control Message Protocol for IPV4 code (RFC0792)"
244 + },
245 + "flowLabel": {
246 + "type": "Hex16",
247 + "format": "Hex16",
248 + "example": "0xffffe",
249 + "description": "IPv6 Flow Label (RFC 6437)"
250 + },
251 + "icmpv6Type": {
252 + "type": "uint16",
253 + "format": "uint16",
254 + "example": 1,
255 + "description": "Internet Control Message Protocol for IPV6 type (RFC2463)"
256 + },
257 + "icmpv6Code": {
258 + "type": "uint16",
259 + "format": "uint16",
260 + "example": 1,
261 + "description": "Internet Control Message Protocol for IPV6 code (RFC2463)"
262 + },
263 + "targetAddress": {
264 + "type": "String",
265 + "example": "10.1.1.0/24",
266 + "description": "IPv6 Neighbor discovery target address"
267 + },
268 + "label": {
269 + "type": "int32",
270 + "format": "int32",
271 + "example": 1,
272 + "description": "MPLS label"
273 + },
274 + "exthdrFlags": {
275 + "type": "int64",
276 + "format": "int64",
277 + "example": 1,
278 + "description": "IPv6 extension header pseudo-field"
279 + },
280 + "lambda": {
281 + "type": "int64",
282 + "format": "int64",
283 + "example": 1,
284 + "description": "wavelength abstraction"
285 + },
286 + "gridType": {
287 + "type": "String",
288 + "example": "DWDM",
289 + "description": "Type of wavelength grid"
290 + },
291 + "channelSpacing": {
292 + "type": "int64",
293 + "format": "int64",
294 + "example": 100,
295 + "description": "Optical channel spacing"
296 + },
297 + "spacingMultiplier": {
298 + "type": "integer",
299 + "format": "int64",
300 + "example": 4,
301 + "description": "Optical channel spacing multiplier"
302 + },
303 + "slotGranularity": {
304 + "type": "int64",
305 + "format": "int64",
306 + "example": 8
307 + },
308 + "ochSignalId": {
309 + "type": "integer",
310 + "format": "int64",
311 + "example": 1,
312 + "description": "Optical channel signal ID"
313 + },
314 + "tunnelId": {
315 + "type": "int64",
316 + "format": "int64",
317 + "example": 5,
318 + "description": "Tunnel ID"
319 + },
320 + "ochSignalType": {
321 + "type": "int64",
322 + "format": "int64",
323 + "example": 1,
324 + "description": "Optical channel signal type"
325 + },
326 + "oduSignalId": {
327 + "type": "int64",
328 + "format": "int64",
329 + "example": 1,
330 + "description": "ODU (Optical channel Data Unit) signal ID."
331 + },
332 + "tributaryPortNumber": {
333 + "type": "int64",
334 + "format": "int64",
335 + "example": 11,
336 + "description": "OPU (Optical channel Payload Unit) port number."
337 + },
338 + "tributarySlotLen": {
339 + "type": "int64",
340 + "format": "int64",
341 + "example": 80,
342 + "description": "OPU (Optical channel Payload Unit) slot length."
343 + },
344 + "tributarySlotBitmap": {
345 + "type": "array",
346 + "title": "tributarySlotBitmap",
347 + "description": "OPU (Optical channel Payload Unit) slot bitmap.",
348 + "required": [
349 + "byte",
350 + "port"
351 + ],
352 + "items": {
353 + "type": "byte",
354 + "title": "byte",
355 + "example": 1
356 + }
357 + },
358 + "oduSignalType": {
359 + "type": "int64",
360 + "format": "int64",
361 + "example": 4,
362 + "description": "ODU (Optical channel Data Unit) signal type."
363 + }
364 + }
359 } 365 }
360 - },
361 - "oduSignalType": {
362 - "type": "int64",
363 - "format": "int64",
364 - "example": 4,
365 - "description":"ODU (Optical channel Data Unit) signal type."
366 } 366 }
367 } 367 }
368 } 368 }
......
1 +{
2 + "type": "object",
3 + "title": "flows",
4 + "required": [
5 + "flows"
6 + ],
7 + "properties": {
8 + "flows": {
9 + "type": "array",
10 + "xml": {
11 + "name": "flows",
12 + "wrapped": true
13 + },
14 + "items": {
15 + "type": "object",
16 + "title": "flow",
17 + "required": [
18 + "id",
19 + "tableId",
20 + "appId",
21 + "groupId",
22 + "priority",
23 + "timeout",
24 + "isPermanent",
25 + "deviceId"
26 + ],
27 + "properties": {
28 + "id": {
29 + "type": "string",
30 + "example": "12103425214920339"
31 + },
32 + "tableId": {
33 + "type": "integer",
34 + "format": "int32",
35 + "example": 3
36 + },
37 + "appId": {
38 + "type": "string",
39 + "example": "org.onosproject.core"
40 + },
41 + "groupId": {
42 + "type": "integer",
43 + "format": "int64",
44 + "example": 0
45 + },
46 + "priority": {
47 + "type": "integer",
48 + "format": "int32",
49 + "example": 40000
50 + },
51 + "timeout": {
52 + "type": "integer",
53 + "format": "int32",
54 + "example": 0
55 + },
56 + "isPermanent": {
57 + "type": "boolean",
58 + "example": true
59 + },
60 + "deviceId": {
61 + "type": "string",
62 + "example": "of:0000000000000003"
63 + },
64 + "treatment": {
65 + "type": "object",
66 + "title": "treatment",
67 + "required": [
68 + "instructions",
69 + "deferred"
70 + ],
71 + "properties": {
72 + "instructions": {
73 + "type": "array",
74 + "title": "treatment",
75 + "required": [
76 + "properties",
77 + "port"
78 + ],
79 + "items": {
80 + "type": "object",
81 + "title": "instruction",
82 + "required": [
83 + "type",
84 + "port"
85 + ],
86 + "properties": {
87 + "type": {
88 + "type": "string",
89 + "example": "OUTPUT"
90 + },
91 + "port": {
92 + "type": "string",
93 + "example": "CONTROLLER"
94 + }
95 + }
96 + }
97 + },
98 + "deferred": {
99 + "type": "array",
100 + "xml": {
101 + "name": "deferred",
102 + "wrapped": true
103 + },
104 + "items": {
105 + "type": "string"
106 + }
107 + }
108 + }
109 + },
110 + "selector": {
111 + "type": "object",
112 + "title": "selector",
113 + "required": [
114 + "criteria"
115 + ],
116 + "properties": {
117 + "criteria": {
118 + "type": "array",
119 + "xml": {
120 + "name": "criteria",
121 + "wrapped": true
122 + },
123 + "items": {
124 + "type": "object",
125 + "title": "criteria",
126 + "properties": {
127 + "type": {
128 + "type": "string",
129 + "description": "Ethernet field name",
130 + "example": "ETH_TYPE"
131 + },
132 + "ethType": {
133 + "type": "int64",
134 + "format": "int64",
135 + "example": "0x88cc",
136 + "description": "Ethernet frame type"
137 + },
138 + "mac": {
139 + "type": "string",
140 + "example": "00:00:11:00:00:01"
141 + },
142 + "port": {
143 + "type": "int64",
144 + "format": "int64",
145 + "example": 1,
146 + "description": "Match port"
147 + },
148 + "metadata": {
149 + "type": "Hex16",
150 + "format": "Hex16",
151 + "example": "0xabcdL",
152 + "description": "Metadata passed between tables"
153 + },
154 + "vlanId": {
155 + "type": "uint16",
156 + "format": "uint16",
157 + "example": "0x1000"
158 + },
159 + "priority": {
160 + "type": "int64",
161 + "format": "int64",
162 + "example": 1,
163 + "description": "VLAN priority."
164 + },
165 + "ipDscp": {
166 + "type": "byte",
167 + "format": "byte",
168 + "description": "IP DSCP (6 bits in ToS field)"
169 + },
170 + "ipEcn": {
171 + "type": "byte",
172 + "format": "byte",
173 + "description": "IP ECN (2 bits in ToS field)."
174 + },
175 + "protocol": {
176 + "type": "uint16",
177 + "format": "uint16",
178 + "example": 1,
179 + "description": "IP protocol"
180 + },
181 + "ip": {
182 + "type": "string",
183 + "example": "10.1.1.0/24",
184 + "description": "IP source address"
185 + },
186 + "tcpPort": {
187 + "type": "integer",
188 + "format": "uint16",
189 + "example": 1,
190 + "description": "TCP source address"
191 + },
192 + "udpPort": {
193 + "type": "uint16",
194 + "format": "uint16",
195 + "example": 1,
196 + "description": "UDP source address"
197 + },
198 + "sctpPort": {
199 + "type": "uint16",
200 + "format": "uint16",
201 + "example": 1,
202 + "description": "SCTP source address"
203 + },
204 + "icmpType": {
205 + "type": "uint16",
206 + "format": "uint16",
207 + "example": 1,
208 + "description": "Internet Control Message Protocol for IPV4 code (RFC0792)"
209 + },
210 + "icmpCode": {
211 + "type": "uint16",
212 + "format": "uint16",
213 + "example": 1,
214 + "description": "Internet Control Message Protocol for IPV4 code (RFC0792)"
215 + },
216 + "flowLabel": {
217 + "type": "Hex16",
218 + "format": "Hex16",
219 + "example": "0xffffe",
220 + "description": "IPv6 Flow Label (RFC 6437)"
221 + },
222 + "icmpv6Type": {
223 + "type": "uint16",
224 + "format": "uint16",
225 + "example": 1,
226 + "description": "Internet Control Message Protocol for IPV6 type (RFC2463)"
227 + },
228 + "icmpv6Code": {
229 + "type": "uint16",
230 + "format": "uint16",
231 + "example": 1,
232 + "description": "Internet Control Message Protocol for IPV6 code (RFC2463)"
233 + },
234 + "targetAddress": {
235 + "type": "String",
236 + "example": "10.1.1.0/24",
237 + "description": "IPv6 Neighbor discovery target address"
238 + },
239 + "label": {
240 + "type": "int32",
241 + "format": "int32",
242 + "example": 1,
243 + "description": "MPLS label"
244 + },
245 + "exthdrFlags": {
246 + "type": "int64",
247 + "format": "int64",
248 + "example": 1,
249 + "description": "IPv6 extension header pseudo-field"
250 + },
251 + "lambda": {
252 + "type": "int64",
253 + "format": "int64",
254 + "example": 1,
255 + "description": "wavelength abstraction"
256 + },
257 + "gridType": {
258 + "type": "String",
259 + "example": "DWDM",
260 + "description": "Type of wavelength grid"
261 + },
262 + "channelSpacing": {
263 + "type": "int64",
264 + "format": "int64",
265 + "example": 100,
266 + "description": "Optical channel spacing"
267 + },
268 + "spacingMultiplier": {
269 + "type": "integer",
270 + "format": "int64",
271 + "example": 4,
272 + "description": "Optical channel spacing multiplier"
273 + },
274 + "slotGranularity": {
275 + "type": "int64",
276 + "format": "int64",
277 + "example": 8
278 + },
279 + "ochSignalId": {
280 + "type": "integer",
281 + "format": "int64",
282 + "example": 1,
283 + "description": "Optical channel signal ID"
284 + },
285 + "tunnelId": {
286 + "type": "int64",
287 + "format": "int64",
288 + "example": 5,
289 + "description": "Tunnel ID"
290 + },
291 + "ochSignalType": {
292 + "type": "int64",
293 + "format": "int64",
294 + "example": 1,
295 + "description": "Optical channel signal type"
296 + },
297 + "oduSignalId": {
298 + "type": "int64",
299 + "format": "int64",
300 + "example": 1,
301 + "description": "ODU (Optical channel Data Unit) signal ID."
302 + },
303 + "tributaryPortNumber": {
304 + "type": "int64",
305 + "format": "int64",
306 + "example": 11,
307 + "description": "OPU (Optical channel Payload Unit) port number."
308 + },
309 + "tributarySlotLen": {
310 + "type": "int64",
311 + "format": "int64",
312 + "example": 80,
313 + "description": "OPU (Optical channel Payload Unit) slot length."
314 + },
315 + "tributarySlotBitmap": {
316 + "type": "array",
317 + "title": "tributarySlotBitmap",
318 + "description": "OPU (Optical channel Payload Unit) slot bitmap.",
319 + "required": [
320 + "byte",
321 + "port"
322 + ],
323 + "items": {
324 + "type": "byte",
325 + "title": "byte",
326 + "example": 1
327 + }
328 + },
329 + "oduSignalType": {
330 + "type": "int64",
331 + "format": "int64",
332 + "example": 4,
333 + "description": "ODU (Optical channel Data Unit) signal type."
334 + }
335 + }
336 + }
337 + }
338 + }
339 + }
340 + }
341 + }
342 + }
343 + }
344 +}
...@@ -19,6 +19,7 @@ import com.eclipsesource.json.Json; ...@@ -19,6 +19,7 @@ import com.eclipsesource.json.Json;
19 import com.eclipsesource.json.JsonArray; 19 import com.eclipsesource.json.JsonArray;
20 import com.eclipsesource.json.JsonObject; 20 import com.eclipsesource.json.JsonObject;
21 import com.google.common.collect.ImmutableSet; 21 import com.google.common.collect.ImmutableSet;
22 +import com.google.common.collect.Sets;
22 import org.hamcrest.Description; 23 import org.hamcrest.Description;
23 import org.hamcrest.Matchers; 24 import org.hamcrest.Matchers;
24 import org.hamcrest.TypeSafeMatcher; 25 import org.hamcrest.TypeSafeMatcher;
...@@ -29,6 +30,7 @@ import org.onlab.osgi.ServiceDirectory; ...@@ -29,6 +30,7 @@ import org.onlab.osgi.ServiceDirectory;
29 import org.onlab.osgi.TestServiceDirectory; 30 import org.onlab.osgi.TestServiceDirectory;
30 import org.onlab.packet.MacAddress; 31 import org.onlab.packet.MacAddress;
31 import org.onlab.rest.BaseResource; 32 import org.onlab.rest.BaseResource;
33 +import org.onosproject.app.ApplicationService;
32 import org.onosproject.codec.CodecService; 34 import org.onosproject.codec.CodecService;
33 import org.onosproject.codec.impl.CodecManager; 35 import org.onosproject.codec.impl.CodecManager;
34 import org.onosproject.codec.impl.FlowRuleCodec; 36 import org.onosproject.codec.impl.FlowRuleCodec;
...@@ -65,6 +67,7 @@ import java.util.Set; ...@@ -65,6 +67,7 @@ import java.util.Set;
65 67
66 import static org.easymock.EasyMock.anyObject; 68 import static org.easymock.EasyMock.anyObject;
67 import static org.easymock.EasyMock.anyShort; 69 import static org.easymock.EasyMock.anyShort;
70 +import static org.easymock.EasyMock.anyString;
68 import static org.easymock.EasyMock.createMock; 71 import static org.easymock.EasyMock.createMock;
69 import static org.easymock.EasyMock.expect; 72 import static org.easymock.EasyMock.expect;
70 import static org.easymock.EasyMock.expectLastCall; 73 import static org.easymock.EasyMock.expectLastCall;
...@@ -98,6 +101,8 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -98,6 +101,8 @@ public class FlowsResourceTest extends ResourceTest {
98 final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER, 101 final Device device2 = new DefaultDevice(null, deviceId2, Device.Type.OTHER,
99 "", "", "", "", null); 102 "", "", "", "", null);
100 103
104 + final ApplicationService mockApplicationService = createMock(ApplicationService.class);
105 +
101 final MockFlowEntry flow1 = new MockFlowEntry(deviceId1, 1); 106 final MockFlowEntry flow1 = new MockFlowEntry(deviceId1, 1);
102 final MockFlowEntry flow2 = new MockFlowEntry(deviceId1, 2); 107 final MockFlowEntry flow2 = new MockFlowEntry(deviceId1, 2);
103 108
...@@ -107,6 +112,14 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -107,6 +112,14 @@ public class FlowsResourceTest extends ResourceTest {
107 final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5); 112 final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5);
108 final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6); 113 final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6);
109 114
115 + final MockFlowRule flowRule1 = new MockFlowRule(deviceId1, 1);
116 + final MockFlowRule flowRule2 = new MockFlowRule(deviceId1, 2);
117 +
118 + final MockFlowRule flowRule3 = new MockFlowRule(deviceId2, 3);
119 + final MockFlowRule flowRule4 = new MockFlowRule(deviceId2, 4);
120 +
121 + final Set<FlowRule> flowRules = Sets.newHashSet();
122 +
110 /** 123 /**
111 * Mock class for a flow entry. 124 * Mock class for a flow entry.
112 */ 125 */
...@@ -219,6 +232,83 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -219,6 +232,83 @@ public class FlowsResourceTest extends ResourceTest {
219 } 232 }
220 233
221 /** 234 /**
235 + * Mock class for a flow rule.
236 + */
237 + private static class MockFlowRule implements FlowRule {
238 +
239 + final DeviceId deviceId;
240 + final long baseValue;
241 + TrafficTreatment treatment;
242 + TrafficSelector selector;
243 +
244 + public MockFlowRule(DeviceId deviceId, long id) {
245 + this.deviceId = deviceId;
246 + this.baseValue = id * 100;
247 + }
248 +
249 + @Override
250 + public FlowId id() {
251 + final long id = baseValue + 55;
252 + return FlowId.valueOf(id);
253 + }
254 +
255 + @Override
256 + public short appId() {
257 + return 4;
258 + }
259 +
260 + @Override
261 + public GroupId groupId() {
262 + return new DefaultGroupId(3);
263 + }
264 +
265 + @Override
266 + public int priority() {
267 + return 0;
268 + }
269 +
270 + @Override
271 + public DeviceId deviceId() {
272 + return deviceId;
273 + }
274 +
275 + @Override
276 + public TrafficSelector selector() {
277 + return selector;
278 + }
279 +
280 + @Override
281 + public TrafficTreatment treatment() {
282 + return treatment;
283 + }
284 +
285 + @Override
286 + public int timeout() {
287 + return (int) (baseValue + 77);
288 + }
289 +
290 + @Override
291 + public boolean isPermanent() {
292 + return false;
293 + }
294 +
295 + @Override
296 + public int tableId() {
297 + return 0;
298 + }
299 +
300 + @Override
301 + public boolean exactMatch(FlowRule rule) {
302 + return false;
303 + }
304 +
305 + @Override
306 + public FlowRuleExtPayLoad payLoad() {
307 + return null;
308 + }
309 + }
310 +
311 + /**
222 * Populates some flows used as testing data. 312 * Populates some flows used as testing data.
223 */ 313 */
224 private void setupMockFlows() { 314 private void setupMockFlows() {
...@@ -249,6 +339,26 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -249,6 +339,26 @@ public class FlowsResourceTest extends ResourceTest {
249 } 339 }
250 340
251 /** 341 /**
342 + * Populates some flow rules used as testing data.
343 + */
344 + private void setupMockFlowRules() {
345 + flowRule2.treatment = DefaultTrafficTreatment.builder()
346 + .setEthDst(MacAddress.BROADCAST)
347 + .build();
348 + flowRule2.selector = DefaultTrafficSelector.builder()
349 + .matchEthType((short) 3)
350 + .matchIPProtocol((byte) 9)
351 + .build();
352 + flowRule4.treatment = DefaultTrafficTreatment.builder()
353 + .build();
354 +
355 + flowRules.add(flowRule1);
356 + flowRules.add(flowRule2);
357 + flowRules.add(flowRule3);
358 + flowRules.add(flowRule4);
359 + }
360 +
361 + /**
252 * Sets up the global values for all the tests. 362 * Sets up the global values for all the tests.
253 */ 363 */
254 @Before 364 @Before
...@@ -264,6 +374,8 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -264,6 +374,8 @@ public class FlowsResourceTest extends ResourceTest {
264 // Mock Core Service 374 // Mock Core Service
265 expect(mockCoreService.getAppId(anyShort())) 375 expect(mockCoreService.getAppId(anyShort()))
266 .andReturn(NetTestTools.APP_ID).anyTimes(); 376 .andReturn(NetTestTools.APP_ID).anyTimes();
377 + expect(mockCoreService.getAppId(anyString()))
378 + .andReturn(NetTestTools.APP_ID).anyTimes();
267 expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID)) 379 expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID))
268 .andReturn(APP_ID).anyTimes(); 380 .andReturn(APP_ID).anyTimes();
269 replay(mockCoreService); 381 replay(mockCoreService);
...@@ -276,7 +388,8 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -276,7 +388,8 @@ public class FlowsResourceTest extends ResourceTest {
276 .add(FlowRuleService.class, mockFlowService) 388 .add(FlowRuleService.class, mockFlowService)
277 .add(DeviceService.class, mockDeviceService) 389 .add(DeviceService.class, mockDeviceService)
278 .add(CodecService.class, codecService) 390 .add(CodecService.class, codecService)
279 - .add(CoreService.class, mockCoreService); 391 + .add(CoreService.class, mockCoreService)
392 + .add(ApplicationService.class, mockApplicationService);
280 393
281 BaseResource.setServiceDirectory(testDirectory); 394 BaseResource.setServiceDirectory(testDirectory);
282 } 395 }
...@@ -294,12 +407,12 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -294,12 +407,12 @@ public class FlowsResourceTest extends ResourceTest {
294 * Hamcrest matcher to check that a flow representation in JSON matches 407 * Hamcrest matcher to check that a flow representation in JSON matches
295 * the actual flow entry. 408 * the actual flow entry.
296 */ 409 */
297 - public static class FlowJsonMatcher extends TypeSafeMatcher<JsonObject> { 410 + public static class FlowEntryJsonMatcher extends TypeSafeMatcher<JsonObject> {
298 private final FlowEntry flow; 411 private final FlowEntry flow;
299 private final String expectedAppId; 412 private final String expectedAppId;
300 private String reason = ""; 413 private String reason = "";
301 414
302 - public FlowJsonMatcher(FlowEntry flowValue, String expectedAppIdValue) { 415 + public FlowEntryJsonMatcher(FlowEntry flowValue, String expectedAppIdValue) {
303 flow = flowValue; 416 flow = flowValue;
304 expectedAppId = expectedAppIdValue; 417 expectedAppId = expectedAppIdValue;
305 } 418 }
...@@ -398,19 +511,19 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -398,19 +511,19 @@ public class FlowsResourceTest extends ResourceTest {
398 * @param flow flow object we are looking for 511 * @param flow flow object we are looking for
399 * @return matcher 512 * @return matcher
400 */ 513 */
401 - private static FlowJsonMatcher matchesFlow(FlowEntry flow, String expectedAppName) { 514 + private static FlowEntryJsonMatcher matchesFlow(FlowEntry flow, String expectedAppName) {
402 - return new FlowJsonMatcher(flow, expectedAppName); 515 + return new FlowEntryJsonMatcher(flow, expectedAppName);
403 } 516 }
404 517
405 /** 518 /**
406 * Hamcrest matcher to check that a flow is represented properly in a JSON 519 * Hamcrest matcher to check that a flow is represented properly in a JSON
407 * array of flows. 520 * array of flows.
408 */ 521 */
409 - public static class FlowJsonArrayMatcher extends TypeSafeMatcher<JsonArray> { 522 + public static class FlowEntryJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
410 private final FlowEntry flow; 523 private final FlowEntry flow;
411 private String reason = ""; 524 private String reason = "";
412 525
413 - public FlowJsonArrayMatcher(FlowEntry flowValue) { 526 + public FlowEntryJsonArrayMatcher(FlowEntry flowValue) {
414 flow = flowValue; 527 flow = flowValue;
415 } 528 }
416 529
...@@ -452,8 +565,174 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -452,8 +565,174 @@ public class FlowsResourceTest extends ResourceTest {
452 * @param flow flow object we are looking for 565 * @param flow flow object we are looking for
453 * @return matcher 566 * @return matcher
454 */ 567 */
455 - private static FlowJsonArrayMatcher hasFlow(FlowEntry flow) { 568 + private static FlowEntryJsonArrayMatcher hasFlow(FlowEntry flow) {
456 - return new FlowJsonArrayMatcher(flow); 569 + return new FlowEntryJsonArrayMatcher(flow);
570 + }
571 +
572 + /**
573 + * Hamcrest matcher to check that a flow representation in JSON matches
574 + * the actual flow rule.
575 + */
576 + public static class FlowRuleJsonMatcher extends TypeSafeMatcher<JsonObject> {
577 + private final FlowRule flow;
578 + private final String expectedAppId;
579 + private String reason = "";
580 +
581 + public FlowRuleJsonMatcher(FlowRule flowValue, String expectedAppIdValue) {
582 + flow = flowValue;
583 + expectedAppId = expectedAppIdValue;
584 + }
585 +
586 + @Override
587 + public boolean matchesSafely(JsonObject jsonFlow) {
588 + // check id
589 + final String jsonId = jsonFlow.get("id").asString();
590 + final String flowId = Long.toString(flow.id().value());
591 + if (!jsonId.equals(flowId)) {
592 + reason = "id " + flow.id().toString();
593 + return false;
594 + }
595 +
596 + // check application id
597 + final String jsonAppId = jsonFlow.get("appId").asString();
598 + if (!jsonAppId.equals(expectedAppId)) {
599 + reason = "appId " + Short.toString(flow.appId());
600 + return false;
601 + }
602 +
603 + // check device id
604 + final String jsonDeviceId = jsonFlow.get("deviceId").asString();
605 + if (!jsonDeviceId.equals(flow.deviceId().toString())) {
606 + reason = "deviceId " + flow.deviceId();
607 + return false;
608 + }
609 +
610 + // check treatment and instructions array
611 + if (flow.treatment() != null) {
612 + final JsonObject jsonTreatment = jsonFlow.get("treatment").asObject();
613 + final JsonArray jsonInstructions = jsonTreatment.get("instructions").asArray();
614 + if (flow.treatment().immediate().size() != jsonInstructions.size()) {
615 + reason = "instructions array size of " +
616 + Integer.toString(flow.treatment().immediate().size());
617 + return false;
618 + }
619 + for (final Instruction instruction : flow.treatment().immediate()) {
620 + boolean instructionFound = false;
621 + for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
622 + final String jsonType =
623 + jsonInstructions.get(instructionIndex)
624 + .asObject().get("type").asString();
625 + final String instructionType = instruction.type().name();
626 + if (jsonType.equals(instructionType)) {
627 + instructionFound = true;
628 + }
629 + }
630 + if (!instructionFound) {
631 + reason = "instruction " + instruction.toString();
632 + return false;
633 + }
634 + }
635 + }
636 +
637 + // check selector and criteria array
638 + if (flow.selector() != null) {
639 + final JsonObject jsonTreatment = jsonFlow.get("selector").asObject();
640 + final JsonArray jsonCriteria = jsonTreatment.get("criteria").asArray();
641 + if (flow.selector().criteria().size() != jsonCriteria.size()) {
642 + reason = "criteria array size of " +
643 + Integer.toString(flow.selector().criteria().size());
644 + return false;
645 + }
646 + for (final Criterion criterion : flow.selector().criteria()) {
647 + boolean criterionFound = false;
648 +
649 + for (int criterionIndex = 0; criterionIndex < jsonCriteria.size(); criterionIndex++) {
650 + final String jsonType =
651 + jsonCriteria.get(criterionIndex)
652 + .asObject().get("type").asString();
653 + final String criterionType = criterion.type().name();
654 + if (jsonType.equals(criterionType)) {
655 + criterionFound = true;
656 + }
657 + }
658 + if (!criterionFound) {
659 + reason = "criterion " + criterion.toString();
660 + return false;
661 + }
662 + }
663 + }
664 +
665 + return true;
666 + }
667 +
668 + @Override
669 + public void describeTo(Description description) {
670 + description.appendText(reason);
671 + }
672 + }
673 +
674 + /**
675 + * Factory to allocate a flow matcher.
676 + *
677 + * @param flow flow rule object we are looking for
678 + * @return matcher
679 + */
680 + private static FlowRuleJsonMatcher matchesFlowRule(FlowRule flow, String expectedAppName) {
681 + return new FlowRuleJsonMatcher(flow, expectedAppName);
682 + }
683 +
684 + /**
685 + * Hamcrest matcher to check that a flow is represented properly in a JSON
686 + * array of flow rules.
687 + */
688 + public static class FlowRuleJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
689 + private final FlowRule flow;
690 + private String reason = "";
691 +
692 + public FlowRuleJsonArrayMatcher(FlowRule flowValue) {
693 + flow = flowValue;
694 + }
695 +
696 + @Override
697 + public boolean matchesSafely(JsonArray json) {
698 + boolean flowFound = false;
699 +
700 + for (int jsonFlowIndex = 0; jsonFlowIndex < json.size();
701 + jsonFlowIndex++) {
702 +
703 + final JsonObject jsonFlow = json.get(jsonFlowIndex).asObject();
704 +
705 + final String flowId = Long.toString(flow.id().value());
706 + final String jsonFlowId = jsonFlow.get("id").asString();
707 + if (jsonFlowId.equals(flowId)) {
708 + flowFound = true;
709 +
710 + // We found the correct flow, check attribute values
711 + assertThat(jsonFlow, matchesFlowRule(flow, APP_ID.name()));
712 + }
713 + }
714 + if (!flowFound) {
715 + reason = "Flow with id " + flow.id().toString() + " not found";
716 + return false;
717 + } else {
718 + return true;
719 + }
720 + }
721 +
722 + @Override
723 + public void describeTo(Description description) {
724 + description.appendText(reason);
725 + }
726 + }
727 +
728 + /**
729 + * Factory to allocate a flow array matcher.
730 + *
731 + * @param flow flow rule object we are looking for
732 + * @return matcher
733 + */
734 + private static FlowRuleJsonArrayMatcher hasFlowRule(FlowRule flow) {
735 + return new FlowRuleJsonArrayMatcher(flow);
457 } 736 }
458 737
459 /** 738 /**
...@@ -572,7 +851,7 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -572,7 +851,7 @@ public class FlowsResourceTest extends ResourceTest {
572 * Tests creating a flow with POST. 851 * Tests creating a flow with POST.
573 */ 852 */
574 @Test 853 @Test
575 - public void testPost() { 854 + public void testPostWithoutAppId() {
576 mockFlowService.applyFlowRules(anyObject()); 855 mockFlowService.applyFlowRules(anyObject());
577 expectLastCall(); 856 expectLastCall();
578 replay(mockFlowService); 857 replay(mockFlowService);
...@@ -590,6 +869,28 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -590,6 +869,28 @@ public class FlowsResourceTest extends ResourceTest {
590 } 869 }
591 870
592 /** 871 /**
872 + * Tests creating a flow with POST while specifying application identifier.
873 + */
874 + @Test
875 + public void testPostWithAppId() {
876 + mockFlowService.applyFlowRules(anyObject());
877 + expectLastCall();
878 + replay(mockFlowService);
879 +
880 + WebTarget wt = target();
881 + InputStream jsonStream = FlowsResourceTest.class
882 + .getResourceAsStream("post-flow.json");
883 +
884 + Response response = wt.path("flows/of:0000000000000001")
885 + .queryParam("appId", "org.onosproject.rest")
886 + .request(MediaType.APPLICATION_JSON_TYPE)
887 + .post(Entity.json(jsonStream));
888 + assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
889 + String location = response.getLocation().getPath();
890 + assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
891 + }
892 +
893 + /**
593 * Tests deleting a flow. 894 * Tests deleting a flow.
594 */ 895 */
595 @Test 896 @Test
...@@ -609,4 +910,55 @@ public class FlowsResourceTest extends ResourceTest { ...@@ -609,4 +910,55 @@ public class FlowsResourceTest extends ResourceTest {
609 assertThat(deleteResponse.getStatus(), 910 assertThat(deleteResponse.getStatus(),
610 is(HttpURLConnection.HTTP_NO_CONTENT)); 911 is(HttpURLConnection.HTTP_NO_CONTENT));
611 } 912 }
913 +
914 + /**
915 + * Tests the result of a rest api GET for an application.
916 + */
917 + @Test
918 + public void testGetFlowByAppId() {
919 + setupMockFlowRules();
920 +
921 + expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
922 + replay(mockApplicationService);
923 +
924 + expect(mockFlowService.getFlowRulesById(APP_ID)).andReturn(flowRules).anyTimes();
925 + replay(mockFlowService);
926 +
927 + final WebTarget wt = target();
928 + final String response = wt.path("flows/application/1").request().get(String.class);
929 + final JsonObject result = Json.parse(response).asObject();
930 + assertThat(result, notNullValue());
931 +
932 + assertThat(result.names(), hasSize(1));
933 + assertThat(result.names().get(0), is("flows"));
934 + final JsonArray jsonFlows = result.get("flows").asArray();
935 + assertThat(jsonFlows, notNullValue());
936 + assertThat(jsonFlows, hasFlowRule(flowRule1));
937 + assertThat(jsonFlows, hasFlowRule(flowRule2));
938 + assertThat(jsonFlows, hasFlowRule(flowRule3));
939 + assertThat(jsonFlows, hasFlowRule(flowRule4));
940 + }
941 +
942 + /**
943 + * Tests the result of a rest api DELETE for an application.
944 + */
945 + @Test
946 + public void testRemoveFlowByAppId() {
947 + expect(mockApplicationService.getId(anyObject())).andReturn(APP_ID).anyTimes();
948 + replay(mockApplicationService);
949 +
950 + mockFlowService.removeFlowRulesById(APP_ID);
951 + expectLastCall();
952 + replay(mockFlowService);
953 +
954 + WebTarget wt = target();
955 +
956 + String location = "/flows/application/1";
957 +
958 + Response deleteResponse = wt.path(location)
959 + .request()
960 + .delete();
961 + assertThat(deleteResponse.getStatus(),
962 + is(HttpURLConnection.HTTP_NO_CONTENT));
963 + }
612 } 964 }
......