blob: fa426e18806a15f64444ee6568307e9f0d9f38d6 [file] [log] [blame]
Simon Huntcda9c032016-04-11 10:32:54 -07001/*
Brian O'Connor0a4e6742016-09-15 23:03:10 -07002 * Copyright 2016-present Open Networking Laboratory
Simon Huntcda9c032016-04-11 10:32:54 -07003 *
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
17package org.onosproject.ui.impl.topo.model;
18
Simon Hunt8eac4ae2017-01-20 12:56:45 -080019import com.fasterxml.jackson.databind.node.ObjectNode;
Simon Hunt23fb1352016-04-11 12:15:19 -070020import org.onosproject.cluster.ControllerNode;
Simon Hunt642bc452016-05-04 19:34:45 -070021import org.onosproject.cluster.NodeId;
Simon Hunt23fb1352016-04-11 12:15:19 -070022import org.onosproject.cluster.RoleInfo;
Simon Huntcda9c032016-04-11 10:32:54 -070023import org.onosproject.event.EventDispatcher;
24import org.onosproject.net.Device;
Simon Hunt23fb1352016-04-11 12:15:19 -070025import org.onosproject.net.DeviceId;
Simon Huntc0f20c12016-05-09 09:30:20 -070026import org.onosproject.net.EdgeLink;
Simon Hunt23fb1352016-04-11 12:15:19 -070027import org.onosproject.net.Host;
Simon Huntc0f20c12016-05-09 09:30:20 -070028import org.onosproject.net.HostId;
29import org.onosproject.net.HostLocation;
Simon Hunt23fb1352016-04-11 12:15:19 -070030import org.onosproject.net.Link;
31import org.onosproject.net.region.Region;
Simon Huntc0f20c12016-05-09 09:30:20 -070032import org.onosproject.net.region.RegionId;
Simon Hunt4f4ffc32016-08-03 18:30:47 -070033import org.onosproject.ui.UiTopoLayoutService;
Simon Hunt8eac4ae2017-01-20 12:56:45 -080034import org.onosproject.ui.impl.topo.Topo2Jsonifier;
Simon Hunt642bc452016-05-04 19:34:45 -070035import org.onosproject.ui.model.ServiceBundle;
Simon Hunt338a3b42016-04-14 09:43:52 -070036import org.onosproject.ui.model.topo.UiClusterMember;
Simon Huntcda9c032016-04-11 10:32:54 -070037import org.onosproject.ui.model.topo.UiDevice;
Simon Huntc13082f2016-08-03 21:20:23 -070038import org.onosproject.ui.model.topo.UiDeviceLink;
39import org.onosproject.ui.model.topo.UiEdgeLink;
Simon Huntc0f20c12016-05-09 09:30:20 -070040import org.onosproject.ui.model.topo.UiElement;
41import org.onosproject.ui.model.topo.UiHost;
Simon Huntc0f20c12016-05-09 09:30:20 -070042import org.onosproject.ui.model.topo.UiLinkId;
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -070043import org.onosproject.ui.model.topo.UiModelEvent;
Simon Huntc0f20c12016-05-09 09:30:20 -070044import org.onosproject.ui.model.topo.UiRegion;
Simon Huntc13082f2016-08-03 21:20:23 -070045import org.onosproject.ui.model.topo.UiSynthLink;
Simon Hunt4f4ffc32016-08-03 18:30:47 -070046import org.onosproject.ui.model.topo.UiTopoLayout;
47import org.onosproject.ui.model.topo.UiTopoLayoutId;
Simon Hunt23fb1352016-04-11 12:15:19 -070048import org.onosproject.ui.model.topo.UiTopology;
Simon Hunt642bc452016-05-04 19:34:45 -070049import org.slf4j.Logger;
50import org.slf4j.LoggerFactory;
Simon Hunt23fb1352016-04-11 12:15:19 -070051
Simon Hunt0e161092017-05-08 17:41:38 -070052import java.util.HashMap;
Simon Huntb1ce2602016-07-23 14:04:31 -070053import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070054import java.util.List;
Simon Hunt0e161092017-05-08 17:41:38 -070055import java.util.Map;
Simon Huntc0f20c12016-05-09 09:30:20 -070056import java.util.Set;
57
58import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -070059import static org.onosproject.ui.model.topo.UiModelEvent.Type.CLUSTER_MEMBER_ADDED_OR_UPDATED;
60import static org.onosproject.ui.model.topo.UiModelEvent.Type.CLUSTER_MEMBER_REMOVED;
61import static org.onosproject.ui.model.topo.UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED;
62import static org.onosproject.ui.model.topo.UiModelEvent.Type.DEVICE_REMOVED;
63import static org.onosproject.ui.model.topo.UiModelEvent.Type.HOST_ADDED_OR_UPDATED;
64import static org.onosproject.ui.model.topo.UiModelEvent.Type.HOST_MOVED;
65import static org.onosproject.ui.model.topo.UiModelEvent.Type.HOST_REMOVED;
66import static org.onosproject.ui.model.topo.UiModelEvent.Type.LINK_ADDED_OR_UPDATED;
67import static org.onosproject.ui.model.topo.UiModelEvent.Type.LINK_REMOVED;
68import static org.onosproject.ui.model.topo.UiModelEvent.Type.REGION_ADDED_OR_UPDATED;
69import static org.onosproject.ui.model.topo.UiModelEvent.Type.REGION_REMOVED;
Simon Huntc0f20c12016-05-09 09:30:20 -070070import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId;
Simon Huntcda9c032016-04-11 10:32:54 -070071
72/**
73 * UI Topology Model cache.
74 */
75class ModelCache {
76
Simon Huntc0f20c12016-05-09 09:30:20 -070077 private static final String E_NO_ELEMENT = "Tried to remove non-member {}: {}";
Simon Hunt8eac4ae2017-01-20 12:56:45 -080078 private static final String MEMO_ADDED = "added";
79 private static final String MEMO_UPDATED = "updated";
80 private static final String MEMO_REMOVED = "removed";
81 private static final String MEMO_MOVED = "moved";
Simon Huntc0f20c12016-05-09 09:30:20 -070082
Simon Hunt642bc452016-05-04 19:34:45 -070083 private static final Logger log = LoggerFactory.getLogger(ModelCache.class);
84
Simon Huntc4ca7102017-04-08 22:28:04 -070085 // TODO: add NetworkConfigService to service bundle
86// private final NetworkConfigService cfgService =
87// DefaultServiceDirectory.getService(NetworkConfigService.class);
88
Simon Hunt642bc452016-05-04 19:34:45 -070089 private final ServiceBundle services;
Simon Huntcda9c032016-04-11 10:32:54 -070090 private final EventDispatcher dispatcher;
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070091 private final UiTopology uiTopology;
Simon Huntcda9c032016-04-11 10:32:54 -070092
Simon Hunt8eac4ae2017-01-20 12:56:45 -080093 private Topo2Jsonifier t2json;
94
Simon Hunt642bc452016-05-04 19:34:45 -070095 ModelCache(ServiceBundle services, EventDispatcher eventDispatcher) {
96 this.services = services;
Simon Huntcda9c032016-04-11 10:32:54 -070097 this.dispatcher = eventDispatcher;
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070098 uiTopology = new UiTopology(services);
Simon Huntcda9c032016-04-11 10:32:54 -070099 }
100
Simon Hunt338a3b42016-04-14 09:43:52 -0700101 @Override
102 public String toString() {
103 return "ModelCache{" + uiTopology + "}";
104 }
105
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800106 private void postEvent(UiModelEvent.Type type, UiElement subject, String memo) {
107 ObjectNode data = t2json != null ? t2json.jsonUiElement(subject) : null;
108 dispatcher.post(new UiModelEvent(type, subject, data, memo));
109 }
110
111 void injectJsonifier(Topo2Jsonifier t2json) {
112 this.t2json = t2json;
Simon Huntc0f20c12016-05-09 09:30:20 -0700113 }
114
Simon Huntcda9c032016-04-11 10:32:54 -0700115 void clear() {
Simon Hunt23fb1352016-04-11 12:15:19 -0700116 uiTopology.clear();
Simon Huntcda9c032016-04-11 10:32:54 -0700117 }
118
119 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700120 * Create our internal model of the global topology. An assumption we are
121 * making is that the topology is empty to start.
Simon Huntcda9c032016-04-11 10:32:54 -0700122 */
123 void load() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700124 loadClusterMembers();
125 loadRegions();
126 loadDevices();
Simon Huntc13082f2016-08-03 21:20:23 -0700127 loadDeviceLinks();
Simon Huntc0f20c12016-05-09 09:30:20 -0700128 loadHosts();
Simon Huntcda9c032016-04-11 10:32:54 -0700129 }
130
131
Simon Huntc0f20c12016-05-09 09:30:20 -0700132 // === CLUSTER MEMBERS
133
134 private UiClusterMember addNewClusterMember(ControllerNode n) {
135 UiClusterMember member = new UiClusterMember(uiTopology, n);
136 uiTopology.add(member);
137 return member;
138 }
139
140 private void updateClusterMember(UiClusterMember member) {
141 ControllerNode.State state = services.cluster().getState(member.id());
142 member.setState(state);
Simon Huntc0f20c12016-05-09 09:30:20 -0700143 }
144
145 private void loadClusterMembers() {
146 for (ControllerNode n : services.cluster().getNodes()) {
147 UiClusterMember member = addNewClusterMember(n);
148 updateClusterMember(member);
149 }
150 }
151
152 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700153 void addOrUpdateClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700154 NodeId id = cnode.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800155 String memo = MEMO_UPDATED;
Simon Hunt642bc452016-05-04 19:34:45 -0700156 UiClusterMember member = uiTopology.findClusterMember(id);
157 if (member == null) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700158 member = addNewClusterMember(cnode);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800159 memo = MEMO_ADDED;
Simon Hunt338a3b42016-04-14 09:43:52 -0700160 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700161 updateClusterMember(member);
Simon Hunt338a3b42016-04-14 09:43:52 -0700162
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800163 postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700164 }
165
Simon Huntc0f20c12016-05-09 09:30:20 -0700166 // package private for unit test access
167 UiClusterMember accessClusterMember(NodeId id) {
168 return uiTopology.findClusterMember(id);
169 }
170
171 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700172 void removeClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700173 NodeId id = cnode.id();
174 UiClusterMember member = uiTopology.findClusterMember(id);
175 if (member != null) {
176 uiTopology.remove(member);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800177 postEvent(CLUSTER_MEMBER_REMOVED, member, MEMO_REMOVED);
Simon Hunt642bc452016-05-04 19:34:45 -0700178 } else {
Simon Huntc0f20c12016-05-09 09:30:20 -0700179 log.warn(E_NO_ELEMENT, "cluster node", id);
Simon Hunt642bc452016-05-04 19:34:45 -0700180 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700181 }
182
Simon Huntd5b96732016-07-08 13:22:27 -0700183 List<UiClusterMember> getAllClusterMembers() {
184 return uiTopology.allClusterMembers();
185 }
186
Simon Huntc0f20c12016-05-09 09:30:20 -0700187
188 // === MASTERSHIP CHANGES
189
190 // invoked from UiSharedTopologyModel mastership listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700191 void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700192 // To think about:: do we need to store mastership info?
193 // or can we rely on looking it up live?
Simon Hunt338a3b42016-04-14 09:43:52 -0700194 // TODO: store the updated mastership information
195 // TODO: post event
196 }
197
Simon Huntb1ce2602016-07-23 14:04:31 -0700198 // === THE NULL REGION
199
200 UiRegion nullRegion() {
201 return uiTopology.nullRegion();
202 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700203
204 // === REGIONS
205
206 private UiRegion addNewRegion(Region r) {
207 UiRegion region = new UiRegion(uiTopology, r);
208 uiTopology.add(region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700209 log.debug("Region {} added to topology", region);
Simon Huntc0f20c12016-05-09 09:30:20 -0700210 return region;
211 }
212
213 private void updateRegion(UiRegion region) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700214 RegionId rid = region.id();
215 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500216 Set<HostId> hostIds = services.region().getRegionHosts(rid);
Simon Huntb1ce2602016-07-23 14:04:31 -0700217
218 // Make sure device objects refer to their region
219 deviceIds.forEach(d -> {
220 UiDevice dev = uiTopology.findDevice(d);
221 if (dev != null) {
222 dev.setRegionId(rid);
223 } else {
224 // if we don't have the UiDevice in the topology, what can we do?
225 log.warn("Region device {}, but we don't have UiDevice in topology", d);
226 }
227 });
228
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500229 hostIds.forEach(d -> {
230 UiHost host = uiTopology.findHost(d);
231 if (host != null) {
232 host.setRegionId(rid);
233 } else {
234 // if we don't have the UiDevice in the topology, what can we do?
235 log.warn("Region host {}, but we don't have UiHost in topology", d);
236 }
237 });
238
Simon Huntb1ce2602016-07-23 14:04:31 -0700239 // Make sure the region object refers to the devices
240 region.reconcileDevices(deviceIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500241 region.reconcileHosts(hostIds);
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700242
243 fixupContainmentHierarchy(region);
244 }
245
246 private void fixupContainmentHierarchy(UiRegion region) {
247 UiTopoLayoutService ls = services.layout();
248 RegionId regionId = region.id();
249
250 UiTopoLayout layout = ls.getLayout(regionId);
251 if (layout == null) {
252 // no layout backed by this region
253 log.warn("No layout backed by region {}", regionId);
254 return;
255 }
256
257 UiTopoLayoutId layoutId = layout.id();
258
259 if (!layout.isRoot()) {
260 UiTopoLayoutId parentId = layout.parent();
261 UiTopoLayout parentLayout = ls.getLayout(parentId);
262 RegionId parentRegionId = parentLayout.regionId();
263 region.setParent(parentRegionId);
264 }
265
266 Set<UiTopoLayout> kids = ls.getChildren(layoutId);
267 Set<RegionId> kidRegionIds = new HashSet<>(kids.size());
268 kids.forEach(k -> kidRegionIds.add(k.regionId()));
269 region.setChildren(kidRegionIds);
Simon Huntc0f20c12016-05-09 09:30:20 -0700270 }
271
272 private void loadRegions() {
273 for (Region r : services.region().getRegions()) {
274 UiRegion region = addNewRegion(r);
275 updateRegion(region);
276 }
277 }
278
279 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700280 void addOrUpdateRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700281 RegionId id = region.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800282 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700283 UiRegion uiRegion = uiTopology.findRegion(id);
284 if (uiRegion == null) {
285 uiRegion = addNewRegion(region);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800286 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700287 }
288 updateRegion(uiRegion);
289
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800290 postEvent(REGION_ADDED_OR_UPDATED, uiRegion, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700291 }
292
Simon Hunt58a0dd02016-05-17 11:54:23 -0700293 // package private for unit test access
294 UiRegion accessRegion(RegionId id) {
Simon Huntd5b96732016-07-08 13:22:27 -0700295 return id == null ? null : uiTopology.findRegion(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700296 }
297
Simon Huntc0f20c12016-05-09 09:30:20 -0700298 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700299 void removeRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700300 RegionId id = region.id();
301 UiRegion uiRegion = uiTopology.findRegion(id);
302 if (uiRegion != null) {
303 uiTopology.remove(uiRegion);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800304 postEvent(REGION_REMOVED, uiRegion, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700305 } else {
306 log.warn(E_NO_ELEMENT, "region", id);
307 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700308 }
309
Simon Hunt10973dd2016-08-01 15:50:35 -0700310 Set<UiRegion> getAllRegions() {
311 return uiTopology.allRegions();
312 }
313
Simon Huntc0f20c12016-05-09 09:30:20 -0700314
315 // === DEVICES
316
317 private UiDevice addNewDevice(Device d) {
318 UiDevice device = new UiDevice(uiTopology, d);
Simon Huntb1ce2602016-07-23 14:04:31 -0700319 updateDevice(device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700320 uiTopology.add(device);
Simon Huntb1ce2602016-07-23 14:04:31 -0700321 log.debug("Device {} added to topology", device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700322 return device;
323 }
324
Simon Huntb1ce2602016-07-23 14:04:31 -0700325 // make sure the UiDevice is tagged with the region it belongs to
Simon Huntc0f20c12016-05-09 09:30:20 -0700326 private void updateDevice(UiDevice device) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700327 Region r = services.region().getRegionForDevice(device.id());
328 RegionId rid = r == null ? UiRegion.NULL_ID : r.id();
329 device.setRegionId(rid);
Simon Huntc0f20c12016-05-09 09:30:20 -0700330 }
331
332 private void loadDevices() {
333 for (Device d : services.device().getDevices()) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700334 addNewDevice(d);
Simon Huntc0f20c12016-05-09 09:30:20 -0700335 }
336 }
337
338 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700339 void addOrUpdateDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700340 DeviceId id = device.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800341 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700342 UiDevice uiDevice = uiTopology.findDevice(id);
343 if (uiDevice == null) {
344 uiDevice = addNewDevice(device);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800345 memo = MEMO_ADDED;
Simon Huntb1ce2602016-07-23 14:04:31 -0700346 } else {
347 updateDevice(uiDevice);
Simon Huntc0f20c12016-05-09 09:30:20 -0700348 }
Simon Huntcda9c032016-04-11 10:32:54 -0700349
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800350 postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice, memo);
Simon Huntcda9c032016-04-11 10:32:54 -0700351 }
352
Simon Hunt58a0dd02016-05-17 11:54:23 -0700353 // package private for unit test access
354 UiDevice accessDevice(DeviceId id) {
355 return uiTopology.findDevice(id);
356 }
357
Simon Huntc0f20c12016-05-09 09:30:20 -0700358 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700359 void removeDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700360 DeviceId id = device.id();
361 UiDevice uiDevice = uiTopology.findDevice(id);
362 if (uiDevice != null) {
363 uiTopology.remove(uiDevice);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800364 postEvent(DEVICE_REMOVED, uiDevice, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700365 } else {
366 log.warn(E_NO_ELEMENT, "device", id);
367 }
Simon Huntcda9c032016-04-11 10:32:54 -0700368 }
369
Simon Hunt4854f3d2016-08-02 18:13:15 -0700370 Set<UiDevice> getAllDevices() {
371 return uiTopology.allDevices();
372 }
373
Simon Huntc0f20c12016-05-09 09:30:20 -0700374
Simon Huntc13082f2016-08-03 21:20:23 -0700375 // === LINKS ===
Simon Huntc0f20c12016-05-09 09:30:20 -0700376
Simon Huntc13082f2016-08-03 21:20:23 -0700377 private UiDeviceLink addNewDeviceLink(UiLinkId id) {
378 UiDeviceLink uiDeviceLink = new UiDeviceLink(uiTopology, id);
379 uiTopology.add(uiDeviceLink);
380 return uiDeviceLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700381 }
382
Simon Huntc13082f2016-08-03 21:20:23 -0700383 private UiEdgeLink addNewEdgeLink(UiLinkId id) {
384 UiEdgeLink uiEdgeLink = new UiEdgeLink(uiTopology, id);
385 uiTopology.add(uiEdgeLink);
386 return uiEdgeLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700387 }
388
Simon Huntc13082f2016-08-03 21:20:23 -0700389 private void updateDeviceLink(UiDeviceLink uiDeviceLink, Link link) {
390 uiDeviceLink.attachBackingLink(link);
391 }
392
393 private void loadDeviceLinks() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700394 for (Link link : services.link().getLinks()) {
395 UiLinkId id = uiLinkId(link);
396
Simon Huntc13082f2016-08-03 21:20:23 -0700397 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
398 if (uiDeviceLink == null) {
399 uiDeviceLink = addNewDeviceLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700400 }
Simon Huntc13082f2016-08-03 21:20:23 -0700401 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700402 }
403 }
404
405 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700406 void addOrUpdateDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700407 UiLinkId id = uiLinkId(link);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800408 String memo = MEMO_UPDATED;
Simon Huntc13082f2016-08-03 21:20:23 -0700409 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
410 if (uiDeviceLink == null) {
411 uiDeviceLink = addNewDeviceLink(id);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800412 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700413 }
Simon Huntc13082f2016-08-03 21:20:23 -0700414 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700415
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800416 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700417 }
418
Simon Hunt58a0dd02016-05-17 11:54:23 -0700419 // package private for unit test access
Simon Huntc13082f2016-08-03 21:20:23 -0700420 UiDeviceLink accessDeviceLink(UiLinkId id) {
421 return uiTopology.findDeviceLink(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700422 }
423
Simon Huntc0f20c12016-05-09 09:30:20 -0700424 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700425 void removeDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700426 UiLinkId id = uiLinkId(link);
Simon Huntc13082f2016-08-03 21:20:23 -0700427 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
428 if (uiDeviceLink != null) {
429 boolean remaining = uiDeviceLink.detachBackingLink(link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700430 if (remaining) {
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800431 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, MEMO_UPDATED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700432 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700433 uiTopology.remove(uiDeviceLink);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800434 postEvent(LINK_REMOVED, uiDeviceLink, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700435 }
436 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700437 log.warn(E_NO_ELEMENT, "Device link", id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700438 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700439 }
440
Simon Huntc13082f2016-08-03 21:20:23 -0700441 Set<UiDeviceLink> getAllDeviceLinks() {
442 return uiTopology.allDeviceLinks();
Simon Hunt4854f3d2016-08-02 18:13:15 -0700443 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700444
445 // === HOSTS
446
Simon Hunt58a0dd02016-05-17 11:54:23 -0700447 private EdgeLink synthesizeLink(Host h) {
448 return createEdgeLink(h, true);
449 }
450
Simon Huntc0f20c12016-05-09 09:30:20 -0700451 private UiHost addNewHost(Host h) {
452 UiHost host = new UiHost(uiTopology, h);
453 uiTopology.add(host);
454
Simon Hunt58a0dd02016-05-17 11:54:23 -0700455 EdgeLink elink = synthesizeLink(h);
456 UiLinkId elinkId = uiLinkId(elink);
457 host.setEdgeLinkId(elinkId);
458
459 // add synthesized edge link to the topology
Simon Huntb7fd0802016-10-27 12:21:40 -0700460 addNewEdgeLink(elinkId);
Simon Huntc0f20c12016-05-09 09:30:20 -0700461
462 return host;
463 }
464
Simon Huntb7fd0802016-10-27 12:21:40 -0700465 private void insertNewUiEdgeLink(UiLinkId id) {
466 addNewEdgeLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700467 }
468
469 private void updateHost(UiHost uiHost, Host h) {
Simon Huntc13082f2016-08-03 21:20:23 -0700470 UiEdgeLink existing = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700471
Simon Huntb7fd0802016-10-27 12:21:40 -0700472 // TODO: review - do we need EdgeLink now that we are creating from id only?
Simon Hunt58a0dd02016-05-17 11:54:23 -0700473 EdgeLink currentElink = synthesizeLink(h);
474 UiLinkId currentElinkId = uiLinkId(currentElink);
475
476 if (existing != null) {
477 if (!currentElinkId.equals(existing.id())) {
478 // edge link has changed
Simon Huntb7fd0802016-10-27 12:21:40 -0700479 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700480 uiHost.setEdgeLinkId(currentElinkId);
481
482 uiTopology.remove(existing);
483 }
484
485 } else {
486 // no previously existing edge link
Simon Huntb7fd0802016-10-27 12:21:40 -0700487 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700488 uiHost.setEdgeLinkId(currentElinkId);
489
490 }
491
Simon Huntc0f20c12016-05-09 09:30:20 -0700492 HostLocation hloc = h.location();
493 uiHost.setLocation(hloc.deviceId(), hloc.port());
Simon Huntc0f20c12016-05-09 09:30:20 -0700494 }
495
496 private void loadHosts() {
497 for (Host h : services.host().getHosts()) {
498 UiHost host = addNewHost(h);
499 updateHost(host, h);
500 }
501 }
502
503 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700504 void addOrUpdateHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700505 HostId id = host.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800506 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700507 UiHost uiHost = uiTopology.findHost(id);
508 if (uiHost == null) {
509 uiHost = addNewHost(host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800510 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700511 }
512 updateHost(uiHost, host);
513
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800514 postEvent(HOST_ADDED_OR_UPDATED, uiHost, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700515 }
516
Simon Huntc0f20c12016-05-09 09:30:20 -0700517 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700518 void moveHost(Host host, Host prevHost) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700519 UiHost uiHost = uiTopology.findHost(prevHost.id());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700520 if (uiHost != null) {
521 updateHost(uiHost, host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800522 postEvent(HOST_MOVED, uiHost, MEMO_MOVED);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700523 } else {
524 log.warn(E_NO_ELEMENT, "host", prevHost.id());
525 }
526 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700527
Simon Hunt58a0dd02016-05-17 11:54:23 -0700528 // package private for unit test access
529 UiHost accessHost(HostId id) {
530 return uiTopology.findHost(id);
Simon Hunt23fb1352016-04-11 12:15:19 -0700531 }
532
Simon Huntc0f20c12016-05-09 09:30:20 -0700533 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700534 void removeHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700535 HostId id = host.id();
536 UiHost uiHost = uiTopology.findHost(id);
537 if (uiHost != null) {
Simon Huntc13082f2016-08-03 21:20:23 -0700538 UiEdgeLink edgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700539 uiTopology.remove(edgeLink);
Simon Huntc0f20c12016-05-09 09:30:20 -0700540 uiTopology.remove(uiHost);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800541 postEvent(HOST_REMOVED, uiHost, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700542 } else {
543 log.warn(E_NO_ELEMENT, "host", id);
544 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700545 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700546
Simon Hunt4854f3d2016-08-02 18:13:15 -0700547 Set<UiHost> getAllHosts() {
548 return uiTopology.allHosts();
549 }
550
Simon Huntc0f20c12016-05-09 09:30:20 -0700551
Simon Huntc13082f2016-08-03 21:20:23 -0700552 // === SYNTHETIC LINKS
553
554 List<UiSynthLink> getSynthLinks(RegionId regionId) {
555 return uiTopology.findSynthLinks(regionId);
556 }
557
Simon Hunt0e161092017-05-08 17:41:38 -0700558 Map<UiLinkId, UiSynthLink> relevantSynthLinks(RegionId regionId) {
559 Map<UiLinkId, UiSynthLink> result = new HashMap<>();
560 for (UiSynthLink sl : getSynthLinks(regionId)) {
561 result.put(sl.original().id(), sl);
562 }
563 return result;
564 }
565
Simon Huntb1ce2602016-07-23 14:04:31 -0700566 /**
567 * Refreshes the internal state.
568 */
569 public void refresh() {
Simon Huntc13082f2016-08-03 21:20:23 -0700570 // fix up internal linkages to ensure they are correct
Simon Huntb1ce2602016-07-23 14:04:31 -0700571
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700572 // make sure regions reflect layout containment hierarchy
573 fixupContainmentHierarchy(uiTopology.nullRegion());
574 uiTopology.allRegions().forEach(this::fixupContainmentHierarchy);
575
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500576 // make sure devices and hosts are in the correct region
Simon Huntb1ce2602016-07-23 14:04:31 -0700577 Set<UiDevice> allDevices = uiTopology.allDevices();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500578 Set<UiHost> allHosts = uiTopology.allHosts();
Simon Huntb1ce2602016-07-23 14:04:31 -0700579
580 services.region().getRegions().forEach(r -> {
581 RegionId rid = r.id();
582 UiRegion region = uiTopology.findRegion(rid);
583 if (region != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500584 reconcileDevicesAndHostsWithRegion(allDevices, allHosts, rid, region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700585 } else {
586 log.warn("No UiRegion in topology for ID {}", rid);
587 }
588 });
589
590 // what is left over, must belong to the null-region
591 Set<DeviceId> leftOver = new HashSet<>(allDevices.size());
592 allDevices.forEach(d -> leftOver.add(d.id()));
593 uiTopology.nullRegion().reconcileDevices(leftOver);
Simon Huntc13082f2016-08-03 21:20:23 -0700594
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500595 Set<HostId> leftOverHosts = new HashSet<>(allHosts.size());
596 allHosts.forEach(h -> leftOverHosts.add(h.id()));
597 uiTopology.nullRegion().reconcileHosts(leftOverHosts);
598
Simon Huntc13082f2016-08-03 21:20:23 -0700599 // now that we have correct region hierarchy, and devices are in their
600 // respective regions, we can compute synthetic links for each region.
601 uiTopology.computeSynthLinks();
Simon Huntb1ce2602016-07-23 14:04:31 -0700602 }
603
Steven Burrows8f45ce22016-10-27 20:04:14 -0500604 private void reconcileDevicesAndHostsWithRegion(Set<UiDevice> allDevices,
605 Set<UiHost> allHosts,
606 RegionId rid,
607 UiRegion region) {
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500608 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows8f45ce22016-10-27 20:04:14 -0500609 Set<HostId> hostIds = new HashSet<>();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500610 region.reconcileDevices(deviceIds);
611
612 deviceIds.forEach(devId -> {
613 UiDevice dev = uiTopology.findDevice(devId);
614 if (dev != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500615 dev.setRegionId(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500616 allDevices.remove(dev);
617 } else {
618 log.warn("Region device ID {} but no UiDevice in topology",
Simon Huntc4ca7102017-04-08 22:28:04 -0700619 devId);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500620 }
Steven Burrows8f45ce22016-10-27 20:04:14 -0500621
622 Set<Host> hosts = services.host().getConnectedHosts(devId);
623 for (Host h : hosts) {
624 HostId hid = h.id();
625 hostIds.add(hid);
626 UiHost host = uiTopology.findHost(hid);
627
628 if (host != null) {
629 host.setRegionId(rid);
630 allHosts.remove(host);
631 } else {
632 log.warn("Region host ID {} but no UiHost in topology",
Simon Huntc4ca7102017-04-08 22:28:04 -0700633 hid);
Steven Burrows8f45ce22016-10-27 20:04:14 -0500634 }
635 }
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500636 });
Steven Burrows8f45ce22016-10-27 20:04:14 -0500637
638 region.reconcileHosts(hostIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500639 }
640
Simon Huntc13082f2016-08-03 21:20:23 -0700641
Simon Huntc0f20c12016-05-09 09:30:20 -0700642 // === CACHE STATISTICS
643
Simon Hunt338a3b42016-04-14 09:43:52 -0700644 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700645 * Returns a detailed (multi-line) string showing the contents of the cache.
646 *
647 * @return detailed string
648 */
649 public String dumpString() {
650 return uiTopology.dumpString();
651 }
652
653 /**
Simon Hunt338a3b42016-04-14 09:43:52 -0700654 * Returns the number of members in the cluster.
655 *
656 * @return number of cluster members
657 */
658 public int clusterMemberCount() {
659 return uiTopology.clusterMemberCount();
660 }
661
662 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700663 * Returns the number of regions in the topology.
Simon Hunt338a3b42016-04-14 09:43:52 -0700664 *
665 * @return number of regions
666 */
667 public int regionCount() {
668 return uiTopology.regionCount();
669 }
Simon Hunt58a0dd02016-05-17 11:54:23 -0700670
671 /**
672 * Returns the number of devices in the topology.
673 *
674 * @return number of devices
675 */
676 public int deviceCount() {
677 return uiTopology.deviceCount();
678 }
679
680 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700681 * Returns the number of device links in the topology.
Simon Hunt58a0dd02016-05-17 11:54:23 -0700682 *
Simon Huntc13082f2016-08-03 21:20:23 -0700683 * @return number of device links
Simon Hunt58a0dd02016-05-17 11:54:23 -0700684 */
Simon Huntc13082f2016-08-03 21:20:23 -0700685 public int deviceLinkCount() {
686 return uiTopology.deviceLinkCount();
687 }
688
689 /**
690 * Returns the number of edge links in the topology.
691 *
692 * @return number of edge links
693 */
694 public int edgeLinkCount() {
695 return uiTopology.edgeLinkCount();
Simon Hunt58a0dd02016-05-17 11:54:23 -0700696 }
697
698 /**
699 * Returns the number of hosts in the topology.
700 *
701 * @return number of hosts
702 */
703 public int hostCount() {
704 return uiTopology.hostCount();
705 }
Simon Huntd5b96732016-07-08 13:22:27 -0700706
Simon Huntc13082f2016-08-03 21:20:23 -0700707 /**
708 * Returns the number of synthetic links in the topology.
709 *
710 * @return the number of synthetic links
711 */
712 public int synthLinkCount() {
713 return uiTopology.synthLinkCount();
714 }
Simon Huntcda9c032016-04-11 10:32:54 -0700715}