blob: ec076737b70b1d02a9e173214b8bf0b4ba32ca8f [file] [log] [blame]
Simon Huntcda9c032016-04-11 10:32:54 -07001/*
Brian O'Connor0a4e6742016-09-15 23:03:10 -07002 * Copyright 2016-present Open Networking Laboratory
Simon Huntcda9c032016-04-11 10:32:54 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.ui.impl.topo.model;
18
Simon Hunt8eac4ae2017-01-20 12:56:45 -080019import com.fasterxml.jackson.databind.node.ObjectNode;
Simon Hunt23fb1352016-04-11 12:15:19 -070020import org.onosproject.cluster.ControllerNode;
Simon Hunt642bc452016-05-04 19:34:45 -070021import org.onosproject.cluster.NodeId;
Simon Hunt23fb1352016-04-11 12:15:19 -070022import org.onosproject.cluster.RoleInfo;
Simon Huntcda9c032016-04-11 10:32:54 -070023import org.onosproject.event.EventDispatcher;
24import org.onosproject.net.Device;
Simon Hunt23fb1352016-04-11 12:15:19 -070025import org.onosproject.net.DeviceId;
Simon Huntc0f20c12016-05-09 09:30:20 -070026import org.onosproject.net.EdgeLink;
Simon Hunt23fb1352016-04-11 12:15:19 -070027import org.onosproject.net.Host;
Simon Huntc0f20c12016-05-09 09:30:20 -070028import org.onosproject.net.HostId;
29import org.onosproject.net.HostLocation;
Simon Hunt23fb1352016-04-11 12:15:19 -070030import org.onosproject.net.Link;
31import org.onosproject.net.region.Region;
Simon Huntc0f20c12016-05-09 09:30:20 -070032import org.onosproject.net.region.RegionId;
Simon Hunt4f4ffc32016-08-03 18:30:47 -070033import org.onosproject.ui.UiTopoLayoutService;
Simon Hunt8eac4ae2017-01-20 12:56:45 -080034import org.onosproject.ui.impl.topo.Topo2Jsonifier;
Simon Hunt642bc452016-05-04 19:34:45 -070035import org.onosproject.ui.model.ServiceBundle;
Simon Hunt338a3b42016-04-14 09:43:52 -070036import org.onosproject.ui.model.topo.UiClusterMember;
Simon Huntcda9c032016-04-11 10:32:54 -070037import org.onosproject.ui.model.topo.UiDevice;
Simon Huntc13082f2016-08-03 21:20:23 -070038import org.onosproject.ui.model.topo.UiDeviceLink;
39import org.onosproject.ui.model.topo.UiEdgeLink;
Simon Huntc0f20c12016-05-09 09:30:20 -070040import org.onosproject.ui.model.topo.UiElement;
41import org.onosproject.ui.model.topo.UiHost;
Simon Huntc0f20c12016-05-09 09:30:20 -070042import org.onosproject.ui.model.topo.UiLinkId;
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -070043import org.onosproject.ui.model.topo.UiModelEvent;
Simon Huntc0f20c12016-05-09 09:30:20 -070044import org.onosproject.ui.model.topo.UiRegion;
Simon Huntc13082f2016-08-03 21:20:23 -070045import org.onosproject.ui.model.topo.UiSynthLink;
Simon Hunt4f4ffc32016-08-03 18:30:47 -070046import org.onosproject.ui.model.topo.UiTopoLayout;
47import org.onosproject.ui.model.topo.UiTopoLayoutId;
Simon Hunt23fb1352016-04-11 12:15:19 -070048import org.onosproject.ui.model.topo.UiTopology;
Simon Hunt642bc452016-05-04 19:34:45 -070049import org.slf4j.Logger;
50import org.slf4j.LoggerFactory;
Simon Hunt23fb1352016-04-11 12:15:19 -070051
Simon Huntb1ce2602016-07-23 14:04:31 -070052import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070053import java.util.List;
Simon Huntc0f20c12016-05-09 09:30:20 -070054import java.util.Set;
55
56import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -070057import static org.onosproject.ui.model.topo.UiModelEvent.Type.CLUSTER_MEMBER_ADDED_OR_UPDATED;
58import static org.onosproject.ui.model.topo.UiModelEvent.Type.CLUSTER_MEMBER_REMOVED;
59import static org.onosproject.ui.model.topo.UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED;
60import static org.onosproject.ui.model.topo.UiModelEvent.Type.DEVICE_REMOVED;
61import static org.onosproject.ui.model.topo.UiModelEvent.Type.HOST_ADDED_OR_UPDATED;
62import static org.onosproject.ui.model.topo.UiModelEvent.Type.HOST_MOVED;
63import static org.onosproject.ui.model.topo.UiModelEvent.Type.HOST_REMOVED;
64import static org.onosproject.ui.model.topo.UiModelEvent.Type.LINK_ADDED_OR_UPDATED;
65import static org.onosproject.ui.model.topo.UiModelEvent.Type.LINK_REMOVED;
66import static org.onosproject.ui.model.topo.UiModelEvent.Type.REGION_ADDED_OR_UPDATED;
67import static org.onosproject.ui.model.topo.UiModelEvent.Type.REGION_REMOVED;
Simon Huntc0f20c12016-05-09 09:30:20 -070068import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId;
Simon Huntcda9c032016-04-11 10:32:54 -070069
70/**
71 * UI Topology Model cache.
72 */
73class ModelCache {
74
Simon Huntc0f20c12016-05-09 09:30:20 -070075 private static final String E_NO_ELEMENT = "Tried to remove non-member {}: {}";
Simon Hunt8eac4ae2017-01-20 12:56:45 -080076 private static final String MEMO_ADDED = "added";
77 private static final String MEMO_UPDATED = "updated";
78 private static final String MEMO_REMOVED = "removed";
79 private static final String MEMO_MOVED = "moved";
Simon Huntc0f20c12016-05-09 09:30:20 -070080
Simon Hunt642bc452016-05-04 19:34:45 -070081 private static final Logger log = LoggerFactory.getLogger(ModelCache.class);
82
Simon Huntc4ca7102017-04-08 22:28:04 -070083 // TODO: add NetworkConfigService to service bundle
84// private final NetworkConfigService cfgService =
85// DefaultServiceDirectory.getService(NetworkConfigService.class);
86
Simon Hunt642bc452016-05-04 19:34:45 -070087 private final ServiceBundle services;
Simon Huntcda9c032016-04-11 10:32:54 -070088 private final EventDispatcher dispatcher;
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070089 private final UiTopology uiTopology;
Simon Huntcda9c032016-04-11 10:32:54 -070090
Simon Hunt8eac4ae2017-01-20 12:56:45 -080091 private Topo2Jsonifier t2json;
92
Simon Hunt642bc452016-05-04 19:34:45 -070093 ModelCache(ServiceBundle services, EventDispatcher eventDispatcher) {
94 this.services = services;
Simon Huntcda9c032016-04-11 10:32:54 -070095 this.dispatcher = eventDispatcher;
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070096 uiTopology = new UiTopology(services);
Simon Huntcda9c032016-04-11 10:32:54 -070097 }
98
Simon Hunt338a3b42016-04-14 09:43:52 -070099 @Override
100 public String toString() {
101 return "ModelCache{" + uiTopology + "}";
102 }
103
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800104 private void postEvent(UiModelEvent.Type type, UiElement subject, String memo) {
105 ObjectNode data = t2json != null ? t2json.jsonUiElement(subject) : null;
106 dispatcher.post(new UiModelEvent(type, subject, data, memo));
107 }
108
109 void injectJsonifier(Topo2Jsonifier t2json) {
110 this.t2json = t2json;
Simon Huntc0f20c12016-05-09 09:30:20 -0700111 }
112
Simon Huntcda9c032016-04-11 10:32:54 -0700113 void clear() {
Simon Hunt23fb1352016-04-11 12:15:19 -0700114 uiTopology.clear();
Simon Huntcda9c032016-04-11 10:32:54 -0700115 }
116
117 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700118 * Create our internal model of the global topology. An assumption we are
119 * making is that the topology is empty to start.
Simon Huntcda9c032016-04-11 10:32:54 -0700120 */
121 void load() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700122 loadClusterMembers();
123 loadRegions();
124 loadDevices();
Simon Huntc13082f2016-08-03 21:20:23 -0700125 loadDeviceLinks();
Simon Huntc0f20c12016-05-09 09:30:20 -0700126 loadHosts();
Simon Huntcda9c032016-04-11 10:32:54 -0700127 }
128
129
Simon Huntc0f20c12016-05-09 09:30:20 -0700130 // === CLUSTER MEMBERS
131
132 private UiClusterMember addNewClusterMember(ControllerNode n) {
133 UiClusterMember member = new UiClusterMember(uiTopology, n);
134 uiTopology.add(member);
135 return member;
136 }
137
138 private void updateClusterMember(UiClusterMember member) {
139 ControllerNode.State state = services.cluster().getState(member.id());
140 member.setState(state);
Simon Huntc0f20c12016-05-09 09:30:20 -0700141 }
142
143 private void loadClusterMembers() {
144 for (ControllerNode n : services.cluster().getNodes()) {
145 UiClusterMember member = addNewClusterMember(n);
146 updateClusterMember(member);
147 }
148 }
149
150 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700151 void addOrUpdateClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700152 NodeId id = cnode.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800153 String memo = MEMO_UPDATED;
Simon Hunt642bc452016-05-04 19:34:45 -0700154 UiClusterMember member = uiTopology.findClusterMember(id);
155 if (member == null) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700156 member = addNewClusterMember(cnode);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800157 memo = MEMO_ADDED;
Simon Hunt338a3b42016-04-14 09:43:52 -0700158 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700159 updateClusterMember(member);
Simon Hunt338a3b42016-04-14 09:43:52 -0700160
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800161 postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700162 }
163
Simon Huntc0f20c12016-05-09 09:30:20 -0700164 // package private for unit test access
165 UiClusterMember accessClusterMember(NodeId id) {
166 return uiTopology.findClusterMember(id);
167 }
168
169 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700170 void removeClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700171 NodeId id = cnode.id();
172 UiClusterMember member = uiTopology.findClusterMember(id);
173 if (member != null) {
174 uiTopology.remove(member);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800175 postEvent(CLUSTER_MEMBER_REMOVED, member, MEMO_REMOVED);
Simon Hunt642bc452016-05-04 19:34:45 -0700176 } else {
Simon Huntc0f20c12016-05-09 09:30:20 -0700177 log.warn(E_NO_ELEMENT, "cluster node", id);
Simon Hunt642bc452016-05-04 19:34:45 -0700178 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700179 }
180
Simon Huntd5b96732016-07-08 13:22:27 -0700181 List<UiClusterMember> getAllClusterMembers() {
182 return uiTopology.allClusterMembers();
183 }
184
Simon Huntc0f20c12016-05-09 09:30:20 -0700185
186 // === MASTERSHIP CHANGES
187
188 // invoked from UiSharedTopologyModel mastership listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700189 void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700190 // To think about:: do we need to store mastership info?
191 // or can we rely on looking it up live?
Simon Hunt338a3b42016-04-14 09:43:52 -0700192 // TODO: store the updated mastership information
193 // TODO: post event
194 }
195
Simon Huntb1ce2602016-07-23 14:04:31 -0700196 // === THE NULL REGION
197
198 UiRegion nullRegion() {
199 return uiTopology.nullRegion();
200 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700201
202 // === REGIONS
203
204 private UiRegion addNewRegion(Region r) {
205 UiRegion region = new UiRegion(uiTopology, r);
206 uiTopology.add(region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700207 log.debug("Region {} added to topology", region);
Simon Huntc0f20c12016-05-09 09:30:20 -0700208 return region;
209 }
210
211 private void updateRegion(UiRegion region) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700212 RegionId rid = region.id();
213 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500214 Set<HostId> hostIds = services.region().getRegionHosts(rid);
Simon Huntb1ce2602016-07-23 14:04:31 -0700215
216 // Make sure device objects refer to their region
217 deviceIds.forEach(d -> {
218 UiDevice dev = uiTopology.findDevice(d);
219 if (dev != null) {
220 dev.setRegionId(rid);
221 } else {
222 // if we don't have the UiDevice in the topology, what can we do?
223 log.warn("Region device {}, but we don't have UiDevice in topology", d);
224 }
225 });
226
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500227 hostIds.forEach(d -> {
228 UiHost host = uiTopology.findHost(d);
229 if (host != null) {
230 host.setRegionId(rid);
231 } else {
232 // if we don't have the UiDevice in the topology, what can we do?
233 log.warn("Region host {}, but we don't have UiHost in topology", d);
234 }
235 });
236
Simon Huntb1ce2602016-07-23 14:04:31 -0700237 // Make sure the region object refers to the devices
238 region.reconcileDevices(deviceIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500239 region.reconcileHosts(hostIds);
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700240
241 fixupContainmentHierarchy(region);
242 }
243
244 private void fixupContainmentHierarchy(UiRegion region) {
245 UiTopoLayoutService ls = services.layout();
246 RegionId regionId = region.id();
247
248 UiTopoLayout layout = ls.getLayout(regionId);
249 if (layout == null) {
250 // no layout backed by this region
251 log.warn("No layout backed by region {}", regionId);
252 return;
253 }
254
255 UiTopoLayoutId layoutId = layout.id();
256
257 if (!layout.isRoot()) {
258 UiTopoLayoutId parentId = layout.parent();
259 UiTopoLayout parentLayout = ls.getLayout(parentId);
260 RegionId parentRegionId = parentLayout.regionId();
261 region.setParent(parentRegionId);
262 }
263
264 Set<UiTopoLayout> kids = ls.getChildren(layoutId);
265 Set<RegionId> kidRegionIds = new HashSet<>(kids.size());
266 kids.forEach(k -> kidRegionIds.add(k.regionId()));
267 region.setChildren(kidRegionIds);
Simon Huntc0f20c12016-05-09 09:30:20 -0700268 }
269
270 private void loadRegions() {
271 for (Region r : services.region().getRegions()) {
272 UiRegion region = addNewRegion(r);
273 updateRegion(region);
274 }
275 }
276
277 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700278 void addOrUpdateRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700279 RegionId id = region.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800280 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700281 UiRegion uiRegion = uiTopology.findRegion(id);
282 if (uiRegion == null) {
283 uiRegion = addNewRegion(region);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800284 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700285 }
286 updateRegion(uiRegion);
287
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800288 postEvent(REGION_ADDED_OR_UPDATED, uiRegion, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700289 }
290
Simon Hunt58a0dd02016-05-17 11:54:23 -0700291 // package private for unit test access
292 UiRegion accessRegion(RegionId id) {
Simon Huntd5b96732016-07-08 13:22:27 -0700293 return id == null ? null : uiTopology.findRegion(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700294 }
295
Simon Huntc0f20c12016-05-09 09:30:20 -0700296 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700297 void removeRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700298 RegionId id = region.id();
299 UiRegion uiRegion = uiTopology.findRegion(id);
300 if (uiRegion != null) {
301 uiTopology.remove(uiRegion);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800302 postEvent(REGION_REMOVED, uiRegion, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700303 } else {
304 log.warn(E_NO_ELEMENT, "region", id);
305 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700306 }
307
Simon Hunt10973dd2016-08-01 15:50:35 -0700308 Set<UiRegion> getAllRegions() {
309 return uiTopology.allRegions();
310 }
311
Simon Huntc0f20c12016-05-09 09:30:20 -0700312
313 // === DEVICES
314
315 private UiDevice addNewDevice(Device d) {
316 UiDevice device = new UiDevice(uiTopology, d);
Simon Huntb1ce2602016-07-23 14:04:31 -0700317 updateDevice(device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700318 uiTopology.add(device);
Simon Huntb1ce2602016-07-23 14:04:31 -0700319 log.debug("Device {} added to topology", device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700320 return device;
321 }
322
Simon Huntb1ce2602016-07-23 14:04:31 -0700323 // make sure the UiDevice is tagged with the region it belongs to
Simon Huntc0f20c12016-05-09 09:30:20 -0700324 private void updateDevice(UiDevice device) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700325 Region r = services.region().getRegionForDevice(device.id());
326 RegionId rid = r == null ? UiRegion.NULL_ID : r.id();
327 device.setRegionId(rid);
Simon Huntc0f20c12016-05-09 09:30:20 -0700328 }
329
330 private void loadDevices() {
331 for (Device d : services.device().getDevices()) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700332 addNewDevice(d);
Simon Huntc0f20c12016-05-09 09:30:20 -0700333 }
334 }
335
336 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700337 void addOrUpdateDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700338 DeviceId id = device.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800339 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700340 UiDevice uiDevice = uiTopology.findDevice(id);
341 if (uiDevice == null) {
342 uiDevice = addNewDevice(device);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800343 memo = MEMO_ADDED;
Simon Huntb1ce2602016-07-23 14:04:31 -0700344 } else {
345 updateDevice(uiDevice);
Simon Huntc0f20c12016-05-09 09:30:20 -0700346 }
Simon Huntcda9c032016-04-11 10:32:54 -0700347
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800348 postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice, memo);
Simon Huntcda9c032016-04-11 10:32:54 -0700349 }
350
Simon Hunt58a0dd02016-05-17 11:54:23 -0700351 // package private for unit test access
352 UiDevice accessDevice(DeviceId id) {
353 return uiTopology.findDevice(id);
354 }
355
Simon Huntc0f20c12016-05-09 09:30:20 -0700356 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700357 void removeDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700358 DeviceId id = device.id();
359 UiDevice uiDevice = uiTopology.findDevice(id);
360 if (uiDevice != null) {
361 uiTopology.remove(uiDevice);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800362 postEvent(DEVICE_REMOVED, uiDevice, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700363 } else {
364 log.warn(E_NO_ELEMENT, "device", id);
365 }
Simon Huntcda9c032016-04-11 10:32:54 -0700366 }
367
Simon Hunt4854f3d2016-08-02 18:13:15 -0700368 Set<UiDevice> getAllDevices() {
369 return uiTopology.allDevices();
370 }
371
Simon Huntc0f20c12016-05-09 09:30:20 -0700372
Simon Huntc13082f2016-08-03 21:20:23 -0700373 // === LINKS ===
Simon Huntc0f20c12016-05-09 09:30:20 -0700374
Simon Huntc13082f2016-08-03 21:20:23 -0700375 private UiDeviceLink addNewDeviceLink(UiLinkId id) {
376 UiDeviceLink uiDeviceLink = new UiDeviceLink(uiTopology, id);
377 uiTopology.add(uiDeviceLink);
378 return uiDeviceLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700379 }
380
Simon Huntc13082f2016-08-03 21:20:23 -0700381 private UiEdgeLink addNewEdgeLink(UiLinkId id) {
382 UiEdgeLink uiEdgeLink = new UiEdgeLink(uiTopology, id);
383 uiTopology.add(uiEdgeLink);
384 return uiEdgeLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700385 }
386
Simon Huntc13082f2016-08-03 21:20:23 -0700387 private void updateDeviceLink(UiDeviceLink uiDeviceLink, Link link) {
388 uiDeviceLink.attachBackingLink(link);
389 }
390
391 private void loadDeviceLinks() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700392 for (Link link : services.link().getLinks()) {
393 UiLinkId id = uiLinkId(link);
394
Simon Huntc13082f2016-08-03 21:20:23 -0700395 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
396 if (uiDeviceLink == null) {
397 uiDeviceLink = addNewDeviceLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700398 }
Simon Huntc13082f2016-08-03 21:20:23 -0700399 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700400 }
401 }
402
403 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700404 void addOrUpdateDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700405 UiLinkId id = uiLinkId(link);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800406 String memo = MEMO_UPDATED;
Simon Huntc13082f2016-08-03 21:20:23 -0700407 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
408 if (uiDeviceLink == null) {
409 uiDeviceLink = addNewDeviceLink(id);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800410 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700411 }
Simon Huntc13082f2016-08-03 21:20:23 -0700412 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700413
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800414 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700415 }
416
Simon Hunt58a0dd02016-05-17 11:54:23 -0700417 // package private for unit test access
Simon Huntc13082f2016-08-03 21:20:23 -0700418 UiDeviceLink accessDeviceLink(UiLinkId id) {
419 return uiTopology.findDeviceLink(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700420 }
421
Simon Huntc0f20c12016-05-09 09:30:20 -0700422 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700423 void removeDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700424 UiLinkId id = uiLinkId(link);
Simon Huntc13082f2016-08-03 21:20:23 -0700425 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
426 if (uiDeviceLink != null) {
427 boolean remaining = uiDeviceLink.detachBackingLink(link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700428 if (remaining) {
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800429 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, MEMO_UPDATED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700430 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700431 uiTopology.remove(uiDeviceLink);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800432 postEvent(LINK_REMOVED, uiDeviceLink, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700433 }
434 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700435 log.warn(E_NO_ELEMENT, "Device link", id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700436 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700437 }
438
Simon Huntc13082f2016-08-03 21:20:23 -0700439 Set<UiDeviceLink> getAllDeviceLinks() {
440 return uiTopology.allDeviceLinks();
Simon Hunt4854f3d2016-08-02 18:13:15 -0700441 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700442
443 // === HOSTS
444
Simon Hunt58a0dd02016-05-17 11:54:23 -0700445 private EdgeLink synthesizeLink(Host h) {
446 return createEdgeLink(h, true);
447 }
448
Simon Huntc0f20c12016-05-09 09:30:20 -0700449 private UiHost addNewHost(Host h) {
450 UiHost host = new UiHost(uiTopology, h);
451 uiTopology.add(host);
452
Simon Hunt58a0dd02016-05-17 11:54:23 -0700453 EdgeLink elink = synthesizeLink(h);
454 UiLinkId elinkId = uiLinkId(elink);
455 host.setEdgeLinkId(elinkId);
456
457 // add synthesized edge link to the topology
Simon Huntb7fd0802016-10-27 12:21:40 -0700458 addNewEdgeLink(elinkId);
Simon Huntc0f20c12016-05-09 09:30:20 -0700459
460 return host;
461 }
462
Simon Huntb7fd0802016-10-27 12:21:40 -0700463 private void insertNewUiEdgeLink(UiLinkId id) {
464 addNewEdgeLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700465 }
466
467 private void updateHost(UiHost uiHost, Host h) {
Simon Huntc13082f2016-08-03 21:20:23 -0700468 UiEdgeLink existing = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700469
Simon Huntb7fd0802016-10-27 12:21:40 -0700470 // TODO: review - do we need EdgeLink now that we are creating from id only?
Simon Hunt58a0dd02016-05-17 11:54:23 -0700471 EdgeLink currentElink = synthesizeLink(h);
472 UiLinkId currentElinkId = uiLinkId(currentElink);
473
474 if (existing != null) {
475 if (!currentElinkId.equals(existing.id())) {
476 // edge link has changed
Simon Huntb7fd0802016-10-27 12:21:40 -0700477 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700478 uiHost.setEdgeLinkId(currentElinkId);
479
480 uiTopology.remove(existing);
481 }
482
483 } else {
484 // no previously existing edge link
Simon Huntb7fd0802016-10-27 12:21:40 -0700485 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700486 uiHost.setEdgeLinkId(currentElinkId);
487
488 }
489
Simon Huntc0f20c12016-05-09 09:30:20 -0700490 HostLocation hloc = h.location();
491 uiHost.setLocation(hloc.deviceId(), hloc.port());
Simon Huntc0f20c12016-05-09 09:30:20 -0700492 }
493
494 private void loadHosts() {
495 for (Host h : services.host().getHosts()) {
496 UiHost host = addNewHost(h);
497 updateHost(host, h);
498 }
499 }
500
501 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700502 void addOrUpdateHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700503 HostId id = host.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800504 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700505 UiHost uiHost = uiTopology.findHost(id);
506 if (uiHost == null) {
507 uiHost = addNewHost(host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800508 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700509 }
510 updateHost(uiHost, host);
511
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800512 postEvent(HOST_ADDED_OR_UPDATED, uiHost, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700513 }
514
Simon Huntc0f20c12016-05-09 09:30:20 -0700515 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700516 void moveHost(Host host, Host prevHost) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700517 UiHost uiHost = uiTopology.findHost(prevHost.id());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700518 if (uiHost != null) {
519 updateHost(uiHost, host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800520 postEvent(HOST_MOVED, uiHost, MEMO_MOVED);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700521 } else {
522 log.warn(E_NO_ELEMENT, "host", prevHost.id());
523 }
524 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700525
Simon Hunt58a0dd02016-05-17 11:54:23 -0700526 // package private for unit test access
527 UiHost accessHost(HostId id) {
528 return uiTopology.findHost(id);
Simon Hunt23fb1352016-04-11 12:15:19 -0700529 }
530
Simon Huntc0f20c12016-05-09 09:30:20 -0700531 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700532 void removeHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700533 HostId id = host.id();
534 UiHost uiHost = uiTopology.findHost(id);
535 if (uiHost != null) {
Simon Huntc13082f2016-08-03 21:20:23 -0700536 UiEdgeLink edgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700537 uiTopology.remove(edgeLink);
Simon Huntc0f20c12016-05-09 09:30:20 -0700538 uiTopology.remove(uiHost);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800539 postEvent(HOST_REMOVED, uiHost, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700540 } else {
541 log.warn(E_NO_ELEMENT, "host", id);
542 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700543 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700544
Simon Hunt4854f3d2016-08-02 18:13:15 -0700545 Set<UiHost> getAllHosts() {
546 return uiTopology.allHosts();
547 }
548
Simon Huntc0f20c12016-05-09 09:30:20 -0700549
Simon Huntc13082f2016-08-03 21:20:23 -0700550 // === SYNTHETIC LINKS
551
552 List<UiSynthLink> getSynthLinks(RegionId regionId) {
553 return uiTopology.findSynthLinks(regionId);
554 }
555
Simon Huntb1ce2602016-07-23 14:04:31 -0700556 /**
557 * Refreshes the internal state.
558 */
559 public void refresh() {
Simon Huntc13082f2016-08-03 21:20:23 -0700560 // fix up internal linkages to ensure they are correct
Simon Huntb1ce2602016-07-23 14:04:31 -0700561
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700562 // make sure regions reflect layout containment hierarchy
563 fixupContainmentHierarchy(uiTopology.nullRegion());
564 uiTopology.allRegions().forEach(this::fixupContainmentHierarchy);
565
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500566 // make sure devices and hosts are in the correct region
Simon Huntb1ce2602016-07-23 14:04:31 -0700567 Set<UiDevice> allDevices = uiTopology.allDevices();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500568 Set<UiHost> allHosts = uiTopology.allHosts();
Simon Huntb1ce2602016-07-23 14:04:31 -0700569
570 services.region().getRegions().forEach(r -> {
571 RegionId rid = r.id();
Simon Huntc4ca7102017-04-08 22:28:04 -0700572
573// BasicRegionConfig rcfg = cfgService.getConfig(rid, BasicRegionConfig.class);
574// services.netcfg() ...
575 // TODO: figure out how to include peer-location data in UiRegion instance
576
Simon Huntb1ce2602016-07-23 14:04:31 -0700577 UiRegion region = uiTopology.findRegion(rid);
578 if (region != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500579 reconcileDevicesAndHostsWithRegion(allDevices, allHosts, rid, region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700580 } else {
581 log.warn("No UiRegion in topology for ID {}", rid);
582 }
583 });
584
585 // what is left over, must belong to the null-region
586 Set<DeviceId> leftOver = new HashSet<>(allDevices.size());
587 allDevices.forEach(d -> leftOver.add(d.id()));
588 uiTopology.nullRegion().reconcileDevices(leftOver);
Simon Huntc13082f2016-08-03 21:20:23 -0700589
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500590 Set<HostId> leftOverHosts = new HashSet<>(allHosts.size());
591 allHosts.forEach(h -> leftOverHosts.add(h.id()));
592 uiTopology.nullRegion().reconcileHosts(leftOverHosts);
593
Simon Huntc13082f2016-08-03 21:20:23 -0700594 // now that we have correct region hierarchy, and devices are in their
595 // respective regions, we can compute synthetic links for each region.
596 uiTopology.computeSynthLinks();
Simon Huntb1ce2602016-07-23 14:04:31 -0700597 }
598
Steven Burrows8f45ce22016-10-27 20:04:14 -0500599 private void reconcileDevicesAndHostsWithRegion(Set<UiDevice> allDevices,
600 Set<UiHost> allHosts,
601 RegionId rid,
602 UiRegion region) {
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500603 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows8f45ce22016-10-27 20:04:14 -0500604 Set<HostId> hostIds = new HashSet<>();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500605 region.reconcileDevices(deviceIds);
606
607 deviceIds.forEach(devId -> {
608 UiDevice dev = uiTopology.findDevice(devId);
609 if (dev != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500610 dev.setRegionId(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500611 allDevices.remove(dev);
612 } else {
613 log.warn("Region device ID {} but no UiDevice in topology",
Simon Huntc4ca7102017-04-08 22:28:04 -0700614 devId);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500615 }
Steven Burrows8f45ce22016-10-27 20:04:14 -0500616
617 Set<Host> hosts = services.host().getConnectedHosts(devId);
618 for (Host h : hosts) {
619 HostId hid = h.id();
620 hostIds.add(hid);
621 UiHost host = uiTopology.findHost(hid);
622
623 if (host != null) {
624 host.setRegionId(rid);
625 allHosts.remove(host);
626 } else {
627 log.warn("Region host ID {} but no UiHost in topology",
Simon Huntc4ca7102017-04-08 22:28:04 -0700628 hid);
Steven Burrows8f45ce22016-10-27 20:04:14 -0500629 }
630 }
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500631 });
Steven Burrows8f45ce22016-10-27 20:04:14 -0500632
633 region.reconcileHosts(hostIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500634 }
635
Simon Huntc13082f2016-08-03 21:20:23 -0700636
Simon Huntc0f20c12016-05-09 09:30:20 -0700637 // === CACHE STATISTICS
638
Simon Hunt338a3b42016-04-14 09:43:52 -0700639 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700640 * Returns a detailed (multi-line) string showing the contents of the cache.
641 *
642 * @return detailed string
643 */
644 public String dumpString() {
645 return uiTopology.dumpString();
646 }
647
648 /**
Simon Hunt338a3b42016-04-14 09:43:52 -0700649 * Returns the number of members in the cluster.
650 *
651 * @return number of cluster members
652 */
653 public int clusterMemberCount() {
654 return uiTopology.clusterMemberCount();
655 }
656
657 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700658 * Returns the number of regions in the topology.
Simon Hunt338a3b42016-04-14 09:43:52 -0700659 *
660 * @return number of regions
661 */
662 public int regionCount() {
663 return uiTopology.regionCount();
664 }
Simon Hunt58a0dd02016-05-17 11:54:23 -0700665
666 /**
667 * Returns the number of devices in the topology.
668 *
669 * @return number of devices
670 */
671 public int deviceCount() {
672 return uiTopology.deviceCount();
673 }
674
675 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700676 * Returns the number of device links in the topology.
Simon Hunt58a0dd02016-05-17 11:54:23 -0700677 *
Simon Huntc13082f2016-08-03 21:20:23 -0700678 * @return number of device links
Simon Hunt58a0dd02016-05-17 11:54:23 -0700679 */
Simon Huntc13082f2016-08-03 21:20:23 -0700680 public int deviceLinkCount() {
681 return uiTopology.deviceLinkCount();
682 }
683
684 /**
685 * Returns the number of edge links in the topology.
686 *
687 * @return number of edge links
688 */
689 public int edgeLinkCount() {
690 return uiTopology.edgeLinkCount();
Simon Hunt58a0dd02016-05-17 11:54:23 -0700691 }
692
693 /**
694 * Returns the number of hosts in the topology.
695 *
696 * @return number of hosts
697 */
698 public int hostCount() {
699 return uiTopology.hostCount();
700 }
Simon Huntd5b96732016-07-08 13:22:27 -0700701
Simon Huntc13082f2016-08-03 21:20:23 -0700702 /**
703 * Returns the number of synthetic links in the topology.
704 *
705 * @return the number of synthetic links
706 */
707 public int synthLinkCount() {
708 return uiTopology.synthLinkCount();
709 }
Simon Huntcda9c032016-04-11 10:32:54 -0700710}