blob: 12d75f3a215730b1415bf6fd69eebf3baf5230a1 [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;
43import org.onosproject.ui.model.topo.UiRegion;
Simon Huntc13082f2016-08-03 21:20:23 -070044import org.onosproject.ui.model.topo.UiSynthLink;
Simon Hunt4f4ffc32016-08-03 18:30:47 -070045import org.onosproject.ui.model.topo.UiTopoLayout;
46import org.onosproject.ui.model.topo.UiTopoLayoutId;
Simon Hunt23fb1352016-04-11 12:15:19 -070047import org.onosproject.ui.model.topo.UiTopology;
Simon Hunt642bc452016-05-04 19:34:45 -070048import org.slf4j.Logger;
49import org.slf4j.LoggerFactory;
Simon Hunt23fb1352016-04-11 12:15:19 -070050
Simon Huntb1ce2602016-07-23 14:04:31 -070051import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070052import java.util.List;
Simon Huntc0f20c12016-05-09 09:30:20 -070053import java.util.Set;
54
55import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Simon Hunt642bc452016-05-04 19:34:45 -070056import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_ADDED_OR_UPDATED;
57import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_REMOVED;
58import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED;
Simon Hunt23fb1352016-04-11 12:15:19 -070059import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_REMOVED;
Simon Huntc0f20c12016-05-09 09:30:20 -070060import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_ADDED_OR_UPDATED;
61import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_MOVED;
62import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_REMOVED;
63import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.LINK_ADDED_OR_UPDATED;
64import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.LINK_REMOVED;
65import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.REGION_ADDED_OR_UPDATED;
66import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.REGION_REMOVED;
67import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId;
Simon Huntcda9c032016-04-11 10:32:54 -070068
69/**
70 * UI Topology Model cache.
71 */
72class ModelCache {
73
Simon Huntc0f20c12016-05-09 09:30:20 -070074 private static final String E_NO_ELEMENT = "Tried to remove non-member {}: {}";
Simon Hunt8eac4ae2017-01-20 12:56:45 -080075 private static final String MEMO_ADDED = "added";
76 private static final String MEMO_UPDATED = "updated";
77 private static final String MEMO_REMOVED = "removed";
78 private static final String MEMO_MOVED = "moved";
Simon Huntc0f20c12016-05-09 09:30:20 -070079
Simon Hunt642bc452016-05-04 19:34:45 -070080 private static final Logger log = LoggerFactory.getLogger(ModelCache.class);
81
Simon Huntc4ca7102017-04-08 22:28:04 -070082 // TODO: add NetworkConfigService to service bundle
83// private final NetworkConfigService cfgService =
84// DefaultServiceDirectory.getService(NetworkConfigService.class);
85
Simon Hunt642bc452016-05-04 19:34:45 -070086 private final ServiceBundle services;
Simon Huntcda9c032016-04-11 10:32:54 -070087 private final EventDispatcher dispatcher;
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070088 private final UiTopology uiTopology;
Simon Huntcda9c032016-04-11 10:32:54 -070089
Simon Hunt8eac4ae2017-01-20 12:56:45 -080090 private Topo2Jsonifier t2json;
91
Simon Hunt642bc452016-05-04 19:34:45 -070092 ModelCache(ServiceBundle services, EventDispatcher eventDispatcher) {
93 this.services = services;
Simon Huntcda9c032016-04-11 10:32:54 -070094 this.dispatcher = eventDispatcher;
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070095 uiTopology = new UiTopology(services);
Simon Huntcda9c032016-04-11 10:32:54 -070096 }
97
Simon Hunt338a3b42016-04-14 09:43:52 -070098 @Override
99 public String toString() {
100 return "ModelCache{" + uiTopology + "}";
101 }
102
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800103 private void postEvent(UiModelEvent.Type type, UiElement subject, String memo) {
104 ObjectNode data = t2json != null ? t2json.jsonUiElement(subject) : null;
105 dispatcher.post(new UiModelEvent(type, subject, data, memo));
106 }
107
108 void injectJsonifier(Topo2Jsonifier t2json) {
109 this.t2json = t2json;
Simon Huntc0f20c12016-05-09 09:30:20 -0700110 }
111
Simon Huntcda9c032016-04-11 10:32:54 -0700112 void clear() {
Simon Hunt23fb1352016-04-11 12:15:19 -0700113 uiTopology.clear();
Simon Huntcda9c032016-04-11 10:32:54 -0700114 }
115
116 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700117 * Create our internal model of the global topology. An assumption we are
118 * making is that the topology is empty to start.
Simon Huntcda9c032016-04-11 10:32:54 -0700119 */
120 void load() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700121 loadClusterMembers();
122 loadRegions();
123 loadDevices();
Simon Huntc13082f2016-08-03 21:20:23 -0700124 loadDeviceLinks();
Simon Huntc0f20c12016-05-09 09:30:20 -0700125 loadHosts();
Simon Huntcda9c032016-04-11 10:32:54 -0700126 }
127
128
Simon Huntc0f20c12016-05-09 09:30:20 -0700129 // === CLUSTER MEMBERS
130
131 private UiClusterMember addNewClusterMember(ControllerNode n) {
132 UiClusterMember member = new UiClusterMember(uiTopology, n);
133 uiTopology.add(member);
134 return member;
135 }
136
137 private void updateClusterMember(UiClusterMember member) {
138 ControllerNode.State state = services.cluster().getState(member.id());
139 member.setState(state);
Simon Huntc0f20c12016-05-09 09:30:20 -0700140 }
141
142 private void loadClusterMembers() {
143 for (ControllerNode n : services.cluster().getNodes()) {
144 UiClusterMember member = addNewClusterMember(n);
145 updateClusterMember(member);
146 }
147 }
148
149 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700150 void addOrUpdateClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700151 NodeId id = cnode.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800152 String memo = MEMO_UPDATED;
Simon Hunt642bc452016-05-04 19:34:45 -0700153 UiClusterMember member = uiTopology.findClusterMember(id);
154 if (member == null) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700155 member = addNewClusterMember(cnode);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800156 memo = MEMO_ADDED;
Simon Hunt338a3b42016-04-14 09:43:52 -0700157 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700158 updateClusterMember(member);
Simon Hunt338a3b42016-04-14 09:43:52 -0700159
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800160 postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700161 }
162
Simon Huntc0f20c12016-05-09 09:30:20 -0700163 // package private for unit test access
164 UiClusterMember accessClusterMember(NodeId id) {
165 return uiTopology.findClusterMember(id);
166 }
167
168 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700169 void removeClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700170 NodeId id = cnode.id();
171 UiClusterMember member = uiTopology.findClusterMember(id);
172 if (member != null) {
173 uiTopology.remove(member);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800174 postEvent(CLUSTER_MEMBER_REMOVED, member, MEMO_REMOVED);
Simon Hunt642bc452016-05-04 19:34:45 -0700175 } else {
Simon Huntc0f20c12016-05-09 09:30:20 -0700176 log.warn(E_NO_ELEMENT, "cluster node", id);
Simon Hunt642bc452016-05-04 19:34:45 -0700177 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700178 }
179
Simon Huntd5b96732016-07-08 13:22:27 -0700180 List<UiClusterMember> getAllClusterMembers() {
181 return uiTopology.allClusterMembers();
182 }
183
Simon Huntc0f20c12016-05-09 09:30:20 -0700184
185 // === MASTERSHIP CHANGES
186
187 // invoked from UiSharedTopologyModel mastership listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700188 void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700189 // To think about:: do we need to store mastership info?
190 // or can we rely on looking it up live?
Simon Hunt338a3b42016-04-14 09:43:52 -0700191 // TODO: store the updated mastership information
192 // TODO: post event
193 }
194
Simon Huntb1ce2602016-07-23 14:04:31 -0700195 // === THE NULL REGION
196
197 UiRegion nullRegion() {
198 return uiTopology.nullRegion();
199 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700200
201 // === REGIONS
202
203 private UiRegion addNewRegion(Region r) {
204 UiRegion region = new UiRegion(uiTopology, r);
205 uiTopology.add(region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700206 log.debug("Region {} added to topology", region);
Simon Huntc0f20c12016-05-09 09:30:20 -0700207 return region;
208 }
209
210 private void updateRegion(UiRegion region) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700211 RegionId rid = region.id();
212 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500213 Set<HostId> hostIds = services.region().getRegionHosts(rid);
Simon Huntb1ce2602016-07-23 14:04:31 -0700214
215 // Make sure device objects refer to their region
216 deviceIds.forEach(d -> {
217 UiDevice dev = uiTopology.findDevice(d);
218 if (dev != null) {
219 dev.setRegionId(rid);
220 } else {
221 // if we don't have the UiDevice in the topology, what can we do?
222 log.warn("Region device {}, but we don't have UiDevice in topology", d);
223 }
224 });
225
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500226 hostIds.forEach(d -> {
227 UiHost host = uiTopology.findHost(d);
228 if (host != null) {
229 host.setRegionId(rid);
230 } else {
231 // if we don't have the UiDevice in the topology, what can we do?
232 log.warn("Region host {}, but we don't have UiHost in topology", d);
233 }
234 });
235
Simon Huntb1ce2602016-07-23 14:04:31 -0700236 // Make sure the region object refers to the devices
237 region.reconcileDevices(deviceIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500238 region.reconcileHosts(hostIds);
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700239
240 fixupContainmentHierarchy(region);
241 }
242
243 private void fixupContainmentHierarchy(UiRegion region) {
244 UiTopoLayoutService ls = services.layout();
245 RegionId regionId = region.id();
246
247 UiTopoLayout layout = ls.getLayout(regionId);
248 if (layout == null) {
249 // no layout backed by this region
250 log.warn("No layout backed by region {}", regionId);
251 return;
252 }
253
254 UiTopoLayoutId layoutId = layout.id();
255
256 if (!layout.isRoot()) {
257 UiTopoLayoutId parentId = layout.parent();
258 UiTopoLayout parentLayout = ls.getLayout(parentId);
259 RegionId parentRegionId = parentLayout.regionId();
260 region.setParent(parentRegionId);
261 }
262
263 Set<UiTopoLayout> kids = ls.getChildren(layoutId);
264 Set<RegionId> kidRegionIds = new HashSet<>(kids.size());
265 kids.forEach(k -> kidRegionIds.add(k.regionId()));
266 region.setChildren(kidRegionIds);
Simon Huntc0f20c12016-05-09 09:30:20 -0700267 }
268
269 private void loadRegions() {
270 for (Region r : services.region().getRegions()) {
271 UiRegion region = addNewRegion(r);
272 updateRegion(region);
273 }
274 }
275
276 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700277 void addOrUpdateRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700278 RegionId id = region.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800279 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700280 UiRegion uiRegion = uiTopology.findRegion(id);
281 if (uiRegion == null) {
282 uiRegion = addNewRegion(region);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800283 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700284 }
285 updateRegion(uiRegion);
286
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800287 postEvent(REGION_ADDED_OR_UPDATED, uiRegion, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700288 }
289
Simon Hunt58a0dd02016-05-17 11:54:23 -0700290 // package private for unit test access
291 UiRegion accessRegion(RegionId id) {
Simon Huntd5b96732016-07-08 13:22:27 -0700292 return id == null ? null : uiTopology.findRegion(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700293 }
294
Simon Huntc0f20c12016-05-09 09:30:20 -0700295 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700296 void removeRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700297 RegionId id = region.id();
298 UiRegion uiRegion = uiTopology.findRegion(id);
299 if (uiRegion != null) {
300 uiTopology.remove(uiRegion);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800301 postEvent(REGION_REMOVED, uiRegion, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700302 } else {
303 log.warn(E_NO_ELEMENT, "region", id);
304 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700305 }
306
Simon Hunt10973dd2016-08-01 15:50:35 -0700307 Set<UiRegion> getAllRegions() {
308 return uiTopology.allRegions();
309 }
310
Simon Huntc0f20c12016-05-09 09:30:20 -0700311
312 // === DEVICES
313
314 private UiDevice addNewDevice(Device d) {
315 UiDevice device = new UiDevice(uiTopology, d);
Simon Huntb1ce2602016-07-23 14:04:31 -0700316 updateDevice(device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700317 uiTopology.add(device);
Simon Huntb1ce2602016-07-23 14:04:31 -0700318 log.debug("Device {} added to topology", device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700319 return device;
320 }
321
Simon Huntb1ce2602016-07-23 14:04:31 -0700322 // make sure the UiDevice is tagged with the region it belongs to
Simon Huntc0f20c12016-05-09 09:30:20 -0700323 private void updateDevice(UiDevice device) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700324 Region r = services.region().getRegionForDevice(device.id());
325 RegionId rid = r == null ? UiRegion.NULL_ID : r.id();
326 device.setRegionId(rid);
Simon Huntc0f20c12016-05-09 09:30:20 -0700327 }
328
329 private void loadDevices() {
330 for (Device d : services.device().getDevices()) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700331 addNewDevice(d);
Simon Huntc0f20c12016-05-09 09:30:20 -0700332 }
333 }
334
335 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700336 void addOrUpdateDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700337 DeviceId id = device.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800338 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700339 UiDevice uiDevice = uiTopology.findDevice(id);
340 if (uiDevice == null) {
341 uiDevice = addNewDevice(device);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800342 memo = MEMO_ADDED;
Simon Huntb1ce2602016-07-23 14:04:31 -0700343 } else {
344 updateDevice(uiDevice);
Simon Huntc0f20c12016-05-09 09:30:20 -0700345 }
Simon Huntcda9c032016-04-11 10:32:54 -0700346
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800347 postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice, memo);
Simon Huntcda9c032016-04-11 10:32:54 -0700348 }
349
Simon Hunt58a0dd02016-05-17 11:54:23 -0700350 // package private for unit test access
351 UiDevice accessDevice(DeviceId id) {
352 return uiTopology.findDevice(id);
353 }
354
Simon Huntc0f20c12016-05-09 09:30:20 -0700355 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700356 void removeDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700357 DeviceId id = device.id();
358 UiDevice uiDevice = uiTopology.findDevice(id);
359 if (uiDevice != null) {
360 uiTopology.remove(uiDevice);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800361 postEvent(DEVICE_REMOVED, uiDevice, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700362 } else {
363 log.warn(E_NO_ELEMENT, "device", id);
364 }
Simon Huntcda9c032016-04-11 10:32:54 -0700365 }
366
Simon Hunt4854f3d2016-08-02 18:13:15 -0700367 Set<UiDevice> getAllDevices() {
368 return uiTopology.allDevices();
369 }
370
Simon Huntc0f20c12016-05-09 09:30:20 -0700371
Simon Huntc13082f2016-08-03 21:20:23 -0700372 // === LINKS ===
Simon Huntc0f20c12016-05-09 09:30:20 -0700373
Simon Huntc13082f2016-08-03 21:20:23 -0700374 private UiDeviceLink addNewDeviceLink(UiLinkId id) {
375 UiDeviceLink uiDeviceLink = new UiDeviceLink(uiTopology, id);
376 uiTopology.add(uiDeviceLink);
377 return uiDeviceLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700378 }
379
Simon Huntc13082f2016-08-03 21:20:23 -0700380 private UiEdgeLink addNewEdgeLink(UiLinkId id) {
381 UiEdgeLink uiEdgeLink = new UiEdgeLink(uiTopology, id);
382 uiTopology.add(uiEdgeLink);
383 return uiEdgeLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700384 }
385
Simon Huntc13082f2016-08-03 21:20:23 -0700386 private void updateDeviceLink(UiDeviceLink uiDeviceLink, Link link) {
387 uiDeviceLink.attachBackingLink(link);
388 }
389
390 private void loadDeviceLinks() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700391 for (Link link : services.link().getLinks()) {
392 UiLinkId id = uiLinkId(link);
393
Simon Huntc13082f2016-08-03 21:20:23 -0700394 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
395 if (uiDeviceLink == null) {
396 uiDeviceLink = addNewDeviceLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700397 }
Simon Huntc13082f2016-08-03 21:20:23 -0700398 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700399 }
400 }
401
402 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700403 void addOrUpdateDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700404 UiLinkId id = uiLinkId(link);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800405 String memo = MEMO_UPDATED;
Simon Huntc13082f2016-08-03 21:20:23 -0700406 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
407 if (uiDeviceLink == null) {
408 uiDeviceLink = addNewDeviceLink(id);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800409 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700410 }
Simon Huntc13082f2016-08-03 21:20:23 -0700411 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700412
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800413 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700414 }
415
Simon Hunt58a0dd02016-05-17 11:54:23 -0700416 // package private for unit test access
Simon Huntc13082f2016-08-03 21:20:23 -0700417 UiDeviceLink accessDeviceLink(UiLinkId id) {
418 return uiTopology.findDeviceLink(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700419 }
420
Simon Huntc0f20c12016-05-09 09:30:20 -0700421 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700422 void removeDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700423 UiLinkId id = uiLinkId(link);
Simon Huntc13082f2016-08-03 21:20:23 -0700424 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
425 if (uiDeviceLink != null) {
426 boolean remaining = uiDeviceLink.detachBackingLink(link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700427 if (remaining) {
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800428 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, MEMO_UPDATED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700429 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700430 uiTopology.remove(uiDeviceLink);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800431 postEvent(LINK_REMOVED, uiDeviceLink, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700432 }
433 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700434 log.warn(E_NO_ELEMENT, "Device link", id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700435 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700436 }
437
Simon Huntc13082f2016-08-03 21:20:23 -0700438 Set<UiDeviceLink> getAllDeviceLinks() {
439 return uiTopology.allDeviceLinks();
Simon Hunt4854f3d2016-08-02 18:13:15 -0700440 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700441
442 // === HOSTS
443
Simon Hunt58a0dd02016-05-17 11:54:23 -0700444 private EdgeLink synthesizeLink(Host h) {
445 return createEdgeLink(h, true);
446 }
447
Simon Huntc0f20c12016-05-09 09:30:20 -0700448 private UiHost addNewHost(Host h) {
449 UiHost host = new UiHost(uiTopology, h);
450 uiTopology.add(host);
451
Simon Hunt58a0dd02016-05-17 11:54:23 -0700452 EdgeLink elink = synthesizeLink(h);
453 UiLinkId elinkId = uiLinkId(elink);
454 host.setEdgeLinkId(elinkId);
455
456 // add synthesized edge link to the topology
Simon Huntb7fd0802016-10-27 12:21:40 -0700457 addNewEdgeLink(elinkId);
Simon Huntc0f20c12016-05-09 09:30:20 -0700458
459 return host;
460 }
461
Simon Huntb7fd0802016-10-27 12:21:40 -0700462 private void insertNewUiEdgeLink(UiLinkId id) {
463 addNewEdgeLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700464 }
465
466 private void updateHost(UiHost uiHost, Host h) {
Simon Huntc13082f2016-08-03 21:20:23 -0700467 UiEdgeLink existing = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700468
Simon Huntb7fd0802016-10-27 12:21:40 -0700469 // TODO: review - do we need EdgeLink now that we are creating from id only?
Simon Hunt58a0dd02016-05-17 11:54:23 -0700470 EdgeLink currentElink = synthesizeLink(h);
471 UiLinkId currentElinkId = uiLinkId(currentElink);
472
473 if (existing != null) {
474 if (!currentElinkId.equals(existing.id())) {
475 // edge link has changed
Simon Huntb7fd0802016-10-27 12:21:40 -0700476 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700477 uiHost.setEdgeLinkId(currentElinkId);
478
479 uiTopology.remove(existing);
480 }
481
482 } else {
483 // no previously existing edge link
Simon Huntb7fd0802016-10-27 12:21:40 -0700484 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700485 uiHost.setEdgeLinkId(currentElinkId);
486
487 }
488
Simon Huntc0f20c12016-05-09 09:30:20 -0700489 HostLocation hloc = h.location();
490 uiHost.setLocation(hloc.deviceId(), hloc.port());
Simon Huntc0f20c12016-05-09 09:30:20 -0700491 }
492
493 private void loadHosts() {
494 for (Host h : services.host().getHosts()) {
495 UiHost host = addNewHost(h);
496 updateHost(host, h);
497 }
498 }
499
500 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700501 void addOrUpdateHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700502 HostId id = host.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800503 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700504 UiHost uiHost = uiTopology.findHost(id);
505 if (uiHost == null) {
506 uiHost = addNewHost(host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800507 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700508 }
509 updateHost(uiHost, host);
510
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800511 postEvent(HOST_ADDED_OR_UPDATED, uiHost, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700512 }
513
Simon Huntc0f20c12016-05-09 09:30:20 -0700514 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700515 void moveHost(Host host, Host prevHost) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700516 UiHost uiHost = uiTopology.findHost(prevHost.id());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700517 if (uiHost != null) {
518 updateHost(uiHost, host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800519 postEvent(HOST_MOVED, uiHost, MEMO_MOVED);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700520 } else {
521 log.warn(E_NO_ELEMENT, "host", prevHost.id());
522 }
523 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700524
Simon Hunt58a0dd02016-05-17 11:54:23 -0700525 // package private for unit test access
526 UiHost accessHost(HostId id) {
527 return uiTopology.findHost(id);
Simon Hunt23fb1352016-04-11 12:15:19 -0700528 }
529
Simon Huntc0f20c12016-05-09 09:30:20 -0700530 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700531 void removeHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700532 HostId id = host.id();
533 UiHost uiHost = uiTopology.findHost(id);
534 if (uiHost != null) {
Simon Huntc13082f2016-08-03 21:20:23 -0700535 UiEdgeLink edgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700536 uiTopology.remove(edgeLink);
Simon Huntc0f20c12016-05-09 09:30:20 -0700537 uiTopology.remove(uiHost);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800538 postEvent(HOST_REMOVED, uiHost, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700539 } else {
540 log.warn(E_NO_ELEMENT, "host", id);
541 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700542 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700543
Simon Hunt4854f3d2016-08-02 18:13:15 -0700544 Set<UiHost> getAllHosts() {
545 return uiTopology.allHosts();
546 }
547
Simon Huntc0f20c12016-05-09 09:30:20 -0700548
Simon Huntc13082f2016-08-03 21:20:23 -0700549 // === SYNTHETIC LINKS
550
551 List<UiSynthLink> getSynthLinks(RegionId regionId) {
552 return uiTopology.findSynthLinks(regionId);
553 }
554
Simon Huntb1ce2602016-07-23 14:04:31 -0700555 /**
556 * Refreshes the internal state.
557 */
558 public void refresh() {
Simon Huntc13082f2016-08-03 21:20:23 -0700559 // fix up internal linkages to ensure they are correct
Simon Huntb1ce2602016-07-23 14:04:31 -0700560
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700561 // make sure regions reflect layout containment hierarchy
562 fixupContainmentHierarchy(uiTopology.nullRegion());
563 uiTopology.allRegions().forEach(this::fixupContainmentHierarchy);
564
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500565 // make sure devices and hosts are in the correct region
Simon Huntb1ce2602016-07-23 14:04:31 -0700566 Set<UiDevice> allDevices = uiTopology.allDevices();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500567 Set<UiHost> allHosts = uiTopology.allHosts();
Simon Huntb1ce2602016-07-23 14:04:31 -0700568
569 services.region().getRegions().forEach(r -> {
570 RegionId rid = r.id();
Simon Huntc4ca7102017-04-08 22:28:04 -0700571
572// BasicRegionConfig rcfg = cfgService.getConfig(rid, BasicRegionConfig.class);
573// services.netcfg() ...
574 // TODO: figure out how to include peer-location data in UiRegion instance
575
Simon Huntb1ce2602016-07-23 14:04:31 -0700576 UiRegion region = uiTopology.findRegion(rid);
577 if (region != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500578 reconcileDevicesAndHostsWithRegion(allDevices, allHosts, rid, region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700579 } else {
580 log.warn("No UiRegion in topology for ID {}", rid);
581 }
582 });
583
584 // what is left over, must belong to the null-region
585 Set<DeviceId> leftOver = new HashSet<>(allDevices.size());
586 allDevices.forEach(d -> leftOver.add(d.id()));
587 uiTopology.nullRegion().reconcileDevices(leftOver);
Simon Huntc13082f2016-08-03 21:20:23 -0700588
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500589 Set<HostId> leftOverHosts = new HashSet<>(allHosts.size());
590 allHosts.forEach(h -> leftOverHosts.add(h.id()));
591 uiTopology.nullRegion().reconcileHosts(leftOverHosts);
592
Simon Huntc13082f2016-08-03 21:20:23 -0700593 // now that we have correct region hierarchy, and devices are in their
594 // respective regions, we can compute synthetic links for each region.
595 uiTopology.computeSynthLinks();
Simon Huntb1ce2602016-07-23 14:04:31 -0700596 }
597
Steven Burrows8f45ce22016-10-27 20:04:14 -0500598 private void reconcileDevicesAndHostsWithRegion(Set<UiDevice> allDevices,
599 Set<UiHost> allHosts,
600 RegionId rid,
601 UiRegion region) {
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500602 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows8f45ce22016-10-27 20:04:14 -0500603 Set<HostId> hostIds = new HashSet<>();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500604 region.reconcileDevices(deviceIds);
605
606 deviceIds.forEach(devId -> {
607 UiDevice dev = uiTopology.findDevice(devId);
608 if (dev != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500609 dev.setRegionId(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500610 allDevices.remove(dev);
611 } else {
612 log.warn("Region device ID {} but no UiDevice in topology",
Simon Huntc4ca7102017-04-08 22:28:04 -0700613 devId);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500614 }
Steven Burrows8f45ce22016-10-27 20:04:14 -0500615
616 Set<Host> hosts = services.host().getConnectedHosts(devId);
617 for (Host h : hosts) {
618 HostId hid = h.id();
619 hostIds.add(hid);
620 UiHost host = uiTopology.findHost(hid);
621
622 if (host != null) {
623 host.setRegionId(rid);
624 allHosts.remove(host);
625 } else {
626 log.warn("Region host ID {} but no UiHost in topology",
Simon Huntc4ca7102017-04-08 22:28:04 -0700627 hid);
Steven Burrows8f45ce22016-10-27 20:04:14 -0500628 }
629 }
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500630 });
Steven Burrows8f45ce22016-10-27 20:04:14 -0500631
632 region.reconcileHosts(hostIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500633 }
634
Simon Huntc13082f2016-08-03 21:20:23 -0700635
Simon Huntc0f20c12016-05-09 09:30:20 -0700636 // === CACHE STATISTICS
637
Simon Hunt338a3b42016-04-14 09:43:52 -0700638 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700639 * Returns a detailed (multi-line) string showing the contents of the cache.
640 *
641 * @return detailed string
642 */
643 public String dumpString() {
644 return uiTopology.dumpString();
645 }
646
647 /**
Simon Hunt338a3b42016-04-14 09:43:52 -0700648 * Returns the number of members in the cluster.
649 *
650 * @return number of cluster members
651 */
652 public int clusterMemberCount() {
653 return uiTopology.clusterMemberCount();
654 }
655
656 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700657 * Returns the number of regions in the topology.
Simon Hunt338a3b42016-04-14 09:43:52 -0700658 *
659 * @return number of regions
660 */
661 public int regionCount() {
662 return uiTopology.regionCount();
663 }
Simon Hunt58a0dd02016-05-17 11:54:23 -0700664
665 /**
666 * Returns the number of devices in the topology.
667 *
668 * @return number of devices
669 */
670 public int deviceCount() {
671 return uiTopology.deviceCount();
672 }
673
674 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700675 * Returns the number of device links in the topology.
Simon Hunt58a0dd02016-05-17 11:54:23 -0700676 *
Simon Huntc13082f2016-08-03 21:20:23 -0700677 * @return number of device links
Simon Hunt58a0dd02016-05-17 11:54:23 -0700678 */
Simon Huntc13082f2016-08-03 21:20:23 -0700679 public int deviceLinkCount() {
680 return uiTopology.deviceLinkCount();
681 }
682
683 /**
684 * Returns the number of edge links in the topology.
685 *
686 * @return number of edge links
687 */
688 public int edgeLinkCount() {
689 return uiTopology.edgeLinkCount();
Simon Hunt58a0dd02016-05-17 11:54:23 -0700690 }
691
692 /**
693 * Returns the number of hosts in the topology.
694 *
695 * @return number of hosts
696 */
697 public int hostCount() {
698 return uiTopology.hostCount();
699 }
Simon Huntd5b96732016-07-08 13:22:27 -0700700
Simon Huntc13082f2016-08-03 21:20:23 -0700701 /**
702 * Returns the number of synthetic links in the topology.
703 *
704 * @return the number of synthetic links
705 */
706 public int synthLinkCount() {
707 return uiTopology.synthLinkCount();
708 }
Simon Huntcda9c032016-04-11 10:32:54 -0700709}