blob: f7c48c93901297ec5d2880c0a9131d200fea27fb [file] [log] [blame]
Brian O'Connor6ccba962015-02-17 18:16:02 -08001/*
2 * Copyright 2015 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 */
16package org.onosproject.intentperf;
17
18import com.google.common.collect.Lists;
19import com.google.common.collect.Maps;
20import com.google.common.collect.Sets;
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
Brian O'Connor6ccba962015-02-17 18:16:02 -080025import org.onlab.util.Counter;
26import org.onosproject.cluster.ClusterService;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.net.ConnectPoint;
30import org.onosproject.net.Device;
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080031import org.onosproject.net.MastershipRole;
Brian O'Connor6ccba962015-02-17 18:16:02 -080032import org.onosproject.net.PortNumber;
33import org.onosproject.net.device.DeviceService;
34import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.flow.TrafficTreatment;
38import org.onosproject.net.intent.Intent;
39import org.onosproject.net.intent.IntentEvent;
40import org.onosproject.net.intent.IntentListener;
41import org.onosproject.net.intent.IntentService;
42import org.onosproject.net.intent.Key;
43import org.onosproject.net.intent.PointToPointIntent;
44import org.slf4j.Logger;
45
46import java.util.Collections;
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080047import java.util.HashSet;
Brian O'Connor6ccba962015-02-17 18:16:02 -080048import java.util.Iterator;
49import java.util.List;
50import java.util.Map;
51import java.util.Set;
52import java.util.Timer;
53import java.util.TimerTask;
54import java.util.concurrent.ExecutorService;
55import java.util.concurrent.Executors;
56import java.util.concurrent.TimeUnit;
57
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080058import static com.google.common.base.Preconditions.checkState;
Thomas Vachuska0249b532015-02-20 16:46:18 -080059import static java.lang.String.format;
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080060import static java.lang.System.currentTimeMillis;
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080061import static org.apache.felix.scr.annotations.ReferenceCardinality.MANDATORY_UNARY;
Brian O'Connor6ccba962015-02-17 18:16:02 -080062import static org.onlab.util.Tools.delay;
63import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska0249b532015-02-20 16:46:18 -080064import static org.onosproject.net.intent.IntentEvent.Type.INSTALLED;
65import static org.onosproject.net.intent.IntentEvent.Type.WITHDRAWN;
Brian O'Connor6ccba962015-02-17 18:16:02 -080066import static org.slf4j.LoggerFactory.getLogger;
67
68/**
69 * Application to set up demos.
70 */
71@Component(immediate = true)
72public class IntentPerfInstaller {
73
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080074 //FIXME make this configurable
75 private static final int NUM_WORKERS = 1;
76 private static final int NUM_KEYS = 10_000;
77
78 public static final int START_DELAY = 5_000; // ms
79 private static final int REPORT_PERIOD = 5_000; //ms
80
Brian O'Connor6ccba962015-02-17 18:16:02 -080081 private final Logger log = getLogger(getClass());
82
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080083 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -080084 protected CoreService coreService;
85
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080086 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -080087 protected IntentService intentService;
88
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080089 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -080090 protected ClusterService clusterService;
91
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080092 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -080093 protected DeviceService deviceService;
94
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080095 private ExecutorService workers;
Brian O'Connor6ccba962015-02-17 18:16:02 -080096 private ApplicationId appId;
97 private Listener listener;
Brian O'Connor6ccba962015-02-17 18:16:02 -080098 private boolean stopped;
99
Brian O'Connor6ccba962015-02-17 18:16:02 -0800100 private Timer reportTimer;
101
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800102 private int lastKey = 0;
Brian O'Connor6ccba962015-02-17 18:16:02 -0800103
104 @Activate
105 public void activate() {
106 String nodeId = clusterService.getLocalNode().ip().toString();
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800107 appId = coreService.registerApplication("org.onosproject.intentperf." + nodeId);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800108
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800109 reportTimer = new Timer("onos-intent-perf-reporter");
110 workers = Executors.newFixedThreadPool(NUM_WORKERS, groupedThreads("onos/intent-perf", "worker-%d"));
Brian O'Connor6ccba962015-02-17 18:16:02 -0800111 log.info("Started with Application ID {}", appId.id());
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800112
113 // Schedule delayed start
114 reportTimer.schedule(new TimerTask() {
115 @Override
116 public void run() {
117 start();
118 }
119 }, START_DELAY);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800120 }
121
122 @Deactivate
123 public void deactivate() {
124 stop();
125 log.info("Stopped");
126 }
127
128 public void start() {
129 // perhaps we want to prime before listening...
130 // we will need to discard the first few results for priming and warmup
131 listener = new Listener();
132 intentService.addListener(listener);
Thomas Vachuska0249b532015-02-20 16:46:18 -0800133
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800134 // Schedule reporter task on report period boundary
Brian O'Connor6ccba962015-02-17 18:16:02 -0800135 reportTimer.scheduleAtFixedRate(new TimerTask() {
136 @Override
137 public void run() {
138 listener.report();
139 }
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800140 }, REPORT_PERIOD - currentTimeMillis() % REPORT_PERIOD, REPORT_PERIOD);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800141
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800142 // Submit workers
Brian O'Connor6ccba962015-02-17 18:16:02 -0800143 stopped = false;
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800144 Set<Device> devices = new HashSet<>();
145 for (int i = 0; i < NUM_WORKERS; i++) {
146 workers.submit(new Submitter(createIntents(NUM_KEYS, 2, lastKey, devices)));
147 }
Brian O'Connor6ccba962015-02-17 18:16:02 -0800148 }
149
150 public void stop() {
151 if (listener != null) {
152 reportTimer.cancel();
153 intentService.removeListener(listener);
154 listener = null;
155 reportTimer = null;
156 }
157 stopped = true;
158 try {
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800159 workers.awaitTermination(5, TimeUnit.SECONDS);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800160 } catch (InterruptedException e) {
161 log.warn("Failed to stop worker.");
162 }
163 }
164
Brian O'Connor6ccba962015-02-17 18:16:02 -0800165 private Iterable<Intent> subset(Set<Intent> intents) {
166 List<Intent> subset = Lists.newArrayList(intents);
167 Collections.shuffle(subset);
168 return subset.subList(0, subset.size() / 2);
169 }
170
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800171 /**
172 * Creates a specified number of intents for testing purposes.
173 *
174 * @param numberOfKeys number of intents
175 * @param pathLength path depth
176 * @param firstKey first key to attempt
177 * @param devices set of previously utilized devices @return set of intents
178 */
179 private Set<Intent> createIntents(int numberOfKeys, int pathLength,
180 int firstKey, Set<Device> devices) {
Brian O'Connor6ccba962015-02-17 18:16:02 -0800181 Iterator<Device> deviceItr = deviceService.getAvailableDevices().iterator();
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800182 Set<Intent> result = new HashSet<>();
Brian O'Connor6ccba962015-02-17 18:16:02 -0800183
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800184 Device ingressDevice = null;
185 while (deviceItr.hasNext()) {
186 Device device = deviceItr.next();
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800187 if (deviceService.getRole(device.id()) == MastershipRole.MASTER &&
188 !devices.contains(device)) {
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800189 ingressDevice = device;
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800190 devices.add(device);
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800191 break;
192 }
Brian O'Connor6ccba962015-02-17 18:16:02 -0800193 }
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800194 checkState(ingressDevice != null, "There are no local devices");
Brian O'Connor6ccba962015-02-17 18:16:02 -0800195
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800196 for (int count = 0, k = firstKey; count < numberOfKeys; k++) {
197 Key key = Key.of(k, appId);
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800198 if (!intentService.isLocal(key)) {
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800199 // Bail if the key is not local
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800200 continue;
201 }
Brian O'Connor6ccba962015-02-17 18:16:02 -0800202
Brian O'Connor6ccba962015-02-17 18:16:02 -0800203 //FIXME
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800204 TrafficSelector selector = DefaultTrafficSelector.builder().build();
205 TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
Brian O'Connor6ccba962015-02-17 18:16:02 -0800206 ConnectPoint ingress = new ConnectPoint(ingressDevice.id(), PortNumber.portNumber(1));
207 ConnectPoint egress = new ConnectPoint(ingressDevice.id(), PortNumber.portNumber(2));
208
209 Intent intent = new PointToPointIntent(appId, key,
210 selector, treatment,
211 ingress, egress,
212 Collections.emptyList());
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800213 result.add(intent);
214
215 // Bump up the counter and remember this as the last key used.
216 count++;
217 lastKey = k;
218 if (lastKey % 1000 == 0) {
219 log.info("Building intents... {} ({})", count, lastKey);
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800220 }
Brian O'Connor6ccba962015-02-17 18:16:02 -0800221 }
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800222 log.info("Created {} intents", numberOfKeys);
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800223 return result;
Brian O'Connor6ccba962015-02-17 18:16:02 -0800224 }
225
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800226 // Submits intent operations.
227 final class Submitter implements Runnable {
228
229 private Set<Intent> intents = Sets.newHashSet();
230 private Set<Intent> submitted = Sets.newHashSet();
231 private Set<Intent> withdrawn = Sets.newHashSet();
232
233 private Submitter(Set<Intent> intents) {
234 this.intents = intents;
235 }
236
237 @Override
238 public void run() {
239 delay(2000); // take a breath to start
240 prime();
241 while (!stopped) {
242 cycle();
243 delay(800); // take a breath
244 }
245 }
246
247 // Submits the specified intent.
248 private void submit(Intent intent) {
249 intentService.submit(intent);
250 submitted.add(intent);
251 withdrawn.remove(intent); //TODO could check result here...
252 }
253
254 // Withdraws the specified intent.
255 private void withdraw(Intent intent) {
256 intentService.withdraw(intent);
257 withdrawn.add(intent);
258 submitted.remove(intent); //TODO could check result here...
259 }
260
261 // Primes the cycle.
262 private void prime() {
263 int i = 0;
264 withdrawn.addAll(intents);
265 for (Intent intent : intents) {
266 submit(intent);
267 // only submit half of the intents to start
268 if (i++ >= intents.size() / 2) {
269 break;
270 }
271 }
272 }
273
274 // Runs a single operation cycle.
275 private void cycle() {
276 long start = currentTimeMillis();
277 subset(submitted).forEach(this::withdraw);
278 subset(withdrawn).forEach(this::submit);
279 long delta = currentTimeMillis() - start;
280 if (delta > 5000 || delta < 0) {
281 log.warn("Cycle took {} ms", delta);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800282 }
283 }
284 }
285
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800286
287 // Event listener to monitor throughput.
288 final class Listener implements IntentListener {
Brian O'Connor6ccba962015-02-17 18:16:02 -0800289
Thomas Vachuska0249b532015-02-20 16:46:18 -0800290 private final Map<IntentEvent.Type, Counter> counters;
291 private final Counter runningTotal = new Counter();
Brian O'Connor6ccba962015-02-17 18:16:02 -0800292
293 public Listener() {
294 counters = initCounters();
Brian O'Connor6ccba962015-02-17 18:16:02 -0800295 }
296
297 private Map<IntentEvent.Type, Counter> initCounters() {
298 Map<IntentEvent.Type, Counter> map = Maps.newHashMap();
299 for (IntentEvent.Type type : IntentEvent.Type.values()) {
300 map.put(type, new Counter());
301 }
302 return map;
303 }
304
305 @Override
306 public void event(IntentEvent event) {
307 if (event.subject().appId().equals(appId)) {
308 counters.get(event.type()).add(1);
309 }
310 }
311
312 public void report() {
313 StringBuilder stringBuilder = new StringBuilder();
Thomas Vachuska0249b532015-02-20 16:46:18 -0800314 Counter installed = counters.get(INSTALLED);
315 Counter withdrawn = counters.get(WITHDRAWN);
316 double current = installed.throughput() + withdrawn.throughput();
317 runningTotal.add(installed.total() + withdrawn.total());
Brian O'Connor6ccba962015-02-17 18:16:02 -0800318 for (IntentEvent.Type type : IntentEvent.Type.values()) {
319 stringBuilder.append(printCounter(type)).append("; ");
320 }
Thomas Vachuska0249b532015-02-20 16:46:18 -0800321 log.info("Throughput: OVERALL={}; CURRENT={}; {}",
322 format("%.2f", runningTotal.throughput()),
323 format("%.2f", current), stringBuilder);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800324 }
325
326 private String printCounter(IntentEvent.Type event) {
327 Counter counter = counters.get(event);
Thomas Vachuska0249b532015-02-20 16:46:18 -0800328 String result = format("%s=%.2f", event, counter.throughput());
Brian O'Connor6ccba962015-02-17 18:16:02 -0800329 counter.reset();
330 return result;
331 }
332 }
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800333
Brian O'Connor6ccba962015-02-17 18:16:02 -0800334}