blob: 3e9687ab143c6ca0dacf5faa40f2ecfd164a5e8d [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;
47import java.util.Iterator;
48import java.util.List;
49import java.util.Map;
50import java.util.Set;
51import java.util.Timer;
52import java.util.TimerTask;
53import java.util.concurrent.ExecutorService;
54import java.util.concurrent.Executors;
55import java.util.concurrent.TimeUnit;
56
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080057import static com.google.common.base.Preconditions.checkState;
58import static org.apache.felix.scr.annotations.ReferenceCardinality.MANDATORY_UNARY;
Brian O'Connor6ccba962015-02-17 18:16:02 -080059import static org.onlab.util.Tools.delay;
60import static org.onlab.util.Tools.groupedThreads;
61import static org.slf4j.LoggerFactory.getLogger;
62
63/**
64 * Application to set up demos.
65 */
66@Component(immediate = true)
67public class IntentPerfInstaller {
68
69 private final Logger log = getLogger(getClass());
70
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080071 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -080072 protected CoreService coreService;
73
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080074 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -080075 protected IntentService intentService;
76
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080077 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -080078 protected ClusterService clusterService;
79
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080080 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -080081 protected DeviceService deviceService;
82
83 private ExecutorService worker;
84 private ApplicationId appId;
85 private Listener listener;
86 private Set<Intent> intents;
87 private Set<Intent> submitted;
88 private Set<Intent> withdrawn;
89 private boolean stopped;
90
91 private static final long REPORT_PERIOD = 5000L; //ms
92 private Timer reportTimer;
93
94 //FIXME make this configurable
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080095 private static final int NUM_KEYS = 10_000;
Brian O'Connor6ccba962015-02-17 18:16:02 -080096
97 @Activate
98 public void activate() {
99 String nodeId = clusterService.getLocalNode().ip().toString();
100 appId = coreService.registerApplication("org.onosproject.intentperf."
101 + nodeId);
102 intents = Sets.newHashSet();
103 submitted = Sets.newHashSet();
104 withdrawn = Sets.newHashSet();
105
106 worker = Executors.newFixedThreadPool(1, groupedThreads("onos/intent-perf", "worker"));
107 log.info("Started with Application ID {}", appId.id());
108 start(); //FIXME
109 }
110
111 @Deactivate
112 public void deactivate() {
113 stop();
114 log.info("Stopped");
115 }
116
117 public void start() {
118 // perhaps we want to prime before listening...
119 // we will need to discard the first few results for priming and warmup
120 listener = new Listener();
121 intentService.addListener(listener);
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800122 reportTimer = new Timer("onos-intent-perf-reporter");
Brian O'Connor6ccba962015-02-17 18:16:02 -0800123 reportTimer.scheduleAtFixedRate(new TimerTask() {
124 @Override
125 public void run() {
126 listener.report();
127 }
128 }, REPORT_PERIOD, REPORT_PERIOD);
129
130 stopped = false;
131 worker.submit(() -> {
132 delay(2000);
133 createIntents(NUM_KEYS, 2); //FIXME
134 prime();
135 while (!stopped) {
136 cycle();
137 // TODO delay if required
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800138 delay(600);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800139 }
140 });
141
142 }
143
144 public void stop() {
145 if (listener != null) {
146 reportTimer.cancel();
147 intentService.removeListener(listener);
148 listener = null;
149 reportTimer = null;
150 }
151 stopped = true;
152 try {
153 worker.awaitTermination(5, TimeUnit.SECONDS);
154 } catch (InterruptedException e) {
155 log.warn("Failed to stop worker.");
156 }
157 }
158
159
160 private void cycle() {
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800161 long start = System.currentTimeMillis();
Brian O'Connor6ccba962015-02-17 18:16:02 -0800162 subset(submitted).forEach(this::withdraw);
163 subset(withdrawn).forEach(this::submit);
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800164 long delta = System.currentTimeMillis() - start;
165 if (delta > 1000 || delta < 0) {
166 log.warn("Cycle took {} ms", delta);
167 }
Brian O'Connor6ccba962015-02-17 18:16:02 -0800168 }
169
170 private Iterable<Intent> subset(Set<Intent> intents) {
171 List<Intent> subset = Lists.newArrayList(intents);
172 Collections.shuffle(subset);
173 return subset.subList(0, subset.size() / 2);
174 }
175
176 private void submit(Intent intent) {
177 intentService.submit(intent);
178 submitted.add(intent);
179 withdrawn.remove(intent); //TODO could check result here...
180 }
181
182 private void withdraw(Intent intent) {
183 intentService.withdraw(intent);
184 withdrawn.add(intent);
185 submitted.remove(intent); //TODO could check result here...
186 }
187
188 private void createIntents(int numberOfKeys, int pathLength) {
189
190 Iterator<Device> deviceItr = deviceService.getAvailableDevices().iterator();
191
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800192 Device ingressDevice = null;
193 while (deviceItr.hasNext()) {
194 Device device = deviceItr.next();
195 if (deviceService.getRole(device.id()) == MastershipRole.MASTER) {
196 ingressDevice = device;
197 break;
198 }
Brian O'Connor6ccba962015-02-17 18:16:02 -0800199 }
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800200 checkState(ingressDevice != null, "There are no local devices");
Brian O'Connor6ccba962015-02-17 18:16:02 -0800201
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800202 for (int local = 0, i = 0; local < numberOfKeys; i++) {
Brian O'Connor6ccba962015-02-17 18:16:02 -0800203 Key key = Key.of(i, appId);
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800204 if (!intentService.isLocal(key)) {
205 continue;
206 }
Brian O'Connor6ccba962015-02-17 18:16:02 -0800207 TrafficSelector selector = DefaultTrafficSelector.builder().build();
208
209 TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
210 //FIXME
211 ConnectPoint ingress = new ConnectPoint(ingressDevice.id(), PortNumber.portNumber(1));
212 ConnectPoint egress = new ConnectPoint(ingressDevice.id(), PortNumber.portNumber(2));
213
214 Intent intent = new PointToPointIntent(appId, key,
215 selector, treatment,
216 ingress, egress,
217 Collections.emptyList());
218 intents.add(intent);
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800219 local++;
220 if (i % 1000 == 0) {
221 log.info("Building intents... {} ({})", local, i);
222 }
Brian O'Connor6ccba962015-02-17 18:16:02 -0800223 }
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800224 log.info("Created {} intents", numberOfKeys);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800225 }
226
227 private void prime() {
228 int i = 0;
229 withdrawn.addAll(intents);
230 for (Intent intent : intents) {
231 submit(intent);
232 // only submit half of the intents to start
233 if (i++ >= intents.size() / 2) {
234 break;
235 }
236 }
237 }
238
239 class Listener implements IntentListener {
240
241
242 private Map<IntentEvent.Type, Counter> counters;
243
244 public Listener() {
245 counters = initCounters();
246
247 }
248
249 private Map<IntentEvent.Type, Counter> initCounters() {
250 Map<IntentEvent.Type, Counter> map = Maps.newHashMap();
251 for (IntentEvent.Type type : IntentEvent.Type.values()) {
252 map.put(type, new Counter());
253 }
254 return map;
255 }
256
257 @Override
258 public void event(IntentEvent event) {
259 if (event.subject().appId().equals(appId)) {
260 counters.get(event.type()).add(1);
261 }
262 }
263
264 public void report() {
265 StringBuilder stringBuilder = new StringBuilder();
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800266 double total = counters.get(IntentEvent.Type.INSTALLED).throughput() +
267 counters.get(IntentEvent.Type.WITHDRAWN).throughput();
Brian O'Connor6ccba962015-02-17 18:16:02 -0800268 for (IntentEvent.Type type : IntentEvent.Type.values()) {
269 stringBuilder.append(printCounter(type)).append("; ");
270 }
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800271 stringBuilder.append(String.format("TOTAL=%.2f", total));
Brian O'Connor6ccba962015-02-17 18:16:02 -0800272 log.info("Intent Throughput:\n{}", stringBuilder);
273 }
274
275 private String printCounter(IntentEvent.Type event) {
276 Counter counter = counters.get(event);
277 String result = String.format("%s=%.2f", event, counter.throughput());
278 counter.reset();
279 return result;
280 }
281 }
282}