ONOS-1479 -- GUI - augmenting topology view for extensibility: WIP.
- Major refactoring of TopologyViewMessageHandler and related classes. Change-Id: I920f7f9f7317f3987a9a8da35ac086e9f8cab8d3
Showing
22 changed files
with
1431 additions
and
42 deletions
| 1 | +/* | ||
| 2 | + * Copyright 2015 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + * | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +package org.onosproject.ui.topo; | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * Partial implementation of the types of highlight to apply to topology | ||
| 22 | + * elements. | ||
| 23 | + */ | ||
| 24 | +public abstract class AbstractHighlight { | ||
| 25 | + private final TopoElementType type; | ||
| 26 | + private final String elementId; | ||
| 27 | + | ||
| 28 | + public AbstractHighlight(TopoElementType type, String elementId) { | ||
| 29 | + this.type = type; | ||
| 30 | + this.elementId = elementId; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public TopoElementType type() { | ||
| 34 | + return type; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + public String elementId() { | ||
| 38 | + return elementId; | ||
| 39 | + } | ||
| 40 | +} |
| 1 | +/* | ||
| 2 | + * Copyright 2015 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + * | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +package org.onosproject.ui.topo; | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * Denotes the types of highlight to apply to a link. | ||
| 22 | + */ | ||
| 23 | +public class DeviceHighlight extends AbstractHighlight { | ||
| 24 | + | ||
| 25 | + public DeviceHighlight(String deviceId) { | ||
| 26 | + super(TopoElementType.DEVICE, deviceId); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +} |
| 1 | +/* | ||
| 2 | + * Copyright 2015 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + * | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +package org.onosproject.ui.topo; | ||
| 19 | + | ||
| 20 | +import java.text.DecimalFormat; | ||
| 21 | +import java.util.Collections; | ||
| 22 | +import java.util.HashSet; | ||
| 23 | +import java.util.Set; | ||
| 24 | + | ||
| 25 | +/** | ||
| 26 | + * Encapsulates highlights to be applied to the topology view, such as | ||
| 27 | + * highlighting links, displaying link labels, perhaps even decorating | ||
| 28 | + * nodes with badges, etc. | ||
| 29 | + */ | ||
| 30 | +public class Highlights { | ||
| 31 | + | ||
| 32 | + private static final DecimalFormat DF0 = new DecimalFormat("#,###"); | ||
| 33 | + | ||
| 34 | + private final Set<DeviceHighlight> devices = new HashSet<>(); | ||
| 35 | + private final Set<HostHighlight> hosts = new HashSet<>(); | ||
| 36 | + private final Set<LinkHighlight> links = new HashSet<>(); | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + public Highlights add(DeviceHighlight d) { | ||
| 40 | + devices.add(d); | ||
| 41 | + return this; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + public Highlights add(HostHighlight h) { | ||
| 45 | + hosts.add(h); | ||
| 46 | + return this; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + public Highlights add(LinkHighlight lh) { | ||
| 50 | + links.add(lh); | ||
| 51 | + return this; | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + | ||
| 55 | + public Set<DeviceHighlight> devices() { | ||
| 56 | + return Collections.unmodifiableSet(devices); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + public Set<HostHighlight> hosts() { | ||
| 60 | + return Collections.unmodifiableSet(hosts); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + public Set<LinkHighlight> links() { | ||
| 64 | + return Collections.unmodifiableSet(links); | ||
| 65 | + } | ||
| 66 | +} |
| 1 | +/* | ||
| 2 | + * Copyright 2015 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + * | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +package org.onosproject.ui.topo; | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * Denotes the types of highlight to apply to a link. | ||
| 22 | + */ | ||
| 23 | +public class HostHighlight extends AbstractHighlight { | ||
| 24 | + | ||
| 25 | + public HostHighlight(String hostId) { | ||
| 26 | + super(TopoElementType.HOST, hostId); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +} |
| 1 | +/* | ||
| 2 | + * Copyright 2015 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + * | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +package org.onosproject.ui.topo; | ||
| 19 | + | ||
| 20 | +import java.util.Collections; | ||
| 21 | +import java.util.Set; | ||
| 22 | +import java.util.TreeSet; | ||
| 23 | + | ||
| 24 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
| 25 | + | ||
| 26 | +/** | ||
| 27 | + * Denotes the highlighting to be applied to a link. | ||
| 28 | + * {@link Flavor} is a closed set of NO-, PRIMARY-, or SECONDARY- highlighting. | ||
| 29 | + * {@link Mod} is an open ended set of additional modifications (CSS classes) | ||
| 30 | + * to apply. Note that {@link #MOD_OPTICAL} and {@link #MOD_ANIMATED} are | ||
| 31 | + * pre-defined mods. | ||
| 32 | + * Label text may be set, which will also be displayed on the link. | ||
| 33 | + */ | ||
| 34 | +public class LinkHighlight extends AbstractHighlight { | ||
| 35 | + | ||
| 36 | + private static final String PLAIN = "plain"; | ||
| 37 | + private static final String PRIMARY = "primary"; | ||
| 38 | + private static final String SECONDARY = "secondary"; | ||
| 39 | + private static final String EMPTY = ""; | ||
| 40 | + private static final String SPACE = " "; | ||
| 41 | + | ||
| 42 | + private final Flavor flavor; | ||
| 43 | + private final Set<Mod> mods = new TreeSet<>(); | ||
| 44 | + private String label = EMPTY; | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * Constructs a link highlight entity. | ||
| 48 | + * | ||
| 49 | + * @param linkId the link identifier | ||
| 50 | + * @param flavor the highlight flavor | ||
| 51 | + */ | ||
| 52 | + public LinkHighlight(String linkId, Flavor flavor) { | ||
| 53 | + super(TopoElementType.LINK, linkId); | ||
| 54 | + this.flavor = checkNotNull(flavor); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * Adds a highlighting modification to this link highlight. | ||
| 59 | + * | ||
| 60 | + * @param mod mod to be added | ||
| 61 | + * @return self, for chaining | ||
| 62 | + */ | ||
| 63 | + public LinkHighlight addMod(Mod mod) { | ||
| 64 | + mods.add(checkNotNull(mod)); | ||
| 65 | + return this; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * Adds a label to be displayed on the link. | ||
| 70 | + * | ||
| 71 | + * @param label the label text | ||
| 72 | + * @return self, for chaining | ||
| 73 | + */ | ||
| 74 | + public LinkHighlight setLabel(String label) { | ||
| 75 | + this.label = label == null ? EMPTY : label; | ||
| 76 | + return this; | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * Returns the highlight flavor. | ||
| 81 | + * | ||
| 82 | + * @return highlight flavor | ||
| 83 | + */ | ||
| 84 | + public Flavor flavor() { | ||
| 85 | + return flavor; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + /** | ||
| 89 | + * Returns the highlight modifications. | ||
| 90 | + * | ||
| 91 | + * @return highlight modifications | ||
| 92 | + */ | ||
| 93 | + public Set<Mod> mods() { | ||
| 94 | + return Collections.unmodifiableSet(mods); | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + /** | ||
| 98 | + * Generates the CSS classes string from the {@link #flavor} and | ||
| 99 | + * any optional {@link #mods}. | ||
| 100 | + * | ||
| 101 | + * @return CSS classes string | ||
| 102 | + */ | ||
| 103 | + public String cssClasses() { | ||
| 104 | + StringBuilder sb = new StringBuilder(flavor.toString()); | ||
| 105 | + mods.forEach(m -> sb.append(SPACE).append(m)); | ||
| 106 | + return sb.toString(); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + /** | ||
| 110 | + * Returns the label text. | ||
| 111 | + * | ||
| 112 | + * @return label text | ||
| 113 | + */ | ||
| 114 | + public String label() { | ||
| 115 | + return label; | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + /** | ||
| 119 | + * Link highlighting flavor. | ||
| 120 | + */ | ||
| 121 | + public enum Flavor { | ||
| 122 | + NO_HIGHLIGHT(PLAIN), | ||
| 123 | + PRIMARY_HIGHLIGHT(PRIMARY), | ||
| 124 | + SECONDARY_HIGHLIGHT(SECONDARY); | ||
| 125 | + | ||
| 126 | + private String cssName; | ||
| 127 | + | ||
| 128 | + Flavor(String s) { | ||
| 129 | + cssName = s; | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + @Override | ||
| 133 | + public String toString() { | ||
| 134 | + return cssName; | ||
| 135 | + } | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + /** | ||
| 139 | + * Link highlighting modification. | ||
| 140 | + * <p> | ||
| 141 | + * Note that this translates to a CSS class name that is applied to | ||
| 142 | + * the link in the Topology UI. | ||
| 143 | + */ | ||
| 144 | + public static final class Mod implements Comparable<Mod> { | ||
| 145 | + private final String modId; | ||
| 146 | + | ||
| 147 | + public Mod(String modId) { | ||
| 148 | + this.modId = checkNotNull(modId); | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + @Override | ||
| 152 | + public String toString() { | ||
| 153 | + return modId; | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + @Override | ||
| 157 | + public boolean equals(Object o) { | ||
| 158 | + if (this == o) { | ||
| 159 | + return true; | ||
| 160 | + } | ||
| 161 | + if (o == null || getClass() != o.getClass()) { | ||
| 162 | + return false; | ||
| 163 | + } | ||
| 164 | + Mod mod = (Mod) o; | ||
| 165 | + return modId.equals(mod.modId); | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + @Override | ||
| 169 | + public int hashCode() { | ||
| 170 | + return modId.hashCode(); | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + | ||
| 174 | + @Override | ||
| 175 | + public int compareTo(Mod o) { | ||
| 176 | + return this.modId.compareTo(o.modId); | ||
| 177 | + } | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + /** | ||
| 181 | + * Denotes a link to be tagged as an optical link. | ||
| 182 | + */ | ||
| 183 | + public static final Mod MOD_OPTICAL = new Mod("optical"); | ||
| 184 | + | ||
| 185 | + /** | ||
| 186 | + * Denotes a link to be tagged with animated traffic ("marching ants"). | ||
| 187 | + */ | ||
| 188 | + public static final Mod MOD_ANIMATED = new Mod("animated"); | ||
| 189 | +} |
| 1 | +/* | ||
| 2 | + * Copyright 2015 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + * | ||
| 16 | + */ | ||
| 17 | + | ||
| 18 | +package org.onosproject.ui.topo; | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * The topology element types to which a highlight can be applied. | ||
| 22 | + */ | ||
| 23 | +public enum TopoElementType { | ||
| 24 | + DEVICE, HOST, LINK | ||
| 25 | +} |
| ... | @@ -24,7 +24,8 @@ import org.onosproject.net.LinkKey; | ... | @@ -24,7 +24,8 @@ import org.onosproject.net.LinkKey; |
| 24 | import org.onosproject.net.link.LinkService; | 24 | import org.onosproject.net.link.LinkService; |
| 25 | import org.onosproject.ui.RequestHandler; | 25 | import org.onosproject.ui.RequestHandler; |
| 26 | import org.onosproject.ui.UiMessageHandler; | 26 | import org.onosproject.ui.UiMessageHandler; |
| 27 | -import org.onosproject.ui.impl.TopologyViewMessageHandlerBase.BiLink; | 27 | +import org.onosproject.ui.impl.topo.BiLink; |
| 28 | +import org.onosproject.ui.impl.topo.TopoUtils; | ||
| 28 | import org.onosproject.ui.table.TableModel; | 29 | import org.onosproject.ui.table.TableModel; |
| 29 | import org.onosproject.ui.table.TableRequestHandler; | 30 | import org.onosproject.ui.table.TableRequestHandler; |
| 30 | import org.onosproject.ui.table.cell.ConnectPointFormatter; | 31 | import org.onosproject.ui.table.cell.ConnectPointFormatter; |
| ... | @@ -33,13 +34,14 @@ import org.onosproject.ui.table.cell.EnumFormatter; | ... | @@ -33,13 +34,14 @@ import org.onosproject.ui.table.cell.EnumFormatter; |
| 33 | import java.util.Collection; | 34 | import java.util.Collection; |
| 34 | import java.util.Map; | 35 | import java.util.Map; |
| 35 | 36 | ||
| 36 | -import static org.onosproject.ui.impl.TopologyViewMessageHandlerBase.addLink; | ||
| 37 | - | ||
| 38 | /** | 37 | /** |
| 39 | * Message handler for link view related messages. | 38 | * Message handler for link view related messages. |
| 40 | */ | 39 | */ |
| 41 | public class LinkViewMessageHandler extends UiMessageHandler { | 40 | public class LinkViewMessageHandler extends UiMessageHandler { |
| 42 | 41 | ||
| 42 | + private static final String A_BOTH_B = "A ↔ 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 | } | ... | ... |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
| 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