blob: 19cc06d32ece547f587743b22683e447c7487392 [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;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.onlab.util.Counter;
27import org.onosproject.cluster.ClusterService;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.net.ConnectPoint;
31import org.onosproject.net.Device;
32import 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
57import static org.onlab.util.Tools.delay;
58import static org.onlab.util.Tools.groupedThreads;
59import static org.slf4j.LoggerFactory.getLogger;
60
61/**
62 * Application to set up demos.
63 */
64@Component(immediate = true)
65public class IntentPerfInstaller {
66
67 private final Logger log = getLogger(getClass());
68
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected CoreService coreService;
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected IntentService intentService;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected ClusterService clusterService;
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected DeviceService deviceService;
80
81 private ExecutorService worker;
82 private ApplicationId appId;
83 private Listener listener;
84 private Set<Intent> intents;
85 private Set<Intent> submitted;
86 private Set<Intent> withdrawn;
87 private boolean stopped;
88
89 private static final long REPORT_PERIOD = 5000L; //ms
90 private Timer reportTimer;
91
92 //FIXME make this configurable
93 private static final int NUM_KEYS = 10000;
94
95 @Activate
96 public void activate() {
97 String nodeId = clusterService.getLocalNode().ip().toString();
98 appId = coreService.registerApplication("org.onosproject.intentperf."
99 + nodeId);
100 intents = Sets.newHashSet();
101 submitted = Sets.newHashSet();
102 withdrawn = Sets.newHashSet();
103
104 worker = Executors.newFixedThreadPool(1, groupedThreads("onos/intent-perf", "worker"));
105 log.info("Started with Application ID {}", appId.id());
106 start(); //FIXME
107 }
108
109 @Deactivate
110 public void deactivate() {
111 stop();
112 log.info("Stopped");
113 }
114
115 public void start() {
116 // perhaps we want to prime before listening...
117 // we will need to discard the first few results for priming and warmup
118 listener = new Listener();
119 intentService.addListener(listener);
120 reportTimer = new Timer("intent-perf-reporter");
121 reportTimer.scheduleAtFixedRate(new TimerTask() {
122 @Override
123 public void run() {
124 listener.report();
125 }
126 }, REPORT_PERIOD, REPORT_PERIOD);
127
128 stopped = false;
129 worker.submit(() -> {
130 delay(2000);
131 createIntents(NUM_KEYS, 2); //FIXME
132 prime();
133 while (!stopped) {
134 cycle();
135 // TODO delay if required
136 }
137 });
138
139 }
140
141 public void stop() {
142 if (listener != null) {
143 reportTimer.cancel();
144 intentService.removeListener(listener);
145 listener = null;
146 reportTimer = null;
147 }
148 stopped = true;
149 try {
150 worker.awaitTermination(5, TimeUnit.SECONDS);
151 } catch (InterruptedException e) {
152 log.warn("Failed to stop worker.");
153 }
154 }
155
156
157 private void cycle() {
158 subset(submitted).forEach(this::withdraw);
159 subset(withdrawn).forEach(this::submit);
160 }
161
162 private Iterable<Intent> subset(Set<Intent> intents) {
163 List<Intent> subset = Lists.newArrayList(intents);
164 Collections.shuffle(subset);
165 return subset.subList(0, subset.size() / 2);
166 }
167
168 private void submit(Intent intent) {
169 intentService.submit(intent);
170 submitted.add(intent);
171 withdrawn.remove(intent); //TODO could check result here...
172 }
173
174 private void withdraw(Intent intent) {
175 intentService.withdraw(intent);
176 withdrawn.add(intent);
177 submitted.remove(intent); //TODO could check result here...
178 }
179
180 private void createIntents(int numberOfKeys, int pathLength) {
181
182 Iterator<Device> deviceItr = deviceService.getAvailableDevices().iterator();
183
184 if (!deviceItr.hasNext()) {
185 throw new IllegalStateException("There are no devices");
186 }
187
188 Device ingressDevice = deviceItr.next();
189
190 for (int i = 0; i < numberOfKeys; i++) {
191 Key key = Key.of(i, appId);
192 TrafficSelector selector = DefaultTrafficSelector.builder().build();
193
194 TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
195 //FIXME
196 ConnectPoint ingress = new ConnectPoint(ingressDevice.id(), PortNumber.portNumber(1));
197 ConnectPoint egress = new ConnectPoint(ingressDevice.id(), PortNumber.portNumber(2));
198
199 Intent intent = new PointToPointIntent(appId, key,
200 selector, treatment,
201 ingress, egress,
202 Collections.emptyList());
203 intents.add(intent);
204 }
205 }
206
207 private void prime() {
208 int i = 0;
209 withdrawn.addAll(intents);
210 for (Intent intent : intents) {
211 submit(intent);
212 // only submit half of the intents to start
213 if (i++ >= intents.size() / 2) {
214 break;
215 }
216 }
217 }
218
219 class Listener implements IntentListener {
220
221
222 private Map<IntentEvent.Type, Counter> counters;
223
224 public Listener() {
225 counters = initCounters();
226
227 }
228
229 private Map<IntentEvent.Type, Counter> initCounters() {
230 Map<IntentEvent.Type, Counter> map = Maps.newHashMap();
231 for (IntentEvent.Type type : IntentEvent.Type.values()) {
232 map.put(type, new Counter());
233 }
234 return map;
235 }
236
237 @Override
238 public void event(IntentEvent event) {
239 if (event.subject().appId().equals(appId)) {
240 counters.get(event.type()).add(1);
241 }
242 }
243
244 public void report() {
245 StringBuilder stringBuilder = new StringBuilder();
246 for (IntentEvent.Type type : IntentEvent.Type.values()) {
247 stringBuilder.append(printCounter(type)).append("; ");
248 }
249 log.info("Intent Throughput:\n{}", stringBuilder);
250 }
251
252 private String printCounter(IntentEvent.Type event) {
253 Counter counter = counters.get(event);
254 String result = String.format("%s=%.2f", event, counter.throughput());
255 counter.reset();
256 return result;
257 }
258 }
259}