Committed by
Ali "The Bomb" Al-Shabibi
Added test code to test the flow subsystem performance
Change-Id: I1a7c68492760a63b7d092c3ca71e4964123c8aa7
Showing
4 changed files
with
162 additions
and
15 deletions
... | @@ -106,6 +106,7 @@ | ... | @@ -106,6 +106,7 @@ |
106 | com.sun.jersey.server.impl.container.servlet, | 106 | com.sun.jersey.server.impl.container.servlet, |
107 | com.fasterxml.jackson.databind, | 107 | com.fasterxml.jackson.databind, |
108 | com.fasterxml.jackson.databind.node, | 108 | com.fasterxml.jackson.databind.node, |
109 | + org.apache.commons.lang.math.*, | ||
109 | com.google.common.*, | 110 | com.google.common.*, |
110 | org.onlab.packet.*, | 111 | org.onlab.packet.*, |
111 | org.onlab.rest.*, | 112 | org.onlab.rest.*, | ... | ... |
... | @@ -27,6 +27,12 @@ public interface DemoAPI { | ... | @@ -27,6 +27,12 @@ public interface DemoAPI { |
27 | enum InstallType { MESH, RANDOM }; | 27 | enum InstallType { MESH, RANDOM }; |
28 | 28 | ||
29 | /** | 29 | /** |
30 | + * Tests flow subsystem based on the parameters supplied. | ||
31 | + * @param params the test parameters | ||
32 | + */ | ||
33 | + JsonNode flowTest(Optional<JsonNode> params); | ||
34 | + | ||
35 | + /** | ||
30 | * Installs intents based on the installation type. | 36 | * Installs intents based on the installation type. |
31 | * @param type the installation type. | 37 | * @param type the installation type. |
32 | * @param runParams run params | 38 | * @param runParams run params | ... | ... |
... | @@ -16,27 +16,40 @@ | ... | @@ -16,27 +16,40 @@ |
16 | package org.onosproject.demo; | 16 | package org.onosproject.demo; |
17 | 17 | ||
18 | import com.fasterxml.jackson.databind.JsonNode; | 18 | import com.fasterxml.jackson.databind.JsonNode; |
19 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
19 | import com.google.common.base.Predicate; | 21 | import com.google.common.base.Predicate; |
22 | +import com.google.common.base.Stopwatch; | ||
20 | import com.google.common.collect.FluentIterable; | 23 | import com.google.common.collect.FluentIterable; |
21 | import com.google.common.collect.Lists; | 24 | import com.google.common.collect.Lists; |
22 | import com.google.common.collect.Sets; | 25 | import com.google.common.collect.Sets; |
23 | import com.google.common.util.concurrent.ThreadFactoryBuilder; | 26 | import com.google.common.util.concurrent.ThreadFactoryBuilder; |
27 | +import org.apache.commons.lang.math.RandomUtils; | ||
24 | import org.apache.felix.scr.annotations.Activate; | 28 | import org.apache.felix.scr.annotations.Activate; |
25 | import org.apache.felix.scr.annotations.Component; | 29 | import org.apache.felix.scr.annotations.Component; |
26 | import org.apache.felix.scr.annotations.Deactivate; | 30 | import org.apache.felix.scr.annotations.Deactivate; |
27 | import org.apache.felix.scr.annotations.Reference; | 31 | import org.apache.felix.scr.annotations.Reference; |
28 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 32 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
29 | import org.apache.felix.scr.annotations.Service; | 33 | import org.apache.felix.scr.annotations.Service; |
30 | - | 34 | +import org.onlab.packet.MacAddress; |
31 | import org.onosproject.cluster.ClusterService; | 35 | import org.onosproject.cluster.ClusterService; |
36 | +import org.onosproject.cluster.ControllerNode; | ||
37 | +import org.onosproject.cluster.NodeId; | ||
32 | import org.onosproject.core.ApplicationId; | 38 | import org.onosproject.core.ApplicationId; |
33 | import org.onosproject.core.CoreService; | 39 | import org.onosproject.core.CoreService; |
34 | import org.onosproject.mastership.MastershipService; | 40 | import org.onosproject.mastership.MastershipService; |
41 | +import org.onosproject.net.Device; | ||
35 | import org.onosproject.net.Host; | 42 | import org.onosproject.net.Host; |
36 | import org.onosproject.net.HostId; | 43 | import org.onosproject.net.HostId; |
37 | import org.onosproject.net.MastershipRole; | 44 | import org.onosproject.net.MastershipRole; |
45 | +import org.onosproject.net.PortNumber; | ||
46 | +import org.onosproject.net.device.DeviceService; | ||
47 | +import org.onosproject.net.flow.DefaultFlowRule; | ||
38 | import org.onosproject.net.flow.DefaultTrafficSelector; | 48 | import org.onosproject.net.flow.DefaultTrafficSelector; |
39 | import org.onosproject.net.flow.DefaultTrafficTreatment; | 49 | import org.onosproject.net.flow.DefaultTrafficTreatment; |
50 | +import org.onosproject.net.flow.FlowRuleOperations; | ||
51 | +import org.onosproject.net.flow.FlowRuleOperationsContext; | ||
52 | +import org.onosproject.net.flow.FlowRuleService; | ||
40 | import org.onosproject.net.flow.TrafficSelector; | 53 | import org.onosproject.net.flow.TrafficSelector; |
41 | import org.onosproject.net.flow.TrafficTreatment; | 54 | import org.onosproject.net.flow.TrafficTreatment; |
42 | import org.onosproject.net.host.HostService; | 55 | import org.onosproject.net.host.HostService; |
... | @@ -48,7 +61,6 @@ import org.onosproject.net.intent.IntentOperations; | ... | @@ -48,7 +61,6 @@ import org.onosproject.net.intent.IntentOperations; |
48 | import org.onosproject.net.intent.IntentService; | 61 | import org.onosproject.net.intent.IntentService; |
49 | import org.slf4j.Logger; | 62 | import org.slf4j.Logger; |
50 | 63 | ||
51 | - | ||
52 | import java.util.Collection; | 64 | import java.util.Collection; |
53 | import java.util.Collections; | 65 | import java.util.Collections; |
54 | import java.util.HashSet; | 66 | import java.util.HashSet; |
... | @@ -59,11 +71,14 @@ import java.util.Objects; | ... | @@ -59,11 +71,14 @@ import java.util.Objects; |
59 | import java.util.Optional; | 71 | import java.util.Optional; |
60 | import java.util.Random; | 72 | import java.util.Random; |
61 | import java.util.Set; | 73 | import java.util.Set; |
74 | +import java.util.concurrent.Callable; | ||
62 | import java.util.concurrent.CountDownLatch; | 75 | import java.util.concurrent.CountDownLatch; |
76 | +import java.util.concurrent.ExecutionException; | ||
63 | import java.util.concurrent.ExecutorService; | 77 | import java.util.concurrent.ExecutorService; |
64 | import java.util.concurrent.Executors; | 78 | import java.util.concurrent.Executors; |
79 | +import java.util.concurrent.Future; | ||
65 | import java.util.concurrent.TimeUnit; | 80 | import java.util.concurrent.TimeUnit; |
66 | - | 81 | +import java.util.concurrent.TimeoutException; |
67 | 82 | ||
68 | import static org.slf4j.LoggerFactory.getLogger; | 83 | import static org.slf4j.LoggerFactory.getLogger; |
69 | 84 | ||
... | @@ -94,15 +109,23 @@ public class DemoInstaller implements DemoAPI { | ... | @@ -94,15 +109,23 @@ public class DemoInstaller implements DemoAPI { |
94 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 109 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
95 | protected ClusterService clusterService; | 110 | protected ClusterService clusterService; |
96 | 111 | ||
112 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
113 | + protected DeviceService deviceService; | ||
114 | + | ||
115 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
116 | + protected FlowRuleService flowService; | ||
117 | + | ||
97 | private ExecutorService worker; | 118 | private ExecutorService worker; |
98 | 119 | ||
99 | - private ExecutorService randomWorker; | 120 | + private ExecutorService installWorker; |
100 | 121 | ||
101 | private ApplicationId appId; | 122 | private ApplicationId appId; |
102 | 123 | ||
103 | private final Set<Intent> existingIntents = new HashSet<>(); | 124 | private final Set<Intent> existingIntents = new HashSet<>(); |
104 | private RandomInstaller randomInstaller; | 125 | private RandomInstaller randomInstaller; |
105 | 126 | ||
127 | + private ObjectMapper mapper = new ObjectMapper(); | ||
128 | + | ||
106 | 129 | ||
107 | 130 | ||
108 | @Activate | 131 | @Activate |
... | @@ -120,13 +143,33 @@ public class DemoInstaller implements DemoAPI { | ... | @@ -120,13 +143,33 @@ public class DemoInstaller implements DemoAPI { |
120 | @Deactivate | 143 | @Deactivate |
121 | public void deactivate() { | 144 | public void deactivate() { |
122 | shutdownAndAwaitTermination(worker); | 145 | shutdownAndAwaitTermination(worker); |
123 | - if (!randomWorker.isShutdown()) { | 146 | + if (installWorker != null && !installWorker.isShutdown()) { |
124 | - shutdownAndAwaitTermination(randomWorker); | 147 | + shutdownAndAwaitTermination(installWorker); |
125 | } | 148 | } |
126 | log.info("Stopped"); | 149 | log.info("Stopped"); |
127 | } | 150 | } |
128 | 151 | ||
129 | @Override | 152 | @Override |
153 | + public JsonNode flowTest(Optional<JsonNode> params) { | ||
154 | + int flowsPerDevice = 1000; | ||
155 | + int neighbours = 0; | ||
156 | + if (params.isPresent()) { | ||
157 | + flowsPerDevice = params.get().get("flowsPerDevice").asInt(); | ||
158 | + neighbours = params.get().get("neighbours").asInt(); | ||
159 | + } | ||
160 | + | ||
161 | + Future<JsonNode> future = worker.submit(new FlowTest(flowsPerDevice, neighbours)); | ||
162 | + | ||
163 | + try { | ||
164 | + return future.get(10, TimeUnit.SECONDS); | ||
165 | + } catch (InterruptedException | ExecutionException | TimeoutException e) { | ||
166 | + ObjectNode node = mapper.createObjectNode(); | ||
167 | + node.put("Error", e.getMessage()); | ||
168 | + return node; | ||
169 | + } | ||
170 | + } | ||
171 | + | ||
172 | + @Override | ||
130 | public void setup(InstallType type, Optional<JsonNode> runParams) { | 173 | public void setup(InstallType type, Optional<JsonNode> runParams) { |
131 | switch (type) { | 174 | switch (type) { |
132 | case MESH: | 175 | case MESH: |
... | @@ -135,14 +178,14 @@ public class DemoInstaller implements DemoAPI { | ... | @@ -135,14 +178,14 @@ public class DemoInstaller implements DemoAPI { |
135 | break; | 178 | break; |
136 | case RANDOM: | 179 | case RANDOM: |
137 | //check that we do not have a random installer running | 180 | //check that we do not have a random installer running |
138 | - if (randomWorker == null || randomWorker.isShutdown()) { | 181 | + if (installWorker == null || installWorker.isShutdown()) { |
139 | - randomWorker = Executors.newFixedThreadPool(1, | 182 | + installWorker = Executors.newFixedThreadPool(1, |
140 | new ThreadFactoryBuilder() | 183 | new ThreadFactoryBuilder() |
141 | .setNameFormat("random-worker") | 184 | .setNameFormat("random-worker") |
142 | .build()); | 185 | .build()); |
143 | log.debug("Installing random sequence of intents"); | 186 | log.debug("Installing random sequence of intents"); |
144 | randomInstaller = new RandomInstaller(runParams); | 187 | randomInstaller = new RandomInstaller(runParams); |
145 | - randomWorker.execute(randomInstaller); | 188 | + installWorker.execute(randomInstaller); |
146 | } else { | 189 | } else { |
147 | log.warn("Random installer is already running"); | 190 | log.warn("Random installer is already running"); |
148 | } | 191 | } |
... | @@ -239,7 +282,7 @@ public class DemoInstaller implements DemoAPI { | ... | @@ -239,7 +282,7 @@ public class DemoInstaller implements DemoAPI { |
239 | 282 | ||
240 | @Override | 283 | @Override |
241 | public void run() { | 284 | public void run() { |
242 | - if (!randomWorker.isShutdown()) { | 285 | + if (!installWorker.isShutdown()) { |
243 | randomize(); | 286 | randomize(); |
244 | latch = new CountDownLatch(1); | 287 | latch = new CountDownLatch(1); |
245 | try { | 288 | try { |
... | @@ -269,12 +312,12 @@ public class DemoInstaller implements DemoAPI { | ... | @@ -269,12 +312,12 @@ public class DemoInstaller implements DemoAPI { |
269 | log.warn("A batch is stuck processing. " + | 312 | log.warn("A batch is stuck processing. " + |
270 | "pending : {}", | 313 | "pending : {}", |
271 | intentBatchService.getPendingOperations()); | 314 | intentBatchService.getPendingOperations()); |
272 | - shutdownAndAwaitTermination(randomWorker); | 315 | + shutdownAndAwaitTermination(installWorker); |
273 | } | 316 | } |
274 | } | 317 | } |
275 | //if everyting is good proceed. | 318 | //if everyting is good proceed. |
276 | - if (!randomWorker.isShutdown()) { | 319 | + if (!installWorker.isShutdown()) { |
277 | - randomWorker.execute(this); | 320 | + installWorker.execute(this); |
278 | } | 321 | } |
279 | 322 | ||
280 | } | 323 | } |
... | @@ -432,8 +475,8 @@ public class DemoInstaller implements DemoAPI { | ... | @@ -432,8 +475,8 @@ public class DemoInstaller implements DemoAPI { |
432 | clearExistingIntents(); | 475 | clearExistingIntents(); |
433 | } | 476 | } |
434 | 477 | ||
435 | - if (randomWorker != null && !randomWorker.isShutdown()) { | 478 | + if (installWorker != null && !installWorker.isShutdown()) { |
436 | - shutdownAndAwaitTermination(randomWorker); | 479 | + shutdownAndAwaitTermination(installWorker); |
437 | randomInstaller.shutdown(); | 480 | randomInstaller.shutdown(); |
438 | } | 481 | } |
439 | } | 482 | } |
... | @@ -470,6 +513,91 @@ public class DemoInstaller implements DemoAPI { | ... | @@ -470,6 +513,91 @@ public class DemoInstaller implements DemoAPI { |
470 | } | 513 | } |
471 | } | 514 | } |
472 | 515 | ||
516 | + private class FlowTest implements Callable<JsonNode> { | ||
517 | + private final int flowPerDevice; | ||
518 | + private final int neighbours; | ||
519 | + private FlowRuleOperations.Builder adds; | ||
520 | + private FlowRuleOperations.Builder removes; | ||
521 | + | ||
522 | + public FlowTest(int flowsPerDevice, int neighbours) { | ||
523 | + this.flowPerDevice = flowsPerDevice; | ||
524 | + this.neighbours = neighbours; | ||
525 | + prepareInstallation(); | ||
526 | + } | ||
527 | + | ||
528 | + private void prepareInstallation() { | ||
529 | + Set<ControllerNode> instances = Sets.newHashSet(clusterService.getNodes()); | ||
530 | + instances.remove(clusterService.getLocalNode()); | ||
531 | + Set<NodeId> acceptableNodes = Sets.newHashSet(); | ||
532 | + if (neighbours >= instances.size()) { | ||
533 | + instances.forEach(instance -> acceptableNodes.add(instance.id())); | ||
534 | + } else { | ||
535 | + Iterator<ControllerNode> nodes = instances.iterator(); | ||
536 | + for (int i = neighbours; i > 0; i--) { | ||
537 | + acceptableNodes.add(nodes.next().id()); | ||
538 | + } | ||
539 | + } | ||
540 | + acceptableNodes.add(clusterService.getLocalNode().id()); | ||
541 | + | ||
542 | + Set<Device> devices = Sets.newHashSet(); | ||
543 | + for (Device dev : deviceService.getDevices()) { | ||
544 | + if (acceptableNodes.contains( | ||
545 | + mastershipService.getMasterFor(dev.id()))) { | ||
546 | + devices.add(dev); | ||
547 | + } | ||
548 | + } | ||
549 | + | ||
550 | + TrafficTreatment treatment = DefaultTrafficTreatment.builder() | ||
551 | + .setOutput(PortNumber.portNumber(RandomUtils.nextInt())).build(); | ||
552 | + TrafficSelector.Builder sbuilder; | ||
553 | + FlowRuleOperations.Builder rules = FlowRuleOperations.builder(); | ||
554 | + FlowRuleOperations.Builder remove = FlowRuleOperations.builder(); | ||
555 | + | ||
556 | + for (Device d : devices) { | ||
557 | + for (int i = 0; i < this.flowPerDevice; i++) { | ||
558 | + sbuilder = DefaultTrafficSelector.builder(); | ||
559 | + | ||
560 | + sbuilder.matchEthSrc(MacAddress.valueOf(RandomUtils.nextInt() * i)) | ||
561 | + .matchEthDst(MacAddress.valueOf((Integer.MAX_VALUE - i) * RandomUtils.nextInt())); | ||
562 | + | ||
563 | + | ||
564 | + int randomPriority = RandomUtils.nextInt(); | ||
565 | + DefaultFlowRule f = new DefaultFlowRule(d.id(), sbuilder.build(), treatment, | ||
566 | + randomPriority, appId, 10, false); | ||
567 | + rules.add(f); | ||
568 | + remove.remove(f); | ||
569 | + | ||
570 | + } | ||
571 | + } | ||
572 | + | ||
573 | + this.adds = rules; | ||
574 | + this.removes = remove; | ||
575 | + } | ||
576 | + | ||
577 | + @Override | ||
578 | + public JsonNode call() throws Exception { | ||
579 | + ObjectNode node = mapper.createObjectNode(); | ||
580 | + CountDownLatch latch = new CountDownLatch(1); | ||
581 | + flowService.apply(adds.build(new FlowRuleOperationsContext() { | ||
582 | + | ||
583 | + private final Stopwatch timer = Stopwatch.createStarted(); | ||
584 | + | ||
585 | + @Override | ||
586 | + public void onSuccess(FlowRuleOperations ops) { | ||
587 | + | ||
588 | + long elapsed = timer.elapsed(TimeUnit.MILLISECONDS); | ||
589 | + node.put("elapsed", elapsed); | ||
590 | + | ||
591 | + | ||
592 | + latch.countDown(); | ||
593 | + } | ||
594 | + })); | ||
595 | + | ||
596 | + latch.await(10, TimeUnit.SECONDS); | ||
597 | + flowService.apply(removes.build()); | ||
598 | + return node; | ||
599 | + } | ||
600 | + } | ||
473 | } | 601 | } |
474 | 602 | ||
475 | 603 | ... | ... |
... | @@ -37,6 +37,18 @@ import java.util.Optional; | ... | @@ -37,6 +37,18 @@ import java.util.Optional; |
37 | public class DemoResource extends BaseResource { | 37 | public class DemoResource extends BaseResource { |
38 | 38 | ||
39 | 39 | ||
40 | + | ||
41 | + @POST | ||
42 | + @Path("flowTest") | ||
43 | + @Consumes(MediaType.APPLICATION_JSON) | ||
44 | + @Produces(MediaType.APPLICATION_JSON) | ||
45 | + public Response flowTest(InputStream input) throws IOException { | ||
46 | + ObjectMapper mapper = new ObjectMapper(); | ||
47 | + JsonNode cfg = mapper.readTree(input); | ||
48 | + DemoAPI demo = get(DemoAPI.class); | ||
49 | + return Response.ok(demo.flowTest(Optional.ofNullable(cfg)).toString()).build(); | ||
50 | + } | ||
51 | + | ||
40 | @POST | 52 | @POST |
41 | @Path("setup") | 53 | @Path("setup") |
42 | @Consumes(MediaType.APPLICATION_JSON) | 54 | @Consumes(MediaType.APPLICATION_JSON) | ... | ... |
-
Please register or login to post a comment