Committed by
Gerrit Code Review
Prototyping GUI & CLI for the intent performance test fixture.
Change-Id: I1f33872e62b55d168ccd8f2904472e41ecba4cc8
Showing
18 changed files
with
798 additions
and
22 deletions
... | @@ -31,6 +31,19 @@ | ... | @@ -31,6 +31,19 @@ |
31 | 31 | ||
32 | <description>ONOS intent perf app bundle</description> | 32 | <description>ONOS intent perf app bundle</description> |
33 | 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 | + </dependencies> | ||
46 | + | ||
34 | <build> | 47 | <build> |
35 | <plugins> | 48 | <plugins> |
36 | <plugin> | 49 | <plugin> | ... | ... |
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_WINDOW = 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 | + this.nodeId = clusterService.getLocalNode().id(); | ||
86 | + this.newestTime = 0; | ||
87 | + | ||
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 | + overall = new Sample(0, nodes.length); | ||
102 | + current = new Sample(0, nodes.length); | ||
103 | + | ||
104 | + log.info("Started"); | ||
105 | + } | ||
106 | + | ||
107 | + @Deactivate | ||
108 | + public void deactivate() { | ||
109 | + messageHandlingExecutor.shutdown(); | ||
110 | + communicationService.removeSubscriber(SAMPLE); | ||
111 | + log.info("Stopped"); | ||
112 | + } | ||
113 | + | ||
114 | + /** | ||
115 | + * Records a sample point of data about intent operation rate. | ||
116 | + * | ||
117 | + * @param overallRate overall rate | ||
118 | + * @param currentRate current rate | ||
119 | + */ | ||
120 | + public void recordSample(double overallRate, double currentRate) { | ||
121 | + try { | ||
122 | + long now = System.currentTimeMillis(); | ||
123 | + addSample(now, nodeId, overallRate, currentRate); | ||
124 | + broadcastSample(now, nodeId, overallRate, currentRate); | ||
125 | + } catch (Exception e) { | ||
126 | + log.error("Boom!", e); | ||
127 | + } | ||
128 | + } | ||
129 | + | ||
130 | + /** | ||
131 | + * Returns set of node ids as headers. | ||
132 | + * | ||
133 | + * @return node id headers | ||
134 | + */ | ||
135 | + public List<String> getSampleHeaders() { | ||
136 | + List<String> headers = new ArrayList<>(); | ||
137 | + for (ControllerNode node : nodes) { | ||
138 | + headers.add(node.id().toString()); | ||
139 | + } | ||
140 | + return headers; | ||
141 | + } | ||
142 | + | ||
143 | + /** | ||
144 | + * Returns set of all accumulated samples normalized to the local set of | ||
145 | + * samples. | ||
146 | + * | ||
147 | + * @return accumulated samples | ||
148 | + */ | ||
149 | + public synchronized List<Sample> getSamples() { | ||
150 | + return ImmutableList.copyOf(samples); | ||
151 | + } | ||
152 | + | ||
153 | + /** | ||
154 | + * Returns overall throughput performance for each of the cluster nodes. | ||
155 | + * | ||
156 | + * @return overall intent throughput | ||
157 | + */ | ||
158 | + public synchronized Sample getOverall() { | ||
159 | + return overall; | ||
160 | + } | ||
161 | + | ||
162 | + // Records a new sample to our collection of samples | ||
163 | + private synchronized void addSample(long time, NodeId nodeId, | ||
164 | + double overallRate, double currentRate) { | ||
165 | + Sample fullSample = createCurrentSampleIfNeeded(time); | ||
166 | + setSampleData(current, nodeId, currentRate); | ||
167 | + setSampleData(overall, nodeId, overallRate); | ||
168 | + pruneSamplesIfNeeded(); | ||
169 | + | ||
170 | + if (fullSample != null && ui != null) { | ||
171 | + ui.reportSample(fullSample); | ||
172 | + } | ||
173 | + } | ||
174 | + | ||
175 | + private Sample createCurrentSampleIfNeeded(long time) { | ||
176 | + Sample oldSample = time - newestTime > SAMPLE_WINDOW || current.isComplete() ? current : null; | ||
177 | + if (oldSample != null) { | ||
178 | + newestTime = time; | ||
179 | + current = new Sample(time, nodes.length); | ||
180 | + if (oldSample.time > 0) { | ||
181 | + samples.add(oldSample); | ||
182 | + } | ||
183 | + } | ||
184 | + return oldSample; | ||
185 | + } | ||
186 | + | ||
187 | + private void setSampleData(Sample sample, NodeId nodeId, double data) { | ||
188 | + Integer index = nodeToIndex.get(nodeId); | ||
189 | + if (index != null) { | ||
190 | + sample.data[index] = data; | ||
191 | + } | ||
192 | + } | ||
193 | + | ||
194 | + private void pruneSamplesIfNeeded() { | ||
195 | + if (samples.size() > MAX_SAMPLES) { | ||
196 | + samples.remove(0); | ||
197 | + } | ||
198 | + } | ||
199 | + | ||
200 | + // Performance data sample. | ||
201 | + static class Sample { | ||
202 | + final long time; | ||
203 | + final double[] data; | ||
204 | + | ||
205 | + public Sample(long time, int nodeCount) { | ||
206 | + this.time = time; | ||
207 | + this.data = new double[nodeCount]; | ||
208 | + Arrays.fill(data, -1); | ||
209 | + } | ||
210 | + | ||
211 | + public boolean isComplete() { | ||
212 | + for (int i = 0; i < data.length; i++) { | ||
213 | + if (data[i] < 0) { | ||
214 | + return false; | ||
215 | + } | ||
216 | + } | ||
217 | + return true; | ||
218 | + } | ||
219 | + } | ||
220 | + | ||
221 | + private void broadcastSample(long time, NodeId nodeId, double overallRate, double currentRate) { | ||
222 | + String data = String.format("%d|%f|%f", time, overallRate, currentRate); | ||
223 | + communicationService.broadcast(new ClusterMessage(nodeId, SAMPLE, data.getBytes())); | ||
224 | + } | ||
225 | + | ||
226 | + private class InternalSampleCollector implements ClusterMessageHandler { | ||
227 | + @Override | ||
228 | + public void handle(ClusterMessage message) { | ||
229 | + String[] fields = new String(message.payload()).split("\\|"); | ||
230 | + log.info("Received sample from {}: {}", message.sender(), fields); | ||
231 | + addSample(Long.parseLong(fields[0]), message.sender(), | ||
232 | + Double.parseDouble(fields[1]), Double.parseDouble(fields[1])); | ||
233 | + } | ||
234 | + } | ||
235 | +} |
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 IntentPerfCommand 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 | + for (int i = 0; i < overall.data.length; i++) { | ||
53 | + print("%12s: %12.2f", headers.get(i), overall.data[i]); | ||
54 | + total += overall.data[i]; | ||
55 | + } | ||
56 | + print("%12s: %12.2f", "total", total); | ||
57 | + } | ||
58 | + | ||
59 | + private void printSamples() { | ||
60 | + IntentPerfCollector collector = get(IntentPerfCollector.class); | ||
61 | + List<String> headers = collector.getSampleHeaders(); | ||
62 | + List<Sample> samples = collector.getSamples(); | ||
63 | + | ||
64 | + System.out.print(String.format("%10s ", "Time")); | ||
65 | + for (String header : headers) { | ||
66 | + System.out.print(String.format("%12s ", header)); | ||
67 | + } | ||
68 | + System.out.println(String.format("%12s", "Total")); | ||
69 | + | ||
70 | + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); | ||
71 | + for (Sample sample : samples) { | ||
72 | + double total = 0; | ||
73 | + System.out.print(String.format("%10s ", sdf.format(new Date(sample.time)))); | ||
74 | + for (int i = 0; i < sample.data.length; i++) { | ||
75 | + if (sample.data[i] >= 0) { | ||
76 | + System.out.print(String.format("%12.2f ", sample.data[i])); | ||
77 | + total += sample.data[i]; | ||
78 | + } else { | ||
79 | + System.out.print(String.format("%12s ", " ")); | ||
80 | + } | ||
81 | + } | ||
82 | + System.out.println(String.format("%12.2f", total)); | ||
83 | + } | ||
84 | + } | ||
85 | + | ||
86 | +} |
... | @@ -94,7 +94,7 @@ public class IntentPerfInstaller { | ... | @@ -94,7 +94,7 @@ public class IntentPerfInstaller { |
94 | //FIXME add path length | 94 | //FIXME add path length |
95 | 95 | ||
96 | @Property(name = "numKeys", intValue = DEFAULT_NUM_KEYS, | 96 | @Property(name = "numKeys", intValue = DEFAULT_NUM_KEYS, |
97 | - label = "Number of keys (i.e. unique intents) to generate per instance") | 97 | + label = "Number of keys (i.e. unique intents) to generate per instance") |
98 | private int numKeys = DEFAULT_NUM_KEYS; | 98 | private int numKeys = DEFAULT_NUM_KEYS; |
99 | 99 | ||
100 | //TODO implement numWorkers property | 100 | //TODO implement numWorkers property |
... | @@ -103,11 +103,11 @@ public class IntentPerfInstaller { | ... | @@ -103,11 +103,11 @@ public class IntentPerfInstaller { |
103 | // private int numWokers = DEFAULT_NUM_WORKERS; | 103 | // private int numWokers = DEFAULT_NUM_WORKERS; |
104 | 104 | ||
105 | @Property(name = "cyclePeriod", intValue = DEFAULT_GOAL_CYCLE_PERIOD, | 105 | @Property(name = "cyclePeriod", intValue = DEFAULT_GOAL_CYCLE_PERIOD, |
106 | - label = "Goal for cycle period (in ms)") | 106 | + label = "Goal for cycle period (in ms)") |
107 | private int cyclePeriod = DEFAULT_GOAL_CYCLE_PERIOD; | 107 | private int cyclePeriod = DEFAULT_GOAL_CYCLE_PERIOD; |
108 | 108 | ||
109 | @Property(name = "numNeighbors", intValue = DEFAULT_NUM_NEIGHBORS, | 109 | @Property(name = "numNeighbors", intValue = DEFAULT_NUM_NEIGHBORS, |
110 | - label = "Number of neighbors to generate intents for") | 110 | + label = "Number of neighbors to generate intents for") |
111 | private int numNeighbors = DEFAULT_NUM_NEIGHBORS; | 111 | private int numNeighbors = DEFAULT_NUM_NEIGHBORS; |
112 | 112 | ||
113 | @Reference(cardinality = MANDATORY_UNARY) | 113 | @Reference(cardinality = MANDATORY_UNARY) |
... | @@ -131,6 +131,9 @@ public class IntentPerfInstaller { | ... | @@ -131,6 +131,9 @@ public class IntentPerfInstaller { |
131 | @Reference(cardinality = MANDATORY_UNARY) | 131 | @Reference(cardinality = MANDATORY_UNARY) |
132 | protected ComponentConfigService configService; | 132 | protected ComponentConfigService configService; |
133 | 133 | ||
134 | + @Reference(cardinality = MANDATORY_UNARY) | ||
135 | + protected IntentPerfCollector sampleCollector; | ||
136 | + | ||
134 | private ExecutorService workers; | 137 | private ExecutorService workers; |
135 | private ApplicationId appId; | 138 | private ApplicationId appId; |
136 | private Listener listener; | 139 | private Listener listener; |
... | @@ -141,9 +144,11 @@ public class IntentPerfInstaller { | ... | @@ -141,9 +144,11 @@ public class IntentPerfInstaller { |
141 | // FIXME this variable isn't shared properly between multiple worker threads | 144 | // FIXME this variable isn't shared properly between multiple worker threads |
142 | private int lastKey = 0; | 145 | private int lastKey = 0; |
143 | 146 | ||
147 | + private IntentPerfUi perfUi; | ||
148 | + | ||
144 | @Activate | 149 | @Activate |
145 | public void activate() { | 150 | public void activate() { |
146 | - configService.registerProperties(getClass()); | 151 | +// configService.registerProperties(getClass()); |
147 | 152 | ||
148 | String nodeId = clusterService.getLocalNode().ip().toString(); | 153 | String nodeId = clusterService.getLocalNode().ip().toString(); |
149 | appId = coreService.registerApplication("org.onosproject.intentperf." + nodeId); | 154 | appId = coreService.registerApplication("org.onosproject.intentperf." + nodeId); |
... | @@ -169,7 +174,7 @@ public class IntentPerfInstaller { | ... | @@ -169,7 +174,7 @@ public class IntentPerfInstaller { |
169 | 174 | ||
170 | @Deactivate | 175 | @Deactivate |
171 | public void deactivate() { | 176 | public void deactivate() { |
172 | - configService.unregisterProperties(getClass(), false); | 177 | +// configService.unregisterProperties(getClass(), false); |
173 | stop(); | 178 | stop(); |
174 | } | 179 | } |
175 | 180 | ||
... | @@ -266,19 +271,15 @@ public class IntentPerfInstaller { | ... | @@ -266,19 +271,15 @@ public class IntentPerfInstaller { |
266 | * @return set of intents | 271 | * @return set of intents |
267 | */ | 272 | */ |
268 | private Set<Intent> createIntents(int numberOfKeys, int pathLength, int firstKey) { | 273 | private Set<Intent> createIntents(int numberOfKeys, int pathLength, int firstKey) { |
269 | - //Set<Intent> result = new HashSet<>(); | ||
270 | - | ||
271 | List<NodeId> neighbors = getNeighbors(); | 274 | List<NodeId> neighbors = getNeighbors(); |
272 | 275 | ||
273 | Multimap<NodeId, Device> devices = ArrayListMultimap.create(); | 276 | Multimap<NodeId, Device> devices = ArrayListMultimap.create(); |
274 | - deviceService.getAvailableDevices().forEach(device -> | 277 | + deviceService.getAvailableDevices() |
275 | - devices.put(mastershipService.getMasterFor(device.id()), device)); | 278 | + .forEach(device -> devices.put(mastershipService.getMasterFor(device.id()), device)); |
276 | 279 | ||
277 | // ensure that we have at least one device per neighbor | 280 | // ensure that we have at least one device per neighbor |
278 | - neighbors.forEach(node -> | 281 | + neighbors.forEach(node -> checkState(devices.get(node).size() > 0, |
279 | - checkState(devices.get(node).size() > 0, | 282 | + "There are no devices for {}", node)); |
280 | - "There are no devices for {}", node)); | ||
281 | - | ||
282 | 283 | ||
283 | // TODO pull this outside so that createIntent can use it | 284 | // TODO pull this outside so that createIntent can use it |
284 | // prefix based on node id for keys generated on this instance | 285 | // prefix based on node id for keys generated on this instance |
... | @@ -401,6 +402,7 @@ public class IntentPerfInstaller { | ... | @@ -401,6 +402,7 @@ public class IntentPerfInstaller { |
401 | } | 402 | } |
402 | 403 | ||
403 | int cycleCount = 0; | 404 | int cycleCount = 0; |
405 | + | ||
404 | private void adjustRates() { | 406 | private void adjustRates() { |
405 | 407 | ||
406 | int addDelta = Math.max(1000 - cycleCount, 10); | 408 | int addDelta = Math.max(1000 - cycleCount, 10); |
... | @@ -483,6 +485,10 @@ public class IntentPerfInstaller { | ... | @@ -483,6 +485,10 @@ public class IntentPerfInstaller { |
483 | format("%.2f", runningTotal.throughput()), | 485 | format("%.2f", runningTotal.throughput()), |
484 | format("%.2f", processedThroughput), | 486 | format("%.2f", processedThroughput), |
485 | stringBuilder); | 487 | stringBuilder); |
488 | + | ||
489 | + sampleCollector.recordSample(runningTotal.throughput(), | ||
490 | + processedThroughput); | ||
486 | } | 491 | } |
487 | } | 492 | } |
493 | + | ||
488 | } | 494 | } | ... | ... |
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 | +<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.IntentPerfCommand"/> | ||
20 | + </command> | ||
21 | + </command-bundle> | ||
22 | +</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 | +}()); |
apps/intent-perf/src/main/resources/css.html
0 → 100644
1 | +<link rel="stylesheet" href="app/view/intentPerf/intentPerf.css"> |
apps/intent-perf/src/main/resources/dev.html
0 → 100644
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 |
apps/intent-perf/src/main/resources/js.html
0 → 100644
1 | +<script src="app/view/intentPerf/intentPerf.js"></script> |
... | @@ -412,11 +412,6 @@ public class EventuallyConsistentMapImpl<K, V> | ... | @@ -412,11 +412,6 @@ public class EventuallyConsistentMapImpl<K, V> |
412 | peerUpdateFunction.apply(key, value)); | 412 | peerUpdateFunction.apply(key, value)); |
413 | notifyListeners(new EventuallyConsistentMapEvent<>( | 413 | notifyListeners(new EventuallyConsistentMapEvent<>( |
414 | EventuallyConsistentMapEvent.Type.REMOVE, key, value)); | 414 | EventuallyConsistentMapEvent.Type.REMOVE, key, value)); |
415 | - } else { | ||
416 | - // TODO remove this extra call when ONOS-1207 is resolved | ||
417 | - Timestamped<V> latest = (Timestamped) items.get(key); | ||
418 | - log.info("Remove of intent {} failed; request time {} vs. latest time {}", | ||
419 | - key, timestamp, latest.timestamp()); | ||
420 | } | 415 | } |
421 | } | 416 | } |
422 | 417 | ... | ... |
... | @@ -26,6 +26,8 @@ import org.onosproject.cluster.ClusterStoreDelegate; | ... | @@ -26,6 +26,8 @@ import org.onosproject.cluster.ClusterStoreDelegate; |
26 | import org.onosproject.cluster.ControllerNode; | 26 | import org.onosproject.cluster.ControllerNode; |
27 | import org.onosproject.cluster.DefaultControllerNode; | 27 | import org.onosproject.cluster.DefaultControllerNode; |
28 | import org.onosproject.cluster.NodeId; | 28 | import org.onosproject.cluster.NodeId; |
29 | +import org.onosproject.net.intent.Key; | ||
30 | +import org.onosproject.net.intent.PartitionService; | ||
29 | import org.onosproject.store.AbstractStore; | 31 | import org.onosproject.store.AbstractStore; |
30 | import org.onlab.packet.IpAddress; | 32 | import org.onlab.packet.IpAddress; |
31 | import org.slf4j.Logger; | 33 | import org.slf4j.Logger; |
... | @@ -42,7 +44,7 @@ import static org.slf4j.LoggerFactory.getLogger; | ... | @@ -42,7 +44,7 @@ import static org.slf4j.LoggerFactory.getLogger; |
42 | @Service | 44 | @Service |
43 | public class SimpleClusterStore | 45 | public class SimpleClusterStore |
44 | extends AbstractStore<ClusterEvent, ClusterStoreDelegate> | 46 | extends AbstractStore<ClusterEvent, ClusterStoreDelegate> |
45 | - implements ClusterStore { | 47 | + implements ClusterStore, PartitionService { |
46 | 48 | ||
47 | public static final IpAddress LOCALHOST = IpAddress.valueOf("127.0.0.1"); | 49 | public static final IpAddress LOCALHOST = IpAddress.valueOf("127.0.0.1"); |
48 | 50 | ||
... | @@ -91,4 +93,13 @@ public class SimpleClusterStore | ... | @@ -91,4 +93,13 @@ public class SimpleClusterStore |
91 | public void removeNode(NodeId nodeId) { | 93 | public void removeNode(NodeId nodeId) { |
92 | } | 94 | } |
93 | 95 | ||
96 | + @Override | ||
97 | + public boolean isMine(Key intentKey) { | ||
98 | + return true; | ||
99 | + } | ||
100 | + | ||
101 | + @Override | ||
102 | + public NodeId getLeader(Key intentKey) { | ||
103 | + return instance.id(); | ||
104 | + } | ||
94 | } | 105 | } | ... | ... |
... | @@ -4,7 +4,7 @@ export ONOS_NIC=192.168.56.* | ... | @@ -4,7 +4,7 @@ export ONOS_NIC=192.168.56.* |
4 | export OC1="192.168.56.11" | 4 | export OC1="192.168.56.11" |
5 | export OC2="192.168.56.12" | 5 | export OC2="192.168.56.12" |
6 | export OC3="192.168.56.13" | 6 | export OC3="192.168.56.13" |
7 | -export OCN="192.168.56.7" | 7 | +export OCN="192.168.56.14" |
8 | export OCI="${OC1}" | 8 | export OCI="${OC1}" |
9 | 9 | ||
10 | -export ONOS_FEATURES=webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-mobility | 10 | +unset ONOS_FEATURES | ... | ... |
... | @@ -9,4 +9,5 @@ export OCN="10.128.11.4" | ... | @@ -9,4 +9,5 @@ export OCN="10.128.11.4" |
9 | export OCI="${OC1}" | 9 | export OCI="${OC1}" |
10 | export OCT="${OC1}" | 10 | export OCT="${OC1}" |
11 | 11 | ||
12 | -export ONOS_FEATURES="webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-null" | 12 | +export ONOS_FEATURES="webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow" |
13 | +export ONOS_FEATURES="webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-null" | ... | ... |
-
Please register or login to post a comment