ONOS-1479 -- GUI - augmenting topology view for extensibility: WIP.
- Major refactoring of TopologyViewMessageHandler and related classes. Change-Id: I920f7f9f7317f3987a9a8da35ac086e9f8cab8d3
Showing
22 changed files
with
2175 additions
and
739 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 ↔ B"; | ||
43 | + private static final String A_SINGLE_B = "A → 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 ↔ B" : "A → B"; | 130 | + return link.two() != null ? A_BOTH_B : A_SINGLE_B; |
129 | } | 131 | } |
130 | } | 132 | } |
133 | + | ||
131 | } | 134 | } | ... | ... |
... | @@ -48,7 +48,6 @@ import org.onosproject.net.flow.TrafficTreatment; | ... | @@ -48,7 +48,6 @@ import org.onosproject.net.flow.TrafficTreatment; |
48 | import org.onosproject.net.host.HostEvent; | 48 | import org.onosproject.net.host.HostEvent; |
49 | import org.onosproject.net.host.HostListener; | 49 | import org.onosproject.net.host.HostListener; |
50 | import org.onosproject.net.intent.HostToHostIntent; | 50 | import org.onosproject.net.intent.HostToHostIntent; |
51 | -import org.onosproject.net.intent.Intent; | ||
52 | import org.onosproject.net.intent.IntentEvent; | 51 | import org.onosproject.net.intent.IntentEvent; |
53 | import org.onosproject.net.intent.IntentListener; | 52 | import org.onosproject.net.intent.IntentListener; |
54 | import org.onosproject.net.intent.MultiPointToSinglePointIntent; | 53 | import org.onosproject.net.intent.MultiPointToSinglePointIntent; |
... | @@ -57,6 +56,9 @@ import org.onosproject.net.link.LinkListener; | ... | @@ -57,6 +56,9 @@ import org.onosproject.net.link.LinkListener; |
57 | import org.onosproject.ui.JsonUtils; | 56 | import org.onosproject.ui.JsonUtils; |
58 | import org.onosproject.ui.RequestHandler; | 57 | import org.onosproject.ui.RequestHandler; |
59 | import org.onosproject.ui.UiConnection; | 58 | import org.onosproject.ui.UiConnection; |
59 | +import org.onosproject.ui.impl.TrafficMonitorObject.Mode; | ||
60 | +import org.onosproject.ui.impl.topo.NodeSelection; | ||
61 | +import org.onosproject.ui.topo.Highlights; | ||
60 | import org.onosproject.ui.topo.PropertyPanel; | 62 | import org.onosproject.ui.topo.PropertyPanel; |
61 | 63 | ||
62 | import java.util.ArrayList; | 64 | import java.util.ArrayList; |
... | @@ -70,7 +72,6 @@ import java.util.Timer; | ... | @@ -70,7 +72,6 @@ import java.util.Timer; |
70 | import java.util.TimerTask; | 72 | import java.util.TimerTask; |
71 | import java.util.concurrent.ExecutorService; | 73 | import java.util.concurrent.ExecutorService; |
72 | 74 | ||
73 | -import static com.google.common.base.Strings.isNullOrEmpty; | ||
74 | import static java.util.concurrent.Executors.newSingleThreadExecutor; | 75 | import static java.util.concurrent.Executors.newSingleThreadExecutor; |
75 | import static org.onlab.util.Tools.groupedThreads; | 76 | import static org.onlab.util.Tools.groupedThreads; |
76 | import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED; | 77 | import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED; |
... | @@ -117,8 +118,6 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -117,8 +118,6 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
117 | 118 | ||
118 | // fields | 119 | // fields |
119 | private static final String ID = "id"; | 120 | private static final String ID = "id"; |
120 | - private static final String IDS = "ids"; | ||
121 | - private static final String HOVER = "hover"; | ||
122 | private static final String DEVICE = "device"; | 121 | private static final String DEVICE = "device"; |
123 | private static final String HOST = "host"; | 122 | private static final String HOST = "host"; |
124 | private static final String CLASS = "class"; | 123 | private static final String CLASS = "class"; |
... | @@ -132,14 +131,12 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -132,14 +131,12 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
132 | private static final String NAMES = "names"; | 131 | private static final String NAMES = "names"; |
133 | private static final String ACTIVATE = "activate"; | 132 | private static final String ACTIVATE = "activate"; |
134 | private static final String DEACTIVATE = "deactivate"; | 133 | private static final String DEACTIVATE = "deactivate"; |
135 | - private static final String PRIMARY = "primary"; | ||
136 | - private static final String SECONDARY = "secondary"; | ||
137 | 134 | ||
138 | 135 | ||
139 | private static final String APP_ID = "org.onosproject.gui"; | 136 | private static final String APP_ID = "org.onosproject.gui"; |
140 | 137 | ||
141 | - private static final long TRAFFIC_FREQUENCY = 5000; | 138 | + private static final long TRAFFIC_PERIOD = 5000; |
142 | - private static final long SUMMARY_FREQUENCY = 30000; | 139 | + private static final long SUMMARY_PERIOD = 30000; |
143 | 140 | ||
144 | private static final Comparator<? super ControllerNode> NODE_COMPARATOR = | 141 | private static final Comparator<? super ControllerNode> NODE_COMPARATOR = |
145 | (o1, o2) -> o1.id().toString().compareTo(o2.id().toString()); | 142 | (o1, o2) -> o1.id().toString().compareTo(o2.id().toString()); |
... | @@ -165,31 +162,21 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -165,31 +162,21 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
165 | private final ExecutorService msgSender = | 162 | private final ExecutorService msgSender = |
166 | newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender")); | 163 | newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender")); |
167 | 164 | ||
168 | - private TopoOverlayCache overlayCache; | 165 | + private TrafficMonitorObject tmo; |
169 | 166 | ||
170 | - private TimerTask trafficTask = null; | 167 | + private TopoOverlayCache overlayCache; |
171 | - private TrafficEvent trafficEvent = null; | ||
172 | 168 | ||
173 | private TimerTask summaryTask = null; | 169 | private TimerTask summaryTask = null; |
174 | private boolean summaryRunning = false; | 170 | private boolean summaryRunning = false; |
175 | 171 | ||
176 | private boolean listenersRemoved = false; | 172 | private boolean listenersRemoved = false; |
177 | 173 | ||
178 | - private TopologyViewIntentFilter intentFilter; | ||
179 | - | ||
180 | - // Current selection context | ||
181 | - private Set<Host> selectedHosts; | ||
182 | - private Set<Device> selectedDevices; | ||
183 | - private List<Intent> selectedIntents; | ||
184 | - private int currentIntentIndex = -1; | ||
185 | - | ||
186 | 174 | ||
187 | @Override | 175 | @Override |
188 | public void init(UiConnection connection, ServiceDirectory directory) { | 176 | public void init(UiConnection connection, ServiceDirectory directory) { |
189 | super.init(connection, directory); | 177 | super.init(connection, directory); |
190 | - intentFilter = new TopologyViewIntentFilter(intentService, deviceService, | ||
191 | - hostService, linkService); | ||
192 | appId = directory.get(CoreService.class).registerApplication(APP_ID); | 178 | appId = directory.get(CoreService.class).registerApplication(APP_ID); |
179 | + tmo = new TrafficMonitorObject(TRAFFIC_PERIOD, servicesBundle, this); | ||
193 | } | 180 | } |
194 | 181 | ||
195 | @Override | 182 | @Override |
... | @@ -214,18 +201,18 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -214,18 +201,18 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
214 | new UpdateMeta(), | 201 | new UpdateMeta(), |
215 | new EqMasters(), | 202 | new EqMasters(), |
216 | 203 | ||
217 | - // TODO: implement "showHighlights" event (replaces "showTraffic") | ||
218 | - | ||
219 | // TODO: migrate traffic related to separate app | 204 | // TODO: migrate traffic related to separate app |
220 | new AddHostIntent(), | 205 | new AddHostIntent(), |
221 | new AddMultiSourceIntent(), | 206 | new AddMultiSourceIntent(), |
207 | + | ||
208 | + new ReqAllFlowTraffic(), | ||
209 | + new ReqAllPortTraffic(), | ||
210 | + new ReqDevLinkFlows(), | ||
222 | new ReqRelatedIntents(), | 211 | new ReqRelatedIntents(), |
223 | new ReqNextIntent(), | 212 | new ReqNextIntent(), |
224 | new ReqPrevIntent(), | 213 | new ReqPrevIntent(), |
225 | new ReqSelectedIntentTraffic(), | 214 | new ReqSelectedIntentTraffic(), |
226 | - new ReqAllFlowTraffic(), | 215 | + |
227 | - new ReqAllPortTraffic(), | ||
228 | - new ReqDevLinkFlows(), | ||
229 | new CancelTraffic() | 216 | new CancelTraffic() |
230 | ); | 217 | ); |
231 | } | 218 | } |
... | @@ -288,7 +275,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -288,7 +275,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
288 | @Override | 275 | @Override |
289 | public void process(long sid, ObjectNode payload) { | 276 | public void process(long sid, ObjectNode payload) { |
290 | stopSummaryMonitoring(); | 277 | stopSummaryMonitoring(); |
291 | - stopTrafficMonitoring(); | 278 | + tmo.stop(); |
292 | } | 279 | } |
293 | } | 280 | } |
294 | 281 | ||
... | @@ -390,6 +377,9 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -390,6 +377,9 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
390 | } | 377 | } |
391 | } | 378 | } |
392 | 379 | ||
380 | + | ||
381 | + // ========= ----------------------------------------------------------------- | ||
382 | + | ||
393 | // === TODO: move traffic related classes to traffic app | 383 | // === TODO: move traffic related classes to traffic app |
394 | 384 | ||
395 | private final class AddHostIntent extends RequestHandler { | 385 | private final class AddHostIntent extends RequestHandler { |
... | @@ -410,7 +400,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -410,7 +400,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
410 | .build(); | 400 | .build(); |
411 | 401 | ||
412 | intentService.submit(intent); | 402 | intentService.submit(intent); |
413 | - startMonitoringIntent(intent); | 403 | + tmo.monitor(intent); |
414 | } | 404 | } |
415 | } | 405 | } |
416 | 406 | ||
... | @@ -443,116 +433,90 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -443,116 +433,90 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
443 | .build(); | 433 | .build(); |
444 | 434 | ||
445 | intentService.submit(intent); | 435 | intentService.submit(intent); |
446 | - startMonitoringIntent(intent); | 436 | + tmo.monitor(intent); |
447 | } | 437 | } |
448 | } | 438 | } |
449 | 439 | ||
450 | - private final class ReqRelatedIntents extends RequestHandler { | 440 | + // ========= ----------------------------------------------------------------- |
451 | - private ReqRelatedIntents() { | 441 | + |
452 | - super(REQ_RELATED_INTENTS); | 442 | + private final class ReqAllFlowTraffic extends RequestHandler { |
443 | + private ReqAllFlowTraffic() { | ||
444 | + super(REQ_ALL_FLOW_TRAFFIC); | ||
453 | } | 445 | } |
454 | 446 | ||
455 | @Override | 447 | @Override |
456 | public void process(long sid, ObjectNode payload) { | 448 | public void process(long sid, ObjectNode payload) { |
457 | - // Cancel any other traffic monitoring mode. | 449 | + tmo.monitor(Mode.ALL_FLOW_TRAFFIC); |
458 | - stopTrafficMonitoring(); | ||
459 | - | ||
460 | - if (!payload.has(IDS)) { | ||
461 | - return; | ||
462 | - } | ||
463 | - | ||
464 | - // Get the set of selected hosts and their intents. | ||
465 | - ArrayNode ids = (ArrayNode) payload.path(IDS); | ||
466 | - selectedHosts = getHosts(ids); | ||
467 | - selectedDevices = getDevices(ids); | ||
468 | - selectedIntents = intentFilter.findPathIntents( | ||
469 | - selectedHosts, selectedDevices, intentService.getIntents()); | ||
470 | - currentIntentIndex = -1; | ||
471 | - | ||
472 | - if (haveSelectedIntents()) { | ||
473 | - // Send a message to highlight all links of all monitored intents. | ||
474 | - sendMessage(trafficMessage(new TrafficClass(PRIMARY, selectedIntents))); | ||
475 | - } | ||
476 | - | ||
477 | - // TODO: Re-introduce once the client click vs hover gesture stuff is sorted out. | ||
478 | -// String hover = string(payload, "hover"); | ||
479 | -// if (!isNullOrEmpty(hover)) { | ||
480 | -// // If there is a hover node, include it in the selection and find intents. | ||
481 | -// processHoverExtendedSelection(sid, hover); | ||
482 | -// } | ||
483 | } | 450 | } |
484 | } | 451 | } |
485 | 452 | ||
486 | - private final class ReqNextIntent extends RequestHandler { | 453 | + private final class ReqAllPortTraffic extends RequestHandler { |
487 | - private ReqNextIntent() { | 454 | + private ReqAllPortTraffic() { |
488 | - super(REQ_NEXT_INTENT); | 455 | + super(REQ_ALL_PORT_TRAFFIC); |
489 | } | 456 | } |
490 | 457 | ||
491 | @Override | 458 | @Override |
492 | public void process(long sid, ObjectNode payload) { | 459 | public void process(long sid, ObjectNode payload) { |
493 | - stopTrafficMonitoring(); | 460 | + tmo.monitor(Mode.ALL_PORT_TRAFFIC); |
494 | - requestAnotherRelatedIntent(+1); | ||
495 | } | 461 | } |
496 | } | 462 | } |
497 | 463 | ||
498 | - private final class ReqPrevIntent extends RequestHandler { | 464 | + private final class ReqDevLinkFlows extends RequestHandler { |
499 | - private ReqPrevIntent() { | 465 | + private ReqDevLinkFlows() { |
500 | - super(REQ_PREV_INTENT); | 466 | + super(REQ_DEV_LINK_FLOWS); |
501 | } | 467 | } |
502 | 468 | ||
503 | @Override | 469 | @Override |
504 | public void process(long sid, ObjectNode payload) { | 470 | public void process(long sid, ObjectNode payload) { |
505 | - stopTrafficMonitoring(); | 471 | + NodeSelection nodeSelection = |
506 | - requestAnotherRelatedIntent(-1); | 472 | + new NodeSelection(payload, deviceService, hostService); |
473 | + tmo.monitor(Mode.DEV_LINK_FLOWS, nodeSelection); | ||
507 | } | 474 | } |
508 | } | 475 | } |
509 | 476 | ||
510 | - private final class ReqSelectedIntentTraffic extends RequestHandler { | 477 | + private final class ReqRelatedIntents extends RequestHandler { |
511 | - private ReqSelectedIntentTraffic() { | 478 | + private ReqRelatedIntents() { |
512 | - super(REQ_SEL_INTENT_TRAFFIC); | 479 | + super(REQ_RELATED_INTENTS); |
513 | } | 480 | } |
514 | 481 | ||
515 | @Override | 482 | @Override |
516 | public void process(long sid, ObjectNode payload) { | 483 | public void process(long sid, ObjectNode payload) { |
517 | - trafficEvent = new TrafficEvent(TrafficEvent.Type.SEL_INTENT, payload); | 484 | + NodeSelection nodeSelection = |
518 | - requestSelectedIntentTraffic(); | 485 | + new NodeSelection(payload, deviceService, hostService); |
519 | - startTrafficMonitoring(); | 486 | + tmo.monitor(Mode.RELATED_INTENTS, nodeSelection); |
520 | } | 487 | } |
521 | } | 488 | } |
522 | 489 | ||
523 | - private final class ReqAllFlowTraffic extends RequestHandler { | 490 | + private final class ReqNextIntent extends RequestHandler { |
524 | - private ReqAllFlowTraffic() { | 491 | + private ReqNextIntent() { |
525 | - super(REQ_ALL_FLOW_TRAFFIC); | 492 | + super(REQ_NEXT_INTENT); |
526 | } | 493 | } |
527 | 494 | ||
528 | @Override | 495 | @Override |
529 | public void process(long sid, ObjectNode payload) { | 496 | public void process(long sid, ObjectNode payload) { |
530 | - trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_FLOW_TRAFFIC, payload); | 497 | + tmo.selectNextIntent(); |
531 | - requestAllFlowTraffic(); | ||
532 | } | 498 | } |
533 | } | 499 | } |
534 | 500 | ||
535 | - private final class ReqAllPortTraffic extends RequestHandler { | 501 | + private final class ReqPrevIntent extends RequestHandler { |
536 | - private ReqAllPortTraffic() { | 502 | + private ReqPrevIntent() { |
537 | - super(REQ_ALL_PORT_TRAFFIC); | 503 | + super(REQ_PREV_INTENT); |
538 | } | 504 | } |
539 | 505 | ||
540 | @Override | 506 | @Override |
541 | public void process(long sid, ObjectNode payload) { | 507 | public void process(long sid, ObjectNode payload) { |
542 | - trafficEvent = new TrafficEvent(TrafficEvent.Type.ALL_PORT_TRAFFIC, payload); | 508 | + tmo.selectPreviousIntent(); |
543 | - requestAllPortTraffic(); | ||
544 | } | 509 | } |
545 | } | 510 | } |
546 | 511 | ||
547 | - private final class ReqDevLinkFlows extends RequestHandler { | 512 | + private final class ReqSelectedIntentTraffic extends RequestHandler { |
548 | - private ReqDevLinkFlows() { | 513 | + private ReqSelectedIntentTraffic() { |
549 | - super(REQ_DEV_LINK_FLOWS); | 514 | + super(REQ_SEL_INTENT_TRAFFIC); |
550 | } | 515 | } |
551 | 516 | ||
552 | @Override | 517 | @Override |
553 | public void process(long sid, ObjectNode payload) { | 518 | public void process(long sid, ObjectNode payload) { |
554 | - trafficEvent = new TrafficEvent(TrafficEvent.Type.DEV_LINK_FLOWS, payload); | 519 | + tmo.monitor(Mode.SEL_INTENT); |
555 | - requestDeviceLinkFlows(payload); | ||
556 | } | 520 | } |
557 | } | 521 | } |
558 | 522 | ||
... | @@ -563,14 +527,16 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -563,14 +527,16 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
563 | 527 | ||
564 | @Override | 528 | @Override |
565 | public void process(long sid, ObjectNode payload) { | 529 | public void process(long sid, ObjectNode payload) { |
566 | - selectedIntents = null; | 530 | + tmo.stop(); |
567 | - sendMessage(trafficMessage()); | ||
568 | - stopTrafficMonitoring(); | ||
569 | } | 531 | } |
570 | } | 532 | } |
571 | 533 | ||
572 | //======================================================================= | 534 | //======================================================================= |
573 | 535 | ||
536 | + // Converts highlights to JSON format and sends the message to the client | ||
537 | + protected void sendHighlights(Highlights highlights) { | ||
538 | + sendMessage(JsonUtils.envelope(SHOW_HIGHLIGHTS, json(highlights))); | ||
539 | + } | ||
574 | 540 | ||
575 | // Sends the specified data to the client. | 541 | // Sends the specified data to the client. |
576 | protected synchronized void sendMessage(ObjectNode data) { | 542 | protected synchronized void sendMessage(ObjectNode data) { |
... | @@ -591,7 +557,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -591,7 +557,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
591 | 557 | ||
592 | private void cancelAllRequests() { | 558 | private void cancelAllRequests() { |
593 | stopSummaryMonitoring(); | 559 | stopSummaryMonitoring(); |
594 | - stopTrafficMonitoring(); | 560 | + tmo.stop(); |
595 | } | 561 | } |
596 | 562 | ||
597 | // Sends all controller nodes to the client as node-added messages. | 563 | // Sends all controller nodes to the client as node-added messages. |
... | @@ -641,18 +607,6 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -641,18 +607,6 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
641 | } | 607 | } |
642 | } | 608 | } |
643 | 609 | ||
644 | - | ||
645 | - private synchronized void startMonitoringIntent(Intent intent) { | ||
646 | - selectedHosts = new HashSet<>(); | ||
647 | - selectedDevices = new HashSet<>(); | ||
648 | - selectedIntents = new ArrayList<>(); | ||
649 | - selectedIntents.add(intent); | ||
650 | - currentIntentIndex = -1; | ||
651 | - requestAnotherRelatedIntent(+1); | ||
652 | - requestSelectedIntentTraffic(); | ||
653 | - } | ||
654 | - | ||
655 | - | ||
656 | private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) { | 610 | private Set<ConnectPoint> getHostLocations(Set<HostId> hostIds) { |
657 | Set<ConnectPoint> points = new HashSet<>(); | 611 | Set<ConnectPoint> points = new HashSet<>(); |
658 | for (HostId hostId : hostIds) { | 612 | for (HostId hostId : hostIds) { |
... | @@ -675,121 +629,10 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -675,121 +629,10 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
675 | } | 629 | } |
676 | 630 | ||
677 | 631 | ||
678 | - private synchronized void startTrafficMonitoring() { | ||
679 | - stopTrafficMonitoring(); | ||
680 | - trafficTask = new TrafficMonitor(); | ||
681 | - timer.schedule(trafficTask, TRAFFIC_FREQUENCY, TRAFFIC_FREQUENCY); | ||
682 | - } | ||
683 | - | ||
684 | - private synchronized void stopTrafficMonitoring() { | ||
685 | - if (trafficTask != null) { | ||
686 | - trafficTask.cancel(); | ||
687 | - trafficTask = null; | ||
688 | - } | ||
689 | - } | ||
690 | - | ||
691 | - // Subscribes for flow traffic messages. | ||
692 | - private synchronized void requestAllFlowTraffic() { | ||
693 | - startTrafficMonitoring(); | ||
694 | - sendMessage(trafficSummaryMessage(StatsType.FLOW)); | ||
695 | - } | ||
696 | - | ||
697 | - // Subscribes for port traffic messages. | ||
698 | - private synchronized void requestAllPortTraffic() { | ||
699 | - startTrafficMonitoring(); | ||
700 | - sendMessage(trafficSummaryMessage(StatsType.PORT)); | ||
701 | - } | ||
702 | - | ||
703 | - private void requestDeviceLinkFlows(ObjectNode payload) { | ||
704 | - startTrafficMonitoring(); | ||
705 | - | ||
706 | - // Get the set of selected hosts and their intents. | ||
707 | - ArrayNode ids = (ArrayNode) payload.path(IDS); | ||
708 | - Set<Host> hosts = new HashSet<>(); | ||
709 | - Set<Device> devices = getDevices(ids); | ||
710 | - | ||
711 | - // If there is a hover node, include it in the hosts and find intents. | ||
712 | - String hover = JsonUtils.string(payload, HOVER); | ||
713 | - if (!isNullOrEmpty(hover)) { | ||
714 | - addHover(hosts, devices, hover); | ||
715 | - } | ||
716 | - sendMessage(flowSummaryMessage(devices)); | ||
717 | - } | ||
718 | - | ||
719 | - | ||
720 | - private boolean haveSelectedIntents() { | ||
721 | - return selectedIntents != null && !selectedIntents.isEmpty(); | ||
722 | - } | ||
723 | - | ||
724 | - // Processes the selection extended with hovered item to segregate items | ||
725 | - // into primary (those including the hover) vs secondary highlights. | ||
726 | - private void processHoverExtendedSelection(long sid, String hover) { | ||
727 | - Set<Host> hoverSelHosts = new HashSet<>(selectedHosts); | ||
728 | - Set<Device> hoverSelDevices = new HashSet<>(selectedDevices); | ||
729 | - addHover(hoverSelHosts, hoverSelDevices, hover); | ||
730 | - | ||
731 | - List<Intent> primary = selectedIntents == null ? new ArrayList<>() : | ||
732 | - intentFilter.findPathIntents(hoverSelHosts, hoverSelDevices, | ||
733 | - selectedIntents); | ||
734 | - Set<Intent> secondary = new HashSet<>(selectedIntents); | ||
735 | - secondary.removeAll(primary); | ||
736 | - | ||
737 | - // Send a message to highlight all links of all monitored intents. | ||
738 | - sendMessage(trafficMessage(new TrafficClass(PRIMARY, primary), | ||
739 | - new TrafficClass(SECONDARY, secondary))); | ||
740 | - } | ||
741 | - | ||
742 | - // Requests next or previous related intent. | ||
743 | - private void requestAnotherRelatedIntent(int offset) { | ||
744 | - if (haveSelectedIntents()) { | ||
745 | - currentIntentIndex = currentIntentIndex + offset; | ||
746 | - if (currentIntentIndex < 0) { | ||
747 | - currentIntentIndex = selectedIntents.size() - 1; | ||
748 | - } else if (currentIntentIndex >= selectedIntents.size()) { | ||
749 | - currentIntentIndex = 0; | ||
750 | - } | ||
751 | - sendSelectedIntent(); | ||
752 | - } | ||
753 | - } | ||
754 | - | ||
755 | - // Sends traffic information on the related intents with the currently | ||
756 | - // selected intent highlighted. | ||
757 | - private void sendSelectedIntent() { | ||
758 | - Intent selectedIntent = selectedIntents.get(currentIntentIndex); | ||
759 | - log.debug("Requested next intent {}", selectedIntent.id()); | ||
760 | - | ||
761 | - Set<Intent> primary = new HashSet<>(); | ||
762 | - primary.add(selectedIntent); | ||
763 | - | ||
764 | - Set<Intent> secondary = new HashSet<>(selectedIntents); | ||
765 | - secondary.remove(selectedIntent); | ||
766 | - | ||
767 | - // Send a message to highlight all links of the selected intent. | ||
768 | - sendMessage(trafficMessage(new TrafficClass(PRIMARY, primary), | ||
769 | - new TrafficClass(SECONDARY, secondary))); | ||
770 | - } | ||
771 | - | ||
772 | - // Requests monitoring of traffic for the selected intent. | ||
773 | - private void requestSelectedIntentTraffic() { | ||
774 | - if (haveSelectedIntents()) { | ||
775 | - if (currentIntentIndex < 0) { | ||
776 | - currentIntentIndex = 0; | ||
777 | - } | ||
778 | - Intent selectedIntent = selectedIntents.get(currentIntentIndex); | ||
779 | - log.debug("Requested traffic for selected {}", selectedIntent.id()); | ||
780 | - | ||
781 | - Set<Intent> primary = new HashSet<>(); | ||
782 | - primary.add(selectedIntent); | ||
783 | - | ||
784 | - // Send a message to highlight all links of the selected intent. | ||
785 | - sendMessage(trafficMessage(new TrafficClass(PRIMARY, primary, true))); | ||
786 | - } | ||
787 | - } | ||
788 | - | ||
789 | private synchronized void startSummaryMonitoring() { | 632 | private synchronized void startSummaryMonitoring() { |
790 | stopSummaryMonitoring(); | 633 | stopSummaryMonitoring(); |
791 | summaryTask = new SummaryMonitor(); | 634 | summaryTask = new SummaryMonitor(); |
792 | - timer.schedule(summaryTask, SUMMARY_FREQUENCY, SUMMARY_FREQUENCY); | 635 | + timer.schedule(summaryTask, SUMMARY_PERIOD, SUMMARY_PERIOD); |
793 | summaryRunning = true; | 636 | summaryRunning = true; |
794 | } | 637 | } |
795 | 638 | ||
... | @@ -883,9 +726,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -883,9 +726,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
883 | private class InternalIntentListener implements IntentListener { | 726 | private class InternalIntentListener implements IntentListener { |
884 | @Override | 727 | @Override |
885 | public void event(IntentEvent event) { | 728 | public void event(IntentEvent event) { |
886 | - if (trafficTask != null) { | 729 | + msgSender.execute(tmo::pokeIntent); |
887 | - msgSender.execute(TopologyViewMessageHandler.this::requestSelectedIntentTraffic); | ||
888 | - } | ||
889 | eventAccummulator.add(event); | 730 | eventAccummulator.add(event); |
890 | } | 731 | } |
891 | } | 732 | } |
... | @@ -898,51 +739,8 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -898,51 +739,8 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
898 | } | 739 | } |
899 | } | 740 | } |
900 | 741 | ||
901 | - // encapsulate | ||
902 | - private static class TrafficEvent { | ||
903 | - enum Type { | ||
904 | - ALL_FLOW_TRAFFIC, ALL_PORT_TRAFFIC, DEV_LINK_FLOWS, SEL_INTENT | ||
905 | - } | ||
906 | - | ||
907 | - private final Type type; | ||
908 | - private final ObjectNode payload; | ||
909 | - | ||
910 | - TrafficEvent(Type type, ObjectNode payload) { | ||
911 | - this.type = type; | ||
912 | - this.payload = payload; | ||
913 | - } | ||
914 | - } | ||
915 | 742 | ||
916 | - // Periodic update of the traffic information | 743 | + // === SUMMARY MONITORING |
917 | - private class TrafficMonitor extends TimerTask { | ||
918 | - @Override | ||
919 | - public void run() { | ||
920 | - try { | ||
921 | - if (trafficEvent != null) { | ||
922 | - switch (trafficEvent.type) { | ||
923 | - case ALL_FLOW_TRAFFIC: | ||
924 | - requestAllFlowTraffic(); | ||
925 | - break; | ||
926 | - case ALL_PORT_TRAFFIC: | ||
927 | - requestAllPortTraffic(); | ||
928 | - break; | ||
929 | - case DEV_LINK_FLOWS: | ||
930 | - requestDeviceLinkFlows(trafficEvent.payload); | ||
931 | - break; | ||
932 | - case SEL_INTENT: | ||
933 | - requestSelectedIntentTraffic(); | ||
934 | - break; | ||
935 | - default: | ||
936 | - // nothing to do | ||
937 | - break; | ||
938 | - } | ||
939 | - } | ||
940 | - } catch (Exception e) { | ||
941 | - log.warn("Unable to handle traffic request due to {}", e.getMessage()); | ||
942 | - log.warn("Boom!", e); | ||
943 | - } | ||
944 | - } | ||
945 | - } | ||
946 | 744 | ||
947 | // Periodic update of the summary information | 745 | // Periodic update of the summary information |
948 | private class SummaryMonitor extends TimerTask { | 746 | private class SummaryMonitor extends TimerTask { |
... | @@ -967,7 +765,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { | ... | @@ -967,7 +765,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { |
967 | 765 | ||
968 | @Override | 766 | @Override |
969 | public void processItems(List<Event> items) { | 767 | public void processItems(List<Event> items) { |
970 | - // Start-of-Debugging | 768 | + // Start-of-Debugging -- Keep in until ONOS-2572 is fixed for reals |
971 | long now = System.currentTimeMillis(); | 769 | long now = System.currentTimeMillis(); |
972 | String me = this.toString(); | 770 | String me = this.toString(); |
973 | String miniMe = me.replaceAll("^.*@", "me@"); | 771 | String miniMe = me.replaceAll("^.*@", "me@"); | ... | ... |
... | @@ -18,7 +18,6 @@ package org.onosproject.ui.impl; | ... | @@ -18,7 +18,6 @@ package org.onosproject.ui.impl; |
18 | import com.fasterxml.jackson.databind.JsonNode; | 18 | import com.fasterxml.jackson.databind.JsonNode; |
19 | import com.fasterxml.jackson.databind.node.ArrayNode; | 19 | import com.fasterxml.jackson.databind.node.ArrayNode; |
20 | import com.fasterxml.jackson.databind.node.ObjectNode; | 20 | import com.fasterxml.jackson.databind.node.ObjectNode; |
21 | -import com.google.common.collect.ImmutableList; | ||
22 | import org.onlab.osgi.ServiceDirectory; | 21 | import org.onlab.osgi.ServiceDirectory; |
23 | import org.onlab.packet.IpAddress; | 22 | import org.onlab.packet.IpAddress; |
24 | import org.onosproject.cluster.ClusterEvent; | 23 | import org.onosproject.cluster.ClusterEvent; |
... | @@ -43,8 +42,6 @@ import org.onosproject.net.Host; | ... | @@ -43,8 +42,6 @@ import org.onosproject.net.Host; |
43 | import org.onosproject.net.HostId; | 42 | import org.onosproject.net.HostId; |
44 | import org.onosproject.net.HostLocation; | 43 | import org.onosproject.net.HostLocation; |
45 | import org.onosproject.net.Link; | 44 | import org.onosproject.net.Link; |
46 | -import org.onosproject.net.LinkKey; | ||
47 | -import org.onosproject.net.NetworkResource; | ||
48 | import org.onosproject.net.PortNumber; | 45 | import org.onosproject.net.PortNumber; |
49 | import org.onosproject.net.device.DeviceEvent; | 46 | import org.onosproject.net.device.DeviceEvent; |
50 | import org.onosproject.net.device.DeviceService; | 47 | import org.onosproject.net.device.DeviceService; |
... | @@ -55,29 +52,26 @@ import org.onosproject.net.flow.instructions.Instruction; | ... | @@ -55,29 +52,26 @@ import org.onosproject.net.flow.instructions.Instruction; |
55 | import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; | 52 | import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; |
56 | import org.onosproject.net.host.HostEvent; | 53 | import org.onosproject.net.host.HostEvent; |
57 | import org.onosproject.net.host.HostService; | 54 | import org.onosproject.net.host.HostService; |
58 | -import org.onosproject.net.intent.FlowRuleIntent; | ||
59 | -import org.onosproject.net.intent.Intent; | ||
60 | import org.onosproject.net.intent.IntentService; | 55 | import org.onosproject.net.intent.IntentService; |
61 | -import org.onosproject.net.intent.LinkCollectionIntent; | ||
62 | -import org.onosproject.net.intent.OpticalConnectivityIntent; | ||
63 | -import org.onosproject.net.intent.OpticalPathIntent; | ||
64 | -import org.onosproject.net.intent.PathIntent; | ||
65 | import org.onosproject.net.link.LinkEvent; | 56 | import org.onosproject.net.link.LinkEvent; |
66 | import org.onosproject.net.link.LinkService; | 57 | import org.onosproject.net.link.LinkService; |
67 | import org.onosproject.net.provider.ProviderId; | 58 | import org.onosproject.net.provider.ProviderId; |
68 | -import org.onosproject.net.statistic.Load; | ||
69 | import org.onosproject.net.statistic.StatisticService; | 59 | import org.onosproject.net.statistic.StatisticService; |
70 | import org.onosproject.net.topology.Topology; | 60 | import org.onosproject.net.topology.Topology; |
71 | import org.onosproject.net.topology.TopologyService; | 61 | import org.onosproject.net.topology.TopologyService; |
72 | import org.onosproject.ui.JsonUtils; | 62 | import org.onosproject.ui.JsonUtils; |
73 | import org.onosproject.ui.UiConnection; | 63 | import org.onosproject.ui.UiConnection; |
74 | import org.onosproject.ui.UiMessageHandler; | 64 | import org.onosproject.ui.UiMessageHandler; |
65 | +import org.onosproject.ui.impl.topo.ServicesBundle; | ||
75 | import org.onosproject.ui.topo.ButtonId; | 66 | import org.onosproject.ui.topo.ButtonId; |
67 | +import org.onosproject.ui.topo.DeviceHighlight; | ||
68 | +import org.onosproject.ui.topo.Highlights; | ||
69 | +import org.onosproject.ui.topo.HostHighlight; | ||
70 | +import org.onosproject.ui.topo.LinkHighlight; | ||
76 | import org.onosproject.ui.topo.PropertyPanel; | 71 | import org.onosproject.ui.topo.PropertyPanel; |
77 | import org.slf4j.Logger; | 72 | import org.slf4j.Logger; |
78 | import org.slf4j.LoggerFactory; | 73 | import org.slf4j.LoggerFactory; |
79 | 74 | ||
80 | -import java.text.DecimalFormat; | ||
81 | import java.util.ArrayList; | 75 | import java.util.ArrayList; |
82 | import java.util.Collection; | 76 | import java.util.Collection; |
83 | import java.util.Collections; | 77 | import java.util.Collections; |
... | @@ -95,10 +89,6 @@ import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED; | ... | @@ -95,10 +89,6 @@ import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED; |
95 | import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_REMOVED; | 89 | import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_REMOVED; |
96 | import static org.onosproject.cluster.ControllerNode.State.ACTIVE; | 90 | import static org.onosproject.cluster.ControllerNode.State.ACTIVE; |
97 | import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; | 91 | import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; |
98 | -import static org.onosproject.net.DeviceId.deviceId; | ||
99 | -import static org.onosproject.net.HostId.hostId; | ||
100 | -import static org.onosproject.net.LinkKey.linkKey; | ||
101 | -import static org.onosproject.net.PortNumber.P0; | ||
102 | import static org.onosproject.net.PortNumber.portNumber; | 92 | import static org.onosproject.net.PortNumber.portNumber; |
103 | import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED; | 93 | import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED; |
104 | import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED; | 94 | import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED; |
... | @@ -106,8 +96,7 @@ import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED; | ... | @@ -106,8 +96,7 @@ import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED; |
106 | import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED; | 96 | import static org.onosproject.net.host.HostEvent.Type.HOST_REMOVED; |
107 | import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED; | 97 | import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED; |
108 | import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED; | 98 | import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED; |
109 | -import static org.onosproject.ui.impl.TopologyViewMessageHandlerBase.StatsType.FLOW; | 99 | +import static org.onosproject.ui.impl.topo.TopoUtils.compactLinkString; |
110 | -import static org.onosproject.ui.impl.TopologyViewMessageHandlerBase.StatsType.PORT; | ||
111 | import static org.onosproject.ui.topo.TopoConstants.CoreButtons; | 100 | import static org.onosproject.ui.topo.TopoConstants.CoreButtons; |
112 | import static org.onosproject.ui.topo.TopoConstants.Properties; | 101 | import static org.onosproject.ui.topo.TopoConstants.Properties; |
113 | 102 | ||
... | @@ -121,24 +110,8 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -121,24 +110,8 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
121 | 110 | ||
122 | private static final ProviderId PID = | 111 | private static final ProviderId PID = |
123 | new ProviderId("core", "org.onosproject.core", true); | 112 | new ProviderId("core", "org.onosproject.core", true); |
124 | - private static final String COMPACT = "%s/%s-%s/%s"; | ||
125 | 113 | ||
126 | - private static final String SHOW_HIGHLIGHTS = "showHighlights"; | 114 | + protected static final String SHOW_HIGHLIGHTS = "showHighlights"; |
127 | - | ||
128 | - private static final double KILO = 1024; | ||
129 | - private static final double MEGA = 1024 * KILO; | ||
130 | - private static final double GIGA = 1024 * MEGA; | ||
131 | - | ||
132 | - private static final String GBITS_UNIT = "Gb"; | ||
133 | - private static final String MBITS_UNIT = "Mb"; | ||
134 | - private static final String KBITS_UNIT = "Kb"; | ||
135 | - private static final String BITS_UNIT = "b"; | ||
136 | - private static final String GBYTES_UNIT = "GB"; | ||
137 | - private static final String MBYTES_UNIT = "MB"; | ||
138 | - private static final String KBYTES_UNIT = "KB"; | ||
139 | - private static final String BYTES_UNIT = "B"; | ||
140 | - //4 Kilo Bytes as threshold | ||
141 | - private static final double BPS_THRESHOLD = 4 * KILO; | ||
142 | 115 | ||
143 | protected ServiceDirectory directory; | 116 | protected ServiceDirectory directory; |
144 | protected ClusterService clusterService; | 117 | protected ClusterService clusterService; |
... | @@ -153,9 +126,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -153,9 +126,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
153 | protected TopologyService topologyService; | 126 | protected TopologyService topologyService; |
154 | protected TunnelService tunnelService; | 127 | protected TunnelService tunnelService; |
155 | 128 | ||
156 | - protected enum StatsType { | 129 | + protected ServicesBundle servicesBundle; |
157 | - FLOW, PORT | ||
158 | - } | ||
159 | 130 | ||
160 | private String version; | 131 | private String version; |
161 | 132 | ||
... | @@ -187,6 +158,11 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -187,6 +158,11 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
187 | topologyService = directory.get(TopologyService.class); | 158 | topologyService = directory.get(TopologyService.class); |
188 | tunnelService = directory.get(TunnelService.class); | 159 | tunnelService = directory.get(TunnelService.class); |
189 | 160 | ||
161 | + servicesBundle = new ServicesBundle(intentService, deviceService, | ||
162 | + hostService, linkService, | ||
163 | + flowService, | ||
164 | + flowStatsService, portStatsService); | ||
165 | + | ||
190 | String ver = directory.get(CoreService.class).version().toString(); | 166 | String ver = directory.get(CoreService.class).version().toString(); |
191 | version = ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", ""); | 167 | version = ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", ""); |
192 | } | 168 | } |
... | @@ -232,64 +208,6 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -232,64 +208,6 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
232 | return JsonUtils.envelope("message", id, payload); | 208 | return JsonUtils.envelope("message", id, payload); |
233 | } | 209 | } |
234 | 210 | ||
235 | - // Produces a set of all hosts listed in the specified JSON array. | ||
236 | - protected Set<Host> getHosts(ArrayNode array) { | ||
237 | - Set<Host> hosts = new HashSet<>(); | ||
238 | - if (array != null) { | ||
239 | - for (JsonNode node : array) { | ||
240 | - try { | ||
241 | - addHost(hosts, hostId(node.asText())); | ||
242 | - } catch (IllegalArgumentException e) { | ||
243 | - log.debug("Skipping ID {}", node.asText()); | ||
244 | - } | ||
245 | - } | ||
246 | - } | ||
247 | - return hosts; | ||
248 | - } | ||
249 | - | ||
250 | - // Adds the specified host to the set of hosts. | ||
251 | - private void addHost(Set<Host> hosts, HostId hostId) { | ||
252 | - Host host = hostService.getHost(hostId); | ||
253 | - if (host != null) { | ||
254 | - hosts.add(host); | ||
255 | - } | ||
256 | - } | ||
257 | - | ||
258 | - | ||
259 | - // Produces a set of all devices listed in the specified JSON array. | ||
260 | - protected Set<Device> getDevices(ArrayNode array) { | ||
261 | - Set<Device> devices = new HashSet<>(); | ||
262 | - if (array != null) { | ||
263 | - for (JsonNode node : array) { | ||
264 | - try { | ||
265 | - addDevice(devices, deviceId(node.asText())); | ||
266 | - } catch (IllegalArgumentException e) { | ||
267 | - log.debug("Skipping ID {}", node.asText()); | ||
268 | - } | ||
269 | - } | ||
270 | - } | ||
271 | - return devices; | ||
272 | - } | ||
273 | - | ||
274 | - private void addDevice(Set<Device> devices, DeviceId deviceId) { | ||
275 | - Device device = deviceService.getDevice(deviceId); | ||
276 | - if (device != null) { | ||
277 | - devices.add(device); | ||
278 | - } | ||
279 | - } | ||
280 | - | ||
281 | - protected void addHover(Set<Host> hosts, Set<Device> devices, String hover) { | ||
282 | - try { | ||
283 | - addHost(hosts, hostId(hover)); | ||
284 | - } catch (IllegalArgumentException e) { | ||
285 | - try { | ||
286 | - addDevice(devices, deviceId(hover)); | ||
287 | - } catch (IllegalArgumentException ne) { | ||
288 | - log.debug("Skipping ID {}", hover); | ||
289 | - } | ||
290 | - } | ||
291 | - } | ||
292 | - | ||
293 | // Produces a cluster instance message to the client. | 211 | // Produces a cluster instance message to the client. |
294 | protected ObjectNode instanceMessage(ClusterEvent event, String messageType) { | 212 | protected ObjectNode instanceMessage(ClusterEvent event, String messageType) { |
295 | ControllerNode node = event.subject(); | 213 | ControllerNode node = event.subject(); |
... | @@ -445,6 +363,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -445,6 +363,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
445 | JsonUtils.node(payload, "memento")); | 363 | JsonUtils.node(payload, "memento")); |
446 | } | 364 | } |
447 | 365 | ||
366 | + | ||
448 | // ----------------------------------------------------------------------- | 367 | // ----------------------------------------------------------------------- |
449 | // Create models of the data to return, that overlays can adjust / augment | 368 | // Create models of the data to return, that overlays can adjust / augment |
450 | 369 | ||
... | @@ -527,24 +446,24 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -527,24 +446,24 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
527 | return count; | 446 | return count; |
528 | } | 447 | } |
529 | 448 | ||
530 | - // Counts all entries that egress on the given device links. | 449 | + // Counts all flow entries that egress on the links of the given device. |
531 | - protected Map<Link, Integer> getFlowCounts(DeviceId deviceId) { | 450 | + private Map<Link, Integer> getLinkFlowCounts(DeviceId deviceId) { |
451 | + // get the flows for the device | ||
532 | List<FlowEntry> entries = new ArrayList<>(); | 452 | List<FlowEntry> entries = new ArrayList<>(); |
533 | - Set<Link> links = new HashSet<>(linkService.getDeviceEgressLinks(deviceId)); | ||
534 | - Set<Host> hosts = hostService.getConnectedHosts(deviceId); | ||
535 | for (FlowEntry flowEntry : flowService.getFlowEntries(deviceId)) { | 453 | for (FlowEntry flowEntry : flowService.getFlowEntries(deviceId)) { |
536 | entries.add(flowEntry); | 454 | entries.add(flowEntry); |
537 | } | 455 | } |
538 | 456 | ||
539 | - // Add all edge links to the set | 457 | + // get egress links from device, and include edge links |
458 | + Set<Link> links = new HashSet<>(linkService.getDeviceEgressLinks(deviceId)); | ||
459 | + Set<Host> hosts = hostService.getConnectedHosts(deviceId); | ||
540 | if (hosts != null) { | 460 | if (hosts != null) { |
541 | for (Host host : hosts) { | 461 | for (Host host : hosts) { |
542 | - links.add(new DefaultEdgeLink(host.providerId(), | 462 | + links.add(createEdgeLink(host, false)); |
543 | - new ConnectPoint(host.id(), P0), | ||
544 | - host.location(), false)); | ||
545 | } | 463 | } |
546 | } | 464 | } |
547 | 465 | ||
466 | + // compile flow counts per link | ||
548 | Map<Link, Integer> counts = new HashMap<>(); | 467 | Map<Link, Integer> counts = new HashMap<>(); |
549 | for (Link link : links) { | 468 | for (Link link : links) { |
550 | counts.put(link, getEgressFlows(link, entries)); | 469 | counts.put(link, getEgressFlows(link, entries)); |
... | @@ -553,7 +472,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -553,7 +472,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
553 | } | 472 | } |
554 | 473 | ||
555 | // Counts all entries that egress on the link source port. | 474 | // Counts all entries that egress on the link source port. |
556 | - private Integer getEgressFlows(Link link, List<FlowEntry> entries) { | 475 | + private int getEgressFlows(Link link, List<FlowEntry> entries) { |
557 | int count = 0; | 476 | int count = 0; |
558 | PortNumber out = link.src().port(); | 477 | PortNumber out = link.src().port(); |
559 | for (FlowEntry entry : entries) { | 478 | for (FlowEntry entry : entries) { |
... | @@ -568,7 +487,6 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -568,7 +487,6 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
568 | return count; | 487 | return count; |
569 | } | 488 | } |
570 | 489 | ||
571 | - | ||
572 | // Returns host details response. | 490 | // Returns host details response. |
573 | protected PropertyPanel hostDetails(HostId hostId, long sid) { | 491 | protected PropertyPanel hostDetails(HostId hostId, long sid) { |
574 | Host host = hostService.getHost(hostId); | 492 | Host host = hostService.getHost(hostId); |
... | @@ -589,270 +507,98 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -589,270 +507,98 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
589 | .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE)) | 507 | .addProp(Properties.LATITUDE, annot.value(AnnotationKeys.LATITUDE)) |
590 | .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE)); | 508 | .addProp(Properties.LONGITUDE, annot.value(AnnotationKeys.LONGITUDE)); |
591 | 509 | ||
592 | - // TODO: add button descriptors | 510 | + // Potentially add button descriptors here |
593 | return pp; | 511 | return pp; |
594 | } | 512 | } |
595 | 513 | ||
596 | 514 | ||
597 | - // TODO: migrate to Traffic overlay | 515 | + // ---------------------------------------------------------------------- |
598 | - // Produces JSON message to trigger flow traffic overview visualization | ||
599 | - protected ObjectNode trafficSummaryMessage(StatsType type) { | ||
600 | - ObjectNode payload = objectNode(); | ||
601 | - ArrayNode paths = arrayNode(); | ||
602 | - payload.set("paths", paths); | ||
603 | - | ||
604 | - ObjectNode pathNodeN = objectNode(); | ||
605 | - ArrayNode linksNodeN = arrayNode(); | ||
606 | - ArrayNode labelsN = arrayNode(); | ||
607 | - | ||
608 | - pathNodeN.put("class", "plain").put("traffic", false); | ||
609 | - pathNodeN.set("links", linksNodeN); | ||
610 | - pathNodeN.set("labels", labelsN); | ||
611 | - paths.add(pathNodeN); | ||
612 | - | ||
613 | - ObjectNode pathNodeT = objectNode(); | ||
614 | - ArrayNode linksNodeT = arrayNode(); | ||
615 | - ArrayNode labelsT = arrayNode(); | ||
616 | - | ||
617 | - pathNodeT.put("class", "secondary").put("traffic", true); | ||
618 | - pathNodeT.set("links", linksNodeT); | ||
619 | - pathNodeT.set("labels", labelsT); | ||
620 | - paths.add(pathNodeT); | ||
621 | - | ||
622 | - Map<LinkKey, BiLink> biLinks = consolidateLinks(linkService.getLinks()); | ||
623 | - addEdgeLinks(biLinks); | ||
624 | - for (BiLink link : biLinks.values()) { | ||
625 | - boolean bi = link.two != null; | ||
626 | - if (type == FLOW) { | ||
627 | - link.addLoad(getLinkLoad(link.one)); | ||
628 | - link.addLoad(bi ? getLinkLoad(link.two) : null); | ||
629 | - } else if (type == PORT) { | ||
630 | - //For a bi-directional traffic links, use | ||
631 | - //the max link rate of either direction | ||
632 | - link.addLoad(portStatsService.load(link.one.src()), | ||
633 | - BPS_THRESHOLD, | ||
634 | - portStatsService.load(link.one.dst()), | ||
635 | - BPS_THRESHOLD); | ||
636 | - } | ||
637 | - if (link.hasTraffic) { | ||
638 | - linksNodeT.add(compactLinkString(link.one)); | ||
639 | - labelsT.add(type == PORT ? | ||
640 | - formatBitRate(link.rate) + "ps" : | ||
641 | - formatBytes(link.bytes)); | ||
642 | - } else { | ||
643 | - linksNodeN.add(compactLinkString(link.one)); | ||
644 | - labelsN.add(""); | ||
645 | - } | ||
646 | - } | ||
647 | - return JsonUtils.envelope(SHOW_HIGHLIGHTS, 0, payload); | ||
648 | - } | ||
649 | - | ||
650 | - private Load getLinkLoad(Link link) { | ||
651 | - if (link.src().elementId() instanceof DeviceId) { | ||
652 | - return flowStatsService.load(link); | ||
653 | - } | ||
654 | - return null; | ||
655 | - } | ||
656 | - | ||
657 | - private void addEdgeLinks(Map<LinkKey, BiLink> biLinks) { | ||
658 | - hostService.getHosts().forEach(host -> { | ||
659 | - addLink(biLinks, createEdgeLink(host, true)); | ||
660 | - addLink(biLinks, createEdgeLink(host, false)); | ||
661 | - }); | ||
662 | - } | ||
663 | - | ||
664 | - private Map<LinkKey, BiLink> consolidateLinks(Iterable<Link> links) { | ||
665 | - Map<LinkKey, BiLink> biLinks = new HashMap<>(); | ||
666 | - for (Link link : links) { | ||
667 | - addLink(biLinks, link); | ||
668 | - } | ||
669 | - return biLinks; | ||
670 | - } | ||
671 | 516 | ||
672 | - // Produces JSON message to trigger flow overview visualization | 517 | + /** |
673 | - protected ObjectNode flowSummaryMessage(Set<Device> devices) { | 518 | + * Transforms the given highlights model into a JSON message payload. |
519 | + * | ||
520 | + * @param highlights the model to transform | ||
521 | + * @return JSON payload | ||
522 | + */ | ||
523 | + protected ObjectNode json(Highlights highlights) { | ||
674 | ObjectNode payload = objectNode(); | 524 | ObjectNode payload = objectNode(); |
675 | - ArrayNode paths = arrayNode(); | ||
676 | - payload.set("paths", paths); | ||
677 | 525 | ||
678 | - for (Device device : devices) { | 526 | + ArrayNode devices = arrayNode(); |
679 | - Map<Link, Integer> counts = getFlowCounts(device.id()); | 527 | + ArrayNode hosts = arrayNode(); |
680 | - for (Link link : counts.keySet()) { | 528 | + ArrayNode links = arrayNode(); |
681 | - addLinkFlows(link, paths, counts.get(link)); | ||
682 | - } | ||
683 | - } | ||
684 | - return JsonUtils.envelope(SHOW_HIGHLIGHTS, 0, payload); | ||
685 | - } | ||
686 | 529 | ||
687 | - private void addLinkFlows(Link link, ArrayNode paths, Integer count) { | 530 | + payload.set("devices", devices); |
688 | - ObjectNode pathNode = objectNode(); | 531 | + payload.set("hosts", hosts); |
689 | - ArrayNode linksNode = arrayNode(); | 532 | + payload.set("links", links); |
690 | - ArrayNode labels = arrayNode(); | 533 | + |
691 | - boolean noFlows = count == null || count == 0; | 534 | + highlights.devices().forEach(dh -> devices.add(json(dh))); |
692 | - pathNode.put("class", noFlows ? "secondary" : "primary"); | 535 | + highlights.hosts().forEach(hh -> hosts.add(json(hh))); |
693 | - pathNode.put("traffic", false); | 536 | + jsonifyLinks(links, highlights.links()); |
694 | - pathNode.set("links", linksNode.add(compactLinkString(link))); | 537 | + |
695 | - pathNode.set("labels", labels.add(noFlows ? "" : (count.toString() + | 538 | + return payload; |
696 | - (count == 1 ? " flow" : " flows")))); | ||
697 | - paths.add(pathNode); | ||
698 | } | 539 | } |
699 | 540 | ||
541 | + private void jsonifyLinks(ArrayNode links, Set<LinkHighlight> hilites) { | ||
542 | + // a little more complicated than devices or hosts, since we are | ||
543 | + // grouping the link highlights by CSS classes | ||
700 | 544 | ||
701 | - // Produces JSON message to trigger traffic visualization | 545 | + // TODO: refactor this method (including client side) to use new format |
702 | - protected ObjectNode trafficMessage(TrafficClass... trafficClasses) { | 546 | + // as a more compact representation of the data... |
703 | - ObjectNode payload = objectNode(); | 547 | + // * links: |
704 | - ArrayNode paths = arrayNode(); | 548 | + // * "primary animated": |
705 | - payload.set("paths", paths); | 549 | + // * "link01" -> "label" |
550 | + // * "link02" -> "label" | ||
551 | + // * "secondary": | ||
552 | + // * "link04" -> "label" | ||
553 | + // * "link05" -> "" | ||
706 | 554 | ||
707 | - // Classify links based on their traffic traffic first... | ||
708 | - Map<LinkKey, BiLink> biLinks = classifyLinkTraffic(trafficClasses); | ||
709 | 555 | ||
710 | - // Then separate the links into their respective classes and send them out. | 556 | + Map<String, List<String>> linkIdMap = new HashMap<>(); |
711 | - Map<String, ObjectNode> pathNodes = new HashMap<>(); | 557 | + Map<String, List<String>> linkLabelMap = new HashMap<>(); |
712 | - for (BiLink biLink : biLinks.values()) { | 558 | + List<String> ids; |
713 | - boolean hasTraffic = biLink.hasTraffic; | 559 | + List<String> labels; |
714 | - String tc = (biLink.classes() + (hasTraffic ? " animated" : "")).trim(); | ||
715 | - ObjectNode pathNode = pathNodes.get(tc); | ||
716 | - if (pathNode == null) { | ||
717 | - pathNode = objectNode() | ||
718 | - .put("class", tc).put("traffic", hasTraffic); | ||
719 | - pathNode.set("links", arrayNode()); | ||
720 | - pathNode.set("labels", arrayNode()); | ||
721 | - pathNodes.put(tc, pathNode); | ||
722 | - paths.add(pathNode); | ||
723 | - } | ||
724 | - ((ArrayNode) pathNode.path("links")).add(compactLinkString(biLink.one)); | ||
725 | - ((ArrayNode) pathNode.path("labels")).add(hasTraffic ? formatBytes(biLink.bytes) : ""); | ||
726 | - } | ||
727 | 560 | ||
728 | - return JsonUtils.envelope(SHOW_HIGHLIGHTS, 0, payload); | 561 | + for (LinkHighlight lh : hilites) { |
729 | - } | 562 | + String cls = lh.cssClasses(); |
563 | + ids = linkIdMap.get(cls); | ||
564 | + labels = linkLabelMap.get(cls); | ||
730 | 565 | ||
731 | - // Classifies the link traffic according to the specified classes. | 566 | + if (ids == null) { // labels will be null also |
732 | - private Map<LinkKey, BiLink> classifyLinkTraffic(TrafficClass... trafficClasses) { | 567 | + ids = new ArrayList<>(); |
733 | - Map<LinkKey, BiLink> biLinks = new HashMap<>(); | 568 | + linkIdMap.put(cls, ids); |
734 | - for (TrafficClass trafficClass : trafficClasses) { | 569 | + labels = new ArrayList<>(); |
735 | - for (Intent intent : trafficClass.intents) { | 570 | + linkLabelMap.put(cls, labels); |
736 | - boolean isOptical = intent instanceof OpticalConnectivityIntent; | ||
737 | - List<Intent> installables = intentService.getInstallableIntents(intent.key()); | ||
738 | - if (installables != null) { | ||
739 | - for (Intent installable : installables) { | ||
740 | - String type = isOptical ? trafficClass.type + " optical" : trafficClass.type; | ||
741 | - if (installable instanceof PathIntent) { | ||
742 | - classifyLinks(type, biLinks, trafficClass.showTraffic, | ||
743 | - ((PathIntent) installable).path().links()); | ||
744 | - } else if (installable instanceof FlowRuleIntent) { | ||
745 | - classifyLinks(type, biLinks, trafficClass.showTraffic, | ||
746 | - linkResources(installable)); | ||
747 | - } else if (installable instanceof LinkCollectionIntent) { | ||
748 | - classifyLinks(type, biLinks, trafficClass.showTraffic, | ||
749 | - ((LinkCollectionIntent) installable).links()); | ||
750 | - } else if (installable instanceof OpticalPathIntent) { | ||
751 | - classifyLinks(type, biLinks, trafficClass.showTraffic, | ||
752 | - ((OpticalPathIntent) installable).path().links()); | ||
753 | - } | ||
754 | - } | ||
755 | - } | ||
756 | - } | ||
757 | - } | ||
758 | - return biLinks; | ||
759 | } | 571 | } |
760 | 572 | ||
761 | - // Extracts links from the specified flow rule intent resources | 573 | + ids.add(lh.elementId()); |
762 | - private Collection<Link> linkResources(Intent installable) { | 574 | + labels.add(lh.label()); |
763 | - ImmutableList.Builder<Link> builder = ImmutableList.builder(); | ||
764 | - for (NetworkResource r : installable.resources()) { | ||
765 | - if (r instanceof Link) { | ||
766 | - builder.add((Link) r); | ||
767 | } | 575 | } |
768 | - } | ||
769 | - return builder.build(); | ||
770 | - } | ||
771 | - | ||
772 | 576 | ||
773 | - // Adds the link segments (path or tree) associated with the specified | 577 | + for (String cls : linkIdMap.keySet()) { |
774 | - // connectivity intent | 578 | + ObjectNode group = objectNode(); |
775 | - private void classifyLinks(String type, Map<LinkKey, BiLink> biLinks, | 579 | + links.add(group); |
776 | - boolean showTraffic, Iterable<Link> links) { | ||
777 | - if (links != null) { | ||
778 | - for (Link link : links) { | ||
779 | - BiLink biLink = addLink(biLinks, link); | ||
780 | - if (showTraffic) { | ||
781 | - biLink.addLoad(getLinkLoad(link)); | ||
782 | - } | ||
783 | - biLink.addClass(type); | ||
784 | - } | ||
785 | - } | ||
786 | - } | ||
787 | 580 | ||
581 | + group.put("class", cls); | ||
788 | 582 | ||
789 | - static BiLink addLink(Map<LinkKey, BiLink> biLinks, Link link) { | 583 | + ArrayNode lnks = arrayNode(); |
790 | - LinkKey key = canonicalLinkKey(link); | 584 | + ArrayNode labs = arrayNode(); |
791 | - BiLink biLink = biLinks.get(key); | 585 | + group.set("links", lnks); |
792 | - if (biLink != null) { | 586 | + group.set("labels", labs); |
793 | - biLink.setOther(link); | ||
794 | - } else { | ||
795 | - biLink = new BiLink(key, link); | ||
796 | - biLinks.put(key, biLink); | ||
797 | - } | ||
798 | - return biLink; | ||
799 | - } | ||
800 | 587 | ||
801 | - // Poor-mans formatting to get the labels with byte counts looking nice. | 588 | + linkIdMap.get(cls).forEach(lnks::add); |
802 | - private String formatBytes(long bytes) { | 589 | + linkLabelMap.get(cls).forEach(labs::add); |
803 | - String unit; | ||
804 | - double value; | ||
805 | - if (bytes > GIGA) { | ||
806 | - value = bytes / GIGA; | ||
807 | - unit = GBYTES_UNIT; | ||
808 | - } else if (bytes > MEGA) { | ||
809 | - value = bytes / MEGA; | ||
810 | - unit = MBYTES_UNIT; | ||
811 | - } else if (bytes > KILO) { | ||
812 | - value = bytes / KILO; | ||
813 | - unit = KBYTES_UNIT; | ||
814 | - } else { | ||
815 | - value = bytes; | ||
816 | - unit = BYTES_UNIT; | ||
817 | } | 590 | } |
818 | - DecimalFormat format = new DecimalFormat("#,###.##"); | ||
819 | - return format.format(value) + " " + unit; | ||
820 | } | 591 | } |
821 | 592 | ||
822 | - // Poor-mans formatting to get the labels with bit rate looking nice. | ||
823 | - private String formatBitRate(long bytes) { | ||
824 | - String unit; | ||
825 | - double value; | ||
826 | - //Convert to bits | ||
827 | - long bits = bytes * 8; | ||
828 | - if (bits > GIGA) { | ||
829 | - value = bits / GIGA; | ||
830 | - unit = GBITS_UNIT; | ||
831 | 593 | ||
832 | - // NOTE: temporary hack to clip rate at 10.0 Gbps | 594 | + protected ObjectNode json(DeviceHighlight dh) { |
833 | - // Added for the CORD Fabric demo at ONS 2015 | 595 | + // TODO: implement this once we know what a device highlight looks like |
834 | - if (value > 10.0) { | 596 | + return objectNode(); |
835 | - value = 10.0; | ||
836 | - } | ||
837 | - | ||
838 | - } else if (bits > MEGA) { | ||
839 | - value = bits / MEGA; | ||
840 | - unit = MBITS_UNIT; | ||
841 | - } else if (bits > KILO) { | ||
842 | - value = bits / KILO; | ||
843 | - unit = KBITS_UNIT; | ||
844 | - } else { | ||
845 | - value = bits; | ||
846 | - unit = BITS_UNIT; | ||
847 | - } | ||
848 | - DecimalFormat format = new DecimalFormat("#,###.##"); | ||
849 | - return format.format(value) + " " + unit; | ||
850 | } | 597 | } |
851 | 598 | ||
852 | - // Produces compact string representation of a link. | 599 | + protected ObjectNode json(HostHighlight hh) { |
853 | - private static String compactLinkString(Link link) { | 600 | + // TODO: implement this once we know what a host highlight looks like |
854 | - return String.format(COMPACT, link.src().elementId(), link.src().port(), | 601 | + return objectNode(); |
855 | - link.dst().elementId(), link.dst().port()); | ||
856 | } | 602 | } |
857 | 603 | ||
858 | // translates the property panel into JSON, for returning to the client | 604 | // translates the property panel into JSON, for returning to the client |
... | @@ -879,95 +625,4 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { | ... | @@ -879,95 +625,4 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler { |
879 | return result; | 625 | return result; |
880 | } | 626 | } |
881 | 627 | ||
882 | - | ||
883 | - // Produces canonical link key, i.e. one that will match link and its inverse. | ||
884 | - static LinkKey canonicalLinkKey(Link link) { | ||
885 | - String sn = link.src().elementId().toString(); | ||
886 | - String dn = link.dst().elementId().toString(); | ||
887 | - return sn.compareTo(dn) < 0 ? | ||
888 | - linkKey(link.src(), link.dst()) : linkKey(link.dst(), link.src()); | ||
889 | - } | ||
890 | - | ||
891 | - // Representation of link and its inverse and any traffic data. | ||
892 | - static class BiLink { | ||
893 | - public final LinkKey key; | ||
894 | - public final Link one; | ||
895 | - public Link two; | ||
896 | - public boolean hasTraffic = false; | ||
897 | - public long bytes = 0; | ||
898 | - | ||
899 | - private Set<String> classes = new HashSet<>(); | ||
900 | - private long rate; | ||
901 | - | ||
902 | - BiLink(LinkKey key, Link link) { | ||
903 | - this.key = key; | ||
904 | - this.one = link; | ||
905 | - } | ||
906 | - | ||
907 | - void setOther(Link link) { | ||
908 | - this.two = link; | ||
909 | - } | ||
910 | - | ||
911 | - void addLoad(Load load) { | ||
912 | - addLoad(load, 0); | ||
913 | - } | ||
914 | - | ||
915 | - void addLoad(Load load, double threshold) { | ||
916 | - if (load != null) { | ||
917 | - this.hasTraffic = hasTraffic || load.rate() > threshold; | ||
918 | - this.bytes += load.latest(); | ||
919 | - this.rate += load.rate(); | ||
920 | - } | ||
921 | - } | ||
922 | - | ||
923 | - void addLoad(Load srcLinkLoad, | ||
924 | - double srcLinkThreshold, | ||
925 | - Load dstLinkLoad, | ||
926 | - double dstLinkThreshold) { | ||
927 | - //use the max of link load at source or destination | ||
928 | - if (srcLinkLoad != null) { | ||
929 | - this.hasTraffic = hasTraffic || srcLinkLoad.rate() > srcLinkThreshold; | ||
930 | - this.bytes = srcLinkLoad.latest(); | ||
931 | - this.rate = srcLinkLoad.rate(); | ||
932 | - } | ||
933 | - | ||
934 | - if (dstLinkLoad != null) { | ||
935 | - if (dstLinkLoad.rate() > this.rate) { | ||
936 | - this.bytes = dstLinkLoad.latest(); | ||
937 | - this.rate = dstLinkLoad.rate(); | ||
938 | - this.hasTraffic = hasTraffic || dstLinkLoad.rate() > dstLinkThreshold; | ||
939 | - } | ||
940 | - } | ||
941 | - } | ||
942 | - | ||
943 | - void addClass(String trafficClass) { | ||
944 | - classes.add(trafficClass); | ||
945 | - } | ||
946 | - | ||
947 | - String classes() { | ||
948 | - StringBuilder sb = new StringBuilder(); | ||
949 | - classes.forEach(c -> sb.append(c).append(" ")); | ||
950 | - return sb.toString().trim(); | ||
951 | - } | ||
952 | - } | ||
953 | - | ||
954 | - | ||
955 | - // TODO: move this to traffic overlay component | ||
956 | - // Auxiliary carrier of data for requesting traffic message. | ||
957 | - static class TrafficClass { | ||
958 | - public final boolean showTraffic; | ||
959 | - public final String type; | ||
960 | - public final Iterable<Intent> intents; | ||
961 | - | ||
962 | - TrafficClass(String type, Iterable<Intent> intents) { | ||
963 | - this(type, intents, false); | ||
964 | - } | ||
965 | - | ||
966 | - TrafficClass(String type, Iterable<Intent> intents, boolean showTraffic) { | ||
967 | - this.type = type; | ||
968 | - this.intents = intents; | ||
969 | - this.showTraffic = showTraffic; | ||
970 | - } | ||
971 | - } | ||
972 | - | ||
973 | } | 628 | } | ... | ... |
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; | ||
19 | + | ||
20 | +import com.google.common.collect.ImmutableList; | ||
21 | +import org.onosproject.net.Device; | ||
22 | +import org.onosproject.net.DeviceId; | ||
23 | +import org.onosproject.net.Host; | ||
24 | +import org.onosproject.net.Link; | ||
25 | +import org.onosproject.net.LinkKey; | ||
26 | +import org.onosproject.net.PortNumber; | ||
27 | +import org.onosproject.net.flow.FlowEntry; | ||
28 | +import org.onosproject.net.flow.TrafficTreatment; | ||
29 | +import org.onosproject.net.flow.instructions.Instruction; | ||
30 | +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; | ||
31 | +import org.onosproject.net.intent.FlowRuleIntent; | ||
32 | +import org.onosproject.net.intent.Intent; | ||
33 | +import org.onosproject.net.intent.LinkCollectionIntent; | ||
34 | +import org.onosproject.net.intent.OpticalConnectivityIntent; | ||
35 | +import org.onosproject.net.intent.OpticalPathIntent; | ||
36 | +import org.onosproject.net.intent.PathIntent; | ||
37 | +import org.onosproject.net.statistic.Load; | ||
38 | +import org.onosproject.ui.impl.topo.BiLink; | ||
39 | +import org.onosproject.ui.impl.topo.IntentSelection; | ||
40 | +import org.onosproject.ui.impl.topo.LinkStatsType; | ||
41 | +import org.onosproject.ui.impl.topo.NodeSelection; | ||
42 | +import org.onosproject.ui.impl.topo.ServicesBundle; | ||
43 | +import org.onosproject.ui.impl.topo.TopoUtils; | ||
44 | +import org.onosproject.ui.impl.topo.TopologyViewIntentFilter; | ||
45 | +import org.onosproject.ui.impl.topo.TrafficClass; | ||
46 | +import org.onosproject.ui.topo.Highlights; | ||
47 | +import org.slf4j.Logger; | ||
48 | +import org.slf4j.LoggerFactory; | ||
49 | + | ||
50 | +import java.util.ArrayList; | ||
51 | +import java.util.Collection; | ||
52 | +import java.util.Collections; | ||
53 | +import java.util.HashMap; | ||
54 | +import java.util.HashSet; | ||
55 | +import java.util.List; | ||
56 | +import java.util.Map; | ||
57 | +import java.util.Set; | ||
58 | +import java.util.Timer; | ||
59 | +import java.util.TimerTask; | ||
60 | + | ||
61 | +import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; | ||
62 | +import static org.onosproject.ui.impl.TrafficMonitorObject.Mode.IDLE; | ||
63 | +import static org.onosproject.ui.impl.TrafficMonitorObject.Mode.SEL_INTENT; | ||
64 | +import static org.onosproject.ui.topo.LinkHighlight.Flavor.PRIMARY_HIGHLIGHT; | ||
65 | +import static org.onosproject.ui.topo.LinkHighlight.Flavor.SECONDARY_HIGHLIGHT; | ||
66 | + | ||
67 | +/** | ||
68 | + * Encapsulates the behavior of monitoring specific traffic patterns. | ||
69 | + */ | ||
70 | +public class TrafficMonitorObject { | ||
71 | + | ||
72 | + // 4 Kilo Bytes as threshold | ||
73 | + private static final double BPS_THRESHOLD = 4 * TopoUtils.KILO; | ||
74 | + | ||
75 | + private static final Logger log = | ||
76 | + LoggerFactory.getLogger(TrafficMonitorObject.class); | ||
77 | + | ||
78 | + /** | ||
79 | + * Designates the different modes of operation. | ||
80 | + */ | ||
81 | + public enum Mode { | ||
82 | + IDLE, | ||
83 | + ALL_FLOW_TRAFFIC, | ||
84 | + ALL_PORT_TRAFFIC, | ||
85 | + DEV_LINK_FLOWS, | ||
86 | + RELATED_INTENTS, | ||
87 | + SEL_INTENT | ||
88 | + } | ||
89 | + | ||
90 | + private final long trafficPeriod; | ||
91 | + private final ServicesBundle servicesBundle; | ||
92 | + private final TopologyViewMessageHandler messageHandler; | ||
93 | + private final TopologyViewIntentFilter intentFilter; | ||
94 | + | ||
95 | + private final Timer timer = new Timer("topo-traffic"); | ||
96 | + | ||
97 | + private TimerTask trafficTask = null; | ||
98 | + private Mode mode = IDLE; | ||
99 | + private NodeSelection selectedNodes = null; | ||
100 | + private IntentSelection selectedIntents = null; | ||
101 | + | ||
102 | + | ||
103 | + /** | ||
104 | + * Constructs a traffic monitor. | ||
105 | + * | ||
106 | + * @param trafficPeriod traffic task period in ms | ||
107 | + * @param servicesBundle bundle of services | ||
108 | + * @param messageHandler our message handler | ||
109 | + */ | ||
110 | + public TrafficMonitorObject(long trafficPeriod, | ||
111 | + ServicesBundle servicesBundle, | ||
112 | + TopologyViewMessageHandler messageHandler) { | ||
113 | + this.trafficPeriod = trafficPeriod; | ||
114 | + this.servicesBundle = servicesBundle; | ||
115 | + this.messageHandler = messageHandler; | ||
116 | + | ||
117 | + intentFilter = new TopologyViewIntentFilter(servicesBundle); | ||
118 | + } | ||
119 | + | ||
120 | + // ======================================================================= | ||
121 | + // === API === // TODO: add javadocs | ||
122 | + | ||
123 | + public synchronized void monitor(Mode mode) { | ||
124 | + log.debug("monitor: {}", mode); | ||
125 | + this.mode = mode; | ||
126 | + | ||
127 | + switch (mode) { | ||
128 | + case ALL_FLOW_TRAFFIC: | ||
129 | + clearSelection(); | ||
130 | + scheduleTask(); | ||
131 | + sendAllFlowTraffic(); | ||
132 | + break; | ||
133 | + | ||
134 | + case ALL_PORT_TRAFFIC: | ||
135 | + clearSelection(); | ||
136 | + scheduleTask(); | ||
137 | + sendAllPortTraffic(); | ||
138 | + break; | ||
139 | + | ||
140 | + case SEL_INTENT: | ||
141 | + scheduleTask(); | ||
142 | + sendSelectedIntentTraffic(); | ||
143 | + break; | ||
144 | + | ||
145 | + default: | ||
146 | + log.debug("Unexpected call to monitor({})", mode); | ||
147 | + clearAll(); | ||
148 | + break; | ||
149 | + } | ||
150 | + } | ||
151 | + | ||
152 | + public synchronized void monitor(Mode mode, NodeSelection nodeSelection) { | ||
153 | + log.debug("monitor: {} -- {}", mode, nodeSelection); | ||
154 | + this.mode = mode; | ||
155 | + this.selectedNodes = nodeSelection; | ||
156 | + | ||
157 | + switch (mode) { | ||
158 | + case DEV_LINK_FLOWS: | ||
159 | + // only care about devices (not hosts) | ||
160 | + if (selectedNodes.devices().isEmpty()) { | ||
161 | + sendClearAll(); | ||
162 | + } else { | ||
163 | + scheduleTask(); | ||
164 | + sendDeviceLinkFlows(); | ||
165 | + } | ||
166 | + break; | ||
167 | + | ||
168 | + case RELATED_INTENTS: | ||
169 | + if (selectedNodes.none()) { | ||
170 | + sendClearAll(); | ||
171 | + } else { | ||
172 | + selectedIntents = new IntentSelection(selectedNodes, intentFilter); | ||
173 | + if (selectedIntents.none()) { | ||
174 | + sendClearAll(); | ||
175 | + } else { | ||
176 | + sendSelectedIntents(); | ||
177 | + } | ||
178 | + } | ||
179 | + break; | ||
180 | + | ||
181 | + default: | ||
182 | + log.debug("Unexpected call to monitor({}, {})", mode, nodeSelection); | ||
183 | + clearAll(); | ||
184 | + break; | ||
185 | + } | ||
186 | + } | ||
187 | + | ||
188 | + public synchronized void monitor(Intent intent) { | ||
189 | + log.debug("monitor intent: {}", intent.id()); | ||
190 | + selectedNodes = null; | ||
191 | + selectedIntents = new IntentSelection(intent); | ||
192 | + mode = SEL_INTENT; | ||
193 | + scheduleTask(); | ||
194 | + sendSelectedIntentTraffic(); | ||
195 | + } | ||
196 | + | ||
197 | + public synchronized void selectNextIntent() { | ||
198 | + if (selectedIntents != null) { | ||
199 | + selectedIntents.next(); | ||
200 | + sendSelectedIntents(); | ||
201 | + } | ||
202 | + } | ||
203 | + | ||
204 | + public synchronized void selectPreviousIntent() { | ||
205 | + if (selectedIntents != null) { | ||
206 | + selectedIntents.prev(); | ||
207 | + sendSelectedIntents(); | ||
208 | + } | ||
209 | + } | ||
210 | + | ||
211 | + public synchronized void pokeIntent() { | ||
212 | + if (mode == SEL_INTENT) { | ||
213 | + sendSelectedIntentTraffic(); | ||
214 | + } | ||
215 | + } | ||
216 | + | ||
217 | + public synchronized void stop() { | ||
218 | + log.debug("STOP"); | ||
219 | + if (mode != IDLE) { | ||
220 | + sendClearAll(); | ||
221 | + } | ||
222 | + } | ||
223 | + | ||
224 | + | ||
225 | + // ======================================================================= | ||
226 | + // === Helper methods === | ||
227 | + | ||
228 | + private void sendClearAll() { | ||
229 | + clearAll(); | ||
230 | + sendClearHighlights(); | ||
231 | + } | ||
232 | + | ||
233 | + private void clearAll() { | ||
234 | + this.mode = IDLE; | ||
235 | + clearSelection(); | ||
236 | + cancelTask(); | ||
237 | + } | ||
238 | + | ||
239 | + private void clearSelection() { | ||
240 | + selectedNodes = null; | ||
241 | + selectedIntents = null; | ||
242 | + } | ||
243 | + | ||
244 | + private synchronized void scheduleTask() { | ||
245 | + if (trafficTask == null) { | ||
246 | + log.debug("Starting up background traffic task..."); | ||
247 | + trafficTask = new TrafficMonitor(); | ||
248 | + timer.schedule(trafficTask, trafficPeriod, trafficPeriod); | ||
249 | + } else { | ||
250 | + // TEMPORARY until we are sure this is working correctly | ||
251 | + log.debug("(traffic task already running)"); | ||
252 | + } | ||
253 | + } | ||
254 | + | ||
255 | + private synchronized void cancelTask() { | ||
256 | + if (trafficTask != null) { | ||
257 | + trafficTask.cancel(); | ||
258 | + trafficTask = null; | ||
259 | + } | ||
260 | + } | ||
261 | + | ||
262 | + // --- | ||
263 | + | ||
264 | + private void sendAllFlowTraffic() { | ||
265 | + log.debug("sendAllFlowTraffic"); | ||
266 | + sendHighlights(trafficSummary(LinkStatsType.FLOW_STATS)); | ||
267 | + } | ||
268 | + | ||
269 | + private void sendAllPortTraffic() { | ||
270 | + log.debug("sendAllPortTraffic"); | ||
271 | + sendHighlights(trafficSummary(LinkStatsType.PORT_STATS)); | ||
272 | + } | ||
273 | + | ||
274 | + private void sendDeviceLinkFlows() { | ||
275 | + log.debug("sendDeviceLinkFlows: {}", selectedNodes); | ||
276 | + sendHighlights(deviceLinkFlows()); | ||
277 | + } | ||
278 | + | ||
279 | + private void sendSelectedIntents() { | ||
280 | + log.debug("sendSelectedIntents: {}", selectedIntents); | ||
281 | + sendHighlights(intentGroup()); | ||
282 | + } | ||
283 | + | ||
284 | + private void sendSelectedIntentTraffic() { | ||
285 | + log.debug("sendSelectedIntentTraffic: {}", selectedIntents); | ||
286 | + sendHighlights(intentTraffic()); | ||
287 | + } | ||
288 | + | ||
289 | + private void sendClearHighlights() { | ||
290 | + log.debug("sendClearHighlights"); | ||
291 | + sendHighlights(new Highlights()); | ||
292 | + } | ||
293 | + | ||
294 | + private void sendHighlights(Highlights highlights) { | ||
295 | + messageHandler.sendHighlights(highlights); | ||
296 | + } | ||
297 | + | ||
298 | + | ||
299 | + // ======================================================================= | ||
300 | + // === Generate messages in JSON object node format | ||
301 | + | ||
302 | + private Highlights trafficSummary(LinkStatsType type) { | ||
303 | + Highlights highlights = new Highlights(); | ||
304 | + | ||
305 | + // compile a set of bilinks (combining pairs of unidirectional links) | ||
306 | + Map<LinkKey, BiLink> linkMap = new HashMap<>(); | ||
307 | + compileLinks(linkMap); | ||
308 | + addEdgeLinks(linkMap); | ||
309 | + | ||
310 | + for (BiLink blink : linkMap.values()) { | ||
311 | + if (type == LinkStatsType.FLOW_STATS) { | ||
312 | + attachFlowLoad(blink); | ||
313 | + } else if (type == LinkStatsType.PORT_STATS) { | ||
314 | + attachPortLoad(blink); | ||
315 | + } | ||
316 | + | ||
317 | + // we only want to report on links deemed to have traffic | ||
318 | + if (blink.hasTraffic()) { | ||
319 | + highlights.add(blink.generateHighlight(type)); | ||
320 | + } | ||
321 | + } | ||
322 | + return highlights; | ||
323 | + } | ||
324 | + | ||
325 | + // create highlights for links, showing flows for selected devices. | ||
326 | + private Highlights deviceLinkFlows() { | ||
327 | + Highlights highlights = new Highlights(); | ||
328 | + | ||
329 | + if (selectedNodes != null && !selectedNodes.devices().isEmpty()) { | ||
330 | + // capture flow counts on bilinks | ||
331 | + Map<LinkKey, BiLink> linkMap = new HashMap<>(); | ||
332 | + | ||
333 | + for (Device device : selectedNodes.devices()) { | ||
334 | + Map<Link, Integer> counts = getLinkFlowCounts(device.id()); | ||
335 | + for (Link link : counts.keySet()) { | ||
336 | + BiLink blink = TopoUtils.addLink(linkMap, link); | ||
337 | + blink.addFlows(counts.get(link)); | ||
338 | + } | ||
339 | + } | ||
340 | + | ||
341 | + // now report on our collated links | ||
342 | + for (BiLink blink : linkMap.values()) { | ||
343 | + highlights.add(blink.generateHighlight(LinkStatsType.FLOW_COUNT)); | ||
344 | + } | ||
345 | + | ||
346 | + } | ||
347 | + return highlights; | ||
348 | + } | ||
349 | + | ||
350 | + private Highlights intentGroup() { | ||
351 | + Highlights highlights = new Highlights(); | ||
352 | + | ||
353 | + if (selectedIntents != null && !selectedIntents.none()) { | ||
354 | + // If 'all' intents are selected, they will all have primary | ||
355 | + // highlighting; otherwise, the specifically selected intent will | ||
356 | + // have primary highlighting, and the remainder will have secondary | ||
357 | + // highlighting. | ||
358 | + Set<Intent> primary; | ||
359 | + Set<Intent> secondary; | ||
360 | + int count = selectedIntents.size(); | ||
361 | + | ||
362 | + Set<Intent> allBut = new HashSet<>(selectedIntents.intents()); | ||
363 | + Intent current; | ||
364 | + | ||
365 | + if (selectedIntents.all()) { | ||
366 | + primary = allBut; | ||
367 | + secondary = Collections.emptySet(); | ||
368 | + log.debug("Highlight all intents ({})", count); | ||
369 | + } else { | ||
370 | + current = selectedIntents.current(); | ||
371 | + primary = new HashSet<>(); | ||
372 | + primary.add(current); | ||
373 | + allBut.remove(current); | ||
374 | + secondary = allBut; | ||
375 | + log.debug("Highlight intent: {} ([{}] of {})", | ||
376 | + current.id(), selectedIntents.index(), count); | ||
377 | + } | ||
378 | + TrafficClass tc1 = new TrafficClass(PRIMARY_HIGHLIGHT, primary); | ||
379 | + TrafficClass tc2 = new TrafficClass(SECONDARY_HIGHLIGHT, secondary); | ||
380 | + // classify primary links after secondary (last man wins) | ||
381 | + highlightIntents(highlights, tc2, tc1); | ||
382 | + } | ||
383 | + return highlights; | ||
384 | + } | ||
385 | + | ||
386 | + private Highlights intentTraffic() { | ||
387 | + Highlights highlights = new Highlights(); | ||
388 | + | ||
389 | + if (selectedIntents != null && selectedIntents.single()) { | ||
390 | + Intent current = selectedIntents.current(); | ||
391 | + Set<Intent> primary = new HashSet<>(); | ||
392 | + primary.add(current); | ||
393 | + log.debug("Highlight traffic for intent: {} ([{}] of {})", | ||
394 | + current.id(), selectedIntents.index(), selectedIntents.size()); | ||
395 | + TrafficClass tc1 = new TrafficClass(PRIMARY_HIGHLIGHT, primary, true); | ||
396 | + highlightIntents(highlights, tc1); | ||
397 | + } | ||
398 | + return highlights; | ||
399 | + } | ||
400 | + | ||
401 | + | ||
402 | + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
403 | + | ||
404 | + private void compileLinks(Map<LinkKey, BiLink> linkMap) { | ||
405 | + servicesBundle.linkService().getLinks() | ||
406 | + .forEach(link -> TopoUtils.addLink(linkMap, link)); | ||
407 | + } | ||
408 | + | ||
409 | + private void addEdgeLinks(Map<LinkKey, BiLink> biLinks) { | ||
410 | + servicesBundle.hostService().getHosts().forEach(host -> { | ||
411 | + TopoUtils.addLink(biLinks, createEdgeLink(host, true)); | ||
412 | + TopoUtils.addLink(biLinks, createEdgeLink(host, false)); | ||
413 | + }); | ||
414 | + } | ||
415 | + | ||
416 | + private Load getLinkFlowLoad(Link link) { | ||
417 | + if (link != null && link.src().elementId() instanceof DeviceId) { | ||
418 | + return servicesBundle.flowStatsService().load(link); | ||
419 | + } | ||
420 | + return null; | ||
421 | + } | ||
422 | + | ||
423 | + private void attachFlowLoad(BiLink link) { | ||
424 | + link.addLoad(getLinkFlowLoad(link.one())); | ||
425 | + link.addLoad(getLinkFlowLoad(link.two())); | ||
426 | + } | ||
427 | + | ||
428 | + private void attachPortLoad(BiLink link) { | ||
429 | + // For bi-directional traffic links, use | ||
430 | + // the max link rate of either direction | ||
431 | + // (we choose 'one' since we know that is never null) | ||
432 | + Link one = link.one(); | ||
433 | + Load egressSrc = servicesBundle.portStatsService().load(one.src()); | ||
434 | + Load egressDst = servicesBundle.portStatsService().load(one.dst()); | ||
435 | +// link.addLoad(maxLoad(egressSrc, egressDst), BPS_THRESHOLD); | ||
436 | + link.addLoad(maxLoad(egressSrc, egressDst), 10); // FIXME - debug only | ||
437 | + } | ||
438 | + | ||
439 | + private Load maxLoad(Load a, Load b) { | ||
440 | + if (a == null) { | ||
441 | + return b; | ||
442 | + } | ||
443 | + if (b == null) { | ||
444 | + return a; | ||
445 | + } | ||
446 | + return a.rate() > b.rate() ? a : b; | ||
447 | + } | ||
448 | + | ||
449 | + // --- | ||
450 | + | ||
451 | + // Counts all flow entries that egress on the links of the given device. | ||
452 | + private Map<Link, Integer> getLinkFlowCounts(DeviceId deviceId) { | ||
453 | + // get the flows for the device | ||
454 | + List<FlowEntry> entries = new ArrayList<>(); | ||
455 | + for (FlowEntry flowEntry : servicesBundle.flowService().getFlowEntries(deviceId)) { | ||
456 | + entries.add(flowEntry); | ||
457 | + } | ||
458 | + | ||
459 | + // get egress links from device, and include edge links | ||
460 | + Set<Link> links = new HashSet<>(servicesBundle.linkService().getDeviceEgressLinks(deviceId)); | ||
461 | + Set<Host> hosts = servicesBundle.hostService().getConnectedHosts(deviceId); | ||
462 | + if (hosts != null) { | ||
463 | + for (Host host : hosts) { | ||
464 | + links.add(createEdgeLink(host, false)); | ||
465 | + } | ||
466 | + } | ||
467 | + | ||
468 | + // compile flow counts per link | ||
469 | + Map<Link, Integer> counts = new HashMap<>(); | ||
470 | + for (Link link : links) { | ||
471 | + counts.put(link, getEgressFlows(link, entries)); | ||
472 | + } | ||
473 | + return counts; | ||
474 | + } | ||
475 | + | ||
476 | + // Counts all entries that egress on the link source port. | ||
477 | + private int getEgressFlows(Link link, List<FlowEntry> entries) { | ||
478 | + int count = 0; | ||
479 | + PortNumber out = link.src().port(); | ||
480 | + for (FlowEntry entry : entries) { | ||
481 | + TrafficTreatment treatment = entry.treatment(); | ||
482 | + for (Instruction instruction : treatment.allInstructions()) { | ||
483 | + if (instruction.type() == Instruction.Type.OUTPUT && | ||
484 | + ((OutputInstruction) instruction).port().equals(out)) { | ||
485 | + count++; | ||
486 | + } | ||
487 | + } | ||
488 | + } | ||
489 | + return count; | ||
490 | + } | ||
491 | + | ||
492 | + // --- | ||
493 | + private void highlightIntents(Highlights highlights, | ||
494 | + TrafficClass... trafficClasses) { | ||
495 | + Map<LinkKey, BiLink> linkMap = new HashMap<>(); | ||
496 | + | ||
497 | + | ||
498 | + for (TrafficClass trafficClass : trafficClasses) { | ||
499 | + classifyLinkTraffic(linkMap, trafficClass); | ||
500 | + } | ||
501 | + | ||
502 | + for (BiLink blink : linkMap.values()) { | ||
503 | + highlights.add(blink.generateHighlight(LinkStatsType.TAGGED)); | ||
504 | + } | ||
505 | + } | ||
506 | + | ||
507 | + private void classifyLinkTraffic(Map<LinkKey, BiLink> linkMap, | ||
508 | + TrafficClass trafficClass) { | ||
509 | + for (Intent intent : trafficClass.intents()) { | ||
510 | + boolean isOptical = intent instanceof OpticalConnectivityIntent; | ||
511 | + List<Intent> installables = servicesBundle.intentService() | ||
512 | + .getInstallableIntents(intent.key()); | ||
513 | + Iterable<Link> links = null; | ||
514 | + | ||
515 | + if (installables != null) { | ||
516 | + for (Intent installable : installables) { | ||
517 | + | ||
518 | + if (installable instanceof PathIntent) { | ||
519 | + links = ((PathIntent) installable).path().links(); | ||
520 | + } else if (installable instanceof FlowRuleIntent) { | ||
521 | + links = linkResources(installable); | ||
522 | + } else if (installable instanceof LinkCollectionIntent) { | ||
523 | + links = ((LinkCollectionIntent) installable).links(); | ||
524 | + } else if (installable instanceof OpticalPathIntent) { | ||
525 | + links = ((OpticalPathIntent) installable).path().links(); | ||
526 | + } | ||
527 | + | ||
528 | + classifyLinks(trafficClass, isOptical, linkMap, links); | ||
529 | + } | ||
530 | + } | ||
531 | + } | ||
532 | + } | ||
533 | + | ||
534 | + private void classifyLinks(TrafficClass trafficClass, boolean isOptical, | ||
535 | + Map<LinkKey, BiLink> linkMap, | ||
536 | + Iterable<Link> links) { | ||
537 | + if (links != null) { | ||
538 | + for (Link link : links) { | ||
539 | + BiLink blink = TopoUtils.addLink(linkMap, link); | ||
540 | + if (trafficClass.showTraffic()) { | ||
541 | + blink.addLoad(getLinkFlowLoad(link)); | ||
542 | + blink.setAntMarch(true); | ||
543 | + } | ||
544 | + blink.setOptical(isOptical); | ||
545 | + blink.tagFlavor(trafficClass.flavor()); | ||
546 | + } | ||
547 | + } | ||
548 | + } | ||
549 | + | ||
550 | + // Extracts links from the specified flow rule intent resources | ||
551 | + private Collection<Link> linkResources(Intent installable) { | ||
552 | + ImmutableList.Builder<Link> builder = ImmutableList.builder(); | ||
553 | + installable.resources().stream().filter(r -> r instanceof Link) | ||
554 | + .forEach(r -> builder.add((Link) r)); | ||
555 | + return builder.build(); | ||
556 | + } | ||
557 | + | ||
558 | + // ======================================================================= | ||
559 | + // === Background Task | ||
560 | + | ||
561 | + // Provides periodic update of traffic information to the client | ||
562 | + private class TrafficMonitor extends TimerTask { | ||
563 | + @Override | ||
564 | + public void run() { | ||
565 | + try { | ||
566 | + switch (mode) { | ||
567 | + case ALL_FLOW_TRAFFIC: | ||
568 | + sendAllFlowTraffic(); | ||
569 | + break; | ||
570 | + case ALL_PORT_TRAFFIC: | ||
571 | + sendAllPortTraffic(); | ||
572 | + break; | ||
573 | + case DEV_LINK_FLOWS: | ||
574 | + sendDeviceLinkFlows(); | ||
575 | + break; | ||
576 | + case SEL_INTENT: | ||
577 | + sendSelectedIntentTraffic(); | ||
578 | + break; | ||
579 | + | ||
580 | + default: | ||
581 | + // RELATED_INTENTS and IDLE modes should never invoke | ||
582 | + // the background task, but if they do, they have | ||
583 | + // nothing to do | ||
584 | + break; | ||
585 | + } | ||
586 | + | ||
587 | + } catch (Exception e) { | ||
588 | + log.warn("Unable to process traffic task due to {}", e.getMessage()); | ||
589 | + log.warn("Boom!", e); | ||
590 | + } | ||
591 | + } | ||
592 | + } | ||
593 | + | ||
594 | +} |
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 | ... | ... |
-
Please register or login to post a comment