Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 1 | /* |
Brian O'Connor | 5ab426f | 2016-04-09 01:19:45 -0700 | [diff] [blame] | 2 | * Copyright 2015-present Open Networking Laboratory |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 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. |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 15 | */ |
| 16 | |
Simon Hunt | 191c84a | 2015-08-21 08:24:48 -0700 | [diff] [blame] | 17 | package org.onosproject.ui.topo; |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 18 | |
| 19 | import com.fasterxml.jackson.databind.JsonNode; |
| 20 | import com.fasterxml.jackson.databind.node.ArrayNode; |
| 21 | import com.fasterxml.jackson.databind.node.ObjectNode; |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 22 | import org.onosproject.net.ConnectPoint; |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 23 | import org.onosproject.net.Device; |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 24 | import org.onosproject.net.Element; |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 25 | import org.onosproject.net.Host; |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 26 | import org.onosproject.net.Link; |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 27 | import org.onosproject.net.device.DeviceService; |
| 28 | import org.onosproject.net.host.HostService; |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 29 | import org.onosproject.net.link.LinkService; |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 30 | import org.onosproject.ui.JsonUtils; |
| 31 | import org.slf4j.Logger; |
| 32 | import org.slf4j.LoggerFactory; |
| 33 | |
| 34 | import java.util.Collections; |
| 35 | import java.util.HashSet; |
| 36 | import java.util.Set; |
| 37 | |
| 38 | import static com.google.common.base.Strings.isNullOrEmpty; |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 39 | import static org.onosproject.net.ConnectPoint.deviceConnectPoint; |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 40 | import static org.onosproject.net.DeviceId.deviceId; |
| 41 | import static org.onosproject.net.HostId.hostId; |
| 42 | |
| 43 | /** |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 44 | * Encapsulates a selection of devices, hosts and links from the topology view. |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 45 | */ |
| 46 | public class NodeSelection { |
| 47 | |
Simon Hunt | 4fc8685 | 2015-08-20 17:57:52 -0700 | [diff] [blame] | 48 | private static final Logger log = |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 49 | LoggerFactory.getLogger(NodeSelection.class); |
| 50 | |
| 51 | private static final String IDS = "ids"; |
| 52 | private static final String HOVER = "hover"; |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 53 | private static final String LINK_ID_DELIM = "-"; |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 54 | |
| 55 | private final DeviceService deviceService; |
| 56 | private final HostService hostService; |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 57 | private final LinkService linkService; |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 58 | |
| 59 | private final Set<String> ids; |
| 60 | private final String hover; |
| 61 | |
| 62 | private final Set<Device> devices = new HashSet<>(); |
| 63 | private final Set<Host> hosts = new HashSet<>(); |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 64 | private final Set<Link> links = new HashSet<>(); |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 65 | private Element hovered; |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 66 | |
| 67 | /** |
| 68 | * Creates a node selection entity, from the given payload, using the |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 69 | * supplied link, device and host services. Note that if a link, device |
| 70 | * or host was hovered over by the mouse, it is available |
| 71 | * via {@link #hovered()}. |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 72 | * |
| 73 | * @param payload message payload |
| 74 | * @param deviceService device service |
| 75 | * @param hostService host service |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 76 | * @param linkService link service |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 77 | */ |
| 78 | public NodeSelection(ObjectNode payload, |
| 79 | DeviceService deviceService, |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 80 | HostService hostService, |
| 81 | LinkService linkService) { |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 82 | this.deviceService = deviceService; |
| 83 | this.hostService = hostService; |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 84 | this.linkService = linkService; |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 85 | |
| 86 | ids = extractIds(payload); |
| 87 | hover = extractHover(payload); |
| 88 | |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 89 | // start by extracting the hovered element if any |
| 90 | if (isNullOrEmpty(hover)) { |
| 91 | hovered = null; |
| 92 | } else { |
| 93 | setHoveredElement(); |
| 94 | } |
| 95 | |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 96 | // now go find the links, devices and hosts that are in the selection list |
| 97 | Set<String> unmatched = findLinks(ids); |
| 98 | unmatched = findDevices(unmatched); |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 99 | unmatched = findHosts(unmatched); |
Jon Hall | cbd1b39 | 2017-01-18 20:15:44 -0800 | [diff] [blame] | 100 | if (!unmatched.isEmpty()) { |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 101 | log.debug("Skipping unmatched IDs {}", unmatched); |
| 102 | } |
| 103 | |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 104 | } |
| 105 | |
| 106 | /** |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 107 | * Returns a view of the selected devices (hover not included). |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 108 | * |
| 109 | * @return selected devices |
| 110 | */ |
| 111 | public Set<Device> devices() { |
| 112 | return Collections.unmodifiableSet(devices); |
| 113 | } |
| 114 | |
| 115 | /** |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 116 | * Returns a view of the selected links (hover not included). |
| 117 | * |
| 118 | * @return selected links |
| 119 | */ |
| 120 | public Set<Link> links() { |
| 121 | return Collections.unmodifiableSet(links); |
| 122 | } |
| 123 | |
| 124 | /** |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 125 | * Returns a view of the selected devices, including the hovered device |
| 126 | * if there was one. |
| 127 | * |
| 128 | * @return selected (plus hovered) devices |
| 129 | */ |
| 130 | public Set<Device> devicesWithHover() { |
| 131 | Set<Device> withHover; |
| 132 | if (hovered != null && hovered instanceof Device) { |
| 133 | withHover = new HashSet<>(devices); |
| 134 | withHover.add((Device) hovered); |
| 135 | } else { |
| 136 | withHover = devices; |
| 137 | } |
| 138 | return Collections.unmodifiableSet(withHover); |
| 139 | } |
| 140 | |
| 141 | /** |
| 142 | * Returns a view of the selected hosts (hover not included). |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 143 | * |
| 144 | * @return selected hosts |
| 145 | */ |
| 146 | public Set<Host> hosts() { |
| 147 | return Collections.unmodifiableSet(hosts); |
| 148 | } |
| 149 | |
| 150 | /** |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 151 | * Returns a view of the selected hosts, including the hovered host |
| 152 | * if thee was one. |
| 153 | * |
| 154 | * @return selected (plus hovered) hosts |
| 155 | */ |
| 156 | public Set<Host> hostsWithHover() { |
| 157 | Set<Host> withHover; |
| 158 | if (hovered != null && hovered instanceof Host) { |
| 159 | withHover = new HashSet<>(hosts); |
| 160 | withHover.add((Host) hovered); |
| 161 | } else { |
| 162 | withHover = hosts; |
| 163 | } |
| 164 | return Collections.unmodifiableSet(withHover); |
| 165 | } |
| 166 | |
| 167 | /** |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 168 | * Returns a view of the selected links, including the hovered link |
| 169 | * if thee was one. |
| 170 | * |
| 171 | * @return selected (plus hovered) links |
| 172 | */ |
| 173 | public Set<Link> linksWithHover() { |
| 174 | Set<Link> withHover; |
| 175 | if (hovered != null && hovered instanceof Link) { |
| 176 | withHover = new HashSet<>(links); |
| 177 | withHover.add((Link) hovered); |
| 178 | } else { |
| 179 | withHover = links; |
| 180 | } |
| 181 | return Collections.unmodifiableSet(withHover); |
| 182 | } |
| 183 | |
| 184 | /** |
| 185 | * Returns the element (link, host or device) over which the mouse was hovering, |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 186 | * or null. |
| 187 | * |
| 188 | * @return element hovered over |
| 189 | */ |
| 190 | public Element hovered() { |
| 191 | return hovered; |
| 192 | } |
| 193 | |
| 194 | /** |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 195 | * Returns true if nothing is selected. |
| 196 | * |
| 197 | * @return true if nothing selected |
| 198 | */ |
| 199 | public boolean none() { |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 200 | return devices().isEmpty() && hosts().isEmpty() && links().isEmpty(); |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 201 | } |
| 202 | |
| 203 | @Override |
| 204 | public String toString() { |
| 205 | return "NodeSelection{" + |
| 206 | "ids=" + ids + |
| 207 | ", hover='" + hover + '\'' + |
| 208 | ", #devices=" + devices.size() + |
| 209 | ", #hosts=" + hosts.size() + |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 210 | ", #links=" + links.size() + |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 211 | '}'; |
| 212 | } |
| 213 | |
| 214 | // == helper methods |
| 215 | |
| 216 | private Set<String> extractIds(ObjectNode payload) { |
| 217 | ArrayNode array = (ArrayNode) payload.path(IDS); |
| 218 | if (array == null || array.size() == 0) { |
| 219 | return Collections.emptySet(); |
| 220 | } |
| 221 | |
| 222 | Set<String> ids = new HashSet<>(); |
| 223 | for (JsonNode node : array) { |
| 224 | ids.add(node.asText()); |
| 225 | } |
| 226 | return ids; |
| 227 | } |
| 228 | |
| 229 | private String extractHover(ObjectNode payload) { |
| 230 | return JsonUtils.string(payload, HOVER); |
| 231 | } |
| 232 | |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 233 | private void setHoveredElement() { |
| 234 | Set<String> unmatched; |
| 235 | unmatched = new HashSet<>(); |
| 236 | unmatched.add(hover); |
| 237 | unmatched = findDevices(unmatched); |
| 238 | if (devices.size() == 1) { |
| 239 | hovered = devices.iterator().next(); |
| 240 | devices.clear(); |
| 241 | } else { |
| 242 | unmatched = findHosts(unmatched); |
| 243 | if (hosts.size() == 1) { |
| 244 | hovered = hosts.iterator().next(); |
| 245 | hosts.clear(); |
| 246 | } else { |
| 247 | hovered = null; |
| 248 | log.debug("Skipping unmatched HOVER {}", unmatched); |
| 249 | } |
| 250 | } |
| 251 | } |
| 252 | |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 253 | private Set<String> findDevices(Set<String> ids) { |
| 254 | Set<String> unmatched = new HashSet<>(); |
| 255 | Device device; |
| 256 | |
| 257 | for (String id : ids) { |
| 258 | try { |
| 259 | device = deviceService.getDevice(deviceId(id)); |
| 260 | if (device != null) { |
| 261 | devices.add(device); |
| 262 | } else { |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 263 | unmatched.add(id); |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 264 | } |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 265 | } catch (Exception e) { |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 266 | unmatched.add(id); |
| 267 | } |
| 268 | } |
| 269 | return unmatched; |
| 270 | } |
| 271 | |
| 272 | private Set<String> findHosts(Set<String> ids) { |
| 273 | Set<String> unmatched = new HashSet<>(); |
| 274 | Host host; |
| 275 | |
| 276 | for (String id : ids) { |
| 277 | try { |
| 278 | host = hostService.getHost(hostId(id)); |
| 279 | if (host != null) { |
| 280 | hosts.add(host); |
| 281 | } else { |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 282 | unmatched.add(id); |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 283 | } |
Simon Hunt | 7229721 | 2015-08-25 10:15:33 -0700 | [diff] [blame] | 284 | } catch (Exception e) { |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 285 | unmatched.add(id); |
| 286 | } |
| 287 | } |
| 288 | return unmatched; |
| 289 | } |
Prince Pereira | 46c82d4 | 2016-09-19 13:30:50 +0530 | [diff] [blame] | 290 | |
| 291 | private Set<String> findLinks(Set<String> ids) { |
| 292 | Set<String> unmatched = new HashSet<>(); |
| 293 | ConnectPoint cpSrc, cpDst; |
| 294 | Link link; |
| 295 | |
| 296 | for (String id : ids) { |
| 297 | try { |
| 298 | String[] connectPoints = id.split(LINK_ID_DELIM); |
| 299 | if (connectPoints.length != 2) { |
| 300 | unmatched.add(id); |
| 301 | continue; |
| 302 | } |
| 303 | |
| 304 | cpSrc = deviceConnectPoint(connectPoints[0]); |
| 305 | cpDst = deviceConnectPoint(connectPoints[1]); |
| 306 | link = linkService.getLink(cpSrc, cpDst); |
| 307 | |
| 308 | if (link != null) { |
| 309 | links.add(link); |
| 310 | } else { |
| 311 | unmatched.add(id); |
| 312 | } |
| 313 | |
| 314 | } catch (Exception e) { |
| 315 | unmatched.add(id); |
| 316 | } |
| 317 | } |
| 318 | return unmatched; |
| 319 | } |
Simon Hunt | a17fa67 | 2015-08-19 18:42:22 -0700 | [diff] [blame] | 320 | } |