Brian O'Connor

Adding intent performance testing app

Change-Id: I1b3a8b6e5b9230066d31f8f520e212973b6f703e
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-apps</artifactId>
<version>1.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-app-intent-perf</artifactId>
<packaging>bundle</packaging>
<description>ONOS intent perf app bundle</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<descriptor>src/assembly/bin.xml</descriptor>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<app name="org.onosproject.intentperf" origin="ON.Lab" version="1.1.0"
features="onos-app-intent-perf">
<description>Intent performance application</description>
</app>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015 Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<formats>
<format>zip</format>
</formats>
<id>onos</id>
<includeBaseDirectory>false</includeBaseDirectory>
<files>
<file>
<source>src/assembly/app.xml</source>
<destName>app.xml</destName>
</file>
<file>
<source>target/${project.artifactId}-${project.version}.jar</source>
<destName>m2/org/onosproject/${project.artifactId}/${project.version}/${project.artifactId}-${project.version}.jar</destName>
</file>
</files>
</assembly>
\ No newline at end of file
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.intentperf;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.util.Counter;
import org.onosproject.cluster.ClusterService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentEvent;
import org.onosproject.net.intent.IntentListener;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.intent.Key;
import org.onosproject.net.intent.PointToPointIntent;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.onlab.util.Tools.delay;
import static org.onlab.util.Tools.groupedThreads;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Application to set up demos.
*/
@Component(immediate = true)
public class IntentPerfInstaller {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentService intentService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterService clusterService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
private ExecutorService worker;
private ApplicationId appId;
private Listener listener;
private Set<Intent> intents;
private Set<Intent> submitted;
private Set<Intent> withdrawn;
private boolean stopped;
private static final long REPORT_PERIOD = 5000L; //ms
private Timer reportTimer;
//FIXME make this configurable
private static final int NUM_KEYS = 10000;
@Activate
public void activate() {
String nodeId = clusterService.getLocalNode().ip().toString();
appId = coreService.registerApplication("org.onosproject.intentperf."
+ nodeId);
intents = Sets.newHashSet();
submitted = Sets.newHashSet();
withdrawn = Sets.newHashSet();
worker = Executors.newFixedThreadPool(1, groupedThreads("onos/intent-perf", "worker"));
log.info("Started with Application ID {}", appId.id());
start(); //FIXME
}
@Deactivate
public void deactivate() {
stop();
log.info("Stopped");
}
public void start() {
// perhaps we want to prime before listening...
// we will need to discard the first few results for priming and warmup
listener = new Listener();
intentService.addListener(listener);
reportTimer = new Timer("intent-perf-reporter");
reportTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
listener.report();
}
}, REPORT_PERIOD, REPORT_PERIOD);
stopped = false;
worker.submit(() -> {
delay(2000);
createIntents(NUM_KEYS, 2); //FIXME
prime();
while (!stopped) {
cycle();
// TODO delay if required
}
});
}
public void stop() {
if (listener != null) {
reportTimer.cancel();
intentService.removeListener(listener);
listener = null;
reportTimer = null;
}
stopped = true;
try {
worker.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Failed to stop worker.");
}
}
private void cycle() {
subset(submitted).forEach(this::withdraw);
subset(withdrawn).forEach(this::submit);
}
private Iterable<Intent> subset(Set<Intent> intents) {
List<Intent> subset = Lists.newArrayList(intents);
Collections.shuffle(subset);
return subset.subList(0, subset.size() / 2);
}
private void submit(Intent intent) {
intentService.submit(intent);
submitted.add(intent);
withdrawn.remove(intent); //TODO could check result here...
}
private void withdraw(Intent intent) {
intentService.withdraw(intent);
withdrawn.add(intent);
submitted.remove(intent); //TODO could check result here...
}
private void createIntents(int numberOfKeys, int pathLength) {
Iterator<Device> deviceItr = deviceService.getAvailableDevices().iterator();
if (!deviceItr.hasNext()) {
throw new IllegalStateException("There are no devices");
}
Device ingressDevice = deviceItr.next();
for (int i = 0; i < numberOfKeys; i++) {
Key key = Key.of(i, appId);
TrafficSelector selector = DefaultTrafficSelector.builder().build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
//FIXME
ConnectPoint ingress = new ConnectPoint(ingressDevice.id(), PortNumber.portNumber(1));
ConnectPoint egress = new ConnectPoint(ingressDevice.id(), PortNumber.portNumber(2));
Intent intent = new PointToPointIntent(appId, key,
selector, treatment,
ingress, egress,
Collections.emptyList());
intents.add(intent);
}
}
private void prime() {
int i = 0;
withdrawn.addAll(intents);
for (Intent intent : intents) {
submit(intent);
// only submit half of the intents to start
if (i++ >= intents.size() / 2) {
break;
}
}
}
class Listener implements IntentListener {
private Map<IntentEvent.Type, Counter> counters;
public Listener() {
counters = initCounters();
}
private Map<IntentEvent.Type, Counter> initCounters() {
Map<IntentEvent.Type, Counter> map = Maps.newHashMap();
for (IntentEvent.Type type : IntentEvent.Type.values()) {
map.put(type, new Counter());
}
return map;
}
@Override
public void event(IntentEvent event) {
if (event.subject().appId().equals(appId)) {
counters.get(event.type()).add(1);
}
}
public void report() {
StringBuilder stringBuilder = new StringBuilder();
for (IntentEvent.Type type : IntentEvent.Type.values()) {
stringBuilder.append(printCounter(type)).append("; ");
}
log.info("Intent Throughput:\n{}", stringBuilder);
}
private String printCounter(IntentEvent.Type event) {
Counter counter = counters.get(event);
String result = String.format("%s=%.2f", event, counter.throughput());
counter.reset();
return result;
}
}
}
......@@ -48,6 +48,7 @@
<module>routing</module>
<module>routing-api</module>
<module>bgprouter</module>
<module>intent-perf</module>
</modules>
<properties>
......
......@@ -257,6 +257,12 @@
<bundle>mvn:org.onosproject/onos-app-demo/@ONOS-VERSION</bundle>
</feature>
<feature name="onos-app-intent-perf" version="@FEATURE-VERSION"
description="ONOS intent perf applications">
<feature>onos-api</feature>
<bundle>mvn:org.onosproject/onos-app-intent-perf/@ONOS-VERSION</bundle>
</feature>
<feature name="onos-app-election" version="@FEATURE-VERSION"
description="ONOS app leadership election test">
<feature>onos-api</feature>
......