blob: 9a6e3a798cd1b68bbeb3dafc98f74dec19833244 [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;
Ray Milkey88dd7e22018-10-24 10:04:03 -070057import static org.onosproject.flowperf.OsgiPropertyConstants.BATCH_SIZE;
58import static org.onosproject.flowperf.OsgiPropertyConstants.BATCH_SIZE_DEFAULT;
59import static org.onosproject.flowperf.OsgiPropertyConstants.TOTAL_FLOWS;
60import static org.onosproject.flowperf.OsgiPropertyConstants.TOTAL_FLOWS_DEFAULT;
61import static org.onosproject.flowperf.OsgiPropertyConstants.TOTAL_THREADS;
62import static org.onosproject.flowperf.OsgiPropertyConstants.TOTAL_THREADS_DEFAULT;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070063import static org.osgi.service.component.annotations.ReferenceCardinality.MANDATORY;
64import static org.slf4j.LoggerFactory.getLogger;
Madan Jampanic23b6262016-04-07 15:57:22 -070065
66/**
67 * Application for measuring flow installation performance.
68 * <p>
69 * This application installs a bunch of flows, validates that all those flows have
70 * been successfully added and immediately proceeds to remove all the added flows.
71 */
Ray Milkey88dd7e22018-10-24 10:04:03 -070072@Component(
73 immediate = true,
74 service = FlowPerfApp.class,
75 property = {
76 TOTAL_FLOWS + ":Integer=" + TOTAL_FLOWS_DEFAULT,
77 BATCH_SIZE + ":Integer=" + BATCH_SIZE_DEFAULT,
78 TOTAL_THREADS + ":Integer=" + TOTAL_THREADS_DEFAULT
79 }
80)
Madan Jampanic23b6262016-04-07 15:57:22 -070081public class FlowPerfApp {
82 private final Logger log = getLogger(getClass());
83
Ray Milkeyd84f89b2018-08-17 14:54:17 -070084 @Reference(cardinality = MANDATORY)
Madan Jampanic23b6262016-04-07 15:57:22 -070085 protected DeviceService deviceService;
86
Ray Milkeyd84f89b2018-08-17 14:54:17 -070087 @Reference(cardinality = MANDATORY)
Madan Jampanic23b6262016-04-07 15:57:22 -070088 protected FlowRuleService flowRuleService;
89
Ray Milkeyd84f89b2018-08-17 14:54:17 -070090 @Reference(cardinality = MANDATORY)
Madan Jampanic23b6262016-04-07 15:57:22 -070091 protected CoreService coreService;
92
Ray Milkeyd84f89b2018-08-17 14:54:17 -070093 @Reference(cardinality = MANDATORY)
Madan Jampanic23b6262016-04-07 15:57:22 -070094 protected ComponentConfigService configService;
95
96 protected ApplicationId appId;
97
Madan Jampanic23b6262016-04-07 15:57:22 -070098 private AtomicInteger pendingBatchCount;
99 private CountDownLatch installationLatch;
100 private CountDownLatch uninstallationLatch;
101 private Iterator<Device> devices;
102 private AtomicLong macIndex;
103
104 List<FlowRule> addedRules = Lists.newArrayList();
105
Ray Milkey88dd7e22018-10-24 10:04:03 -0700106 /** Total number of flows. */
107 private int totalFlows = TOTAL_FLOWS_DEFAULT;
Madan Jampanic23b6262016-04-07 15:57:22 -0700108
Ray Milkey88dd7e22018-10-24 10:04:03 -0700109 /** Number of flows per batch. */
110 private int batchSize = BATCH_SIZE_DEFAULT;
Madan Jampanic23b6262016-04-07 15:57:22 -0700111
Ray Milkey88dd7e22018-10-24 10:04:03 -0700112 /** Number of installer threads. */
113 private int totalThreads = TOTAL_THREADS_DEFAULT;
Madan Jampanic23b6262016-04-07 15:57:22 -0700114
115 private ExecutorService installer;
116 private ExecutorService testRunner =
117 Executors.newSingleThreadExecutor(Tools.groupedThreads("app/flow-perf-test-runner", ""));
118
119 @Activate
120 public void activate(ComponentContext context) {
121 appId = coreService.registerApplication("org.onosproject.flowperf");
122 configService.registerProperties(getClass());
123 installer = Executors.newFixedThreadPool(totalThreads, Tools.groupedThreads("app/flow-perf-worker", "%d"));
124 testRunner.submit(this::runTest);
125 log.info("Started");
126 }
127
128 @Deactivate
129 public void deactivate(ComponentContext context) {
130 installer.shutdown();
131 testRunner.shutdown();
132 configService.unregisterProperties(getClass(), false);
133 log.info("Stopped.");
134 }
135
136 private void runTest() {
137 pendingBatchCount = new AtomicInteger(totalFlows / batchSize);
138 installationLatch = new CountDownLatch(totalFlows);
139 List<Device> deviceList = Lists.newArrayList();
140 deviceService.getAvailableDevices().forEach(deviceList::add);
141 devices = Iterables.cycle(deviceList).iterator();
142 log.info("Starting installation. Total flows: {}, Total threads: {}, "
143 + "Batch Size: {}", totalFlows, totalThreads, batchSize);
144
145 macIndex = new AtomicLong(0);
146 FlowRuleListener addMonitor = event -> {
147 if (event.type() == FlowRuleEvent.Type.RULE_ADDED) {
148 installationLatch.countDown();
149 }
150 };
151
152 flowRuleService.addListener(addMonitor);
153 long addStartTime = System.currentTimeMillis();
154 for (int i = 0; i < totalThreads; ++i) {
155 installer.submit(() -> {
156 while (pendingBatchCount.getAndDecrement() > 0) {
157 List<FlowRule> batch = nextBatch(batchSize);
158 addedRules.addAll(batch);
159 flowRuleService.applyFlowRules(batch.toArray(new FlowRule[]{}));
160 }
161 });
162 }
163
164 // Wait till all the flows are in ADDED state.
165 try {
166 installationLatch.await();
167 } catch (InterruptedException e) {
168 Thread.interrupted();
169 }
170 log.info("Time to install {} flows: {} ms", totalFlows, System.currentTimeMillis() - addStartTime);
171 flowRuleService.removeListener(addMonitor);
172
173
174 uninstallationLatch = new CountDownLatch(totalFlows);
175 FlowRuleListener removeListener = event -> {
176 if (event.type() == FlowRuleEvent.Type.RULE_REMOVED) {
177 uninstallationLatch.countDown();
178 }
179 };
180 AtomicInteger currentIndex = new AtomicInteger(0);
181 long removeStartTime = System.currentTimeMillis();
182 flowRuleService.addListener(removeListener);
183 // Uninstallation runs on a single thread.
184 installer.submit(() -> {
185 while (currentIndex.get() < addedRules.size()) {
186 List<FlowRule> removeBatch = addedRules.subList(currentIndex.get(),
187 Math.min(currentIndex.get() + batchSize, addedRules.size()));
188 currentIndex.addAndGet(removeBatch.size());
189 flowRuleService.removeFlowRules(removeBatch.toArray(new FlowRule[]{}));
190 }
191 });
192 try {
193 uninstallationLatch.await();
194 } catch (InterruptedException e) {
195 Thread.interrupted();
196 }
197 log.info("Time to uninstall {} flows: {} ms", totalFlows, System.currentTimeMillis() - removeStartTime);
198 flowRuleService.removeListener(removeListener);
199 }
200
201 private List<FlowRule> nextBatch(int size) {
202 List<FlowRule> rules = Lists.newArrayList();
203 for (int i = 0; i < size; ++i) {
204 Device device = devices.next();
205 long srcMac = macIndex.incrementAndGet();
206 long dstMac = srcMac + 1;
207 TrafficSelector selector = DefaultTrafficSelector.builder()
208 .matchEthSrc(MacAddress.valueOf(srcMac))
209 .matchEthDst(MacAddress.valueOf(dstMac))
210 .matchInPort(PortNumber.portNumber(2))
211 .build();
212 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
213 .add(Instructions.createOutput(PortNumber.portNumber(3))).build();
214 FlowRule rule = new DefaultFlowRule(device.id(),
215 selector,
216 treatment,
217 100,
218 appId,
219 50000,
220 true,
221 null);
222 rules.add(rule);
223 }
224 return rules;
225 }
226
227 @Modified
228 public void modified(ComponentContext context) {
229 if (context == null) {
Ray Milkey88dd7e22018-10-24 10:04:03 -0700230 totalFlows = TOTAL_FLOWS_DEFAULT;
231 batchSize = BATCH_SIZE_DEFAULT;
232 totalThreads = TOTAL_THREADS_DEFAULT;
Madan Jampanic23b6262016-04-07 15:57:22 -0700233 return;
234 }
235
236 Dictionary properties = context.getProperties();
237
238 int newTotalFlows = totalFlows;
239 int newBatchSize = batchSize;
240 int newTotalThreads = totalThreads;
241 try {
Ray Milkey88dd7e22018-10-24 10:04:03 -0700242 String s = get(properties, TOTAL_FLOWS);
Madan Jampanic23b6262016-04-07 15:57:22 -0700243 newTotalFlows = isNullOrEmpty(s)
244 ? totalFlows : Integer.parseInt(s.trim());
245
Ray Milkey88dd7e22018-10-24 10:04:03 -0700246 s = get(properties, BATCH_SIZE);
Madan Jampanic23b6262016-04-07 15:57:22 -0700247 newBatchSize = isNullOrEmpty(s)
248 ? batchSize : Integer.parseInt(s.trim());
249
Ray Milkey88dd7e22018-10-24 10:04:03 -0700250 s = get(properties, TOTAL_THREADS);
Madan Jampanic23b6262016-04-07 15:57:22 -0700251 newTotalThreads = isNullOrEmpty(s)
252 ? totalThreads : Integer.parseInt(s.trim());
253
254 } catch (NumberFormatException | ClassCastException e) {
255 return;
256 }
257
258 boolean modified = newTotalFlows != totalFlows || newTotalThreads != totalThreads ||
259 newBatchSize != batchSize;
260
261 // If nothing has changed, simply return.
262 if (!modified) {
263 return;
264 }
265
266 totalFlows = newTotalFlows;
267 batchSize = newBatchSize;
268 if (totalThreads != newTotalThreads) {
269 totalThreads = newTotalThreads;
270 installer.shutdown();
271 installer = Executors.newFixedThreadPool(totalThreads, Tools.groupedThreads("flow-perf-worker", "%d"));
272 }
273 }
Ray Milkey88dd7e22018-10-24 10:04:03 -0700274}