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