Simon Hunt

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

- Major refactoring of TopologyViewMessageHandler and related classes.

Change-Id: I920f7f9f7317f3987a9a8da35ac086e9f8cab8d3
Showing 22 changed files with 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 &harr; B";
43 + private static final String A_SINGLE_B = "A &rarr; B";
44 +
43 private static final String LINK_DATA_REQ = "linkDataRequest"; 45 private static final String LINK_DATA_REQ = "linkDataRequest";
44 private static final String LINK_DATA_RESP = "linkDataResponse"; 46 private static final String LINK_DATA_RESP = "linkDataResponse";
45 private static final String LINKS = "links"; 47 private static final String LINKS = "links";
...@@ -94,38 +96,39 @@ public class LinkViewMessageHandler extends UiMessageHandler { ...@@ -94,38 +96,39 @@ public class LinkViewMessageHandler extends UiMessageHandler {
94 96
95 // First consolidate all uni-directional links into two-directional ones. 97 // First consolidate all uni-directional links into two-directional ones.
96 Map<LinkKey, BiLink> biLinks = Maps.newHashMap(); 98 Map<LinkKey, BiLink> biLinks = Maps.newHashMap();
97 - ls.getLinks().forEach(link -> addLink(biLinks, link)); 99 + ls.getLinks().forEach(link -> TopoUtils.addLink(biLinks, link));
98 100
99 // Now scan over all bi-links and produce table rows from them. 101 // Now scan over all bi-links and produce table rows from them.
100 biLinks.values().forEach(biLink -> populateRow(tm.addRow(), biLink)); 102 biLinks.values().forEach(biLink -> populateRow(tm.addRow(), biLink));
101 } 103 }
102 104
103 private void populateRow(TableModel.Row row, BiLink biLink) { 105 private void populateRow(TableModel.Row row, BiLink biLink) {
104 - row.cell(ONE, biLink.one.src()) 106 + row.cell(ONE, biLink.one().src())
105 - .cell(TWO, biLink.one.dst()) 107 + .cell(TWO, biLink.one().dst())
106 .cell(TYPE, linkType(biLink)) 108 .cell(TYPE, linkType(biLink))
107 .cell(STATE, linkState(biLink)) 109 .cell(STATE, linkState(biLink))
108 .cell(DIRECTION, linkDir(biLink)) 110 .cell(DIRECTION, linkDir(biLink))
109 - .cell(DURABLE, biLink.one.isDurable()); 111 + .cell(DURABLE, biLink.one().isDurable());
110 } 112 }
111 113
112 private String linkType(BiLink link) { 114 private String linkType(BiLink link) {
113 StringBuilder sb = new StringBuilder(); 115 StringBuilder sb = new StringBuilder();
114 - sb.append(link.one.type()); 116 + sb.append(link.one().type());
115 - if (link.two != null && link.two.type() != link.one.type()) { 117 + if (link.two() != null && link.two().type() != link.one().type()) {
116 - sb.append(" / ").append(link.two.type()); 118 + sb.append(" / ").append(link.two().type());
117 } 119 }
118 return sb.toString(); 120 return sb.toString();
119 } 121 }
120 122
121 private String linkState(BiLink link) { 123 private String linkState(BiLink link) {
122 - return (link.one.state() == Link.State.ACTIVE || 124 + return (link.one().state() == Link.State.ACTIVE ||
123 - link.two.state() == Link.State.ACTIVE) ? 125 + link.two().state() == Link.State.ACTIVE) ?
124 ICON_ID_ONLINE : ICON_ID_OFFLINE; 126 ICON_ID_ONLINE : ICON_ID_OFFLINE;
125 } 127 }
126 128
127 private String linkDir(BiLink link) { 129 private String linkDir(BiLink link) {
128 - return link.two != null ? "A &harr; B" : "A &rarr; B"; 130 + return link.two() != null ? A_BOTH_B : A_SINGLE_B;
129 } 131 }
130 } 132 }
133 +
131 } 134 }
......
...@@ -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
......