Simon Hunt

ONOS-1479 -- GUI - augmenting topology view for extensibility: WIP.

- Major refactoring of TopologyViewMessageHandler and related classes.

Change-Id: I920f7f9f7317f3987a9a8da35ac086e9f8cab8d3
Showing 22 changed files with 1431 additions and 42 deletions
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 +package org.onosproject.ui.topo;
19 +
20 +/**
21 + * Partial implementation of the types of highlight to apply to topology
22 + * elements.
23 + */
24 +public abstract class AbstractHighlight {
25 + private final TopoElementType type;
26 + private final String elementId;
27 +
28 + public AbstractHighlight(TopoElementType type, String elementId) {
29 + this.type = type;
30 + this.elementId = elementId;
31 + }
32 +
33 + public TopoElementType type() {
34 + return type;
35 + }
36 +
37 + public String elementId() {
38 + return elementId;
39 + }
40 +}
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 +package org.onosproject.ui.topo;
19 +
20 +/**
21 + * Denotes the types of highlight to apply to a link.
22 + */
23 +public class DeviceHighlight extends AbstractHighlight {
24 +
25 + public DeviceHighlight(String deviceId) {
26 + super(TopoElementType.DEVICE, deviceId);
27 + }
28 +
29 +
30 +}
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 +package org.onosproject.ui.topo;
19 +
20 +import java.text.DecimalFormat;
21 +import java.util.Collections;
22 +import java.util.HashSet;
23 +import java.util.Set;
24 +
25 +/**
26 + * Encapsulates highlights to be applied to the topology view, such as
27 + * highlighting links, displaying link labels, perhaps even decorating
28 + * nodes with badges, etc.
29 + */
30 +public class Highlights {
31 +
32 + private static final DecimalFormat DF0 = new DecimalFormat("#,###");
33 +
34 + private final Set<DeviceHighlight> devices = new HashSet<>();
35 + private final Set<HostHighlight> hosts = new HashSet<>();
36 + private final Set<LinkHighlight> links = new HashSet<>();
37 +
38 +
39 + public Highlights add(DeviceHighlight d) {
40 + devices.add(d);
41 + return this;
42 + }
43 +
44 + public Highlights add(HostHighlight h) {
45 + hosts.add(h);
46 + return this;
47 + }
48 +
49 + public Highlights add(LinkHighlight lh) {
50 + links.add(lh);
51 + return this;
52 + }
53 +
54 +
55 + public Set<DeviceHighlight> devices() {
56 + return Collections.unmodifiableSet(devices);
57 + }
58 +
59 + public Set<HostHighlight> hosts() {
60 + return Collections.unmodifiableSet(hosts);
61 + }
62 +
63 + public Set<LinkHighlight> links() {
64 + return Collections.unmodifiableSet(links);
65 + }
66 +}
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 +package org.onosproject.ui.topo;
19 +
20 +/**
21 + * Denotes the types of highlight to apply to a link.
22 + */
23 +public class HostHighlight extends AbstractHighlight {
24 +
25 + public HostHighlight(String hostId) {
26 + super(TopoElementType.HOST, hostId);
27 + }
28 +
29 +
30 +}
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 +package org.onosproject.ui.topo;
19 +
20 +import java.util.Collections;
21 +import java.util.Set;
22 +import java.util.TreeSet;
23 +
24 +import static com.google.common.base.Preconditions.checkNotNull;
25 +
26 +/**
27 + * Denotes the highlighting to be applied to a link.
28 + * {@link Flavor} is a closed set of NO-, PRIMARY-, or SECONDARY- highlighting.
29 + * {@link Mod} is an open ended set of additional modifications (CSS classes)
30 + * to apply. Note that {@link #MOD_OPTICAL} and {@link #MOD_ANIMATED} are
31 + * pre-defined mods.
32 + * Label text may be set, which will also be displayed on the link.
33 + */
34 +public class LinkHighlight extends AbstractHighlight {
35 +
36 + private static final String PLAIN = "plain";
37 + private static final String PRIMARY = "primary";
38 + private static final String SECONDARY = "secondary";
39 + private static final String EMPTY = "";
40 + private static final String SPACE = " ";
41 +
42 + private final Flavor flavor;
43 + private final Set<Mod> mods = new TreeSet<>();
44 + private String label = EMPTY;
45 +
46 + /**
47 + * Constructs a link highlight entity.
48 + *
49 + * @param linkId the link identifier
50 + * @param flavor the highlight flavor
51 + */
52 + public LinkHighlight(String linkId, Flavor flavor) {
53 + super(TopoElementType.LINK, linkId);
54 + this.flavor = checkNotNull(flavor);
55 + }
56 +
57 + /**
58 + * Adds a highlighting modification to this link highlight.
59 + *
60 + * @param mod mod to be added
61 + * @return self, for chaining
62 + */
63 + public LinkHighlight addMod(Mod mod) {
64 + mods.add(checkNotNull(mod));
65 + return this;
66 + }
67 +
68 + /**
69 + * Adds a label to be displayed on the link.
70 + *
71 + * @param label the label text
72 + * @return self, for chaining
73 + */
74 + public LinkHighlight setLabel(String label) {
75 + this.label = label == null ? EMPTY : label;
76 + return this;
77 + }
78 +
79 + /**
80 + * Returns the highlight flavor.
81 + *
82 + * @return highlight flavor
83 + */
84 + public Flavor flavor() {
85 + return flavor;
86 + }
87 +
88 + /**
89 + * Returns the highlight modifications.
90 + *
91 + * @return highlight modifications
92 + */
93 + public Set<Mod> mods() {
94 + return Collections.unmodifiableSet(mods);
95 + }
96 +
97 + /**
98 + * Generates the CSS classes string from the {@link #flavor} and
99 + * any optional {@link #mods}.
100 + *
101 + * @return CSS classes string
102 + */
103 + public String cssClasses() {
104 + StringBuilder sb = new StringBuilder(flavor.toString());
105 + mods.forEach(m -> sb.append(SPACE).append(m));
106 + return sb.toString();
107 + }
108 +
109 + /**
110 + * Returns the label text.
111 + *
112 + * @return label text
113 + */
114 + public String label() {
115 + return label;
116 + }
117 +
118 + /**
119 + * Link highlighting flavor.
120 + */
121 + public enum Flavor {
122 + NO_HIGHLIGHT(PLAIN),
123 + PRIMARY_HIGHLIGHT(PRIMARY),
124 + SECONDARY_HIGHLIGHT(SECONDARY);
125 +
126 + private String cssName;
127 +
128 + Flavor(String s) {
129 + cssName = s;
130 + }
131 +
132 + @Override
133 + public String toString() {
134 + return cssName;
135 + }
136 + }
137 +
138 + /**
139 + * Link highlighting modification.
140 + * <p>
141 + * Note that this translates to a CSS class name that is applied to
142 + * the link in the Topology UI.
143 + */
144 + public static final class Mod implements Comparable<Mod> {
145 + private final String modId;
146 +
147 + public Mod(String modId) {
148 + this.modId = checkNotNull(modId);
149 + }
150 +
151 + @Override
152 + public String toString() {
153 + return modId;
154 + }
155 +
156 + @Override
157 + public boolean equals(Object o) {
158 + if (this == o) {
159 + return true;
160 + }
161 + if (o == null || getClass() != o.getClass()) {
162 + return false;
163 + }
164 + Mod mod = (Mod) o;
165 + return modId.equals(mod.modId);
166 + }
167 +
168 + @Override
169 + public int hashCode() {
170 + return modId.hashCode();
171 + }
172 +
173 +
174 + @Override
175 + public int compareTo(Mod o) {
176 + return this.modId.compareTo(o.modId);
177 + }
178 + }
179 +
180 + /**
181 + * Denotes a link to be tagged as an optical link.
182 + */
183 + public static final Mod MOD_OPTICAL = new Mod("optical");
184 +
185 + /**
186 + * Denotes a link to be tagged with animated traffic ("marching ants").
187 + */
188 + public static final Mod MOD_ANIMATED = new Mod("animated");
189 +}
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 +package org.onosproject.ui.topo;
19 +
20 +/**
21 + * The topology element types to which a highlight can be applied.
22 + */
23 +public enum TopoElementType {
24 + DEVICE, HOST, LINK
25 +}
...@@ -24,7 +24,8 @@ import org.onosproject.net.LinkKey; ...@@ -24,7 +24,8 @@ import org.onosproject.net.LinkKey;
24 import org.onosproject.net.link.LinkService; 24 import org.onosproject.net.link.LinkService;
25 import org.onosproject.ui.RequestHandler; 25 import org.onosproject.ui.RequestHandler;
26 import org.onosproject.ui.UiMessageHandler; 26 import org.onosproject.ui.UiMessageHandler;
27 -import org.onosproject.ui.impl.TopologyViewMessageHandlerBase.BiLink; 27 +import org.onosproject.ui.impl.topo.BiLink;
28 +import org.onosproject.ui.impl.topo.TopoUtils;
28 import org.onosproject.ui.table.TableModel; 29 import org.onosproject.ui.table.TableModel;
29 import org.onosproject.ui.table.TableRequestHandler; 30 import org.onosproject.ui.table.TableRequestHandler;
30 import org.onosproject.ui.table.cell.ConnectPointFormatter; 31 import org.onosproject.ui.table.cell.ConnectPointFormatter;
...@@ -33,13 +34,14 @@ import org.onosproject.ui.table.cell.EnumFormatter; ...@@ -33,13 +34,14 @@ import org.onosproject.ui.table.cell.EnumFormatter;
33 import java.util.Collection; 34 import java.util.Collection;
34 import java.util.Map; 35 import java.util.Map;
35 36
36 -import static org.onosproject.ui.impl.TopologyViewMessageHandlerBase.addLink;
37 -
38 /** 37 /**
39 * Message handler for link view related messages. 38 * Message handler for link view related messages.
40 */ 39 */
41 public class LinkViewMessageHandler extends UiMessageHandler { 40 public class LinkViewMessageHandler extends UiMessageHandler {
42 41
42 + private static final String A_BOTH_B = "A &harr; B";
43 + private static final String A_SINGLE_B = "A &rarr; B";
44 +
43 private static final String LINK_DATA_REQ = "linkDataRequest"; 45 private static final String LINK_DATA_REQ = "linkDataRequest";
44 private static final String LINK_DATA_RESP = "linkDataResponse"; 46 private static final String LINK_DATA_RESP = "linkDataResponse";
45 private static final String LINKS = "links"; 47 private static final String LINKS = "links";
...@@ -94,38 +96,39 @@ public class LinkViewMessageHandler extends UiMessageHandler { ...@@ -94,38 +96,39 @@ public class LinkViewMessageHandler extends UiMessageHandler {
94 96
95 // First consolidate all uni-directional links into two-directional ones. 97 // First consolidate all uni-directional links into two-directional ones.
96 Map<LinkKey, BiLink> biLinks = Maps.newHashMap(); 98 Map<LinkKey, BiLink> biLinks = Maps.newHashMap();
97 - ls.getLinks().forEach(link -> addLink(biLinks, link)); 99 + ls.getLinks().forEach(link -> TopoUtils.addLink(biLinks, link));
98 100
99 // Now scan over all bi-links and produce table rows from them. 101 // Now scan over all bi-links and produce table rows from them.
100 biLinks.values().forEach(biLink -> populateRow(tm.addRow(), biLink)); 102 biLinks.values().forEach(biLink -> populateRow(tm.addRow(), biLink));
101 } 103 }
102 104
103 private void populateRow(TableModel.Row row, BiLink biLink) { 105 private void populateRow(TableModel.Row row, BiLink biLink) {
104 - row.cell(ONE, biLink.one.src()) 106 + row.cell(ONE, biLink.one().src())
105 - .cell(TWO, biLink.one.dst()) 107 + .cell(TWO, biLink.one().dst())
106 .cell(TYPE, linkType(biLink)) 108 .cell(TYPE, linkType(biLink))
107 .cell(STATE, linkState(biLink)) 109 .cell(STATE, linkState(biLink))
108 .cell(DIRECTION, linkDir(biLink)) 110 .cell(DIRECTION, linkDir(biLink))
109 - .cell(DURABLE, biLink.one.isDurable()); 111 + .cell(DURABLE, biLink.one().isDurable());
110 } 112 }
111 113
112 private String linkType(BiLink link) { 114 private String linkType(BiLink link) {
113 StringBuilder sb = new StringBuilder(); 115 StringBuilder sb = new StringBuilder();
114 - sb.append(link.one.type()); 116 + sb.append(link.one().type());
115 - if (link.two != null && link.two.type() != link.one.type()) { 117 + if (link.two() != null && link.two().type() != link.one().type()) {
116 - sb.append(" / ").append(link.two.type()); 118 + sb.append(" / ").append(link.two().type());
117 } 119 }
118 return sb.toString(); 120 return sb.toString();
119 } 121 }
120 122
121 private String linkState(BiLink link) { 123 private String linkState(BiLink link) {
122 - return (link.one.state() == Link.State.ACTIVE || 124 + return (link.one().state() == Link.State.ACTIVE ||
123 - link.two.state() == Link.State.ACTIVE) ? 125 + link.two().state() == Link.State.ACTIVE) ?
124 ICON_ID_ONLINE : ICON_ID_OFFLINE; 126 ICON_ID_ONLINE : ICON_ID_OFFLINE;
125 } 127 }
126 128
127 private String linkDir(BiLink link) { 129 private String linkDir(BiLink link) {
128 - return link.two != null ? "A &harr; B" : "A &rarr; B"; 130 + return link.two() != null ? A_BOTH_B : A_SINGLE_B;
129 } 131 }
130 } 132 }
133 +
131 } 134 }
......
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 +package org.onosproject.ui.impl.topo;
19 +
20 +import org.onosproject.net.Link;
21 +import org.onosproject.net.LinkKey;
22 +import org.onosproject.net.statistic.Load;
23 +import org.onosproject.ui.topo.LinkHighlight;
24 +
25 +import static org.onosproject.ui.topo.LinkHighlight.Flavor.NO_HIGHLIGHT;
26 +import static org.onosproject.ui.topo.LinkHighlight.Flavor.PRIMARY_HIGHLIGHT;
27 +import static org.onosproject.ui.topo.LinkHighlight.Flavor.SECONDARY_HIGHLIGHT;
28 +
29 +/**
30 + * Representation of a link and its inverse, and any associated traffic data.
31 + * This class understands how to generate {@link LinkHighlight}s for sending
32 + * back to the topology view.
33 + */
34 +public class BiLink {
35 +
36 + private static final String EMPTY = "";
37 +
38 + private final LinkKey key;
39 + private final Link one;
40 + private Link two;
41 +
42 + private boolean hasTraffic = false;
43 + private long bytes = 0;
44 + private long rate = 0;
45 + private long flows = 0;
46 + private boolean isOptical = false;
47 + private LinkHighlight.Flavor taggedFlavor = NO_HIGHLIGHT;
48 + private boolean antMarch = false;
49 +
50 + /**
51 + * Constructs a bilink for the given key and initial link.
52 + *
53 + * @param key canonical key for this bilink
54 + * @param link first link
55 + */
56 + public BiLink(LinkKey key, Link link) {
57 + this.key = key;
58 + this.one = link;
59 + }
60 +
61 + /**
62 + * Sets the second link for this bilink.
63 + *
64 + * @param link second link
65 + */
66 + public void setOther(Link link) {
67 + this.two = link;
68 + }
69 +
70 + /**
71 + * Sets the optical flag to the given value.
72 + *
73 + * @param b true if an optical link
74 + */
75 + public void setOptical(boolean b) {
76 + isOptical = b;
77 + }
78 +
79 + /**
80 + * Sets the ant march flag to the given value.
81 + *
82 + * @param b true if marching ants required
83 + */
84 + public void setAntMarch(boolean b) {
85 + antMarch = b;
86 + }
87 +
88 + /**
89 + * Tags this bilink with a link flavor to be used in visual rendering.
90 + *
91 + * @param flavor the flavor to tag
92 + */
93 + public void tagFlavor(LinkHighlight.Flavor flavor) {
94 + this.taggedFlavor = flavor;
95 + }
96 +
97 + /**
98 + * Adds load statistics, marks the bilink as having traffic.
99 + *
100 + * @param load load to add
101 + */
102 + public void addLoad(Load load) {
103 + addLoad(load, 0);
104 + }
105 +
106 + /**
107 + * Adds load statistics, marks the bilink as having traffic, if the
108 + * load rate is greater than the given threshold.
109 + *
110 + * @param load load to add
111 + * @param threshold threshold to register traffic
112 + */
113 + public void addLoad(Load load, double threshold) {
114 + if (load != null) {
115 + this.hasTraffic = hasTraffic || load.rate() > threshold;
116 + this.bytes += load.latest();
117 + this.rate += load.rate();
118 + }
119 + }
120 +
121 + /**
122 + * Adds the given count of flows to this bilink.
123 + *
124 + * @param count count of flows
125 + */
126 + public void addFlows(int count) {
127 + this.flows += count;
128 + }
129 +
130 + /**
131 + * Generates a link highlight entity, based on state of this bilink.
132 + *
133 + * @param type the type of statistics to use to interpret the data
134 + * @return link highlight data for this bilink
135 + */
136 + public LinkHighlight generateHighlight(LinkStatsType type) {
137 + switch (type) {
138 + case FLOW_COUNT:
139 + return highlightForFlowCount(type);
140 +
141 + case FLOW_STATS:
142 + case PORT_STATS:
143 + return highlightForStats(type);
144 +
145 + case TAGGED:
146 + return highlightForTagging(type);
147 +
148 + default:
149 + throw new IllegalStateException("unexpected case: " + type);
150 + }
151 + }
152 +
153 + private LinkHighlight highlightForStats(LinkStatsType type) {
154 + return new LinkHighlight(linkId(), SECONDARY_HIGHLIGHT)
155 + .setLabel(generateLabel(type));
156 + }
157 +
158 + private LinkHighlight highlightForFlowCount(LinkStatsType type) {
159 + LinkHighlight.Flavor flavor = flows() > 0 ?
160 + PRIMARY_HIGHLIGHT : SECONDARY_HIGHLIGHT;
161 + return new LinkHighlight(linkId(), flavor)
162 + .setLabel(generateLabel(type));
163 + }
164 +
165 + private LinkHighlight highlightForTagging(LinkStatsType type) {
166 + LinkHighlight hlite = new LinkHighlight(linkId(), flavor())
167 + .setLabel(generateLabel(type));
168 + if (isOptical()) {
169 + hlite.addMod(LinkHighlight.MOD_OPTICAL);
170 + }
171 + if (isAntMarch()) {
172 + hlite.addMod(LinkHighlight.MOD_ANIMATED);
173 + }
174 + return hlite;
175 + }
176 +
177 + // Generates a link identifier in the form that the Topology View on the
178 + private String linkId() {
179 + return TopoUtils.compactLinkString(one);
180 + }
181 +
182 + // Generates a string representation of the load, to be used as a label
183 + private String generateLabel(LinkStatsType type) {
184 + switch (type) {
185 + case FLOW_COUNT:
186 + return TopoUtils.formatFlows(flows());
187 +
188 + case FLOW_STATS:
189 + return TopoUtils.formatBytes(bytes());
190 +
191 + case PORT_STATS:
192 + return TopoUtils.formatBitRate(rate());
193 +
194 + case TAGGED:
195 + return hasTraffic() ? TopoUtils.formatBytes(bytes()) : EMPTY;
196 +
197 + default:
198 + return "?";
199 + }
200 + }
201 +
202 + // === ----------------------------------------------------------------
203 + // accessors
204 +
205 + public LinkKey key() {
206 + return key;
207 + }
208 +
209 + public Link one() {
210 + return one;
211 + }
212 +
213 + public Link two() {
214 + return two;
215 + }
216 +
217 + public boolean hasTraffic() {
218 + return hasTraffic;
219 + }
220 +
221 + public boolean isOptical() {
222 + return isOptical;
223 + }
224 +
225 + public boolean isAntMarch() {
226 + return antMarch;
227 + }
228 +
229 + public LinkHighlight.Flavor flavor() {
230 + return taggedFlavor;
231 + }
232 +
233 + public long bytes() {
234 + return bytes;
235 + }
236 +
237 + public long rate() {
238 + return rate;
239 + }
240 +
241 + public long flows() {
242 + return flows;
243 + }
244 +}
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 +package org.onosproject.ui.impl.topo;
19 +
20 +import org.onosproject.net.intent.Intent;
21 +import org.slf4j.Logger;
22 +import org.slf4j.LoggerFactory;
23 +
24 +import java.util.ArrayList;
25 +import java.util.Collections;
26 +import java.util.List;
27 +
28 +/**
29 + * Encapsulates a selection of intents (paths) inferred from a selection
30 + * of devices and/or hosts from the topology view.
31 + */
32 +public class IntentSelection {
33 +
34 + private static final int ALL = -1;
35 +
36 + protected static final Logger log =
37 + LoggerFactory.getLogger(IntentSelection.class);
38 +
39 + private final NodeSelection nodes;
40 +
41 + private final List<Intent> intents;
42 + private int index = ALL;
43 +
44 + /**
45 + * Creates an intent selection group, based on selected nodes.
46 + *
47 + * @param nodes node selection
48 + * @param filter intent filter
49 + */
50 + public IntentSelection(NodeSelection nodes, TopologyViewIntentFilter filter) {
51 + this.nodes = nodes;
52 + intents = filter.findPathIntents(nodes.hosts(), nodes.devices());
53 + }
54 +
55 + /**
56 + * Creates an intent selection group, for a single intent.
57 + *
58 + * @param intent the intent
59 + */
60 + public IntentSelection(Intent intent) {
61 + nodes = null;
62 + intents = new ArrayList<>(1);
63 + intents.add(intent);
64 + index = 0;
65 + }
66 +
67 + /**
68 + * Returns true if no intents are selected.
69 + *
70 + * @return true if nothing selected
71 + */
72 + public boolean none() {
73 + return intents.isEmpty();
74 + }
75 +
76 + /**
77 + * Returns true if all intents in this select group are currently selected.
78 + * This is the initial state, so that all intents are shown on the
79 + * topology view with primary highlighting.
80 + *
81 + * @return true if all selected
82 + */
83 + public boolean all() {
84 + return index == ALL;
85 + }
86 +
87 + /**
88 + * Returns true if there is a single intent in this select group, or if
89 + * a specific intent has been marked (index != ALL).
90 + *
91 + * @return true if single intent marked
92 + */
93 + public boolean single() {
94 + return !all();
95 + }
96 +
97 + /**
98 + * Returns the number of intents in this selection group.
99 + *
100 + * @return number of intents
101 + */
102 + public int size() {
103 + return intents.size();
104 + }
105 +
106 + /**
107 + * Returns the index of the currently selected intent.
108 + *
109 + * @return the current index
110 + */
111 + public int index() {
112 + return index;
113 + }
114 +
115 + /**
116 + * The list of intents in this selection group.
117 + *
118 + * @return list of intents
119 + */
120 + public List<Intent> intents() {
121 + return Collections.unmodifiableList(intents);
122 + }
123 +
124 + /**
125 + * Marks and returns the next intent in this group. Note that the
126 + * selection wraps around to the beginning again, if necessary.
127 + *
128 + * @return the next intent in the group
129 + */
130 + public Intent next() {
131 + index += 1;
132 + if (index >= intents.size()) {
133 + index = 0;
134 + }
135 + return intents.get(index);
136 + }
137 +
138 + /**
139 + * Marks and returns the previous intent in this group. Note that the
140 + * selection wraps around to the end again, if necessary.
141 + *
142 + * @return the previous intent in the group
143 + */
144 + public Intent prev() {
145 + index -= 1;
146 + if (index < 0) {
147 + index = intents.size() - 1;
148 + }
149 + return intents.get(index);
150 + }
151 +
152 + /**
153 + * Returns the currently marked intent, or null if "all" intents
154 + * are marked.
155 + *
156 + * @return the currently marked intent
157 + */
158 + public Intent current() {
159 + return all() ? null : intents.get(index);
160 + }
161 +
162 + @Override
163 + public String toString() {
164 + return "IntentSelection{" +
165 + "nodes=" + nodes +
166 + ", #intents=" + intents.size() +
167 + ", index=" + index +
168 + '}';
169 + }
170 +
171 +}
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 +package org.onosproject.ui.impl.topo;
19 +
20 +/**
21 + * Designates type of stats to report on a highlighted link.
22 + */
23 +public enum LinkStatsType {
24 + /**
25 + * Number of flows.
26 + */
27 + FLOW_COUNT,
28 +
29 + /**
30 + * Number of bytes.
31 + */
32 + FLOW_STATS,
33 +
34 + /**
35 + * Number of bits per second.
36 + */
37 + PORT_STATS,
38 +
39 + /**
40 + * Custom tagged information.
41 + */
42 + TAGGED
43 +}
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 +package org.onosproject.ui.impl.topo;
19 +
20 +import com.fasterxml.jackson.databind.JsonNode;
21 +import com.fasterxml.jackson.databind.node.ArrayNode;
22 +import com.fasterxml.jackson.databind.node.ObjectNode;
23 +import org.onosproject.net.Device;
24 +import org.onosproject.net.Host;
25 +import org.onosproject.net.device.DeviceService;
26 +import org.onosproject.net.host.HostService;
27 +import org.onosproject.ui.JsonUtils;
28 +import org.slf4j.Logger;
29 +import org.slf4j.LoggerFactory;
30 +
31 +import java.util.Collections;
32 +import java.util.HashSet;
33 +import java.util.Set;
34 +
35 +import static com.google.common.base.Strings.isNullOrEmpty;
36 +import static org.onosproject.net.DeviceId.deviceId;
37 +import static org.onosproject.net.HostId.hostId;
38 +
39 +/**
40 + * Encapsulates a selection of devices and/or hosts from the topology view.
41 + */
42 +public class NodeSelection {
43 +
44 + protected static final Logger log =
45 + LoggerFactory.getLogger(NodeSelection.class);
46 +
47 + private static final String IDS = "ids";
48 + private static final String HOVER = "hover";
49 +
50 + private final DeviceService deviceService;
51 + private final HostService hostService;
52 +
53 + private final Set<String> ids;
54 + private final String hover;
55 +
56 + private final Set<Device> devices = new HashSet<>();
57 + private final Set<Host> hosts = new HashSet<>();
58 +
59 + /**
60 + * Creates a node selection entity, from the given payload, using the
61 + * supplied device and host services.
62 + *
63 + * @param payload message payload
64 + * @param deviceService device service
65 + * @param hostService host service
66 + */
67 + public NodeSelection(ObjectNode payload,
68 + DeviceService deviceService,
69 + HostService hostService) {
70 + this.deviceService = deviceService;
71 + this.hostService = hostService;
72 +
73 + ids = extractIds(payload);
74 + hover = extractHover(payload);
75 +
76 + Set<String> unmatched = findDevices(ids);
77 + unmatched = findHosts(unmatched);
78 + if (unmatched.size() > 0) {
79 + log.debug("Skipping unmatched IDs {}", unmatched);
80 + }
81 +
82 + if (!isNullOrEmpty(hover)) {
83 + unmatched = new HashSet<>();
84 + unmatched.add(hover);
85 + unmatched = findDevices(unmatched);
86 + unmatched = findHosts(unmatched);
87 + if (unmatched.size() > 0) {
88 + log.debug("Skipping unmatched HOVER {}", unmatched);
89 + }
90 + }
91 + }
92 +
93 + /**
94 + * Returns a view of the selected devices.
95 + *
96 + * @return selected devices
97 + */
98 + public Set<Device> devices() {
99 + return Collections.unmodifiableSet(devices);
100 + }
101 +
102 + /**
103 + * Returns a view of the selected hosts.
104 + *
105 + * @return selected hosts
106 + */
107 + public Set<Host> hosts() {
108 + return Collections.unmodifiableSet(hosts);
109 + }
110 +
111 + /**
112 + * Returns true if nothing is selected.
113 + *
114 + * @return true if nothing selected
115 + */
116 + public boolean none() {
117 + return devices().size() == 0 && hosts().size() == 0;
118 + }
119 +
120 + @Override
121 + public String toString() {
122 + return "NodeSelection{" +
123 + "ids=" + ids +
124 + ", hover='" + hover + '\'' +
125 + ", #devices=" + devices.size() +
126 + ", #hosts=" + hosts.size() +
127 + '}';
128 + }
129 +
130 + // == helper methods
131 +
132 + private Set<String> extractIds(ObjectNode payload) {
133 + ArrayNode array = (ArrayNode) payload.path(IDS);
134 + if (array == null || array.size() == 0) {
135 + return Collections.emptySet();
136 + }
137 +
138 + Set<String> ids = new HashSet<>();
139 + for (JsonNode node : array) {
140 + ids.add(node.asText());
141 + }
142 + return ids;
143 + }
144 +
145 + private String extractHover(ObjectNode payload) {
146 + return JsonUtils.string(payload, HOVER);
147 + }
148 +
149 + private Set<String> findDevices(Set<String> ids) {
150 + Set<String> unmatched = new HashSet<>();
151 + Device device;
152 +
153 + for (String id : ids) {
154 + try {
155 + device = deviceService.getDevice(deviceId(id));
156 + if (device != null) {
157 + devices.add(device);
158 + } else {
159 + log.debug("Device with ID {} not found", id);
160 + }
161 + } catch (IllegalArgumentException e) {
162 + unmatched.add(id);
163 + }
164 + }
165 + return unmatched;
166 + }
167 +
168 + private Set<String> findHosts(Set<String> ids) {
169 + Set<String> unmatched = new HashSet<>();
170 + Host host;
171 +
172 + for (String id : ids) {
173 + try {
174 + host = hostService.getHost(hostId(id));
175 + if (host != null) {
176 + hosts.add(host);
177 + } else {
178 + log.debug("Host with ID {} not found", id);
179 + }
180 + } catch (IllegalArgumentException e) {
181 + unmatched.add(id);
182 + }
183 + }
184 + return unmatched;
185 + }
186 +
187 +}
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 +package org.onosproject.ui.impl.topo;
19 +
20 +import org.onosproject.incubator.net.PortStatisticsService;
21 +import org.onosproject.net.device.DeviceService;
22 +import org.onosproject.net.flow.FlowRuleService;
23 +import org.onosproject.net.host.HostService;
24 +import org.onosproject.net.intent.IntentService;
25 +import org.onosproject.net.link.LinkService;
26 +import org.onosproject.net.statistic.StatisticService;
27 +
28 +import static com.google.common.base.Preconditions.checkNotNull;
29 +
30 +/**
31 + * A bundle of services that the topology view requires to get its job done.
32 + */
33 +public class ServicesBundle {
34 +
35 + private final IntentService intentService;
36 + private final DeviceService deviceService;
37 + private final HostService hostService;
38 + private final LinkService linkService;
39 + private final FlowRuleService flowService;
40 + private final StatisticService flowStatsService;
41 + private final PortStatisticsService portStatsService;
42 +
43 + /**
44 + * Creates the services bundle.
45 + * @param intentService intent service reference
46 + * @param deviceService device service reference
47 + * @param hostService host service reference
48 + * @param linkService link service reference
49 + * @param flowService flow service reference
50 + * @param flowStatsService flow statistics service reference
51 + * @param portStatsService port statistics service reference
52 + */
53 + public ServicesBundle(IntentService intentService,
54 + DeviceService deviceService,
55 + HostService hostService,
56 + LinkService linkService,
57 + FlowRuleService flowService,
58 + StatisticService flowStatsService,
59 + PortStatisticsService portStatsService) {
60 + this.intentService = checkNotNull(intentService);
61 + this.deviceService = checkNotNull(deviceService);
62 + this.hostService = checkNotNull(hostService);
63 + this.linkService = checkNotNull(linkService);
64 + this.flowService = checkNotNull(flowService);
65 + this.flowStatsService = checkNotNull(flowStatsService);
66 + this.portStatsService = checkNotNull(portStatsService);
67 + }
68 +
69 + public IntentService intentService() {
70 + return intentService;
71 + }
72 +
73 + public DeviceService deviceService() {
74 + return deviceService;
75 + }
76 +
77 + public HostService hostService() {
78 + return hostService;
79 + }
80 +
81 + public LinkService linkService() {
82 + return linkService;
83 + }
84 +
85 + public FlowRuleService flowService() {
86 + return flowService;
87 + }
88 +
89 + public StatisticService flowStatsService() {
90 + return flowStatsService;
91 + }
92 +
93 + public PortStatisticsService portStatsService() {
94 + return portStatsService;
95 + }
96 +}
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 +package org.onosproject.ui.impl.topo;
19 +
20 +import org.onosproject.net.Link;
21 +import org.onosproject.net.LinkKey;
22 +
23 +import java.text.DecimalFormat;
24 +import java.util.Map;
25 +
26 +import static org.onosproject.net.LinkKey.linkKey;
27 +
28 +/**
29 + * Utility methods for helping out with the topology view.
30 + */
31 +public final class TopoUtils {
32 +
33 + public static final double KILO = 1024;
34 + public static final double MEGA = 1024 * KILO;
35 + public static final double GIGA = 1024 * MEGA;
36 +
37 + public static final String GBITS_UNIT = "Gb";
38 + public static final String MBITS_UNIT = "Mb";
39 + public static final String KBITS_UNIT = "Kb";
40 + public static final String BITS_UNIT = "b";
41 + public static final String GBYTES_UNIT = "GB";
42 + public static final String MBYTES_UNIT = "MB";
43 + public static final String KBYTES_UNIT = "KB";
44 + public static final String BYTES_UNIT = "B";
45 +
46 +
47 + private static final DecimalFormat DF2 = new DecimalFormat("#,###.##");
48 +
49 + private static final String COMPACT = "%s/%s-%s/%s";
50 + private static final String EMPTY = "";
51 + private static final String SPACE = " ";
52 + private static final String PER_SEC = "ps";
53 + private static final String FLOW = "flow";
54 + private static final String FLOWS = "flows";
55 +
56 + // non-instantiable
57 + private TopoUtils() { }
58 +
59 + /**
60 + * Returns a compact identity for the given link, in the form
61 + * used to identify links in the Topology View on the client.
62 + *
63 + * @param link link
64 + * @return compact link identity
65 + */
66 + public static String compactLinkString(Link link) {
67 + return String.format(COMPACT, link.src().elementId(), link.src().port(),
68 + link.dst().elementId(), link.dst().port());
69 + }
70 +
71 + /**
72 + * Produces a canonical link key, that is, one that will match both a link
73 + * and its inverse.
74 + *
75 + * @param link the link
76 + * @return canonical key
77 + */
78 + public static LinkKey canonicalLinkKey(Link link) {
79 + String sn = link.src().elementId().toString();
80 + String dn = link.dst().elementId().toString();
81 + return sn.compareTo(dn) < 0 ?
82 + linkKey(link.src(), link.dst()) : linkKey(link.dst(), link.src());
83 + }
84 +
85 + /**
86 + * Returns human readable count of bytes, to be displayed as a label.
87 + *
88 + * @param bytes number of bytes
89 + * @return formatted byte count
90 + */
91 + public static String formatBytes(long bytes) {
92 + String unit;
93 + double value;
94 + if (bytes > GIGA) {
95 + value = bytes / GIGA;
96 + unit = GBYTES_UNIT;
97 + } else if (bytes > MEGA) {
98 + value = bytes / MEGA;
99 + unit = MBYTES_UNIT;
100 + } else if (bytes > KILO) {
101 + value = bytes / KILO;
102 + unit = KBYTES_UNIT;
103 + } else {
104 + value = bytes;
105 + unit = BYTES_UNIT;
106 + }
107 + return DF2.format(value) + SPACE + unit;
108 + }
109 +
110 + /**
111 + * Returns human readable bit rate, to be displayed as a label.
112 + *
113 + * @param bytes bytes per second
114 + * @return formatted bits per second
115 + */
116 + public static String formatBitRate(long bytes) {
117 + String unit;
118 + double value;
119 +
120 + //Convert to bits
121 + long bits = bytes * 8;
122 + if (bits > GIGA) {
123 + value = bits / GIGA;
124 + unit = GBITS_UNIT;
125 +
126 + // NOTE: temporary hack to clip rate at 10.0 Gbps
127 + // Added for the CORD Fabric demo at ONS 2015
128 + // TODO: provide a more elegant solution to this issue
129 + if (value > 10.0) {
130 + value = 10.0;
131 + }
132 +
133 + } else if (bits > MEGA) {
134 + value = bits / MEGA;
135 + unit = MBITS_UNIT;
136 + } else if (bits > KILO) {
137 + value = bits / KILO;
138 + unit = KBITS_UNIT;
139 + } else {
140 + value = bits;
141 + unit = BITS_UNIT;
142 + }
143 + return DF2.format(value) + SPACE + unit + PER_SEC;
144 + }
145 +
146 + /**
147 + * Returns human readable flow count, to be displayed as a label.
148 + *
149 + * @param flows number of flows
150 + * @return formatted flow count
151 + */
152 + public static String formatFlows(long flows) {
153 + if (flows < 1) {
154 + return EMPTY;
155 + }
156 + return String.valueOf(flows) + SPACE + (flows > 1 ? FLOWS : FLOW);
157 + }
158 +
159 +
160 + /**
161 + * Creates a new biLink with the supplied link (and adds it to the map),
162 + * or attaches the link to an existing biLink (which already has the
163 + * peer link).
164 + *
165 + * @param linkMap map of biLinks
166 + * @param link the link to add
167 + * @return the biLink to which the link was added
168 + */
169 + // creates a new biLink with supplied link, or attaches link to the
170 + // existing biLink (which already has its peer link)
171 + public static BiLink addLink(Map<LinkKey, BiLink> linkMap, Link link) {
172 + LinkKey key = TopoUtils.canonicalLinkKey(link);
173 + BiLink biLink = linkMap.get(key);
174 + if (biLink != null) {
175 + biLink.setOther(link);
176 + } else {
177 + biLink = new BiLink(key, link);
178 + linkMap.put(key, biLink);
179 + }
180 + return biLink;
181 + }
182 +
183 +
184 +}
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.onosproject.ui.impl; 16 +package org.onosproject.ui.impl.topo;
17 17
18 import org.onosproject.net.ConnectPoint; 18 import org.onosproject.net.ConnectPoint;
19 import org.onosproject.net.Device; 19 import org.onosproject.net.Device;
...@@ -56,32 +56,35 @@ public class TopologyViewIntentFilter { ...@@ -56,32 +56,35 @@ public class TopologyViewIntentFilter {
56 private final LinkService linkService; 56 private final LinkService linkService;
57 57
58 /** 58 /**
59 - * Crreates an intent filter. 59 + * Creates an intent filter.
60 * 60 *
61 - * @param intentService intent service reference 61 + * @param services service references bundle
62 - * @param deviceService device service reference
63 - * @param hostService host service reference
64 - * @param linkService link service reference
65 */ 62 */
66 - TopologyViewIntentFilter(IntentService intentService, DeviceService deviceService, 63 + public TopologyViewIntentFilter(ServicesBundle services) {
67 - HostService hostService, LinkService linkService) { 64 + this.intentService = services.intentService();
68 - this.intentService = intentService; 65 + this.deviceService = services.deviceService();
69 - this.deviceService = deviceService; 66 + this.hostService = services.hostService();
70 - this.hostService = hostService; 67 + this.linkService = services.linkService();
71 - this.linkService = linkService;
72 } 68 }
73 69
70 +
71 + // TODO: Review - do we need this signature, with sourceIntents??
72 +// public List<Intent> findPathIntents(Set<Host> hosts, Set<Device> devices,
73 +// Iterable<Intent> sourceIntents) {
74 +// }
75 +
74 /** 76 /**
75 - * Finds all path (host-to-host or point-to-point) intents that pertains 77 + * Finds all path (host-to-host or point-to-point) intents that pertain
76 - * to the given hosts. 78 + * to the given hosts and devices.
77 * 79 *
78 * @param hosts set of hosts to query by 80 * @param hosts set of hosts to query by
79 * @param devices set of devices to query by 81 * @param devices set of devices to query by
80 - * @param sourceIntents collection of intents to search
81 * @return set of intents that 'match' all hosts and devices given 82 * @return set of intents that 'match' all hosts and devices given
82 */ 83 */
83 - List<Intent> findPathIntents(Set<Host> hosts, Set<Device> devices, 84 + public List<Intent> findPathIntents(Set<Host> hosts, Set<Device> devices) {
84 - Iterable<Intent> sourceIntents) { 85 + // start with all intents
86 + Iterable<Intent> sourceIntents = intentService.getIntents();
87 +
85 // Derive from this the set of edge connect points. 88 // Derive from this the set of edge connect points.
86 Set<ConnectPoint> edgePoints = getEdgePoints(hosts); 89 Set<ConnectPoint> edgePoints = getEdgePoints(hosts);
87 90
......
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 +package org.onosproject.ui.impl.topo;
19 +
20 +import org.onosproject.net.intent.Intent;
21 +import org.onosproject.ui.topo.LinkHighlight;
22 +
23 +/**
24 + * Auxiliary data carrier for assigning a highlight class to a set of
25 + * intents, for visualization in the topology view.
26 + */
27 +public class TrafficClass {
28 +
29 + private final LinkHighlight.Flavor flavor;
30 + private final Iterable<Intent> intents;
31 + private final boolean showTraffic;
32 +
33 + public TrafficClass(LinkHighlight.Flavor flavor, Iterable<Intent> intents) {
34 + this(flavor, intents, false);
35 + }
36 +
37 + public TrafficClass(LinkHighlight.Flavor flavor, Iterable<Intent> intents,
38 + boolean showTraffic) {
39 + this.flavor = flavor;
40 + this.intents = intents;
41 + this.showTraffic = showTraffic;
42 + }
43 +
44 + public LinkHighlight.Flavor flavor() {
45 + return flavor;
46 + }
47 +
48 + public Iterable<Intent> intents() {
49 + return intents;
50 + }
51 +
52 + public boolean showTraffic() {
53 + return showTraffic;
54 + }
55 +}
...@@ -58,6 +58,7 @@ ...@@ -58,6 +58,7 @@
58 showHosts = false, // whether hosts are displayed 58 showHosts = false, // whether hosts are displayed
59 showOffline = true, // whether offline devices are displayed 59 showOffline = true, // whether offline devices are displayed
60 nodeLock = false, // whether nodes can be dragged or not (locked) 60 nodeLock = false, // whether nodes can be dragged or not (locked)
61 + fTimer, // timer for delayed force layout
61 fNodesTimer, // timer for delayed nodes update 62 fNodesTimer, // timer for delayed nodes update
62 fLinksTimer, // timer for delayed links update 63 fLinksTimer, // timer for delayed links update
63 dim, // the dimensions of the force layout [w,h] 64 dim, // the dimensions of the force layout [w,h]
...@@ -117,6 +118,7 @@ ...@@ -117,6 +118,7 @@
117 network.nodes.push(d); 118 network.nodes.push(d);
118 lu[id] = d; 119 lu[id] = d;
119 updateNodes(); 120 updateNodes();
121 + fStart();
120 } 122 }
121 123
122 function updateDevice(data) { 124 function updateDevice(data) {
...@@ -170,6 +172,7 @@ ...@@ -170,6 +172,7 @@
170 lu[d.egress] = lnk; 172 lu[d.egress] = lnk;
171 updateLinks(); 173 updateLinks();
172 } 174 }
175 + fStart();
173 } 176 }
174 177
175 function updateHost(data) { 178 function updateHost(data) {
...@@ -215,6 +218,7 @@ ...@@ -215,6 +218,7 @@
215 aggregateLink(d, data); 218 aggregateLink(d, data);
216 lu[d.key] = d; 219 lu[d.key] = d;
217 updateLinks(); 220 updateLinks();
221 + fStart();
218 } 222 }
219 } 223 }
220 224
...@@ -322,6 +326,7 @@ ...@@ -322,6 +326,7 @@
322 // remove from lookup cache 326 // remove from lookup cache
323 delete lu[removed[0].key]; 327 delete lu[removed[0].key];
324 updateLinks(); 328 updateLinks();
329 + fResume();
325 } 330 }
326 } 331 }
327 332
...@@ -343,6 +348,7 @@ ...@@ -343,6 +348,7 @@
343 // NOTE: upd is false if we were called from removeDeviceElement() 348 // NOTE: upd is false if we were called from removeDeviceElement()
344 if (upd) { 349 if (upd) {
345 updateNodes(); 350 updateNodes();
351 + fResume();
346 } 352 }
347 } 353 }
348 354
...@@ -367,6 +373,7 @@ ...@@ -367,6 +373,7 @@
367 373
368 // remove from SVG 374 // remove from SVG
369 updateNodes(); 375 updateNodes();
376 + fResume();
370 } 377 }
371 378
372 function updateHostVisibility() { 379 function updateHostVisibility() {
...@@ -520,8 +527,9 @@ ...@@ -520,8 +527,9 @@
520 fNodesTimer = $timeout(_updateNodes, 150); 527 fNodesTimer = $timeout(_updateNodes, 150);
521 } 528 }
522 529
530 + // IMPLEMENTATION NOTE: _updateNodes() should NOT stop, start, or resume
531 + // the force layout; that needs to be determined and implemented elsewhere
523 function _updateNodes() { 532 function _updateNodes() {
524 - force.stop();
525 // select all the nodes in the layout: 533 // select all the nodes in the layout:
526 node = nodeG.selectAll('.node') 534 node = nodeG.selectAll('.node')
527 .data(network.nodes, function (d) { return d.id; }); 535 .data(network.nodes, function (d) { return d.id; });
...@@ -536,7 +544,10 @@ ...@@ -536,7 +544,10 @@
536 .attr({ 544 .attr({
537 id: function (d) { return sus.safeId(d.id); }, 545 id: function (d) { return sus.safeId(d.id); },
538 class: mkSvgClass, 546 class: mkSvgClass,
539 - transform: function (d) { return sus.translate(d.x, d.y); }, 547 + transform: function (d) {
548 + // Need to guard against NaN here ??
549 + return sus.translate(d.x, d.y);
550 + },
540 opacity: 0 551 opacity: 0
541 }) 552 })
542 .call(drag) 553 .call(drag)
...@@ -564,7 +575,6 @@ ...@@ -564,7 +575,6 @@
564 // exiting node specifics: 575 // exiting node specifics:
565 exiting.filter('.host').each(td3.hostExit); 576 exiting.filter('.host').each(td3.hostExit);
566 exiting.filter('.device').each(td3.deviceExit); 577 exiting.filter('.device').each(td3.deviceExit);
567 - fStart();
568 } 578 }
569 579
570 // ========================== 580 // ==========================
...@@ -659,9 +669,10 @@ ...@@ -659,9 +669,10 @@
659 fLinksTimer = $timeout(_updateLinks, 150); 669 fLinksTimer = $timeout(_updateLinks, 150);
660 } 670 }
661 671
672 + // IMPLEMENTATION NOTE: _updateLinks() should NOT stop, start, or resume
673 + // the force layout; that needs to be determined and implemented elsewhere
662 function _updateLinks() { 674 function _updateLinks() {
663 var th = ts.theme(); 675 var th = ts.theme();
664 - force.stop();
665 676
666 link = linkG.selectAll('.link') 677 link = linkG.selectAll('.link')
667 .data(network.links, function (d) { return d.key; }); 678 .data(network.links, function (d) { return d.key; });
...@@ -714,7 +725,6 @@ ...@@ -714,7 +725,6 @@
714 }) 725 })
715 .style('opacity', 0.0) 726 .style('opacity', 0.0)
716 .remove(); 727 .remove();
717 - fStart();
718 } 728 }
719 729
720 730
...@@ -729,14 +739,23 @@ ...@@ -729,14 +739,23 @@
729 739
730 function fStart() { 740 function fStart() {
731 if (!tos.isOblique()) { 741 if (!tos.isOblique()) {
742 + if (fTimer) {
743 + $timeout.cancel(fTimer);
744 + }
745 + fTimer = $timeout(function () {
732 $log.debug("Starting force-layout"); 746 $log.debug("Starting force-layout");
733 force.start(); 747 force.start();
748 + }, 200);
734 } 749 }
735 } 750 }
736 751
737 var tickStuff = { 752 var tickStuff = {
738 nodeAttr: { 753 nodeAttr: {
739 - transform: function (d) { return sus.translate(d.x, d.y); } 754 + transform: function (d) {
755 + var dx = isNaN(d.x) ? 0 : d.x,
756 + dy = isNaN(d.y) ? 0 : d.y;
757 + return sus.translate(dx, dy);
758 + }
740 }, 759 },
741 linkAttr: { 760 linkAttr: {
742 x1: function (d) { return d.position.x1; }, 761 x1: function (d) { return d.position.x1; },
...@@ -1046,6 +1065,9 @@ ...@@ -1046,6 +1065,9 @@
1046 force = drag = null; 1065 force = drag = null;
1047 1066
1048 // clean up $timeout promises 1067 // clean up $timeout promises
1068 + if (fTimer) {
1069 + $timeout.cancel(fTimer);
1070 + }
1049 if (fNodesTimer) { 1071 if (fNodesTimer) {
1050 $timeout.cancel(fNodesTimer); 1072 $timeout.cancel(fNodesTimer);
1051 } 1073 }
......
...@@ -293,7 +293,7 @@ ...@@ -293,7 +293,7 @@
293 findLinkById( id ) 293 findLinkById( id )
294 */ 294 */
295 295
296 - var paths = data.paths; 296 + var paths = data.links;
297 297
298 api.clearLinkTrafficStyle(); 298 api.clearLinkTrafficStyle();
299 api.removeLinkLabels(); 299 api.removeLinkLabels();
......
...@@ -114,7 +114,7 @@ ...@@ -114,7 +114,7 @@
114 } 114 }
115 115
116 if (!ev.shiftKey) { 116 if (!ev.shiftKey) {
117 - deselectAll(); 117 + deselectAll(true);
118 } 118 }
119 119
120 selections[obj.id] = { obj: obj, el: el }; 120 selections[obj.id] = { obj: obj, el: el };
...@@ -135,7 +135,7 @@ ...@@ -135,7 +135,7 @@
135 } 135 }
136 } 136 }
137 137
138 - function deselectAll() { 138 + function deselectAll(skipUpdate) {
139 var something = (selectOrder.length > 0); 139 var something = (selectOrder.length > 0);
140 140
141 // deselect all nodes in the network... 141 // deselect all nodes in the network...
...@@ -143,7 +143,9 @@ ...@@ -143,7 +143,9 @@
143 selections = {}; 143 selections = {};
144 selectOrder = []; 144 selectOrder = [];
145 api.updateDeviceColors(); 145 api.updateDeviceColors();
146 + if (!skipUpdate) {
146 updateDetail(); 147 updateDetail();
148 + }
147 149
148 // return true if something was selected 150 // return true if something was selected
149 return something; 151 return something;
......
...@@ -42,9 +42,9 @@ ...@@ -42,9 +42,9 @@
42 42
43 // invoked in response to change in selection and/or mouseover/out: 43 // invoked in response to change in selection and/or mouseover/out:
44 function requestTrafficForMode() { 44 function requestTrafficForMode() {
45 - if (hoverMode === 'flows') { 45 + if (trafficMode === 'flows') {
46 requestDeviceLinkFlows(); 46 requestDeviceLinkFlows();
47 - } else if (hoverMode === 'intents') { 47 + } else if (trafficMode === 'intents') {
48 requestRelatedIntents(); 48 requestRelatedIntents();
49 } else { 49 } else {
50 cancelTraffic(); 50 cancelTraffic();
...@@ -175,7 +175,6 @@ ...@@ -175,7 +175,6 @@
175 } 175 }
176 176
177 177
178 -
179 // === ----------------------------------------------------- 178 // === -----------------------------------------------------
180 // === MODULE DEFINITION === 179 // === MODULE DEFINITION ===
181 180
......