blob: 61c4b42cea47d0736c39417ac3efe0db5a65cc39 [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();
Ray Milkey47f09c52019-02-08 18:51:34 -0800214 FlowRule rule = DefaultFlowRule.builder()
215 .forDevice(device.id())
216 .withSelector(selector)
217 .withTreatment(treatment)
218 .withPriority(100)
219 .fromApp(appId)
220 .withHardTimeout(50000)
221 .makePermanent()
222 .build();
223
Madan Jampanic23b6262016-04-07 15:57:22 -0700224 rules.add(rule);
225 }
226 return rules;
227 }
228
229 @Modified
230 public void modified(ComponentContext context) {
231 if (context == null) {
Ray Milkey88dd7e22018-10-24 10:04:03 -0700232 totalFlows = TOTAL_FLOWS_DEFAULT;
233 batchSize = BATCH_SIZE_DEFAULT;
234 totalThreads = TOTAL_THREADS_DEFAULT;
Madan Jampanic23b6262016-04-07 15:57:22 -0700235 return;
236 }
237
238 Dictionary properties = context.getProperties();
239
240 int newTotalFlows = totalFlows;
241 int newBatchSize = batchSize;
242 int newTotalThreads = totalThreads;
243 try {
Ray Milkey88dd7e22018-10-24 10:04:03 -0700244 String s = get(properties, TOTAL_FLOWS);
Madan Jampanic23b6262016-04-07 15:57:22 -0700245 newTotalFlows = isNullOrEmpty(s)
246 ? totalFlows : Integer.parseInt(s.trim());
247
Ray Milkey88dd7e22018-10-24 10:04:03 -0700248 s = get(properties, BATCH_SIZE);
Madan Jampanic23b6262016-04-07 15:57:22 -0700249 newBatchSize = isNullOrEmpty(s)
250 ? batchSize : Integer.parseInt(s.trim());
251
Ray Milkey88dd7e22018-10-24 10:04:03 -0700252 s = get(properties, TOTAL_THREADS);
Madan Jampanic23b6262016-04-07 15:57:22 -0700253 newTotalThreads = isNullOrEmpty(s)
254 ? totalThreads : Integer.parseInt(s.trim());
255
256 } catch (NumberFormatException | ClassCastException e) {
257 return;
258 }
259
260 boolean modified = newTotalFlows != totalFlows || newTotalThreads != totalThreads ||
261 newBatchSize != batchSize;
262
263 // If nothing has changed, simply return.
264 if (!modified) {
265 return;
266 }
267
268 totalFlows = newTotalFlows;
269 batchSize = newBatchSize;
270 if (totalThreads != newTotalThreads) {
271 totalThreads = newTotalThreads;
272 installer.shutdown();
273 installer = Executors.newFixedThreadPool(totalThreads, Tools.groupedThreads("flow-perf-worker", "%d"));
274 }
275 }
Ray Milkey88dd7e22018-10-24 10:04:03 -0700276}