blob: 1efa2e4b8f6f7837ee1f1d01e36da06901864971 [file] [log] [blame]
Simon Huntcda9c032016-04-11 10:32:54 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
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) {
Simon Hunt708a5b32017-08-01 15:06:04 -0700141 // maybe something to update in the future?
Simon Huntc0f20c12016-05-09 09:30:20 -0700142 }
143
144 private void loadClusterMembers() {
145 for (ControllerNode n : services.cluster().getNodes()) {
146 UiClusterMember member = addNewClusterMember(n);
147 updateClusterMember(member);
148 }
149 }
150
151 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700152 void addOrUpdateClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700153 NodeId id = cnode.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800154 String memo = MEMO_UPDATED;
Simon Hunt642bc452016-05-04 19:34:45 -0700155 UiClusterMember member = uiTopology.findClusterMember(id);
156 if (member == null) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700157 member = addNewClusterMember(cnode);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800158 memo = MEMO_ADDED;
Simon Hunt338a3b42016-04-14 09:43:52 -0700159 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700160 updateClusterMember(member);
Simon Hunt338a3b42016-04-14 09:43:52 -0700161
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800162 postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700163 }
164
Simon Huntc0f20c12016-05-09 09:30:20 -0700165 // package private for unit test access
166 UiClusterMember accessClusterMember(NodeId id) {
167 return uiTopology.findClusterMember(id);
168 }
169
170 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700171 void removeClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700172 NodeId id = cnode.id();
173 UiClusterMember member = uiTopology.findClusterMember(id);
174 if (member != null) {
175 uiTopology.remove(member);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800176 postEvent(CLUSTER_MEMBER_REMOVED, member, MEMO_REMOVED);
Simon Hunt642bc452016-05-04 19:34:45 -0700177 } else {
Simon Huntc0f20c12016-05-09 09:30:20 -0700178 log.warn(E_NO_ELEMENT, "cluster node", id);
Simon Hunt642bc452016-05-04 19:34:45 -0700179 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700180 }
181
Simon Huntd5b96732016-07-08 13:22:27 -0700182 List<UiClusterMember> getAllClusterMembers() {
183 return uiTopology.allClusterMembers();
184 }
185
Simon Huntc0f20c12016-05-09 09:30:20 -0700186
187 // === MASTERSHIP CHANGES
188
189 // invoked from UiSharedTopologyModel mastership listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700190 void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700191 // To think about:: do we need to store mastership info?
192 // or can we rely on looking it up live?
Simon Hunt338a3b42016-04-14 09:43:52 -0700193 // TODO: store the updated mastership information
Sean Condon058804c2019-04-16 09:41:52 +0100194 UiDevice uiDevice = uiTopology.findDevice(deviceId);
195 ObjectNode data = t2json != null ? t2json.jsonUiElement(uiDevice) : null;
196 String oldMaster = data.get("master").asText();
197 NodeId newMaster = roleInfo.master();
198 data.put("master", newMaster.toString());
199 log.debug("Changed master of {} to {} (was {})", deviceId, newMaster.toString(), oldMaster);
200 dispatcher.post(new UiModelEvent(DEVICE_ADDED_OR_UPDATED, uiDevice, data, MEMO_UPDATED));
Simon Hunt338a3b42016-04-14 09:43:52 -0700201 }
202
Simon Huntb1ce2602016-07-23 14:04:31 -0700203 // === THE NULL REGION
204
205 UiRegion nullRegion() {
206 return uiTopology.nullRegion();
207 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700208
209 // === REGIONS
210
211 private UiRegion addNewRegion(Region r) {
212 UiRegion region = new UiRegion(uiTopology, r);
213 uiTopology.add(region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700214 log.debug("Region {} added to topology", region);
Simon Huntc0f20c12016-05-09 09:30:20 -0700215 return region;
216 }
217
218 private void updateRegion(UiRegion region) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700219 RegionId rid = region.id();
220 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500221 Set<HostId> hostIds = services.region().getRegionHosts(rid);
Simon Huntb1ce2602016-07-23 14:04:31 -0700222
223 // Make sure device objects refer to their region
224 deviceIds.forEach(d -> {
225 UiDevice dev = uiTopology.findDevice(d);
226 if (dev != null) {
227 dev.setRegionId(rid);
228 } else {
229 // if we don't have the UiDevice in the topology, what can we do?
230 log.warn("Region device {}, but we don't have UiDevice in topology", d);
231 }
232 });
233
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500234 hostIds.forEach(d -> {
235 UiHost host = uiTopology.findHost(d);
236 if (host != null) {
237 host.setRegionId(rid);
238 } else {
239 // if we don't have the UiDevice in the topology, what can we do?
240 log.warn("Region host {}, but we don't have UiHost in topology", d);
241 }
242 });
243
Simon Huntb1ce2602016-07-23 14:04:31 -0700244 // Make sure the region object refers to the devices
245 region.reconcileDevices(deviceIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500246 region.reconcileHosts(hostIds);
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700247
248 fixupContainmentHierarchy(region);
249 }
250
251 private void fixupContainmentHierarchy(UiRegion region) {
252 UiTopoLayoutService ls = services.layout();
253 RegionId regionId = region.id();
254
255 UiTopoLayout layout = ls.getLayout(regionId);
256 if (layout == null) {
257 // no layout backed by this region
258 log.warn("No layout backed by region {}", regionId);
259 return;
260 }
261
262 UiTopoLayoutId layoutId = layout.id();
263
264 if (!layout.isRoot()) {
265 UiTopoLayoutId parentId = layout.parent();
266 UiTopoLayout parentLayout = ls.getLayout(parentId);
267 RegionId parentRegionId = parentLayout.regionId();
268 region.setParent(parentRegionId);
269 }
270
271 Set<UiTopoLayout> kids = ls.getChildren(layoutId);
272 Set<RegionId> kidRegionIds = new HashSet<>(kids.size());
273 kids.forEach(k -> kidRegionIds.add(k.regionId()));
274 region.setChildren(kidRegionIds);
Simon Huntc0f20c12016-05-09 09:30:20 -0700275 }
276
277 private void loadRegions() {
278 for (Region r : services.region().getRegions()) {
279 UiRegion region = addNewRegion(r);
280 updateRegion(region);
281 }
282 }
283
284 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700285 void addOrUpdateRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700286 RegionId id = region.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800287 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700288 UiRegion uiRegion = uiTopology.findRegion(id);
289 if (uiRegion == null) {
290 uiRegion = addNewRegion(region);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800291 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700292 }
293 updateRegion(uiRegion);
294
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800295 postEvent(REGION_ADDED_OR_UPDATED, uiRegion, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700296 }
297
Simon Hunt58a0dd02016-05-17 11:54:23 -0700298 // package private for unit test access
299 UiRegion accessRegion(RegionId id) {
Simon Huntd5b96732016-07-08 13:22:27 -0700300 return id == null ? null : uiTopology.findRegion(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700301 }
302
Simon Huntc0f20c12016-05-09 09:30:20 -0700303 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700304 void removeRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700305 RegionId id = region.id();
306 UiRegion uiRegion = uiTopology.findRegion(id);
307 if (uiRegion != null) {
308 uiTopology.remove(uiRegion);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800309 postEvent(REGION_REMOVED, uiRegion, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700310 } else {
311 log.warn(E_NO_ELEMENT, "region", id);
312 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700313 }
314
Simon Hunt10973dd2016-08-01 15:50:35 -0700315 Set<UiRegion> getAllRegions() {
316 return uiTopology.allRegions();
317 }
318
Simon Huntc0f20c12016-05-09 09:30:20 -0700319
320 // === DEVICES
321
322 private UiDevice addNewDevice(Device d) {
323 UiDevice device = new UiDevice(uiTopology, d);
Simon Huntb1ce2602016-07-23 14:04:31 -0700324 updateDevice(device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700325 uiTopology.add(device);
Simon Huntb1ce2602016-07-23 14:04:31 -0700326 log.debug("Device {} added to topology", device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700327 return device;
328 }
329
Simon Huntb1ce2602016-07-23 14:04:31 -0700330 // make sure the UiDevice is tagged with the region it belongs to
Simon Huntc0f20c12016-05-09 09:30:20 -0700331 private void updateDevice(UiDevice device) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700332 Region r = services.region().getRegionForDevice(device.id());
333 RegionId rid = r == null ? UiRegion.NULL_ID : r.id();
334 device.setRegionId(rid);
Simon Huntc0f20c12016-05-09 09:30:20 -0700335 }
336
337 private void loadDevices() {
338 for (Device d : services.device().getDevices()) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700339 addNewDevice(d);
Simon Huntc0f20c12016-05-09 09:30:20 -0700340 }
341 }
342
343 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700344 void addOrUpdateDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700345 DeviceId id = device.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800346 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700347 UiDevice uiDevice = uiTopology.findDevice(id);
348 if (uiDevice == null) {
349 uiDevice = addNewDevice(device);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800350 memo = MEMO_ADDED;
Simon Huntb1ce2602016-07-23 14:04:31 -0700351 } else {
352 updateDevice(uiDevice);
Simon Huntc0f20c12016-05-09 09:30:20 -0700353 }
Simon Huntcda9c032016-04-11 10:32:54 -0700354
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800355 postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice, memo);
Simon Huntcda9c032016-04-11 10:32:54 -0700356 }
357
Simon Hunt58a0dd02016-05-17 11:54:23 -0700358 // package private for unit test access
359 UiDevice accessDevice(DeviceId id) {
360 return uiTopology.findDevice(id);
361 }
362
Simon Huntc0f20c12016-05-09 09:30:20 -0700363 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700364 void removeDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700365 DeviceId id = device.id();
366 UiDevice uiDevice = uiTopology.findDevice(id);
367 if (uiDevice != null) {
368 uiTopology.remove(uiDevice);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800369 postEvent(DEVICE_REMOVED, uiDevice, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700370 } else {
371 log.warn(E_NO_ELEMENT, "device", id);
372 }
Simon Huntcda9c032016-04-11 10:32:54 -0700373 }
374
Simon Hunt4854f3d2016-08-02 18:13:15 -0700375 Set<UiDevice> getAllDevices() {
376 return uiTopology.allDevices();
377 }
378
Simon Huntc0f20c12016-05-09 09:30:20 -0700379
Simon Huntc13082f2016-08-03 21:20:23 -0700380 // === LINKS ===
Simon Huntc0f20c12016-05-09 09:30:20 -0700381
Simon Huntc13082f2016-08-03 21:20:23 -0700382 private UiDeviceLink addNewDeviceLink(UiLinkId id) {
383 UiDeviceLink uiDeviceLink = new UiDeviceLink(uiTopology, id);
384 uiTopology.add(uiDeviceLink);
385 return uiDeviceLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700386 }
387
Simon Huntc13082f2016-08-03 21:20:23 -0700388 private UiEdgeLink addNewEdgeLink(UiLinkId id) {
389 UiEdgeLink uiEdgeLink = new UiEdgeLink(uiTopology, id);
390 uiTopology.add(uiEdgeLink);
391 return uiEdgeLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700392 }
393
Simon Huntc13082f2016-08-03 21:20:23 -0700394 private void updateDeviceLink(UiDeviceLink uiDeviceLink, Link link) {
395 uiDeviceLink.attachBackingLink(link);
396 }
397
398 private void loadDeviceLinks() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700399 for (Link link : services.link().getLinks()) {
400 UiLinkId id = uiLinkId(link);
401
Simon Huntc13082f2016-08-03 21:20:23 -0700402 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
403 if (uiDeviceLink == null) {
404 uiDeviceLink = addNewDeviceLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700405 }
Simon Huntc13082f2016-08-03 21:20:23 -0700406 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700407 }
408 }
409
410 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700411 void addOrUpdateDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700412 UiLinkId id = uiLinkId(link);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800413 String memo = MEMO_UPDATED;
Simon Huntc13082f2016-08-03 21:20:23 -0700414 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
415 if (uiDeviceLink == null) {
416 uiDeviceLink = addNewDeviceLink(id);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800417 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700418 }
Simon Huntc13082f2016-08-03 21:20:23 -0700419 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700420
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800421 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700422 }
423
Simon Hunt58a0dd02016-05-17 11:54:23 -0700424 // package private for unit test access
Simon Huntc13082f2016-08-03 21:20:23 -0700425 UiDeviceLink accessDeviceLink(UiLinkId id) {
426 return uiTopology.findDeviceLink(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700427 }
428
Simon Huntc0f20c12016-05-09 09:30:20 -0700429 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700430 void removeDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700431 UiLinkId id = uiLinkId(link);
Simon Huntc13082f2016-08-03 21:20:23 -0700432 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
433 if (uiDeviceLink != null) {
434 boolean remaining = uiDeviceLink.detachBackingLink(link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700435 if (remaining) {
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800436 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, MEMO_UPDATED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700437 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700438 uiTopology.remove(uiDeviceLink);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800439 postEvent(LINK_REMOVED, uiDeviceLink, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700440 }
441 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700442 log.warn(E_NO_ELEMENT, "Device link", id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700443 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700444 }
445
Simon Huntc13082f2016-08-03 21:20:23 -0700446 Set<UiDeviceLink> getAllDeviceLinks() {
447 return uiTopology.allDeviceLinks();
Simon Hunt4854f3d2016-08-02 18:13:15 -0700448 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700449
450 // === HOSTS
451
Simon Hunt58a0dd02016-05-17 11:54:23 -0700452 private EdgeLink synthesizeLink(Host h) {
453 return createEdgeLink(h, true);
454 }
455
Simon Huntc0f20c12016-05-09 09:30:20 -0700456 private UiHost addNewHost(Host h) {
457 UiHost host = new UiHost(uiTopology, h);
458 uiTopology.add(host);
459
Simon Hunt58a0dd02016-05-17 11:54:23 -0700460 EdgeLink elink = synthesizeLink(h);
461 UiLinkId elinkId = uiLinkId(elink);
462 host.setEdgeLinkId(elinkId);
463
464 // add synthesized edge link to the topology
Simon Huntb7fd0802016-10-27 12:21:40 -0700465 addNewEdgeLink(elinkId);
Simon Huntc0f20c12016-05-09 09:30:20 -0700466
467 return host;
468 }
469
Simon Huntb7fd0802016-10-27 12:21:40 -0700470 private void insertNewUiEdgeLink(UiLinkId id) {
471 addNewEdgeLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700472 }
473
474 private void updateHost(UiHost uiHost, Host h) {
Simon Huntc13082f2016-08-03 21:20:23 -0700475 UiEdgeLink existing = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700476
Simon Huntb7fd0802016-10-27 12:21:40 -0700477 // TODO: review - do we need EdgeLink now that we are creating from id only?
Simon Hunt58a0dd02016-05-17 11:54:23 -0700478 EdgeLink currentElink = synthesizeLink(h);
479 UiLinkId currentElinkId = uiLinkId(currentElink);
480
481 if (existing != null) {
482 if (!currentElinkId.equals(existing.id())) {
483 // edge link has changed
Simon Huntb7fd0802016-10-27 12:21:40 -0700484 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700485 uiHost.setEdgeLinkId(currentElinkId);
486
487 uiTopology.remove(existing);
488 }
489
490 } else {
491 // no previously existing edge link
Simon Huntb7fd0802016-10-27 12:21:40 -0700492 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700493 uiHost.setEdgeLinkId(currentElinkId);
494
495 }
496
Simon Huntc0f20c12016-05-09 09:30:20 -0700497 HostLocation hloc = h.location();
498 uiHost.setLocation(hloc.deviceId(), hloc.port());
Simon Huntc0f20c12016-05-09 09:30:20 -0700499 }
500
501 private void loadHosts() {
502 for (Host h : services.host().getHosts()) {
503 UiHost host = addNewHost(h);
504 updateHost(host, h);
505 }
506 }
507
508 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700509 void addOrUpdateHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700510 HostId id = host.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800511 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700512 UiHost uiHost = uiTopology.findHost(id);
513 if (uiHost == null) {
514 uiHost = addNewHost(host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800515 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700516 }
517 updateHost(uiHost, host);
518
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800519 postEvent(HOST_ADDED_OR_UPDATED, uiHost, memo);
Sean Condon5f7d3bc2019-04-15 11:18:34 +0100520 // Link event must be sent after the host event
521 UiEdgeLink uiEdgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId());
522 postEvent(LINK_ADDED_OR_UPDATED, uiEdgeLink, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700523 }
524
Simon Huntc0f20c12016-05-09 09:30:20 -0700525 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700526 void moveHost(Host host, Host prevHost) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700527 UiHost uiHost = uiTopology.findHost(prevHost.id());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700528 if (uiHost != null) {
529 updateHost(uiHost, host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800530 postEvent(HOST_MOVED, uiHost, MEMO_MOVED);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700531 } else {
532 log.warn(E_NO_ELEMENT, "host", prevHost.id());
533 }
534 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700535
Simon Hunt58a0dd02016-05-17 11:54:23 -0700536 // package private for unit test access
537 UiHost accessHost(HostId id) {
538 return uiTopology.findHost(id);
Simon Hunt23fb1352016-04-11 12:15:19 -0700539 }
540
Simon Huntc0f20c12016-05-09 09:30:20 -0700541 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700542 void removeHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700543 HostId id = host.id();
544 UiHost uiHost = uiTopology.findHost(id);
545 if (uiHost != null) {
Simon Huntc13082f2016-08-03 21:20:23 -0700546 UiEdgeLink edgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700547 uiTopology.remove(edgeLink);
Sean Condon5f7d3bc2019-04-15 11:18:34 +0100548 postEvent(LINK_REMOVED, edgeLink, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700549 uiTopology.remove(uiHost);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800550 postEvent(HOST_REMOVED, uiHost, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700551 } else {
552 log.warn(E_NO_ELEMENT, "host", id);
553 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700554 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700555
Simon Hunt4854f3d2016-08-02 18:13:15 -0700556 Set<UiHost> getAllHosts() {
557 return uiTopology.allHosts();
558 }
559
Simon Huntc0f20c12016-05-09 09:30:20 -0700560
Simon Huntc13082f2016-08-03 21:20:23 -0700561 // === SYNTHETIC LINKS
562
563 List<UiSynthLink> getSynthLinks(RegionId regionId) {
564 return uiTopology.findSynthLinks(regionId);
565 }
566
Simon Hunt0e161092017-05-08 17:41:38 -0700567 Map<UiLinkId, UiSynthLink> relevantSynthLinks(RegionId regionId) {
568 Map<UiLinkId, UiSynthLink> result = new HashMap<>();
569 for (UiSynthLink sl : getSynthLinks(regionId)) {
570 result.put(sl.original().id(), sl);
571 }
572 return result;
573 }
574
Simon Huntb1ce2602016-07-23 14:04:31 -0700575 /**
576 * Refreshes the internal state.
577 */
578 public void refresh() {
Simon Huntc13082f2016-08-03 21:20:23 -0700579 // fix up internal linkages to ensure they are correct
Simon Huntb1ce2602016-07-23 14:04:31 -0700580
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700581 // make sure regions reflect layout containment hierarchy
582 fixupContainmentHierarchy(uiTopology.nullRegion());
583 uiTopology.allRegions().forEach(this::fixupContainmentHierarchy);
584
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500585 // make sure devices and hosts are in the correct region
Simon Huntb1ce2602016-07-23 14:04:31 -0700586 Set<UiDevice> allDevices = uiTopology.allDevices();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500587 Set<UiHost> allHosts = uiTopology.allHosts();
Simon Huntb1ce2602016-07-23 14:04:31 -0700588
589 services.region().getRegions().forEach(r -> {
590 RegionId rid = r.id();
591 UiRegion region = uiTopology.findRegion(rid);
592 if (region != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500593 reconcileDevicesAndHostsWithRegion(allDevices, allHosts, rid, region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700594 } else {
595 log.warn("No UiRegion in topology for ID {}", rid);
596 }
597 });
598
599 // what is left over, must belong to the null-region
600 Set<DeviceId> leftOver = new HashSet<>(allDevices.size());
601 allDevices.forEach(d -> leftOver.add(d.id()));
602 uiTopology.nullRegion().reconcileDevices(leftOver);
Simon Huntc13082f2016-08-03 21:20:23 -0700603
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500604 Set<HostId> leftOverHosts = new HashSet<>(allHosts.size());
605 allHosts.forEach(h -> leftOverHosts.add(h.id()));
606 uiTopology.nullRegion().reconcileHosts(leftOverHosts);
607
Simon Huntc13082f2016-08-03 21:20:23 -0700608 // now that we have correct region hierarchy, and devices are in their
609 // respective regions, we can compute synthetic links for each region.
610 uiTopology.computeSynthLinks();
Simon Huntb1ce2602016-07-23 14:04:31 -0700611 }
612
Steven Burrows8f45ce22016-10-27 20:04:14 -0500613 private void reconcileDevicesAndHostsWithRegion(Set<UiDevice> allDevices,
614 Set<UiHost> allHosts,
615 RegionId rid,
616 UiRegion region) {
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500617 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows8f45ce22016-10-27 20:04:14 -0500618 Set<HostId> hostIds = new HashSet<>();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500619 region.reconcileDevices(deviceIds);
620
621 deviceIds.forEach(devId -> {
622 UiDevice dev = uiTopology.findDevice(devId);
623 if (dev != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500624 dev.setRegionId(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500625 allDevices.remove(dev);
626 } else {
627 log.warn("Region device ID {} but no UiDevice in topology",
Simon Huntc4ca7102017-04-08 22:28:04 -0700628 devId);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500629 }
Steven Burrows8f45ce22016-10-27 20:04:14 -0500630
631 Set<Host> hosts = services.host().getConnectedHosts(devId);
632 for (Host h : hosts) {
633 HostId hid = h.id();
634 hostIds.add(hid);
635 UiHost host = uiTopology.findHost(hid);
636
637 if (host != null) {
638 host.setRegionId(rid);
639 allHosts.remove(host);
640 } else {
641 log.warn("Region host ID {} but no UiHost in topology",
Simon Huntc4ca7102017-04-08 22:28:04 -0700642 hid);
Steven Burrows8f45ce22016-10-27 20:04:14 -0500643 }
644 }
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500645 });
Steven Burrows8f45ce22016-10-27 20:04:14 -0500646
647 region.reconcileHosts(hostIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500648 }
649
Simon Huntc13082f2016-08-03 21:20:23 -0700650
Simon Huntc0f20c12016-05-09 09:30:20 -0700651 // === CACHE STATISTICS
652
Simon Hunt338a3b42016-04-14 09:43:52 -0700653 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700654 * Returns a detailed (multi-line) string showing the contents of the cache.
655 *
656 * @return detailed string
657 */
658 public String dumpString() {
659 return uiTopology.dumpString();
660 }
661
662 /**
Simon Hunt338a3b42016-04-14 09:43:52 -0700663 * Returns the number of members in the cluster.
664 *
665 * @return number of cluster members
666 */
667 public int clusterMemberCount() {
668 return uiTopology.clusterMemberCount();
669 }
670
671 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700672 * Returns the number of regions in the topology.
Simon Hunt338a3b42016-04-14 09:43:52 -0700673 *
674 * @return number of regions
675 */
676 public int regionCount() {
677 return uiTopology.regionCount();
678 }
Simon Hunt58a0dd02016-05-17 11:54:23 -0700679
680 /**
681 * Returns the number of devices in the topology.
682 *
683 * @return number of devices
684 */
685 public int deviceCount() {
686 return uiTopology.deviceCount();
687 }
688
689 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700690 * Returns the number of device links in the topology.
Simon Hunt58a0dd02016-05-17 11:54:23 -0700691 *
Simon Huntc13082f2016-08-03 21:20:23 -0700692 * @return number of device links
Simon Hunt58a0dd02016-05-17 11:54:23 -0700693 */
Simon Huntc13082f2016-08-03 21:20:23 -0700694 public int deviceLinkCount() {
695 return uiTopology.deviceLinkCount();
696 }
697
698 /**
699 * Returns the number of edge links in the topology.
700 *
701 * @return number of edge links
702 */
703 public int edgeLinkCount() {
704 return uiTopology.edgeLinkCount();
Simon Hunt58a0dd02016-05-17 11:54:23 -0700705 }
706
707 /**
708 * Returns the number of hosts in the topology.
709 *
710 * @return number of hosts
711 */
712 public int hostCount() {
713 return uiTopology.hostCount();
714 }
Simon Huntd5b96732016-07-08 13:22:27 -0700715
Simon Huntc13082f2016-08-03 21:20:23 -0700716 /**
717 * Returns the number of synthetic links in the topology.
718 *
719 * @return the number of synthetic links
720 */
721 public int synthLinkCount() {
722 return uiTopology.synthLinkCount();
723 }
Simon Huntcda9c032016-04-11 10:32:54 -0700724}