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
Showing
6 changed files
with
1145 additions
and
242 deletions
... | @@ -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 | } | ... | ... |
-
Please register or login to post a comment