blob: 9ee3559427ead1def1e6916ca5ba08b3331958a6 [file] [log] [blame]
Madan Jampanic23b6262016-04-07 15:57:22 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Madan Jampanic23b6262016-04-07 15:57:22 -07003 *
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 */
16package org.onosproject.flowperf;
17
Ray Milkeyd84f89b2018-08-17 14:54:17 -070018import com.google.common.collect.Iterables;
19import com.google.common.collect.Lists;
Madan Jampanic23b6262016-04-07 15:57:22 -070020import org.onlab.packet.MacAddress;
21import org.onlab.util.Tools;
22import org.onosproject.cfg.ComponentConfigService;
23import org.onosproject.core.ApplicationId;
24import org.onosproject.core.CoreService;
25import org.onosproject.net.Device;
26import org.onosproject.net.PortNumber;
27import org.onosproject.net.device.DeviceService;
28import org.onosproject.net.flow.DefaultFlowRule;
29import org.onosproject.net.flow.DefaultTrafficSelector;
30import org.onosproject.net.flow.DefaultTrafficTreatment;
31import org.onosproject.net.flow.FlowRule;
32import org.onosproject.net.flow.FlowRuleEvent;
33import org.onosproject.net.flow.FlowRuleListener;
34import org.onosproject.net.flow.FlowRuleService;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.TrafficTreatment;
37import org.onosproject.net.flow.instructions.Instructions;
38import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070039import org.osgi.service.component.annotations.Activate;
40import org.osgi.service.component.annotations.Component;
41import org.osgi.service.component.annotations.Deactivate;
42import org.osgi.service.component.annotations.Modified;
43import org.osgi.service.component.annotations.Reference;
Madan Jampanic23b6262016-04-07 15:57:22 -070044import org.slf4j.Logger;
45
Ray Milkeyd84f89b2018-08-17 14:54:17 -070046import java.util.Dictionary;
47import java.util.Iterator;
48import java.util.List;
49import java.util.concurrent.CountDownLatch;
50import java.util.concurrent.ExecutorService;
51import java.util.concurrent.Executors;
52import java.util.concurrent.atomic.AtomicInteger;
53import java.util.concurrent.atomic.AtomicLong;
54
55import static com.google.common.base.Strings.isNullOrEmpty;
56import static org.onlab.util.Tools.get;
57import static org.osgi.service.component.annotations.ReferenceCardinality.MANDATORY;
58import static org.slf4j.LoggerFactory.getLogger;
Madan Jampanic23b6262016-04-07 15:57:22 -070059
60/**
61 * Application for measuring flow installation performance.
62 * <p>
63 * This application installs a bunch of flows, validates that all those flows have
64 * been successfully added and immediately proceeds to remove all the added flows.
65 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070066@Component(immediate = true, service = FlowPerfApp.class)
Madan Jampanic23b6262016-04-07 15:57:22 -070067public class FlowPerfApp {
68 private final Logger log = getLogger(getClass());
69
Ray Milkeyd84f89b2018-08-17 14:54:17 -070070 @Reference(cardinality = MANDATORY)
Madan Jampanic23b6262016-04-07 15:57:22 -070071 protected DeviceService deviceService;
72
Ray Milkeyd84f89b2018-08-17 14:54:17 -070073 @Reference(cardinality = MANDATORY)
Madan Jampanic23b6262016-04-07 15:57:22 -070074 protected FlowRuleService flowRuleService;
75
Ray Milkeyd84f89b2018-08-17 14:54:17 -070076 @Reference(cardinality = MANDATORY)
Madan Jampanic23b6262016-04-07 15:57:22 -070077 protected CoreService coreService;
78
Ray Milkeyd84f89b2018-08-17 14:54:17 -070079 @Reference(cardinality = MANDATORY)
Madan Jampanic23b6262016-04-07 15:57:22 -070080 protected ComponentConfigService configService;
81
82 protected ApplicationId appId;
83
84 private static final int DEFAULT_BATCH_SIZE = 200;
85 private static final int DEFAULT_TOTAL_THREADS = 1;
86 private static final int DEFAULT_TOTAL_FLOWS = 100000;
87 private AtomicInteger pendingBatchCount;
88 private CountDownLatch installationLatch;
89 private CountDownLatch uninstallationLatch;
90 private Iterator<Device> devices;
91 private AtomicLong macIndex;
92
93 List<FlowRule> addedRules = Lists.newArrayList();
94
Ray Milkeyd84f89b2018-08-17 14:54:17 -070095 //@Property(name = "totalFlows", intValue = DEFAULT_TOTAL_FLOWS,
96 // label = "Total number of flows")
Madan Jampanic23b6262016-04-07 15:57:22 -070097 protected int totalFlows = DEFAULT_TOTAL_FLOWS;
98
Ray Milkeyd84f89b2018-08-17 14:54:17 -070099 //@Property(name = "batchSize", intValue = DEFAULT_BATCH_SIZE,
100 // label = "Number of flows per batch")
Madan Jampanic23b6262016-04-07 15:57:22 -0700101 protected int batchSize = DEFAULT_BATCH_SIZE;
102
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700103 //@Property(name = "totalThreads", intValue = DEFAULT_TOTAL_THREADS,
104 // label = "Number of installer threads")
Madan Jampanic23b6262016-04-07 15:57:22 -0700105 protected int totalThreads = DEFAULT_TOTAL_THREADS;
106
107 private ExecutorService installer;
108 private ExecutorService testRunner =
109 Executors.newSingleThreadExecutor(Tools.groupedThreads("app/flow-perf-test-runner", ""));
110
111 @Activate
112 public void activate(ComponentContext context) {
113 appId = coreService.registerApplication("org.onosproject.flowperf");
114 configService.registerProperties(getClass());
115 installer = Executors.newFixedThreadPool(totalThreads, Tools.groupedThreads("app/flow-perf-worker", "%d"));
116 testRunner.submit(this::runTest);
117 log.info("Started");
118 }
119
120 @Deactivate
121 public void deactivate(ComponentContext context) {
122 installer.shutdown();
123 testRunner.shutdown();
124 configService.unregisterProperties(getClass(), false);
125 log.info("Stopped.");
126 }
127
128 private void runTest() {
129 pendingBatchCount = new AtomicInteger(totalFlows / batchSize);
130 installationLatch = new CountDownLatch(totalFlows);
131 List<Device> deviceList = Lists.newArrayList();
132 deviceService.getAvailableDevices().forEach(deviceList::add);
133 devices = Iterables.cycle(deviceList).iterator();
134 log.info("Starting installation. Total flows: {}, Total threads: {}, "
135 + "Batch Size: {}", totalFlows, totalThreads, batchSize);
136
137 macIndex = new AtomicLong(0);
138 FlowRuleListener addMonitor = event -> {
139 if (event.type() == FlowRuleEvent.Type.RULE_ADDED) {
140 installationLatch.countDown();
141 }
142 };
143
144 flowRuleService.addListener(addMonitor);
145 long addStartTime = System.currentTimeMillis();
146 for (int i = 0; i < totalThreads; ++i) {
147 installer.submit(() -> {
148 while (pendingBatchCount.getAndDecrement() > 0) {
149 List<FlowRule> batch = nextBatch(batchSize);
150 addedRules.addAll(batch);
151 flowRuleService.applyFlowRules(batch.toArray(new FlowRule[]{}));
152 }
153 });
154 }
155
156 // Wait till all the flows are in ADDED state.
157 try {
158 installationLatch.await();
159 } catch (InterruptedException e) {
160 Thread.interrupted();
161 }
162 log.info("Time to install {} flows: {} ms", totalFlows, System.currentTimeMillis() - addStartTime);
163 flowRuleService.removeListener(addMonitor);
164
165
166 uninstallationLatch = new CountDownLatch(totalFlows);
167 FlowRuleListener removeListener = event -> {
168 if (event.type() == FlowRuleEvent.Type.RULE_REMOVED) {
169 uninstallationLatch.countDown();
170 }
171 };
172 AtomicInteger currentIndex = new AtomicInteger(0);
173 long removeStartTime = System.currentTimeMillis();
174 flowRuleService.addListener(removeListener);
175 // Uninstallation runs on a single thread.
176 installer.submit(() -> {
177 while (currentIndex.get() < addedRules.size()) {
178 List<FlowRule> removeBatch = addedRules.subList(currentIndex.get(),
179 Math.min(currentIndex.get() + batchSize, addedRules.size()));
180 currentIndex.addAndGet(removeBatch.size());
181 flowRuleService.removeFlowRules(removeBatch.toArray(new FlowRule[]{}));
182 }
183 });
184 try {
185 uninstallationLatch.await();
186 } catch (InterruptedException e) {
187 Thread.interrupted();
188 }
189 log.info("Time to uninstall {} flows: {} ms", totalFlows, System.currentTimeMillis() - removeStartTime);
190 flowRuleService.removeListener(removeListener);
191 }
192
193 private List<FlowRule> nextBatch(int size) {
194 List<FlowRule> rules = Lists.newArrayList();
195 for (int i = 0; i < size; ++i) {
196 Device device = devices.next();
197 long srcMac = macIndex.incrementAndGet();
198 long dstMac = srcMac + 1;
199 TrafficSelector selector = DefaultTrafficSelector.builder()
200 .matchEthSrc(MacAddress.valueOf(srcMac))
201 .matchEthDst(MacAddress.valueOf(dstMac))
202 .matchInPort(PortNumber.portNumber(2))
203 .build();
204 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
205 .add(Instructions.createOutput(PortNumber.portNumber(3))).build();
206 FlowRule rule = new DefaultFlowRule(device.id(),
207 selector,
208 treatment,
209 100,
210 appId,
211 50000,
212 true,
213 null);
214 rules.add(rule);
215 }
216 return rules;
217 }
218
219 @Modified
220 public void modified(ComponentContext context) {
221 if (context == null) {
222 totalFlows = DEFAULT_TOTAL_FLOWS;
223 batchSize = DEFAULT_BATCH_SIZE;
224 totalThreads = DEFAULT_TOTAL_THREADS;
225 return;
226 }
227
228 Dictionary properties = context.getProperties();
229
230 int newTotalFlows = totalFlows;
231 int newBatchSize = batchSize;
232 int newTotalThreads = totalThreads;
233 try {
234 String s = get(properties, "batchSize");
235 newTotalFlows = isNullOrEmpty(s)
236 ? totalFlows : Integer.parseInt(s.trim());
237
238 s = get(properties, "batchSize");
239 newBatchSize = isNullOrEmpty(s)
240 ? batchSize : Integer.parseInt(s.trim());
241
242 s = get(properties, "totalThreads");
243 newTotalThreads = isNullOrEmpty(s)
244 ? totalThreads : Integer.parseInt(s.trim());
245
246 } catch (NumberFormatException | ClassCastException e) {
247 return;
248 }
249
250 boolean modified = newTotalFlows != totalFlows || newTotalThreads != totalThreads ||
251 newBatchSize != batchSize;
252
253 // If nothing has changed, simply return.
254 if (!modified) {
255 return;
256 }
257
258 totalFlows = newTotalFlows;
259 batchSize = newBatchSize;
260 if (totalThreads != newTotalThreads) {
261 totalThreads = newTotalThreads;
262 installer.shutdown();
263 installer = Executors.newFixedThreadPool(totalThreads, Tools.groupedThreads("flow-perf-worker", "%d"));
264 }
265 }
266}