Madan Jampani
Committed by Gerrit Code Review

ONOS test application for measuring flow installation throughput

Change-Id: I1ba34656d0f33578f21c5f89fda0919bca0080d8
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2016 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<project xmlns="http://maven.apache.org/POM/4.0.0"
18 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
20 + <modelVersion>4.0.0</modelVersion>
21 +
22 + <parent>
23 + <groupId>org.onosproject</groupId>
24 + <artifactId>onos-apps-test</artifactId>
25 + <version>1.6.0-SNAPSHOT</version>
26 + <relativePath>../pom.xml</relativePath>
27 + </parent>
28 +
29 + <artifactId>onos-app-flow-perf</artifactId>
30 + <packaging>bundle</packaging>
31 +
32 + <description>Messaging performance test application</description>
33 +
34 + <properties>
35 + <onos.app.name>org.onosproject.flowgperf</onos.app.name>
36 + <onos.app.title>Flow Performance Test App</onos.app.title>
37 + <onos.app.category>Test</onos.app.category>
38 + <onos.app.url>http://onosproject.org</onos.app.url>
39 + <onos.app.readme>Flow performance test application.</onos.app.readme>
40 + </properties>
41 +
42 + <dependencies>
43 + <dependency>
44 + <groupId>org.onosproject</groupId>
45 + <artifactId>onos-api</artifactId>
46 + <version>${project.version}</version>
47 + </dependency>
48 + <dependency>
49 + <groupId>org.onosproject</groupId>
50 + <artifactId>onos-core-serializers</artifactId>
51 + <version>${project.version}</version>
52 + </dependency>
53 + <dependency>
54 + <groupId>org.osgi</groupId>
55 + <artifactId>org.osgi.compendium</artifactId>
56 + </dependency>
57 + <!-- Required for javadoc generation -->
58 + <dependency>
59 + <groupId>org.osgi</groupId>
60 + <artifactId>org.osgi.core</artifactId>
61 + </dependency>
62 + </dependencies>
63 +
64 +</project>
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.flowperf;
17 +
18 +import static com.google.common.base.Strings.isNullOrEmpty;
19 +import static org.apache.felix.scr.annotations.ReferenceCardinality.MANDATORY_UNARY;
20 +import static org.onlab.util.Tools.get;
21 +import static org.slf4j.LoggerFactory.getLogger;
22 +
23 +import java.util.Dictionary;
24 +import java.util.Iterator;
25 +import java.util.List;
26 +import java.util.concurrent.CountDownLatch;
27 +import java.util.concurrent.ExecutorService;
28 +import java.util.concurrent.Executors;
29 +import java.util.concurrent.atomic.AtomicInteger;
30 +import java.util.concurrent.atomic.AtomicLong;
31 +
32 +import org.apache.felix.scr.annotations.Activate;
33 +import org.apache.felix.scr.annotations.Component;
34 +import org.apache.felix.scr.annotations.Deactivate;
35 +import org.apache.felix.scr.annotations.Modified;
36 +import org.apache.felix.scr.annotations.Property;
37 +import org.apache.felix.scr.annotations.Reference;
38 +import org.apache.felix.scr.annotations.ReferenceCardinality;
39 +import org.apache.felix.scr.annotations.Service;
40 +import org.onlab.packet.MacAddress;
41 +import org.onlab.util.Tools;
42 +import org.onosproject.cfg.ComponentConfigService;
43 +import org.onosproject.core.ApplicationId;
44 +import org.onosproject.core.CoreService;
45 +import org.onosproject.net.Device;
46 +import org.onosproject.net.PortNumber;
47 +import org.onosproject.net.device.DeviceService;
48 +import org.onosproject.net.flow.DefaultFlowRule;
49 +import org.onosproject.net.flow.DefaultTrafficSelector;
50 +import org.onosproject.net.flow.DefaultTrafficTreatment;
51 +import org.onosproject.net.flow.FlowRule;
52 +import org.onosproject.net.flow.FlowRuleEvent;
53 +import org.onosproject.net.flow.FlowRuleListener;
54 +import org.onosproject.net.flow.FlowRuleService;
55 +import org.onosproject.net.flow.TrafficSelector;
56 +import org.onosproject.net.flow.TrafficTreatment;
57 +import org.onosproject.net.flow.instructions.Instructions;
58 +import org.osgi.service.component.ComponentContext;
59 +import org.slf4j.Logger;
60 +
61 +import com.google.common.collect.Iterables;
62 +import com.google.common.collect.Lists;
63 +
64 +/**
65 + * Application for measuring flow installation performance.
66 + * <p>
67 + * This application installs a bunch of flows, validates that all those flows have
68 + * been successfully added and immediately proceeds to remove all the added flows.
69 + */
70 +@Component(immediate = true, enabled = true)
71 +@Service(value = FlowPerfApp.class)
72 +public class FlowPerfApp {
73 + private final Logger log = getLogger(getClass());
74 +
75 + @Reference(cardinality = MANDATORY_UNARY)
76 + protected DeviceService deviceService;
77 +
78 + @Reference(cardinality = MANDATORY_UNARY)
79 + protected FlowRuleService flowRuleService;
80 +
81 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 + protected CoreService coreService;
83 +
84 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 + protected ComponentConfigService configService;
86 +
87 + protected ApplicationId appId;
88 +
89 + private static final int DEFAULT_BATCH_SIZE = 200;
90 + private static final int DEFAULT_TOTAL_THREADS = 1;
91 + private static final int DEFAULT_TOTAL_FLOWS = 100000;
92 + private AtomicInteger pendingBatchCount;
93 + private CountDownLatch installationLatch;
94 + private CountDownLatch uninstallationLatch;
95 + private Iterator<Device> devices;
96 + private AtomicLong macIndex;
97 +
98 + List<FlowRule> addedRules = Lists.newArrayList();
99 +
100 + @Property(name = "totalFlows", intValue = DEFAULT_TOTAL_FLOWS,
101 + label = "Total number of flows")
102 + protected int totalFlows = DEFAULT_TOTAL_FLOWS;
103 +
104 + @Property(name = "batchSize", intValue = DEFAULT_BATCH_SIZE,
105 + label = "Number of flows per batch")
106 + protected int batchSize = DEFAULT_BATCH_SIZE;
107 +
108 + @Property(name = "totalThreads", intValue = DEFAULT_TOTAL_THREADS,
109 + label = "Number of installer threads")
110 + protected int totalThreads = DEFAULT_TOTAL_THREADS;
111 +
112 + private ExecutorService installer;
113 + private ExecutorService testRunner =
114 + Executors.newSingleThreadExecutor(Tools.groupedThreads("app/flow-perf-test-runner", ""));
115 +
116 + @Activate
117 + public void activate(ComponentContext context) {
118 + appId = coreService.registerApplication("org.onosproject.flowperf");
119 + configService.registerProperties(getClass());
120 + installer = Executors.newFixedThreadPool(totalThreads, Tools.groupedThreads("app/flow-perf-worker", "%d"));
121 + testRunner.submit(this::runTest);
122 + log.info("Started");
123 + }
124 +
125 + @Deactivate
126 + public void deactivate(ComponentContext context) {
127 + installer.shutdown();
128 + testRunner.shutdown();
129 + configService.unregisterProperties(getClass(), false);
130 + log.info("Stopped.");
131 + }
132 +
133 + private void runTest() {
134 + pendingBatchCount = new AtomicInteger(totalFlows / batchSize);
135 + installationLatch = new CountDownLatch(totalFlows);
136 + List<Device> deviceList = Lists.newArrayList();
137 + deviceService.getAvailableDevices().forEach(deviceList::add);
138 + devices = Iterables.cycle(deviceList).iterator();
139 + log.info("Starting installation. Total flows: {}, Total threads: {}, "
140 + + "Batch Size: {}", totalFlows, totalThreads, batchSize);
141 +
142 + macIndex = new AtomicLong(0);
143 + FlowRuleListener addMonitor = event -> {
144 + if (event.type() == FlowRuleEvent.Type.RULE_ADDED) {
145 + installationLatch.countDown();
146 + }
147 + };
148 +
149 + flowRuleService.addListener(addMonitor);
150 + long addStartTime = System.currentTimeMillis();
151 + for (int i = 0; i < totalThreads; ++i) {
152 + installer.submit(() -> {
153 + while (pendingBatchCount.getAndDecrement() > 0) {
154 + List<FlowRule> batch = nextBatch(batchSize);
155 + addedRules.addAll(batch);
156 + flowRuleService.applyFlowRules(batch.toArray(new FlowRule[]{}));
157 + }
158 + });
159 + }
160 +
161 + // Wait till all the flows are in ADDED state.
162 + try {
163 + installationLatch.await();
164 + } catch (InterruptedException e) {
165 + Thread.interrupted();
166 + }
167 + log.info("Time to install {} flows: {} ms", totalFlows, System.currentTimeMillis() - addStartTime);
168 + flowRuleService.removeListener(addMonitor);
169 +
170 +
171 + uninstallationLatch = new CountDownLatch(totalFlows);
172 + FlowRuleListener removeListener = event -> {
173 + if (event.type() == FlowRuleEvent.Type.RULE_REMOVED) {
174 + uninstallationLatch.countDown();
175 + }
176 + };
177 + AtomicInteger currentIndex = new AtomicInteger(0);
178 + long removeStartTime = System.currentTimeMillis();
179 + flowRuleService.addListener(removeListener);
180 + // Uninstallation runs on a single thread.
181 + installer.submit(() -> {
182 + while (currentIndex.get() < addedRules.size()) {
183 + List<FlowRule> removeBatch = addedRules.subList(currentIndex.get(),
184 + Math.min(currentIndex.get() + batchSize, addedRules.size()));
185 + currentIndex.addAndGet(removeBatch.size());
186 + flowRuleService.removeFlowRules(removeBatch.toArray(new FlowRule[]{}));
187 + }
188 + });
189 + try {
190 + uninstallationLatch.await();
191 + } catch (InterruptedException e) {
192 + Thread.interrupted();
193 + }
194 + log.info("Time to uninstall {} flows: {} ms", totalFlows, System.currentTimeMillis() - removeStartTime);
195 + flowRuleService.removeListener(removeListener);
196 + }
197 +
198 + private List<FlowRule> nextBatch(int size) {
199 + List<FlowRule> rules = Lists.newArrayList();
200 + for (int i = 0; i < size; ++i) {
201 + Device device = devices.next();
202 + long srcMac = macIndex.incrementAndGet();
203 + long dstMac = srcMac + 1;
204 + TrafficSelector selector = DefaultTrafficSelector.builder()
205 + .matchEthSrc(MacAddress.valueOf(srcMac))
206 + .matchEthDst(MacAddress.valueOf(dstMac))
207 + .matchInPort(PortNumber.portNumber(2))
208 + .build();
209 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
210 + .add(Instructions.createOutput(PortNumber.portNumber(3))).build();
211 + FlowRule rule = new DefaultFlowRule(device.id(),
212 + selector,
213 + treatment,
214 + 100,
215 + appId,
216 + 50000,
217 + true,
218 + null);
219 + rules.add(rule);
220 + }
221 + return rules;
222 + }
223 +
224 + @Modified
225 + public void modified(ComponentContext context) {
226 + if (context == null) {
227 + totalFlows = DEFAULT_TOTAL_FLOWS;
228 + batchSize = DEFAULT_BATCH_SIZE;
229 + totalThreads = DEFAULT_TOTAL_THREADS;
230 + return;
231 + }
232 +
233 + Dictionary properties = context.getProperties();
234 +
235 + int newTotalFlows = totalFlows;
236 + int newBatchSize = batchSize;
237 + int newTotalThreads = totalThreads;
238 + try {
239 + String s = get(properties, "batchSize");
240 + newTotalFlows = isNullOrEmpty(s)
241 + ? totalFlows : Integer.parseInt(s.trim());
242 +
243 + s = get(properties, "batchSize");
244 + newBatchSize = isNullOrEmpty(s)
245 + ? batchSize : Integer.parseInt(s.trim());
246 +
247 + s = get(properties, "totalThreads");
248 + newTotalThreads = isNullOrEmpty(s)
249 + ? totalThreads : Integer.parseInt(s.trim());
250 +
251 + } catch (NumberFormatException | ClassCastException e) {
252 + return;
253 + }
254 +
255 + boolean modified = newTotalFlows != totalFlows || newTotalThreads != totalThreads ||
256 + newBatchSize != batchSize;
257 +
258 + // If nothing has changed, simply return.
259 + if (!modified) {
260 + return;
261 + }
262 +
263 + totalFlows = newTotalFlows;
264 + batchSize = newBatchSize;
265 + if (totalThreads != newTotalThreads) {
266 + totalThreads = newTotalThreads;
267 + installer.shutdown();
268 + installer = Executors.newFixedThreadPool(totalThreads, Tools.groupedThreads("flow-perf-worker", "%d"));
269 + }
270 + }
271 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/**
18 + * Performance test application for the flow subsystem.
19 + */
20 +package org.onosproject.flowperf;
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
36 <module>loadtest</module> 36 <module>loadtest</module>
37 <module>intent-perf</module> 37 <module>intent-perf</module>
38 <module>messaging-perf</module> 38 <module>messaging-perf</module>
39 + <module>flow-perf</module>
39 <module>demo</module> 40 <module>demo</module>
40 <module>distributed-primitives</module> 41 <module>distributed-primitives</module>
41 </modules> 42 </modules>
......