blob: 24fda23efff308fc111906f37eecdbd6ede1a9e9 [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
82 private final ServiceBundle services;
Simon Huntcda9c032016-04-11 10:32:54 -070083 private final EventDispatcher dispatcher;
Simon Hunt23fb1352016-04-11 12:15:19 -070084 private final UiTopology uiTopology = new UiTopology();
Simon Huntcda9c032016-04-11 10:32:54 -070085
Simon Hunt8eac4ae2017-01-20 12:56:45 -080086 private Topo2Jsonifier t2json;
87
Simon Hunt642bc452016-05-04 19:34:45 -070088 ModelCache(ServiceBundle services, EventDispatcher eventDispatcher) {
89 this.services = services;
Simon Huntcda9c032016-04-11 10:32:54 -070090 this.dispatcher = eventDispatcher;
91 }
92
Simon Hunt338a3b42016-04-14 09:43:52 -070093 @Override
94 public String toString() {
95 return "ModelCache{" + uiTopology + "}";
96 }
97
Simon Hunt8eac4ae2017-01-20 12:56:45 -080098 private void postEvent(UiModelEvent.Type type, UiElement subject, String memo) {
99 ObjectNode data = t2json != null ? t2json.jsonUiElement(subject) : null;
100 dispatcher.post(new UiModelEvent(type, subject, data, memo));
101 }
102
103 void injectJsonifier(Topo2Jsonifier t2json) {
104 this.t2json = t2json;
Simon Huntc0f20c12016-05-09 09:30:20 -0700105 }
106
Simon Huntcda9c032016-04-11 10:32:54 -0700107 void clear() {
Simon Hunt23fb1352016-04-11 12:15:19 -0700108 uiTopology.clear();
Simon Huntcda9c032016-04-11 10:32:54 -0700109 }
110
111 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700112 * Create our internal model of the global topology. An assumption we are
113 * making is that the topology is empty to start.
Simon Huntcda9c032016-04-11 10:32:54 -0700114 */
115 void load() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700116 loadClusterMembers();
117 loadRegions();
118 loadDevices();
Simon Huntc13082f2016-08-03 21:20:23 -0700119 loadDeviceLinks();
Simon Huntc0f20c12016-05-09 09:30:20 -0700120 loadHosts();
Simon Huntcda9c032016-04-11 10:32:54 -0700121 }
122
123
Simon Huntc0f20c12016-05-09 09:30:20 -0700124 // === CLUSTER MEMBERS
125
126 private UiClusterMember addNewClusterMember(ControllerNode n) {
127 UiClusterMember member = new UiClusterMember(uiTopology, n);
128 uiTopology.add(member);
129 return member;
130 }
131
132 private void updateClusterMember(UiClusterMember member) {
133 ControllerNode.State state = services.cluster().getState(member.id());
134 member.setState(state);
Simon Huntc0f20c12016-05-09 09:30:20 -0700135 }
136
137 private void loadClusterMembers() {
138 for (ControllerNode n : services.cluster().getNodes()) {
139 UiClusterMember member = addNewClusterMember(n);
140 updateClusterMember(member);
141 }
142 }
143
144 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700145 void addOrUpdateClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700146 NodeId id = cnode.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800147 String memo = MEMO_UPDATED;
Simon Hunt642bc452016-05-04 19:34:45 -0700148 UiClusterMember member = uiTopology.findClusterMember(id);
149 if (member == null) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700150 member = addNewClusterMember(cnode);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800151 memo = MEMO_ADDED;
Simon Hunt338a3b42016-04-14 09:43:52 -0700152 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700153 updateClusterMember(member);
Simon Hunt338a3b42016-04-14 09:43:52 -0700154
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800155 postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700156 }
157
Simon Huntc0f20c12016-05-09 09:30:20 -0700158 // package private for unit test access
159 UiClusterMember accessClusterMember(NodeId id) {
160 return uiTopology.findClusterMember(id);
161 }
162
163 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700164 void removeClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700165 NodeId id = cnode.id();
166 UiClusterMember member = uiTopology.findClusterMember(id);
167 if (member != null) {
168 uiTopology.remove(member);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800169 postEvent(CLUSTER_MEMBER_REMOVED, member, MEMO_REMOVED);
Simon Hunt642bc452016-05-04 19:34:45 -0700170 } else {
Simon Huntc0f20c12016-05-09 09:30:20 -0700171 log.warn(E_NO_ELEMENT, "cluster node", id);
Simon Hunt642bc452016-05-04 19:34:45 -0700172 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700173 }
174
Simon Huntd5b96732016-07-08 13:22:27 -0700175 List<UiClusterMember> getAllClusterMembers() {
176 return uiTopology.allClusterMembers();
177 }
178
Simon Huntc0f20c12016-05-09 09:30:20 -0700179
180 // === MASTERSHIP CHANGES
181
182 // invoked from UiSharedTopologyModel mastership listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700183 void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700184 // To think about:: do we need to store mastership info?
185 // or can we rely on looking it up live?
Simon Hunt338a3b42016-04-14 09:43:52 -0700186 // TODO: store the updated mastership information
187 // TODO: post event
188 }
189
Simon Huntb1ce2602016-07-23 14:04:31 -0700190 // === THE NULL REGION
191
192 UiRegion nullRegion() {
193 return uiTopology.nullRegion();
194 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700195
196 // === REGIONS
197
198 private UiRegion addNewRegion(Region r) {
199 UiRegion region = new UiRegion(uiTopology, r);
200 uiTopology.add(region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700201 log.debug("Region {} added to topology", region);
Simon Huntc0f20c12016-05-09 09:30:20 -0700202 return region;
203 }
204
205 private void updateRegion(UiRegion region) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700206 RegionId rid = region.id();
207 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500208 Set<HostId> hostIds = services.region().getRegionHosts(rid);
Simon Huntb1ce2602016-07-23 14:04:31 -0700209
210 // Make sure device objects refer to their region
211 deviceIds.forEach(d -> {
212 UiDevice dev = uiTopology.findDevice(d);
213 if (dev != null) {
214 dev.setRegionId(rid);
215 } else {
216 // if we don't have the UiDevice in the topology, what can we do?
217 log.warn("Region device {}, but we don't have UiDevice in topology", d);
218 }
219 });
220
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500221 hostIds.forEach(d -> {
222 UiHost host = uiTopology.findHost(d);
223 if (host != null) {
224 host.setRegionId(rid);
225 } else {
226 // if we don't have the UiDevice in the topology, what can we do?
227 log.warn("Region host {}, but we don't have UiHost in topology", d);
228 }
229 });
230
Simon Huntb1ce2602016-07-23 14:04:31 -0700231 // Make sure the region object refers to the devices
232 region.reconcileDevices(deviceIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500233 region.reconcileHosts(hostIds);
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700234
235 fixupContainmentHierarchy(region);
236 }
237
238 private void fixupContainmentHierarchy(UiRegion region) {
239 UiTopoLayoutService ls = services.layout();
240 RegionId regionId = region.id();
241
242 UiTopoLayout layout = ls.getLayout(regionId);
243 if (layout == null) {
244 // no layout backed by this region
245 log.warn("No layout backed by region {}", regionId);
246 return;
247 }
248
249 UiTopoLayoutId layoutId = layout.id();
250
251 if (!layout.isRoot()) {
252 UiTopoLayoutId parentId = layout.parent();
253 UiTopoLayout parentLayout = ls.getLayout(parentId);
254 RegionId parentRegionId = parentLayout.regionId();
255 region.setParent(parentRegionId);
256 }
257
258 Set<UiTopoLayout> kids = ls.getChildren(layoutId);
259 Set<RegionId> kidRegionIds = new HashSet<>(kids.size());
260 kids.forEach(k -> kidRegionIds.add(k.regionId()));
261 region.setChildren(kidRegionIds);
Simon Huntc0f20c12016-05-09 09:30:20 -0700262 }
263
264 private void loadRegions() {
265 for (Region r : services.region().getRegions()) {
266 UiRegion region = addNewRegion(r);
267 updateRegion(region);
268 }
269 }
270
271 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700272 void addOrUpdateRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700273 RegionId id = region.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800274 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700275 UiRegion uiRegion = uiTopology.findRegion(id);
276 if (uiRegion == null) {
277 uiRegion = addNewRegion(region);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800278 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700279 }
280 updateRegion(uiRegion);
281
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800282 postEvent(REGION_ADDED_OR_UPDATED, uiRegion, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700283 }
284
Simon Hunt58a0dd02016-05-17 11:54:23 -0700285 // package private for unit test access
286 UiRegion accessRegion(RegionId id) {
Simon Huntd5b96732016-07-08 13:22:27 -0700287 return id == null ? null : uiTopology.findRegion(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700288 }
289
Simon Huntc0f20c12016-05-09 09:30:20 -0700290 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700291 void removeRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700292 RegionId id = region.id();
293 UiRegion uiRegion = uiTopology.findRegion(id);
294 if (uiRegion != null) {
295 uiTopology.remove(uiRegion);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800296 postEvent(REGION_REMOVED, uiRegion, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700297 } else {
298 log.warn(E_NO_ELEMENT, "region", id);
299 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700300 }
301
Simon Hunt10973dd2016-08-01 15:50:35 -0700302 Set<UiRegion> getAllRegions() {
303 return uiTopology.allRegions();
304 }
305
Simon Huntc0f20c12016-05-09 09:30:20 -0700306
307 // === DEVICES
308
309 private UiDevice addNewDevice(Device d) {
310 UiDevice device = new UiDevice(uiTopology, d);
Simon Huntb1ce2602016-07-23 14:04:31 -0700311 updateDevice(device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700312 uiTopology.add(device);
Simon Huntb1ce2602016-07-23 14:04:31 -0700313 log.debug("Device {} added to topology", device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700314 return device;
315 }
316
Simon Huntb1ce2602016-07-23 14:04:31 -0700317 // make sure the UiDevice is tagged with the region it belongs to
Simon Huntc0f20c12016-05-09 09:30:20 -0700318 private void updateDevice(UiDevice device) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700319 Region r = services.region().getRegionForDevice(device.id());
320 RegionId rid = r == null ? UiRegion.NULL_ID : r.id();
321 device.setRegionId(rid);
Simon Huntc0f20c12016-05-09 09:30:20 -0700322 }
323
324 private void loadDevices() {
325 for (Device d : services.device().getDevices()) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700326 addNewDevice(d);
Simon Huntc0f20c12016-05-09 09:30:20 -0700327 }
328 }
329
330 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700331 void addOrUpdateDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700332 DeviceId id = device.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800333 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700334 UiDevice uiDevice = uiTopology.findDevice(id);
335 if (uiDevice == null) {
336 uiDevice = addNewDevice(device);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800337 memo = MEMO_ADDED;
Simon Huntb1ce2602016-07-23 14:04:31 -0700338 } else {
339 updateDevice(uiDevice);
Simon Huntc0f20c12016-05-09 09:30:20 -0700340 }
Simon Huntcda9c032016-04-11 10:32:54 -0700341
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800342 postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice, memo);
Simon Huntcda9c032016-04-11 10:32:54 -0700343 }
344
Simon Hunt58a0dd02016-05-17 11:54:23 -0700345 // package private for unit test access
346 UiDevice accessDevice(DeviceId id) {
347 return uiTopology.findDevice(id);
348 }
349
Simon Huntc0f20c12016-05-09 09:30:20 -0700350 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700351 void removeDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700352 DeviceId id = device.id();
353 UiDevice uiDevice = uiTopology.findDevice(id);
354 if (uiDevice != null) {
355 uiTopology.remove(uiDevice);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800356 postEvent(DEVICE_REMOVED, uiDevice, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700357 } else {
358 log.warn(E_NO_ELEMENT, "device", id);
359 }
Simon Huntcda9c032016-04-11 10:32:54 -0700360 }
361
Simon Hunt4854f3d2016-08-02 18:13:15 -0700362 Set<UiDevice> getAllDevices() {
363 return uiTopology.allDevices();
364 }
365
Simon Huntc0f20c12016-05-09 09:30:20 -0700366
Simon Huntc13082f2016-08-03 21:20:23 -0700367 // === LINKS ===
Simon Huntc0f20c12016-05-09 09:30:20 -0700368
Simon Huntc13082f2016-08-03 21:20:23 -0700369 private UiDeviceLink addNewDeviceLink(UiLinkId id) {
370 UiDeviceLink uiDeviceLink = new UiDeviceLink(uiTopology, id);
371 uiTopology.add(uiDeviceLink);
372 return uiDeviceLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700373 }
374
Simon Huntc13082f2016-08-03 21:20:23 -0700375 private UiEdgeLink addNewEdgeLink(UiLinkId id) {
376 UiEdgeLink uiEdgeLink = new UiEdgeLink(uiTopology, id);
377 uiTopology.add(uiEdgeLink);
378 return uiEdgeLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700379 }
380
Simon Huntc13082f2016-08-03 21:20:23 -0700381 private void updateDeviceLink(UiDeviceLink uiDeviceLink, Link link) {
382 uiDeviceLink.attachBackingLink(link);
383 }
384
385 private void loadDeviceLinks() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700386 for (Link link : services.link().getLinks()) {
387 UiLinkId id = uiLinkId(link);
388
Simon Huntc13082f2016-08-03 21:20:23 -0700389 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
390 if (uiDeviceLink == null) {
391 uiDeviceLink = addNewDeviceLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700392 }
Simon Huntc13082f2016-08-03 21:20:23 -0700393 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700394 }
395 }
396
397 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700398 void addOrUpdateDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700399 UiLinkId id = uiLinkId(link);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800400 String memo = MEMO_UPDATED;
Simon Huntc13082f2016-08-03 21:20:23 -0700401 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
402 if (uiDeviceLink == null) {
403 uiDeviceLink = addNewDeviceLink(id);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800404 memo = MEMO_ADDED;
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
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800408 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700409 }
410
Simon Hunt58a0dd02016-05-17 11:54:23 -0700411 // package private for unit test access
Simon Huntc13082f2016-08-03 21:20:23 -0700412 UiDeviceLink accessDeviceLink(UiLinkId id) {
413 return uiTopology.findDeviceLink(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700414 }
415
Simon Huntc0f20c12016-05-09 09:30:20 -0700416 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700417 void removeDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700418 UiLinkId id = uiLinkId(link);
Simon Huntc13082f2016-08-03 21:20:23 -0700419 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
420 if (uiDeviceLink != null) {
421 boolean remaining = uiDeviceLink.detachBackingLink(link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700422 if (remaining) {
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800423 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, MEMO_UPDATED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700424 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700425 uiTopology.remove(uiDeviceLink);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800426 postEvent(LINK_REMOVED, uiDeviceLink, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700427 }
428 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700429 log.warn(E_NO_ELEMENT, "Device link", id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700430 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700431 }
432
Simon Huntc13082f2016-08-03 21:20:23 -0700433 Set<UiDeviceLink> getAllDeviceLinks() {
434 return uiTopology.allDeviceLinks();
Simon Hunt4854f3d2016-08-02 18:13:15 -0700435 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700436
437 // === HOSTS
438
Simon Hunt58a0dd02016-05-17 11:54:23 -0700439 private EdgeLink synthesizeLink(Host h) {
440 return createEdgeLink(h, true);
441 }
442
Simon Huntc0f20c12016-05-09 09:30:20 -0700443 private UiHost addNewHost(Host h) {
444 UiHost host = new UiHost(uiTopology, h);
445 uiTopology.add(host);
446
Simon Hunt58a0dd02016-05-17 11:54:23 -0700447 EdgeLink elink = synthesizeLink(h);
448 UiLinkId elinkId = uiLinkId(elink);
449 host.setEdgeLinkId(elinkId);
450
451 // add synthesized edge link to the topology
Simon Huntb7fd0802016-10-27 12:21:40 -0700452 addNewEdgeLink(elinkId);
Simon Huntc0f20c12016-05-09 09:30:20 -0700453
454 return host;
455 }
456
Simon Huntb7fd0802016-10-27 12:21:40 -0700457 private void insertNewUiEdgeLink(UiLinkId id) {
458 addNewEdgeLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700459 }
460
461 private void updateHost(UiHost uiHost, Host h) {
Simon Huntc13082f2016-08-03 21:20:23 -0700462 UiEdgeLink existing = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700463
Simon Huntb7fd0802016-10-27 12:21:40 -0700464 // TODO: review - do we need EdgeLink now that we are creating from id only?
Simon Hunt58a0dd02016-05-17 11:54:23 -0700465 EdgeLink currentElink = synthesizeLink(h);
466 UiLinkId currentElinkId = uiLinkId(currentElink);
467
468 if (existing != null) {
469 if (!currentElinkId.equals(existing.id())) {
470 // edge link has changed
Simon Huntb7fd0802016-10-27 12:21:40 -0700471 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700472 uiHost.setEdgeLinkId(currentElinkId);
473
474 uiTopology.remove(existing);
475 }
476
477 } else {
478 // no previously existing edge link
Simon Huntb7fd0802016-10-27 12:21:40 -0700479 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700480 uiHost.setEdgeLinkId(currentElinkId);
481
482 }
483
Simon Huntc0f20c12016-05-09 09:30:20 -0700484 HostLocation hloc = h.location();
485 uiHost.setLocation(hloc.deviceId(), hloc.port());
Simon Huntc0f20c12016-05-09 09:30:20 -0700486 }
487
488 private void loadHosts() {
489 for (Host h : services.host().getHosts()) {
490 UiHost host = addNewHost(h);
491 updateHost(host, h);
492 }
493 }
494
495 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700496 void addOrUpdateHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700497 HostId id = host.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800498 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700499 UiHost uiHost = uiTopology.findHost(id);
500 if (uiHost == null) {
501 uiHost = addNewHost(host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800502 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700503 }
504 updateHost(uiHost, host);
505
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800506 postEvent(HOST_ADDED_OR_UPDATED, uiHost, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700507 }
508
Simon Huntc0f20c12016-05-09 09:30:20 -0700509 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700510 void moveHost(Host host, Host prevHost) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700511 UiHost uiHost = uiTopology.findHost(prevHost.id());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700512 if (uiHost != null) {
513 updateHost(uiHost, host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800514 postEvent(HOST_MOVED, uiHost, MEMO_MOVED);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700515 } else {
516 log.warn(E_NO_ELEMENT, "host", prevHost.id());
517 }
518 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700519
Simon Hunt58a0dd02016-05-17 11:54:23 -0700520 // package private for unit test access
521 UiHost accessHost(HostId id) {
522 return uiTopology.findHost(id);
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 removeHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700527 HostId id = host.id();
528 UiHost uiHost = uiTopology.findHost(id);
529 if (uiHost != null) {
Simon Huntc13082f2016-08-03 21:20:23 -0700530 UiEdgeLink edgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700531 uiTopology.remove(edgeLink);
Simon Huntc0f20c12016-05-09 09:30:20 -0700532 uiTopology.remove(uiHost);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800533 postEvent(HOST_REMOVED, uiHost, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700534 } else {
535 log.warn(E_NO_ELEMENT, "host", id);
536 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700537 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700538
Simon Hunt4854f3d2016-08-02 18:13:15 -0700539 Set<UiHost> getAllHosts() {
540 return uiTopology.allHosts();
541 }
542
Simon Huntc0f20c12016-05-09 09:30:20 -0700543
Simon Huntc13082f2016-08-03 21:20:23 -0700544 // === SYNTHETIC LINKS
545
546 List<UiSynthLink> getSynthLinks(RegionId regionId) {
547 return uiTopology.findSynthLinks(regionId);
548 }
549
Simon Huntb1ce2602016-07-23 14:04:31 -0700550 /**
551 * Refreshes the internal state.
552 */
553 public void refresh() {
Simon Huntc13082f2016-08-03 21:20:23 -0700554 // fix up internal linkages to ensure they are correct
Simon Huntb1ce2602016-07-23 14:04:31 -0700555
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700556 // make sure regions reflect layout containment hierarchy
557 fixupContainmentHierarchy(uiTopology.nullRegion());
558 uiTopology.allRegions().forEach(this::fixupContainmentHierarchy);
559
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500560 // make sure devices and hosts are in the correct region
Simon Huntb1ce2602016-07-23 14:04:31 -0700561 Set<UiDevice> allDevices = uiTopology.allDevices();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500562 Set<UiHost> allHosts = uiTopology.allHosts();
Simon Huntb1ce2602016-07-23 14:04:31 -0700563
564 services.region().getRegions().forEach(r -> {
565 RegionId rid = r.id();
566 UiRegion region = uiTopology.findRegion(rid);
567 if (region != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500568 reconcileDevicesAndHostsWithRegion(allDevices, allHosts, rid, region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700569 } else {
570 log.warn("No UiRegion in topology for ID {}", rid);
571 }
572 });
573
574 // what is left over, must belong to the null-region
575 Set<DeviceId> leftOver = new HashSet<>(allDevices.size());
576 allDevices.forEach(d -> leftOver.add(d.id()));
577 uiTopology.nullRegion().reconcileDevices(leftOver);
Simon Huntc13082f2016-08-03 21:20:23 -0700578
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500579 Set<HostId> leftOverHosts = new HashSet<>(allHosts.size());
580 allHosts.forEach(h -> leftOverHosts.add(h.id()));
581 uiTopology.nullRegion().reconcileHosts(leftOverHosts);
582
Simon Huntc13082f2016-08-03 21:20:23 -0700583 // now that we have correct region hierarchy, and devices are in their
584 // respective regions, we can compute synthetic links for each region.
585 uiTopology.computeSynthLinks();
Simon Huntb1ce2602016-07-23 14:04:31 -0700586 }
587
Steven Burrows8f45ce22016-10-27 20:04:14 -0500588 private void reconcileDevicesAndHostsWithRegion(Set<UiDevice> allDevices,
589 Set<UiHost> allHosts,
590 RegionId rid,
591 UiRegion region) {
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500592 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows8f45ce22016-10-27 20:04:14 -0500593 Set<HostId> hostIds = new HashSet<>();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500594 region.reconcileDevices(deviceIds);
595
596 deviceIds.forEach(devId -> {
597 UiDevice dev = uiTopology.findDevice(devId);
598 if (dev != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500599 dev.setRegionId(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500600 allDevices.remove(dev);
601 } else {
602 log.warn("Region device ID {} but no UiDevice in topology",
603 devId);
604 }
Steven Burrows8f45ce22016-10-27 20:04:14 -0500605
606 Set<Host> hosts = services.host().getConnectedHosts(devId);
607 for (Host h : hosts) {
608 HostId hid = h.id();
609 hostIds.add(hid);
610 UiHost host = uiTopology.findHost(hid);
611
612 if (host != null) {
613 host.setRegionId(rid);
614 allHosts.remove(host);
615 } else {
616 log.warn("Region host ID {} but no UiHost in topology",
617 hid);
618 }
619 }
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500620 });
Steven Burrows8f45ce22016-10-27 20:04:14 -0500621
622 region.reconcileHosts(hostIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500623 }
624
Simon Huntc13082f2016-08-03 21:20:23 -0700625
Simon Huntc0f20c12016-05-09 09:30:20 -0700626 // === CACHE STATISTICS
627
Simon Hunt338a3b42016-04-14 09:43:52 -0700628 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700629 * Returns a detailed (multi-line) string showing the contents of the cache.
630 *
631 * @return detailed string
632 */
633 public String dumpString() {
634 return uiTopology.dumpString();
635 }
636
637 /**
Simon Hunt338a3b42016-04-14 09:43:52 -0700638 * Returns the number of members in the cluster.
639 *
640 * @return number of cluster members
641 */
642 public int clusterMemberCount() {
643 return uiTopology.clusterMemberCount();
644 }
645
646 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700647 * Returns the number of regions in the topology.
Simon Hunt338a3b42016-04-14 09:43:52 -0700648 *
649 * @return number of regions
650 */
651 public int regionCount() {
652 return uiTopology.regionCount();
653 }
Simon Hunt58a0dd02016-05-17 11:54:23 -0700654
655 /**
656 * Returns the number of devices in the topology.
657 *
658 * @return number of devices
659 */
660 public int deviceCount() {
661 return uiTopology.deviceCount();
662 }
663
664 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700665 * Returns the number of device links in the topology.
Simon Hunt58a0dd02016-05-17 11:54:23 -0700666 *
Simon Huntc13082f2016-08-03 21:20:23 -0700667 * @return number of device links
Simon Hunt58a0dd02016-05-17 11:54:23 -0700668 */
Simon Huntc13082f2016-08-03 21:20:23 -0700669 public int deviceLinkCount() {
670 return uiTopology.deviceLinkCount();
671 }
672
673 /**
674 * Returns the number of edge links in the topology.
675 *
676 * @return number of edge links
677 */
678 public int edgeLinkCount() {
679 return uiTopology.edgeLinkCount();
Simon Hunt58a0dd02016-05-17 11:54:23 -0700680 }
681
682 /**
683 * Returns the number of hosts in the topology.
684 *
685 * @return number of hosts
686 */
687 public int hostCount() {
688 return uiTopology.hostCount();
689 }
Simon Huntd5b96732016-07-08 13:22:27 -0700690
Simon Huntc13082f2016-08-03 21:20:23 -0700691 /**
692 * Returns the number of synthetic links in the topology.
693 *
694 * @return the number of synthetic links
695 */
696 public int synthLinkCount() {
697 return uiTopology.synthLinkCount();
698 }
Simon Huntcda9c032016-04-11 10:32:54 -0700699}