Brian O'Connor

Adding intent performance testing app

Change-Id: I1b3a8b6e5b9230066d31f8f520e212973b6f703e
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2015 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<project xmlns="http://maven.apache.org/POM/4.0.0"
18 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
20 + <modelVersion>4.0.0</modelVersion>
21 +
22 + <parent>
23 + <groupId>org.onosproject</groupId>
24 + <artifactId>onos-apps</artifactId>
25 + <version>1.1.0-SNAPSHOT</version>
26 + <relativePath>../pom.xml</relativePath>
27 + </parent>
28 +
29 + <artifactId>onos-app-intent-perf</artifactId>
30 + <packaging>bundle</packaging>
31 +
32 + <description>ONOS intent perf app bundle</description>
33 +
34 + <build>
35 + <plugins>
36 + <plugin>
37 + <groupId>org.apache.maven.plugins</groupId>
38 + <artifactId>maven-assembly-plugin</artifactId>
39 + <version>2.5.3</version>
40 + <configuration>
41 + <descriptor>src/assembly/bin.xml</descriptor>
42 + </configuration>
43 + <executions>
44 + <execution>
45 + <phase>package</phase>
46 + <goals>
47 + <goal>single</goal>
48 + </goals>
49 + </execution>
50 + </executions>
51 + </plugin>
52 + </plugins>
53 + </build>
54 +</project>
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2015 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<app name="org.onosproject.intentperf" origin="ON.Lab" version="1.1.0"
18 + features="onos-app-intent-perf">
19 + <description>Intent performance application</description>
20 +</app>
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2015 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<assembly
18 + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
19 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20 + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
21 + <formats>
22 + <format>zip</format>
23 + </formats>
24 + <id>onos</id>
25 + <includeBaseDirectory>false</includeBaseDirectory>
26 + <files>
27 + <file>
28 + <source>src/assembly/app.xml</source>
29 + <destName>app.xml</destName>
30 + </file>
31 + <file>
32 + <source>target/${project.artifactId}-${project.version}.jar</source>
33 + <destName>m2/org/onosproject/${project.artifactId}/${project.version}/${project.artifactId}-${project.version}.jar</destName>
34 + </file>
35 + </files>
36 +</assembly>
...\ No newline at end of file ...\ No newline at end of file
1 +/*
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 + */
16 +package org.onosproject.intentperf;
17 +
18 +import com.google.common.collect.Lists;
19 +import com.google.common.collect.Maps;
20 +import com.google.common.collect.Sets;
21 +import org.apache.felix.scr.annotations.Activate;
22 +import org.apache.felix.scr.annotations.Component;
23 +import org.apache.felix.scr.annotations.Deactivate;
24 +import org.apache.felix.scr.annotations.Reference;
25 +import org.apache.felix.scr.annotations.ReferenceCardinality;
26 +import org.onlab.util.Counter;
27 +import org.onosproject.cluster.ClusterService;
28 +import org.onosproject.core.ApplicationId;
29 +import org.onosproject.core.CoreService;
30 +import org.onosproject.net.ConnectPoint;
31 +import org.onosproject.net.Device;
32 +import org.onosproject.net.PortNumber;
33 +import org.onosproject.net.device.DeviceService;
34 +import org.onosproject.net.flow.DefaultTrafficSelector;
35 +import org.onosproject.net.flow.DefaultTrafficTreatment;
36 +import org.onosproject.net.flow.TrafficSelector;
37 +import org.onosproject.net.flow.TrafficTreatment;
38 +import org.onosproject.net.intent.Intent;
39 +import org.onosproject.net.intent.IntentEvent;
40 +import org.onosproject.net.intent.IntentListener;
41 +import org.onosproject.net.intent.IntentService;
42 +import org.onosproject.net.intent.Key;
43 +import org.onosproject.net.intent.PointToPointIntent;
44 +import org.slf4j.Logger;
45 +
46 +import java.util.Collections;
47 +import java.util.Iterator;
48 +import java.util.List;
49 +import java.util.Map;
50 +import java.util.Set;
51 +import java.util.Timer;
52 +import java.util.TimerTask;
53 +import java.util.concurrent.ExecutorService;
54 +import java.util.concurrent.Executors;
55 +import java.util.concurrent.TimeUnit;
56 +
57 +import static org.onlab.util.Tools.delay;
58 +import static org.onlab.util.Tools.groupedThreads;
59 +import static org.slf4j.LoggerFactory.getLogger;
60 +
61 +/**
62 + * Application to set up demos.
63 + */
64 +@Component(immediate = true)
65 +public 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 +}
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
48 <module>routing</module> 48 <module>routing</module>
49 <module>routing-api</module> 49 <module>routing-api</module>
50 <module>bgprouter</module> 50 <module>bgprouter</module>
51 + <module>intent-perf</module>
51 </modules> 52 </modules>
52 53
53 <properties> 54 <properties>
......
...@@ -257,6 +257,12 @@ ...@@ -257,6 +257,12 @@
257 <bundle>mvn:org.onosproject/onos-app-demo/@ONOS-VERSION</bundle> 257 <bundle>mvn:org.onosproject/onos-app-demo/@ONOS-VERSION</bundle>
258 </feature> 258 </feature>
259 259
260 + <feature name="onos-app-intent-perf" version="@FEATURE-VERSION"
261 + description="ONOS intent perf applications">
262 + <feature>onos-api</feature>
263 + <bundle>mvn:org.onosproject/onos-app-intent-perf/@ONOS-VERSION</bundle>
264 + </feature>
265 +
260 <feature name="onos-app-election" version="@FEATURE-VERSION" 266 <feature name="onos-app-election" version="@FEATURE-VERSION"
261 description="ONOS app leadership election test"> 267 description="ONOS app leadership election test">
262 <feature>onos-api</feature> 268 <feature>onos-api</feature>
......