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