Brian O'Connor
Committed by Gerrit Code Review

Adding test apps as submodule to apps

Moving election and intent-perf there

Change-Id: Ia71e98438b33d3a1c5c12b08ae98c32930c4bd81
Showing 29 changed files with 1999 additions and 0 deletions
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
43 <module>routing</module> 43 <module>routing</module>
44 <module>routing-api</module> 44 <module>routing-api</module>
45 <module>bgprouter</module> 45 <module>bgprouter</module>
46 + <module>test</module>
46 </modules> 47 </modules>
47 48
48 <properties> 49 <properties>
......
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2014 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 +
18 +<project xmlns="http://maven.apache.org/POM/4.0.0"
19 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
21 + <modelVersion>4.0.0</modelVersion>
22 +
23 + <parent>
24 + <groupId>org.onosproject</groupId>
25 + <artifactId>onos-app-samples</artifactId>
26 + <version>1.2.0-SNAPSHOT</version>
27 + <relativePath>../pom.xml</relativePath>
28 + </parent>
29 +
30 + <artifactId>onos-app-election</artifactId>
31 + <packaging>bundle</packaging>
32 +
33 + <description>ONOS app leadership election test</description>
34 +
35 + <dependencies>
36 +
37 + <dependency>
38 + <groupId>org.onosproject</groupId>
39 + <artifactId>onos-api</artifactId>
40 + <version>${project.version}</version>
41 + <scope>test</scope>
42 + <classifier>tests</classifier>
43 + </dependency>
44 +
45 + <dependency>
46 + <groupId>org.onosproject</groupId>
47 + <artifactId>onos-cli</artifactId>
48 + <version>${project.version}</version>
49 + </dependency>
50 +
51 + <dependency>
52 + <groupId>org.osgi</groupId>
53 + <artifactId>org.osgi.core</artifactId>
54 + </dependency>
55 + <dependency>
56 + <groupId>org.apache.karaf.shell</groupId>
57 + <artifactId>org.apache.karaf.shell.console</artifactId>
58 + </dependency>
59 +
60 + </dependencies>
61 +
62 +</project>
1 +/*
2 + * Copyright 2014 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.election;
17 +
18 +import static org.slf4j.LoggerFactory.getLogger;
19 +
20 +import org.apache.felix.scr.annotations.Activate;
21 +import org.apache.felix.scr.annotations.Component;
22 +import org.apache.felix.scr.annotations.Deactivate;
23 +import org.apache.felix.scr.annotations.Reference;
24 +import org.apache.felix.scr.annotations.ReferenceCardinality;
25 +import org.onosproject.cluster.ClusterService;
26 +import org.onosproject.core.CoreService;
27 +import org.onosproject.cluster.ControllerNode;
28 +import org.onosproject.cluster.LeadershipEvent;
29 +import org.onosproject.cluster.LeadershipEventListener;
30 +import org.onosproject.cluster.LeadershipService;
31 +import org.onosproject.core.ApplicationId;
32 +
33 +import org.slf4j.Logger;
34 +
35 +
36 +/**
37 + * Simple application to test leadership election.
38 + */
39 +@Component(immediate = true)
40 +public class ElectionTest {
41 +
42 + private final Logger log = getLogger(getClass());
43 +
44 + private static final String ELECTION_APP = "org.onosproject.election";
45 + private ApplicationId appId;
46 +
47 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
48 + protected ClusterService clusterService;
49 +
50 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
51 + protected CoreService coreService;
52 +
53 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
54 + protected LeadershipService leadershipService;
55 +
56 + private LeadershipEventListener leadershipEventListener =
57 + new InnerLeadershipEventListener();
58 +
59 + private ControllerNode localControllerNode;
60 +
61 +
62 + @Activate
63 + protected void activate() {
64 + log.info("Election-test app started");
65 +
66 + appId = coreService.registerApplication(ELECTION_APP);
67 +
68 + localControllerNode = clusterService.getLocalNode();
69 +
70 + leadershipService.addListener(leadershipEventListener);
71 + leadershipService.runForLeadership(appId.name());
72 + }
73 +
74 + @Deactivate
75 + protected void deactivate() {
76 +
77 + leadershipService.withdraw(appId.name());
78 + leadershipService.removeListener(leadershipEventListener);
79 +
80 + log.info("Election-test app Stopped");
81 + }
82 +
83 + /**
84 + * A listener for Leadership Events.
85 + */
86 + private class InnerLeadershipEventListener
87 + implements LeadershipEventListener {
88 +
89 + @Override
90 + public void event(LeadershipEvent event) {
91 +
92 +
93 + if (!event.subject().topic().equals(appId.name())) {
94 + return; // Not our topic: ignore
95 + }
96 +
97 + //only log what pertains to us
98 + log.debug("Leadership Event: time = {} type = {} event = {}",
99 + event.time(), event.type(), event);
100 +
101 + if (!event.subject().leader().equals(
102 + localControllerNode.id())) {
103 + return; // The event is not about this instance: ignore
104 + }
105 +
106 + switch (event.type()) {
107 + case LEADER_ELECTED:
108 + log.info("Election-test app leader elected");
109 + break;
110 + case LEADER_BOOTED:
111 + log.info("Election-test app lost election");
112 + break;
113 + case LEADER_REELECTED:
114 + log.debug("Election-test app was re-elected");
115 + break;
116 + default:
117 + break;
118 + }
119 + }
120 + }
121 +
122 +}
1 +/*
2 + * Copyright 2014 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.election.cli;
17 +
18 +import org.onosproject.cluster.NodeId;
19 +import org.apache.karaf.shell.commands.Command;
20 +import org.onosproject.cli.AbstractShellCommand;
21 +import org.onosproject.cluster.LeadershipService;
22 +
23 +/**
24 + * CLI command to get the current leader for the Election test application.
25 + */
26 +@Command(scope = "onos", name = "election-test-leader",
27 + description = "Get the current leader for the Election test application")
28 +public class ElectionTestLeaderCommand extends AbstractShellCommand {
29 +
30 + private NodeId leader;
31 + private static final String ELECTION_APP = "org.onosproject.election";
32 +
33 + @Override
34 + protected void execute() {
35 + LeadershipService service = get(LeadershipService.class);
36 +
37 + //print the current leader
38 + leader = service.getLeader(ELECTION_APP);
39 + printLeader(leader);
40 + }
41 +
42 + /**
43 + * Prints the leader.
44 + *
45 + * @param leader the leader to print
46 + */
47 + private void printLeader(NodeId leader) {
48 + if (leader != null) {
49 + print("The current leader for the Election app is %s.", leader);
50 + } else {
51 + print("There is currently no leader elected for the Election app");
52 + }
53 + }
54 +}
1 +/*
2 + * Copyright 2014 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.election.cli;
17 +
18 +import org.apache.karaf.shell.commands.Command;
19 +import org.onosproject.cli.AbstractShellCommand;
20 +import org.onosproject.cluster.LeadershipService;
21 +
22 +/**
23 + * CLI command to run for leadership of the Election test application.
24 + */
25 +@Command(scope = "onos", name = "election-test-run",
26 + description = "Run for leader of the Election test application")
27 +public class ElectionTestRunCommand extends AbstractShellCommand {
28 +
29 + private static final String ELECTION_APP = "org.onosproject.election";
30 +
31 + @Override
32 + protected void execute() {
33 + LeadershipService service = get(LeadershipService.class);
34 +
35 + service.runForLeadership(ELECTION_APP);
36 + //print the current leader
37 + print("Entering leadership elections for the Election app.");
38 + }
39 +}
1 +/*
2 + * Copyright 2014 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.election.cli;
17 +
18 +import org.apache.karaf.shell.commands.Command;
19 +import org.onosproject.cli.AbstractShellCommand;
20 +import org.onosproject.cluster.LeadershipService;
21 +
22 +/**
23 + * CLI command to withdraw the local node from leadership election for
24 + * the Election test application.
25 + */
26 +@Command(scope = "onos", name = "election-test-withdraw",
27 + description = "Withdraw node from leadership election for the Election test application")
28 +public class ElectionTestWithdrawCommand extends AbstractShellCommand {
29 +
30 + private static final String ELECTION_APP = "org.onosproject.election";
31 +
32 + @Override
33 + protected void execute() {
34 + LeadershipService service = get(LeadershipService.class);
35 +
36 + service.withdraw(ELECTION_APP);
37 + //print the current leader
38 + print("Withdrawing from leadership elections for the Election app.");
39 + }
40 +}
1 +/*
2 + * Copyright 2014 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 +
17 +/**
18 + * Election test command-line handlers.
19 + */
20 +package org.onosproject.election.cli;
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2014 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 +
17 +/**
18 + * Sample application for use in various experiments.
19 + */
20 +package org.onosproject.election;
1 +<!--
2 + ~ Copyright 2014 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 +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
17 +
18 + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
19 + <command>
20 + <action class="org.onosproject.election.cli.ElectionTestLeaderCommand"/>
21 + </command>
22 + <command>
23 + <action class="org.onosproject.election.cli.ElectionTestRunCommand"/>
24 + </command>
25 + <command>
26 + <action class="org.onosproject.election.cli.ElectionTestWithdrawCommand"/>
27 + </command>
28 + </command-bundle>
29 +
30 +</blueprint>
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-app-samples</artifactId>
25 + <version>1.2.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 + <dependencies>
35 + <dependency>
36 + <groupId>org.apache.karaf.shell</groupId>
37 + <artifactId>org.apache.karaf.shell.console</artifactId>
38 + </dependency>
39 +
40 + <dependency>
41 + <groupId>org.onosproject</groupId>
42 + <artifactId>onos-cli</artifactId>
43 + <version>${project.version}</version>
44 + </dependency>
45 + <dependency>
46 + <groupId>org.osgi</groupId>
47 + <artifactId>org.osgi.compendium</artifactId>
48 + </dependency>
49 + <!-- Required for javadoc generation -->
50 + <dependency>
51 + <groupId>org.osgi</groupId>
52 + <artifactId>org.osgi.core</artifactId>
53 + </dependency>
54 + </dependencies>
55 +
56 + <build>
57 + <plugins>
58 + <plugin>
59 + <groupId>org.apache.maven.plugins</groupId>
60 + <artifactId>maven-assembly-plugin</artifactId>
61 + <version>2.5.3</version>
62 + <configuration>
63 + <descriptor>src/assembly/bin.xml</descriptor>
64 + </configuration>
65 + <executions>
66 + <execution>
67 + <phase>package</phase>
68 + <goals>
69 + <goal>single</goal>
70 + </goals>
71 + </execution>
72 + </executions>
73 + </plugin>
74 + </plugins>
75 + </build>
76 +</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.2.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.ImmutableList;
19 +import org.apache.felix.scr.annotations.Activate;
20 +import org.apache.felix.scr.annotations.Component;
21 +import org.apache.felix.scr.annotations.Deactivate;
22 +import org.apache.felix.scr.annotations.Reference;
23 +import org.apache.felix.scr.annotations.ReferenceCardinality;
24 +import org.apache.felix.scr.annotations.Service;
25 +import org.onosproject.cluster.ClusterService;
26 +import org.onosproject.cluster.ControllerNode;
27 +import org.onosproject.cluster.NodeId;
28 +import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
29 +import org.onosproject.store.cluster.messaging.ClusterMessage;
30 +import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
31 +import org.onosproject.store.cluster.messaging.MessageSubject;
32 +import org.slf4j.Logger;
33 +
34 +import java.util.ArrayList;
35 +import java.util.Arrays;
36 +import java.util.HashMap;
37 +import java.util.LinkedList;
38 +import java.util.List;
39 +import java.util.Map;
40 +import java.util.concurrent.ExecutorService;
41 +import java.util.concurrent.Executors;
42 +
43 +import static org.onlab.util.Tools.groupedThreads;
44 +import static org.slf4j.LoggerFactory.getLogger;
45 +
46 +/**
47 + * Collects and distributes performance samples.
48 + */
49 +@Component(immediate = true)
50 +@Service(value = IntentPerfCollector.class)
51 +public class IntentPerfCollector {
52 +
53 + private static final long SAMPLE_TIME_WINDOW_MS = 5_000;
54 + private final Logger log = getLogger(getClass());
55 +
56 + private static final int MAX_SAMPLES = 1_000;
57 +
58 + private final List<Sample> samples = new LinkedList<>();
59 +
60 + private static final MessageSubject SAMPLE = new MessageSubject("intent-perf-sample");
61 +
62 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
63 + protected ClusterCommunicationService communicationService;
64 +
65 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 + protected ClusterService clusterService;
67 +
68 + @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY)
69 + protected IntentPerfUi ui;
70 +
71 + // Auxiliary structures used to accrue data for normalized time interval
72 + // across all nodes.
73 + private long newestTime;
74 + private Sample overall;
75 + private Sample current;
76 +
77 + private ControllerNode[] nodes;
78 + private Map<NodeId, Integer> nodeToIndex;
79 +
80 + private NodeId nodeId;
81 + private ExecutorService messageHandlingExecutor;
82 +
83 + @Activate
84 + public void activate() {
85 + nodeId = clusterService.getLocalNode().id();
86 +
87 + // TODO: replace with shared executor
88 + messageHandlingExecutor = Executors.newSingleThreadExecutor(
89 + groupedThreads("onos/perf", "message-handler"));
90 +
91 + communicationService.addSubscriber(SAMPLE, new InternalSampleCollector(),
92 + messageHandlingExecutor);
93 +
94 + nodes = clusterService.getNodes().toArray(new ControllerNode[]{});
95 + Arrays.sort(nodes, (a, b) -> a.id().toString().compareTo(b.id().toString()));
96 +
97 + nodeToIndex = new HashMap<>();
98 + for (int i = 0; i < nodes.length; i++) {
99 + nodeToIndex.put(nodes[i].id(), i);
100 + }
101 +
102 + clearSamples();
103 + log.info("Started");
104 + }
105 +
106 + @Deactivate
107 + public void deactivate() {
108 + messageHandlingExecutor.shutdown();
109 + communicationService.removeSubscriber(SAMPLE);
110 + log.info("Stopped");
111 + }
112 +
113 + /**
114 + * Clears all previously accumulated data.
115 + */
116 + public void clearSamples() {
117 + newestTime = 0;
118 + overall = new Sample(0, nodes.length);
119 + current = new Sample(0, nodes.length);
120 + samples.clear();
121 + }
122 +
123 +
124 + /**
125 + * Records a sample point of data about intent operation rate.
126 + *
127 + * @param overallRate overall rate
128 + * @param currentRate current rate
129 + */
130 + public void recordSample(double overallRate, double currentRate) {
131 + long now = System.currentTimeMillis();
132 + addSample(now, nodeId, overallRate, currentRate);
133 + broadcastSample(now, nodeId, overallRate, currentRate);
134 + }
135 +
136 + /**
137 + * Returns set of node ids as headers.
138 + *
139 + * @return node id headers
140 + */
141 + public List<String> getSampleHeaders() {
142 + List<String> headers = new ArrayList<>();
143 + for (ControllerNode node : nodes) {
144 + headers.add(node.id().toString());
145 + }
146 + return headers;
147 + }
148 +
149 + /**
150 + * Returns set of all accumulated samples normalized to the local set of
151 + * samples.
152 + *
153 + * @return accumulated samples
154 + */
155 + public synchronized List<Sample> getSamples() {
156 + return ImmutableList.copyOf(samples);
157 + }
158 +
159 + /**
160 + * Returns overall throughput performance for each of the cluster nodes.
161 + *
162 + * @return overall intent throughput
163 + */
164 + public synchronized Sample getOverall() {
165 + return overall;
166 + }
167 +
168 + // Records a new sample to our collection of samples
169 + private synchronized void addSample(long time, NodeId nodeId,
170 + double overallRate, double currentRate) {
171 + Sample fullSample = createCurrentSampleIfNeeded(time);
172 + setSampleData(current, nodeId, currentRate);
173 + setSampleData(overall, nodeId, overallRate);
174 + pruneSamplesIfNeeded();
175 +
176 + if (fullSample != null && ui != null) {
177 + ui.reportSample(fullSample);
178 + }
179 + }
180 +
181 + private Sample createCurrentSampleIfNeeded(long time) {
182 + Sample oldSample = time - newestTime > SAMPLE_TIME_WINDOW_MS || current.isComplete() ? current : null;
183 + if (oldSample != null) {
184 + newestTime = time;
185 + current = new Sample(time, nodes.length);
186 + if (oldSample.time > 0) {
187 + samples.add(oldSample);
188 + }
189 + }
190 + return oldSample;
191 + }
192 +
193 + private void setSampleData(Sample sample, NodeId nodeId, double data) {
194 + Integer index = nodeToIndex.get(nodeId);
195 + if (index != null) {
196 + sample.data[index] = data;
197 + }
198 + }
199 +
200 + private void pruneSamplesIfNeeded() {
201 + if (samples.size() > MAX_SAMPLES) {
202 + samples.remove(0);
203 + }
204 + }
205 +
206 + // Performance data sample.
207 + static class Sample {
208 + final long time;
209 + final double[] data;
210 +
211 + public Sample(long time, int nodeCount) {
212 + this.time = time;
213 + this.data = new double[nodeCount];
214 + Arrays.fill(data, -1);
215 + }
216 +
217 + public boolean isComplete() {
218 + for (int i = 0; i < data.length; i++) {
219 + if (data[i] < 0) {
220 + return false;
221 + }
222 + }
223 + return true;
224 + }
225 + }
226 +
227 + private void broadcastSample(long time, NodeId nodeId, double overallRate, double currentRate) {
228 + String data = String.format("%d|%f|%f", time, overallRate, currentRate);
229 + communicationService.broadcast(new ClusterMessage(nodeId, SAMPLE, data.getBytes()));
230 + }
231 +
232 + private class InternalSampleCollector implements ClusterMessageHandler {
233 + @Override
234 + public void handle(ClusterMessage message) {
235 + String[] fields = new String(message.payload()).split("\\|");
236 + log.debug("Received sample from {}: {}", message.sender(), fields);
237 + addSample(Long.parseLong(fields[0]), message.sender(),
238 + Double.parseDouble(fields[1]), Double.parseDouble(fields[2]));
239 + }
240 + }
241 +}
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.ArrayListMultimap;
19 +import com.google.common.collect.Lists;
20 +import com.google.common.collect.Maps;
21 +import com.google.common.collect.Multimap;
22 +import com.google.common.collect.Sets;
23 +import org.apache.commons.lang.math.RandomUtils;
24 +import org.apache.felix.scr.annotations.Activate;
25 +import org.apache.felix.scr.annotations.Component;
26 +import org.apache.felix.scr.annotations.Deactivate;
27 +import org.apache.felix.scr.annotations.Modified;
28 +import org.apache.felix.scr.annotations.Property;
29 +import org.apache.felix.scr.annotations.Reference;
30 +import org.apache.felix.scr.annotations.ReferenceCardinality;
31 +import org.apache.felix.scr.annotations.Service;
32 +import org.onlab.packet.MacAddress;
33 +import org.onlab.util.Counter;
34 +import org.onosproject.cfg.ComponentConfigService;
35 +import org.onosproject.cluster.ClusterService;
36 +import org.onosproject.cluster.ControllerNode;
37 +import org.onosproject.cluster.NodeId;
38 +import org.onosproject.core.ApplicationId;
39 +import org.onosproject.core.CoreService;
40 +import org.onosproject.mastership.MastershipService;
41 +import org.onosproject.net.ConnectPoint;
42 +import org.onosproject.net.Device;
43 +import org.onosproject.net.PortNumber;
44 +import org.onosproject.net.device.DeviceService;
45 +import org.onosproject.net.flow.DefaultTrafficSelector;
46 +import org.onosproject.net.flow.DefaultTrafficTreatment;
47 +import org.onosproject.net.flow.TrafficSelector;
48 +import org.onosproject.net.flow.TrafficTreatment;
49 +import org.onosproject.net.intent.Intent;
50 +import org.onosproject.net.intent.IntentEvent;
51 +import org.onosproject.net.intent.IntentListener;
52 +import org.onosproject.net.intent.IntentService;
53 +import org.onosproject.net.intent.Key;
54 +import org.onosproject.net.intent.PartitionService;
55 +import org.onosproject.net.intent.PointToPointIntent;
56 +import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
57 +import org.onosproject.store.cluster.messaging.ClusterMessage;
58 +import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
59 +import org.onosproject.store.cluster.messaging.MessageSubject;
60 +import org.osgi.service.component.ComponentContext;
61 +import org.slf4j.Logger;
62 +
63 +import java.util.ArrayList;
64 +import java.util.Collections;
65 +import java.util.Dictionary;
66 +import java.util.List;
67 +import java.util.Map;
68 +import java.util.Set;
69 +import java.util.Timer;
70 +import java.util.TimerTask;
71 +import java.util.concurrent.ExecutorService;
72 +import java.util.concurrent.Executors;
73 +import java.util.concurrent.TimeUnit;
74 +import java.util.stream.Collectors;
75 +
76 +import static com.google.common.base.Preconditions.checkState;
77 +import static com.google.common.base.Strings.isNullOrEmpty;
78 +import static java.lang.String.format;
79 +import static java.lang.System.currentTimeMillis;
80 +import static org.apache.felix.scr.annotations.ReferenceCardinality.MANDATORY_UNARY;
81 +import static org.onlab.util.Tools.*;
82 +import static org.onosproject.net.intent.IntentEvent.Type.*;
83 +import static org.slf4j.LoggerFactory.getLogger;
84 +
85 +/**
86 + * Application to test sustained intent throughput.
87 + */
88 +@Component(immediate = true)
89 +@Service(value = IntentPerfInstaller.class)
90 +public class IntentPerfInstaller {
91 +
92 + private final Logger log = getLogger(getClass());
93 +
94 + private static final int DEFAULT_NUM_WORKERS = 1;
95 +
96 + private static final int DEFAULT_NUM_KEYS = 40000;
97 + private static final int DEFAULT_GOAL_CYCLE_PERIOD = 1000; //ms
98 +
99 + private static final int DEFAULT_NUM_NEIGHBORS = 0;
100 +
101 + private static final int START_DELAY = 5_000; // ms
102 + private static final int REPORT_PERIOD = 5_000; //ms
103 +
104 + private static final String START = "start";
105 + private static final String STOP = "stop";
106 + private static final MessageSubject CONTROL = new MessageSubject("intent-perf-ctl");
107 +
108 + //FIXME add path length
109 +
110 + @Property(name = "numKeys", intValue = DEFAULT_NUM_KEYS,
111 + label = "Number of keys (i.e. unique intents) to generate per instance")
112 + private int numKeys = DEFAULT_NUM_KEYS;
113 +
114 + //TODO implement numWorkers property
115 +// @Property(name = "numThreads", intValue = DEFAULT_NUM_WORKERS,
116 +// label = "Number of installer threads per instance")
117 +// private int numWokers = DEFAULT_NUM_WORKERS;
118 +
119 + @Property(name = "cyclePeriod", intValue = DEFAULT_GOAL_CYCLE_PERIOD,
120 + label = "Goal for cycle period (in ms)")
121 + private int cyclePeriod = DEFAULT_GOAL_CYCLE_PERIOD;
122 +
123 + @Property(name = "numNeighbors", intValue = DEFAULT_NUM_NEIGHBORS,
124 + label = "Number of neighbors to generate intents for")
125 + private int numNeighbors = DEFAULT_NUM_NEIGHBORS;
126 +
127 + @Reference(cardinality = MANDATORY_UNARY)
128 + protected CoreService coreService;
129 +
130 + @Reference(cardinality = MANDATORY_UNARY)
131 + protected IntentService intentService;
132 +
133 + @Reference(cardinality = MANDATORY_UNARY)
134 + protected ClusterService clusterService;
135 +
136 + @Reference(cardinality = MANDATORY_UNARY)
137 + protected DeviceService deviceService;
138 +
139 + @Reference(cardinality = MANDATORY_UNARY)
140 + protected MastershipService mastershipService;
141 +
142 + @Reference(cardinality = MANDATORY_UNARY)
143 + protected PartitionService partitionService;
144 +
145 + @Reference(cardinality = MANDATORY_UNARY)
146 + protected ComponentConfigService configService;
147 +
148 + @Reference(cardinality = MANDATORY_UNARY)
149 + protected IntentPerfCollector sampleCollector;
150 +
151 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
152 + protected ClusterCommunicationService communicationService;
153 +
154 + private ExecutorService messageHandlingExecutor;
155 +
156 + private ExecutorService workers;
157 + private ApplicationId appId;
158 + private Listener listener;
159 + private boolean stopped;
160 +
161 + private Timer reportTimer;
162 +
163 + // FIXME this variable isn't shared properly between multiple worker threads
164 + private int lastKey = 0;
165 +
166 + private IntentPerfUi perfUi;
167 + private NodeId nodeId;
168 + private TimerTask reporterTask;
169 +
170 + @Activate
171 + public void activate(ComponentContext context) {
172 + configService.registerProperties(getClass());
173 +
174 + nodeId = clusterService.getLocalNode().id();
175 + appId = coreService.registerApplication("org.onosproject.intentperf." + nodeId.toString());
176 +
177 + // TODO: replace with shared timer
178 + reportTimer = new Timer("onos-intent-perf-reporter");
179 + workers = Executors.newFixedThreadPool(DEFAULT_NUM_WORKERS, groupedThreads("onos/intent-perf", "worker-%d"));
180 +
181 + // disable flow backups for testing
182 + configService.setProperty("org.onosproject.store.flow.impl.DistributedFlowRuleStore",
183 + "backupEnabled", "false");
184 +
185 + // TODO: replace with shared executor
186 + messageHandlingExecutor = Executors.newSingleThreadExecutor(
187 + groupedThreads("onos/perf", "command-handler"));
188 +
189 + communicationService.addSubscriber(CONTROL, new InternalControl(),
190 + messageHandlingExecutor);
191 +
192 + listener = new Listener();
193 + intentService.addListener(listener);
194 +
195 + // TODO: investigate why this seems to be necessary for configs to get picked up on initial activation
196 + modify(context);
197 + }
198 +
199 + @Deactivate
200 + public void deactivate() {
201 + stopTestRun();
202 +
203 + configService.unregisterProperties(getClass(), false);
204 + messageHandlingExecutor.shutdown();
205 + communicationService.removeSubscriber(CONTROL);
206 +
207 + if (listener != null) {
208 + reportTimer.cancel();
209 + intentService.removeListener(listener);
210 + listener = null;
211 + reportTimer = null;
212 + }
213 + }
214 +
215 + @Modified
216 + public void modify(ComponentContext context) {
217 + if (context == null) {
218 + logConfig("Reconfigured");
219 + return;
220 + }
221 +
222 + Dictionary<?, ?> properties = context.getProperties();
223 + int newNumKeys, newCyclePeriod, newNumNeighbors;
224 + try {
225 + String s = get(properties, "numKeys");
226 + newNumKeys = isNullOrEmpty(s) ? numKeys : Integer.parseInt(s.trim());
227 +
228 + s = get(properties, "cyclePeriod");
229 + newCyclePeriod = isNullOrEmpty(s) ? cyclePeriod : Integer.parseInt(s.trim());
230 +
231 + s = get(properties, "numNeighbors");
232 + newNumNeighbors = isNullOrEmpty(s) ? numNeighbors : Integer.parseInt(s.trim());
233 +
234 + } catch (NumberFormatException | ClassCastException e) {
235 + log.warn("Malformed configuration detected; using defaults", e);
236 + newNumKeys = DEFAULT_NUM_KEYS;
237 + newCyclePeriod = DEFAULT_GOAL_CYCLE_PERIOD;
238 + newNumNeighbors = DEFAULT_NUM_NEIGHBORS;
239 + }
240 +
241 + if (newNumKeys != numKeys || newCyclePeriod != cyclePeriod || newNumNeighbors != numNeighbors) {
242 + numKeys = newNumKeys;
243 + cyclePeriod = newCyclePeriod;
244 + numNeighbors = newNumNeighbors;
245 + logConfig("Reconfigured");
246 + }
247 + }
248 +
249 + public void start() {
250 + communicationService.broadcast(new ClusterMessage(nodeId, CONTROL, START.getBytes()));
251 + startTestRun();
252 + }
253 +
254 + public void stop() {
255 + communicationService.broadcast(new ClusterMessage(nodeId, CONTROL, STOP.getBytes()));
256 + stopTestRun();
257 + }
258 +
259 + private void logConfig(String prefix) {
260 + log.info("{} with appId {}; numKeys = {}; cyclePeriod = {} ms; numNeighbors={}",
261 + prefix, appId.id(), numKeys, cyclePeriod, numNeighbors);
262 + }
263 +
264 + private void startTestRun() {
265 + sampleCollector.clearSamples();
266 +
267 + // adjust numNeighbors and generate list of neighbors
268 + numNeighbors = Math.min(clusterService.getNodes().size() - 1, numNeighbors);
269 +
270 + // Schedule reporter task on report period boundary
271 + reporterTask = new ReporterTask();
272 + reportTimer.scheduleAtFixedRate(reporterTask,
273 + REPORT_PERIOD - currentTimeMillis() % REPORT_PERIOD,
274 + REPORT_PERIOD);
275 +
276 + // Submit workers
277 + stopped = false;
278 + for (int i = 0; i < DEFAULT_NUM_WORKERS; i++) {
279 + workers.submit(new Submitter(createIntents(numKeys, /*FIXME*/ 2, lastKey)));
280 + }
281 + log.info("Started test run");
282 + }
283 +
284 + private void stopTestRun() {
285 + stopped = true;
286 + if (reporterTask != null) {
287 + reporterTask.cancel();
288 + reporterTask = null;
289 + }
290 +
291 + try {
292 + workers.awaitTermination(5 * cyclePeriod, TimeUnit.MILLISECONDS);
293 + } catch (InterruptedException e) {
294 + log.warn("Failed to stop worker", e);
295 + }
296 + log.info("Stopped test run");
297 + }
298 +
299 + private List<NodeId> getNeighbors() {
300 + List<NodeId> nodes = clusterService.getNodes().stream()
301 + .map(ControllerNode::id)
302 + .collect(Collectors.toCollection(ArrayList::new));
303 + // sort neighbors by id
304 + Collections.sort(nodes, (node1, node2) ->
305 + node1.toString().compareTo(node2.toString()));
306 + // rotate the local node to index 0
307 + Collections.rotate(nodes, -1 * nodes.indexOf(clusterService.getLocalNode().id()));
308 + log.debug("neighbors (raw): {}", nodes); //TODO remove
309 + // generate the sub-list that will contain local node and selected neighbors
310 + nodes = nodes.subList(0, numNeighbors + 1);
311 + log.debug("neighbors: {}", nodes); //TODO remove
312 + return nodes;
313 + }
314 +
315 + private Intent createIntent(Key key, long mac, NodeId node, Multimap<NodeId, Device> devices) {
316 + // choose a random device for which this node is master
317 + List<Device> deviceList = devices.get(node).stream().collect(Collectors.toList());
318 + Device device = deviceList.get(RandomUtils.nextInt(deviceList.size()));
319 +
320 + //FIXME we currently ignore the path length and always use the same device
321 + TrafficSelector selector = DefaultTrafficSelector.builder()
322 + .matchEthDst(MacAddress.valueOf(mac)).build();
323 + TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
324 + ConnectPoint ingress = new ConnectPoint(device.id(), PortNumber.portNumber(1));
325 + ConnectPoint egress = new ConnectPoint(device.id(), PortNumber.portNumber(2));
326 +
327 + return PointToPointIntent.builder()
328 + .appId(appId)
329 + .key(key)
330 + .selector(selector)
331 + .treatment(treatment)
332 + .ingressPoint(ingress)
333 + .egressPoint(egress)
334 + .build();
335 + }
336 +
337 + /**
338 + * Creates a specified number of intents for testing purposes.
339 + *
340 + * @param numberOfKeys number of intents
341 + * @param pathLength path depth
342 + * @param firstKey first key to attempt
343 + * @return set of intents
344 + */
345 + private Set<Intent> createIntents(int numberOfKeys, int pathLength, int firstKey) {
346 + List<NodeId> neighbors = getNeighbors();
347 +
348 + Multimap<NodeId, Device> devices = ArrayListMultimap.create();
349 + deviceService.getAvailableDevices()
350 + .forEach(device -> devices.put(mastershipService.getMasterFor(device.id()), device));
351 +
352 + // ensure that we have at least one device per neighbor
353 + neighbors.forEach(node -> checkState(devices.get(node).size() > 0,
354 + "There are no devices for {}", node));
355 +
356 + // TODO pull this outside so that createIntent can use it
357 + // prefix based on node id for keys generated on this instance
358 + long keyPrefix = ((long) clusterService.getLocalNode().ip().getIp4Address().toInt()) << 32;
359 +
360 + int maxKeysPerNode = (int) Math.ceil((double) numberOfKeys / neighbors.size());
361 + Multimap<NodeId, Intent> intents = ArrayListMultimap.create();
362 +
363 + for (int count = 0, k = firstKey; count < numberOfKeys; k++) {
364 + Key key = Key.of(keyPrefix + k, appId);
365 +
366 + NodeId leader = partitionService.getLeader(key);
367 + if (!neighbors.contains(leader) || intents.get(leader).size() >= maxKeysPerNode) {
368 + // Bail if we are not sending to this node or we have enough for this node
369 + continue;
370 + }
371 + intents.put(leader, createIntent(key, keyPrefix + k, leader, devices));
372 +
373 + // Bump up the counter and remember this as the last key used.
374 + count++;
375 + lastKey = k;
376 + if (count % 1000 == 0) {
377 + log.info("Building intents... {} (attempt: {})", count, lastKey);
378 + }
379 + }
380 + checkState(intents.values().size() == numberOfKeys,
381 + "Generated wrong number of intents");
382 + log.info("Created {} intents", numberOfKeys);
383 + intents.keySet().forEach(node -> log.info("\t{}\t{}", node, intents.get(node).size()));
384 +
385 + return Sets.newHashSet(intents.values());
386 + }
387 +
388 + // Submits intent operations.
389 + final class Submitter implements Runnable {
390 +
391 + private long lastDuration;
392 + private int lastCount;
393 +
394 + private Set<Intent> intents = Sets.newHashSet();
395 + private Set<Intent> submitted = Sets.newHashSet();
396 + private Set<Intent> withdrawn = Sets.newHashSet();
397 +
398 + private Submitter(Set<Intent> intents) {
399 + this.intents = intents;
400 + lastCount = numKeys / 4;
401 + lastDuration = 1_000; // 1 second
402 + }
403 +
404 + @Override
405 + public void run() {
406 + prime();
407 + while (!stopped) {
408 + try {
409 + cycle();
410 + } catch (Exception e) {
411 + log.warn("Exception during cycle", e);
412 + }
413 + }
414 + clear();
415 + }
416 +
417 + private Iterable<Intent> subset(Set<Intent> intents) {
418 + List<Intent> subset = Lists.newArrayList(intents);
419 + Collections.shuffle(subset);
420 + return subset.subList(0, lastCount);
421 + }
422 +
423 + // Submits the specified intent.
424 + private void submit(Intent intent) {
425 + intentService.submit(intent);
426 + submitted.add(intent);
427 + withdrawn.remove(intent); //TODO could check result here...
428 + }
429 +
430 + // Withdraws the specified intent.
431 + private void withdraw(Intent intent) {
432 + intentService.withdraw(intent);
433 + withdrawn.add(intent);
434 + submitted.remove(intent); //TODO could check result here...
435 + }
436 +
437 + // Primes the cycle.
438 + private void prime() {
439 + int i = 0;
440 + withdrawn.addAll(intents);
441 + for (Intent intent : intents) {
442 + submit(intent);
443 + // only submit half of the intents to start
444 + if (i++ >= intents.size() / 2) {
445 + break;
446 + }
447 + }
448 + }
449 +
450 + private void clear() {
451 + submitted.forEach(this::withdraw);
452 + }
453 +
454 + // Runs a single operation cycle.
455 + private void cycle() {
456 + //TODO consider running without rate adjustment
457 + adjustRates();
458 +
459 + long start = currentTimeMillis();
460 + subset(submitted).forEach(this::withdraw);
461 + subset(withdrawn).forEach(this::submit);
462 + long delta = currentTimeMillis() - start;
463 +
464 + if (delta > cyclePeriod * 3 || delta < 0) {
465 + log.warn("Cycle took {} ms", delta);
466 + }
467 +
468 + int difference = cyclePeriod - (int) delta;
469 + if (difference > 0) {
470 + delay(difference);
471 + }
472 +
473 + lastDuration = delta;
474 + }
475 +
476 + int cycleCount = 0;
477 +
478 + private void adjustRates() {
479 +
480 + int addDelta = Math.max(1000 - cycleCount, 10);
481 + double multRatio = Math.min(0.8 + cycleCount * 0.0002, 0.995);
482 +
483 + //FIXME need to iron out the rate adjustment
484 + //FIXME we should taper the adjustments over time
485 + //FIXME don't just use the lastDuration, take an average
486 + if (++cycleCount % 5 == 0) { //TODO: maybe use a timer (we should do this every 5-10 sec)
487 + if (listener.requestThroughput() - listener.processedThroughput() <= 2000 && //was 500
488 + lastDuration <= cyclePeriod) {
489 + lastCount = Math.min(lastCount + addDelta, intents.size() / 2);
490 + } else {
491 + lastCount *= multRatio;
492 + }
493 + log.info("last count: {}, last duration: {} ms (sub: {} vs inst: {})",
494 + lastCount, lastDuration, listener.requestThroughput(), listener.processedThroughput());
495 + }
496 +
497 + }
498 + }
499 +
500 + // Event listener to monitor throughput.
501 + final class Listener implements IntentListener {
502 +
503 + private final Counter runningTotal = new Counter();
504 + private volatile Map<IntentEvent.Type, Counter> counters;
505 +
506 + private volatile double processedThroughput = 0;
507 + private volatile double requestThroughput = 0;
508 +
509 + public Listener() {
510 + counters = initCounters();
511 + }
512 +
513 + private Map<IntentEvent.Type, Counter> initCounters() {
514 + Map<IntentEvent.Type, Counter> map = Maps.newHashMap();
515 + for (IntentEvent.Type type : IntentEvent.Type.values()) {
516 + map.put(type, new Counter());
517 + }
518 + return map;
519 + }
520 +
521 + public double processedThroughput() {
522 + return processedThroughput;
523 + }
524 +
525 + public double requestThroughput() {
526 + return requestThroughput;
527 + }
528 +
529 + @Override
530 + public void event(IntentEvent event) {
531 + if (event.subject().appId().equals(appId)) {
532 + counters.get(event.type()).add(1);
533 + }
534 + }
535 +
536 + public void report() {
537 + Map<IntentEvent.Type, Counter> reportCounters = counters;
538 + counters = initCounters();
539 +
540 + // update running total and latest throughput
541 + Counter installed = reportCounters.get(INSTALLED);
542 + Counter withdrawn = reportCounters.get(WITHDRAWN);
543 + processedThroughput = installed.throughput() + withdrawn.throughput();
544 + runningTotal.add(installed.total() + withdrawn.total());
545 +
546 + Counter installReq = reportCounters.get(INSTALL_REQ);
547 + Counter withdrawReq = reportCounters.get(WITHDRAW_REQ);
548 + requestThroughput = installReq.throughput() + withdrawReq.throughput();
549 +
550 + // build the string to report
551 + StringBuilder stringBuilder = new StringBuilder();
552 + for (IntentEvent.Type type : IntentEvent.Type.values()) {
553 + Counter counter = reportCounters.get(type);
554 + stringBuilder.append(format("%s=%.2f;", type, counter.throughput()));
555 + }
556 + log.info("Throughput: OVERALL={}; CURRENT={}; {}",
557 + format("%.2f", runningTotal.throughput()),
558 + format("%.2f", processedThroughput),
559 + stringBuilder);
560 +
561 + sampleCollector.recordSample(runningTotal.throughput(),
562 + processedThroughput);
563 + }
564 + }
565 +
566 + private class InternalControl implements ClusterMessageHandler {
567 + @Override
568 + public void handle(ClusterMessage message) {
569 + String cmd = new String(message.payload());
570 + log.info("Received command {}", cmd);
571 + if (cmd.equals(START)) {
572 + startTestRun();
573 + } else {
574 + stopTestRun();
575 + }
576 + }
577 + }
578 +
579 + private class ReporterTask extends TimerTask {
580 + @Override
581 + public void run() {
582 + //adjustRates(); // FIXME we currently adjust rates in the cycle thread
583 + listener.report();
584 + }
585 + }
586 +
587 +}
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 org.apache.karaf.shell.commands.Command;
19 +import org.apache.karaf.shell.commands.Option;
20 +import org.onosproject.cli.AbstractShellCommand;
21 +import org.onosproject.intentperf.IntentPerfCollector.Sample;
22 +
23 +import java.text.SimpleDateFormat;
24 +import java.util.Date;
25 +import java.util.List;
26 +
27 +/**
28 + * Displays accumulated performance metrics.
29 + */
30 +@Command(scope = "onos", name = "intent-perf",
31 + description = "Displays accumulated performance metrics")
32 +public class IntentPerfListCommand extends AbstractShellCommand {
33 +
34 + @Option(name = "-s", aliases = "--summary", description = "Output just summary",
35 + required = false, multiValued = false)
36 + private boolean summary = false;
37 +
38 + @Override
39 + protected void execute() {
40 + if (summary) {
41 + printSummary();
42 + } else {
43 + printSamples();
44 + }
45 + }
46 +
47 + private void printSummary() {
48 + IntentPerfCollector collector = get(IntentPerfCollector.class);
49 + List<String> headers = collector.getSampleHeaders();
50 + Sample overall = collector.getOverall();
51 + double total = 0;
52 + print("%12s: %14s", "Node ID", "Overall Rate");
53 + for (int i = 0; i < overall.data.length; i++) {
54 + if (overall.data[i] >= 0) {
55 + print("%12s: %14.2f", headers.get(i), overall.data[i]);
56 + total += overall.data[i];
57 + } else {
58 + print("%12s: %14s", headers.get(i), " ");
59 + }
60 + }
61 + print("%12s: %14.2f", "total", total);
62 + }
63 +
64 + private void printSamples() {
65 + IntentPerfCollector collector = get(IntentPerfCollector.class);
66 + List<String> headers = collector.getSampleHeaders();
67 + List<Sample> samples = collector.getSamples();
68 +
69 + System.out.print(String.format("%10s ", "Time"));
70 + for (String header : headers) {
71 + System.out.print(String.format("%12s ", header));
72 + }
73 + System.out.println(String.format("%12s", "Total"));
74 +
75 + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
76 + for (Sample sample : samples) {
77 + double total = 0;
78 + System.out.print(String.format("%10s ", sdf.format(new Date(sample.time))));
79 + for (int i = 0; i < sample.data.length; i++) {
80 + if (sample.data[i] >= 0) {
81 + System.out.print(String.format("%12.2f ", sample.data[i]));
82 + total += sample.data[i];
83 + } else {
84 + System.out.print(String.format("%12s ", " "));
85 + }
86 + }
87 + System.out.println(String.format("%12.2f", total));
88 + }
89 + }
90 +
91 +}
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 org.apache.karaf.shell.commands.Command;
19 +import org.onosproject.cli.AbstractShellCommand;
20 +
21 +/**
22 + * Starts intent performance test run.
23 + */
24 +@Command(scope = "onos", name = "intent-perf-start",
25 + description = "Starts intent performance test run")
26 +public class IntentPerfStartCommand extends AbstractShellCommand {
27 +
28 + @Override
29 + protected void execute() {
30 + get(IntentPerfInstaller.class).start();
31 + }
32 +
33 +}
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 org.apache.karaf.shell.commands.Command;
19 +import org.onosproject.cli.AbstractShellCommand;
20 +
21 +/**
22 + * Stops intent performance test run.
23 + */
24 +@Command(scope = "onos", name = "intent-perf-stop",
25 + description = "Stops intent performance test run")
26 +public class IntentPerfStopCommand extends AbstractShellCommand {
27 +
28 + @Override
29 + protected void execute() {
30 + get(IntentPerfInstaller.class).stop();
31 + }
32 +
33 +}
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.fasterxml.jackson.databind.node.ObjectNode;
19 +import com.google.common.collect.ImmutableList;
20 +import com.google.common.collect.ImmutableSet;
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.osgi.ServiceDirectory;
27 +import org.onosproject.intentperf.IntentPerfCollector.Sample;
28 +import org.onosproject.ui.UiConnection;
29 +import org.onosproject.ui.UiExtension;
30 +import org.onosproject.ui.UiExtensionService;
31 +import org.onosproject.ui.UiMessageHandler;
32 +import org.onosproject.ui.UiView;
33 +
34 +import java.util.Collection;
35 +import java.util.HashSet;
36 +import java.util.List;
37 +import java.util.Set;
38 +
39 +import static java.util.Collections.synchronizedSet;
40 +
41 +/**
42 + * Mechanism to stream data to the GUI.
43 + */
44 +@Component(immediate = true, enabled = false)
45 +public class IntentPerfUi {
46 +
47 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
48 + protected UiExtensionService uiExtensionService;
49 +
50 + private final Set<StreamingControl> handlers = synchronizedSet(new HashSet<>());
51 +
52 + private List<UiView> views = ImmutableList.of(new UiView("intentPerf", "Intent Performance"));
53 + private UiExtension uiExtension = new UiExtension(views, this::newHandlers,
54 + getClass().getClassLoader());
55 +
56 + @Activate
57 + protected void activate() {
58 + uiExtensionService.register(uiExtension);
59 + }
60 +
61 + @Deactivate
62 + protected void deactivate() {
63 + uiExtensionService.unregister(uiExtension);
64 + }
65 +
66 + /**
67 + * Reports a single sample of performance data.
68 + *
69 + * @param sample performance sample
70 + */
71 + public void reportSample(Sample sample) {
72 + synchronized (handlers) {
73 + handlers.forEach(h -> h.send(sample));
74 + }
75 + }
76 +
77 + // Creates and returns session specific message handler.
78 + private Collection<UiMessageHandler> newHandlers() {
79 + return ImmutableList.of(new StreamingControl());
80 + }
81 +
82 + // UI Message handlers for turning on/off reporting to a session.
83 + private class StreamingControl extends UiMessageHandler {
84 +
85 + private boolean streamingEnabled = false;
86 +
87 + protected StreamingControl() {
88 + super(ImmutableSet.of("intentPerfStart", "intentPerfStop"));
89 + }
90 +
91 + @Override
92 + public void process(ObjectNode message) {
93 + streamingEnabled = message.path("event").asText("unknown").equals("initPerfStart");
94 + }
95 +
96 + @Override
97 + public void init(UiConnection connection, ServiceDirectory directory) {
98 + super.init(connection, directory);
99 + handlers.add(this);
100 + }
101 +
102 + @Override
103 + public void destroy() {
104 + super.destroy();
105 + handlers.remove(this);
106 + }
107 +
108 + private void send(Sample sample) {
109 + // FIXME: finish this
110 + ObjectNode sn = mapper.createObjectNode()
111 + .put("time", sample.time);
112 + connection().sendMessage("intentPerf", 0, sn);
113 + }
114 + }
115 +
116 +}
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 +
17 +/**
18 + * Performance test application that induces steady load on the intent subsystem.
19 + */
20 +package org.onosproject.intentperf;
...\ 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 +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
17 + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
18 + <command>
19 + <action class="org.onosproject.intentperf.IntentPerfListCommand"/>
20 + </command>
21 + <command>
22 + <action class="org.onosproject.intentperf.IntentPerfStartCommand"/>
23 + </command>
24 + <command>
25 + <action class="org.onosproject.intentperf.IntentPerfStopCommand"/>
26 + </command>
27 + </command-bundle>
28 +</blueprint>
1 +date,value,node
2 +00:55:15,68.38,node1
3 +00:55:15,55.61,node2
4 +00:55:15,74.00,node3
5 +00:55:30,74.20,node1
6 +00:55:30,77.60,node2
7 +00:55:30,74.80,node3
8 +00:55:45,74.60,node1
9 +00:55:45,72.80,node2
10 +00:55:45,77.00,node3
11 +00:56:00,73.60,node1
12 +00:56:00,75.00,node2
13 +00:56:00,76.98,node3
14 +00:56:15,75.82,node1
15 +00:56:15,75.40,node2
16 +00:56:15,76.00,node3
17 +00:56:30,75.60,node1
18 +00:56:30,74.59,node2
19 +00:56:30,74.01,node3
...\ No newline at end of file ...\ No newline at end of file
1 +key,value,date
2 +Group1,37,00:23:00
3 +Group2,12,00:23:00
4 +Group3,46,00:23:00
5 +Group1,32,00:23:05
6 +Group2,19,00:23:05
7 +Group3,42,00:23:05
8 +Group1,45,00:23:10
9 +Group2,16,00:23:10
10 +Group3,44,00:23:10
11 +Group1,24,00:23:15
12 +Group2,52,00:23:15
13 +Group3,64,00:23:15
14 +Group1,34,00:23:20
15 +Group2,62,00:23:20
16 +Group3,74,00:23:20
17 +Group1,34,00:23:25
18 +Group2,62,00:23:25
19 +Group3,74,00:23:25
...\ 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 +
17 +/*
18 + ONOS GUI -- Intent Perf View -- CSS file
19 + */
20 +
21 +.light #ov-intentPerf {
22 + color: navy;
23 +}
24 +
25 +.dark #ov-intentPerf {
26 + color: #1e5e6f;
27 +}
28 +
29 +.dark a {
30 + color: #88c;
31 +}
32 +
33 +#ov-intentPerf .msg {
34 + color: darkorange;
35 +}
36 +
37 +.light #ov-intentPerf .msg {
38 + color: darkorange;
39 +}
40 +
41 +.dark #ov-intentPerf .msg {
42 + color: #904e00;
43 +}
44 +
45 +
46 +
47 +.axis path,
48 +.axis line {
49 + fill: none;
50 + stroke: #000;
51 + shape-rendering: crispEdges;
52 +}
53 +
54 +.browser text {
55 + text-anchor: end;
56 +}
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 +
17 +<!-- Intent Performance partial HTML -->
18 +<div id="ov-sample">
19 + <h2> Intent Performance View </h2>
20 +
21 + <span class="msg">{{ ctrl.message }}</span>
22 +
23 + <div id="intent-perf-chart"></div>
24 +</div>
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 +
17 +/*
18 + ONOS GUI -- Intent Performance View Module
19 + */
20 +(function () {
21 + 'use strict';
22 +
23 + // injected refs
24 + var $log, tbs, flash;
25 +
26 + function start() {
27 + //var format = d3.time.format("%m/%d/%y");
28 + var format = d3.time.format("%H:%M:%S");
29 + var samples = [];
30 +
31 + var margin = {top: 20, right: 30, bottom: 30, left: 40},
32 + width = 960 - margin.left - margin.right,
33 + height = 500 - margin.top - margin.bottom;
34 +
35 + var x = d3.time.scale()
36 + .range([0, width]);
37 +
38 + var y = d3.scale.linear()
39 + .range([height, 0]);
40 +
41 + var z = d3.scale.category20c();
42 +
43 + var xAxis = d3.svg.axis()
44 + .scale(x)
45 + .orient("bottom")
46 + .ticks(d3.time.seconds);
47 +
48 + var yAxis = d3.svg.axis()
49 + .scale(y)
50 + .orient("left");
51 +
52 + var stack = d3.layout.stack()
53 + .offset("zero")
54 + .values(function(d) { return d.values; })
55 + .x(function(d) { return d.date; })
56 + .y(function(d) { return d.value; });
57 +
58 + var nest = d3.nest()
59 + .key(function(d) { return d.key; });
60 +
61 + var area = d3.svg.area()
62 + .interpolate("cardinal")
63 + .x(function(d) { return x(d.date); })
64 + .y0(function(d) { return y(d.y0); })
65 + .y1(function(d) { return y(d.y0 + d.y); });
66 +
67 + var svg = d3.select("body").append("svg")
68 + .attr("width", width + margin.left + margin.right)
69 + .attr("height", height + margin.top + margin.bottom)
70 + .append("g")
71 + .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
72 +
73 + svg.append("g")
74 + .attr("class", "x axis")
75 + .attr("transform", "translate(0," + height + ")")
76 + .call(xAxis);
77 +
78 + svg.append("g")
79 + .attr("class", "y axis")
80 + .call(yAxis);
81 +
82 + function fetchData() {
83 + d3.csv("app/view/intentPerf/data.csv", function (data) {
84 + samples = data;
85 + updateGraph();
86 + });
87 + }
88 +
89 + function updateGraph() {
90 + samples.forEach(function(d) {
91 + d.date = format.parse(d.date);
92 + d.value = +d.value;
93 + });
94 +
95 + var layers = stack(nest.entries(samples));
96 +
97 + x.domain(d3.extent(samples, function(d) { return d.date; }));
98 + y.domain([0, d3.max(samples, function(d) { return d.y0 + d.y; })]);
99 +
100 + svg.selectAll(".layer")
101 + .data(layers)
102 + .enter().append("path")
103 + .attr("class", "layer")
104 + .attr("d", function(d) { return area(d.values); })
105 + .style("fill", function(d, i) { return z(i); });
106 +
107 + svg.select(".x")
108 + .attr("transform", "translate(0," + height + ")")
109 + .call(xAxis);
110 +
111 + svg.select(".y")
112 + .call(yAxis);
113 +
114 + console.log('tick');
115 + }
116 + }
117 +
118 + start();
119 +
120 + // define the controller
121 +
122 + angular.module('ovIntentPerf', ['onosUtil'])
123 + .controller('OvIntentPerfCtrl',
124 + ['$scope', '$log', 'ToolbarService', 'FlashService',
125 +
126 + function ($scope, _$log_, _tbs_, _flash_) {
127 + var self = this
128 +
129 + $log = _$log_;
130 + tbs = _tbs_;
131 + flash = _flash_;
132 +
133 + self.message = 'Hey there dudes!';
134 + start();
135 +
136 + // Clean up on destroyed scope
137 + $scope.$on('$destroy', function () {
138 + });
139 +
140 + $log.log('OvIntentPerfCtrl has been created');
141 + }]);
142 +}());
1 +<link rel="stylesheet" href="app/view/intentPerf/intentPerf.css">
1 +<!DOCTYPE html>
2 +<!--
3 + ~ Copyright 2014 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 +<html>
18 +<head>
19 + <title>Dev View</title>
20 + <script src="tp/d3.min.js"></script>
21 + <script src="tp/jquery-2.1.1.min.js"></script>
22 +
23 + <link rel="stylesheet" href="app/view/intentPerf/intentPerf.css">
24 +</head>
25 +<body>
26 +<div id="intent-perf-chart" style="width: 1024px; height: 800px"></div>
27 +<script src="app/view/intentPerf/intentPerf.js"></script>
28 +</body>
29 +</html>
...\ No newline at end of file ...\ No newline at end of file
1 +<script src="app/view/intentPerf/intentPerf.js"></script>
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</artifactId>
25 + <version>1.2.0-SNAPSHOT</version>
26 + <relativePath>../pom.xml</relativePath>
27 + </parent>
28 +
29 + <artifactId>onos-apps-test</artifactId>
30 + <packaging>pom</packaging>
31 +
32 + <description>ONOS test applications</description>
33 +
34 + <modules>
35 + <module>election</module>
36 + <module>intent-perf</module>
37 + </modules>
38 +
39 +</project>