blob: 8259fb3bdb2e997a4193ceb2d72e9f1ac5633be3 [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 Hunt23fb1352016-04-11 12:15:19 -070019import org.onosproject.cluster.ControllerNode;
Simon Hunt642bc452016-05-04 19:34:45 -070020import org.onosproject.cluster.NodeId;
Simon Hunt23fb1352016-04-11 12:15:19 -070021import org.onosproject.cluster.RoleInfo;
Simon Huntcda9c032016-04-11 10:32:54 -070022import org.onosproject.event.EventDispatcher;
23import org.onosproject.net.Device;
Simon Hunt23fb1352016-04-11 12:15:19 -070024import org.onosproject.net.DeviceId;
Simon Huntc0f20c12016-05-09 09:30:20 -070025import org.onosproject.net.EdgeLink;
Simon Hunt23fb1352016-04-11 12:15:19 -070026import org.onosproject.net.Host;
Simon Huntc0f20c12016-05-09 09:30:20 -070027import org.onosproject.net.HostId;
28import org.onosproject.net.HostLocation;
Simon Hunt23fb1352016-04-11 12:15:19 -070029import org.onosproject.net.Link;
30import org.onosproject.net.region.Region;
Simon Huntc0f20c12016-05-09 09:30:20 -070031import org.onosproject.net.region.RegionId;
Simon Hunt4f4ffc32016-08-03 18:30:47 -070032import org.onosproject.ui.UiTopoLayoutService;
Simon Hunt642bc452016-05-04 19:34:45 -070033import org.onosproject.ui.model.ServiceBundle;
Simon Hunt338a3b42016-04-14 09:43:52 -070034import org.onosproject.ui.model.topo.UiClusterMember;
Simon Huntcda9c032016-04-11 10:32:54 -070035import org.onosproject.ui.model.topo.UiDevice;
Simon Huntc13082f2016-08-03 21:20:23 -070036import org.onosproject.ui.model.topo.UiDeviceLink;
37import org.onosproject.ui.model.topo.UiEdgeLink;
Simon Huntc0f20c12016-05-09 09:30:20 -070038import org.onosproject.ui.model.topo.UiElement;
39import org.onosproject.ui.model.topo.UiHost;
Simon Huntc0f20c12016-05-09 09:30:20 -070040import org.onosproject.ui.model.topo.UiLinkId;
41import org.onosproject.ui.model.topo.UiRegion;
Simon Huntc13082f2016-08-03 21:20:23 -070042import org.onosproject.ui.model.topo.UiSynthLink;
Simon Hunt4f4ffc32016-08-03 18:30:47 -070043import org.onosproject.ui.model.topo.UiTopoLayout;
44import org.onosproject.ui.model.topo.UiTopoLayoutId;
Simon Hunt23fb1352016-04-11 12:15:19 -070045import org.onosproject.ui.model.topo.UiTopology;
Simon Hunt642bc452016-05-04 19:34:45 -070046import org.slf4j.Logger;
47import org.slf4j.LoggerFactory;
Simon Hunt23fb1352016-04-11 12:15:19 -070048
Simon Huntb1ce2602016-07-23 14:04:31 -070049import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070050import java.util.List;
Simon Huntc0f20c12016-05-09 09:30:20 -070051import java.util.Set;
52
53import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Simon Hunt642bc452016-05-04 19:34:45 -070054import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_ADDED_OR_UPDATED;
55import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_REMOVED;
56import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED;
Simon Hunt23fb1352016-04-11 12:15:19 -070057import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_REMOVED;
Simon Huntc0f20c12016-05-09 09:30:20 -070058import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_ADDED_OR_UPDATED;
59import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_MOVED;
60import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_REMOVED;
61import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.LINK_ADDED_OR_UPDATED;
62import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.LINK_REMOVED;
63import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.REGION_ADDED_OR_UPDATED;
64import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.REGION_REMOVED;
65import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId;
Simon Huntcda9c032016-04-11 10:32:54 -070066
67/**
68 * UI Topology Model cache.
69 */
70class ModelCache {
71
Simon Huntc0f20c12016-05-09 09:30:20 -070072 private static final String E_NO_ELEMENT = "Tried to remove non-member {}: {}";
73
Simon Hunt642bc452016-05-04 19:34:45 -070074 private static final Logger log = LoggerFactory.getLogger(ModelCache.class);
75
76 private final ServiceBundle services;
Simon Huntcda9c032016-04-11 10:32:54 -070077 private final EventDispatcher dispatcher;
Simon Hunt23fb1352016-04-11 12:15:19 -070078 private final UiTopology uiTopology = new UiTopology();
Simon Huntcda9c032016-04-11 10:32:54 -070079
Simon Hunt642bc452016-05-04 19:34:45 -070080 ModelCache(ServiceBundle services, EventDispatcher eventDispatcher) {
81 this.services = services;
Simon Huntcda9c032016-04-11 10:32:54 -070082 this.dispatcher = eventDispatcher;
83 }
84
Simon Hunt338a3b42016-04-14 09:43:52 -070085 @Override
86 public String toString() {
87 return "ModelCache{" + uiTopology + "}";
88 }
89
Simon Huntc0f20c12016-05-09 09:30:20 -070090 private void postEvent(UiModelEvent.Type type, UiElement subject) {
91 dispatcher.post(new UiModelEvent(type, subject));
92 }
93
Simon Huntcda9c032016-04-11 10:32:54 -070094 void clear() {
Simon Hunt23fb1352016-04-11 12:15:19 -070095 uiTopology.clear();
Simon Huntcda9c032016-04-11 10:32:54 -070096 }
97
98 /**
Simon Huntc0f20c12016-05-09 09:30:20 -070099 * Create our internal model of the global topology. An assumption we are
100 * making is that the topology is empty to start.
Simon Huntcda9c032016-04-11 10:32:54 -0700101 */
102 void load() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700103 loadClusterMembers();
104 loadRegions();
105 loadDevices();
Simon Huntc13082f2016-08-03 21:20:23 -0700106 loadDeviceLinks();
Simon Huntc0f20c12016-05-09 09:30:20 -0700107 loadHosts();
Simon Huntcda9c032016-04-11 10:32:54 -0700108 }
109
110
Simon Huntc0f20c12016-05-09 09:30:20 -0700111 // === CLUSTER MEMBERS
112
113 private UiClusterMember addNewClusterMember(ControllerNode n) {
114 UiClusterMember member = new UiClusterMember(uiTopology, n);
115 uiTopology.add(member);
116 return member;
117 }
118
119 private void updateClusterMember(UiClusterMember member) {
120 ControllerNode.State state = services.cluster().getState(member.id());
121 member.setState(state);
122 member.setMastership(services.mastership().getDevicesOf(member.id()));
123 // NOTE: 'UI-attached' is session-based data, not global, so will
124 // be set elsewhere
125 }
126
127 private void loadClusterMembers() {
128 for (ControllerNode n : services.cluster().getNodes()) {
129 UiClusterMember member = addNewClusterMember(n);
130 updateClusterMember(member);
131 }
132 }
133
134 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700135 void addOrUpdateClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700136 NodeId id = cnode.id();
137 UiClusterMember member = uiTopology.findClusterMember(id);
138 if (member == null) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700139 member = addNewClusterMember(cnode);
Simon Hunt338a3b42016-04-14 09:43:52 -0700140 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700141 updateClusterMember(member);
Simon Hunt338a3b42016-04-14 09:43:52 -0700142
Simon Huntc0f20c12016-05-09 09:30:20 -0700143 postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member);
Simon Hunt338a3b42016-04-14 09:43:52 -0700144 }
145
Simon Huntc0f20c12016-05-09 09:30:20 -0700146 // package private for unit test access
147 UiClusterMember accessClusterMember(NodeId id) {
148 return uiTopology.findClusterMember(id);
149 }
150
151 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700152 void removeClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700153 NodeId id = cnode.id();
154 UiClusterMember member = uiTopology.findClusterMember(id);
155 if (member != null) {
156 uiTopology.remove(member);
Simon Huntc0f20c12016-05-09 09:30:20 -0700157 postEvent(CLUSTER_MEMBER_REMOVED, member);
Simon Hunt642bc452016-05-04 19:34:45 -0700158 } else {
Simon Huntc0f20c12016-05-09 09:30:20 -0700159 log.warn(E_NO_ELEMENT, "cluster node", id);
Simon Hunt642bc452016-05-04 19:34:45 -0700160 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700161 }
162
Simon Huntd5b96732016-07-08 13:22:27 -0700163 List<UiClusterMember> getAllClusterMembers() {
164 return uiTopology.allClusterMembers();
165 }
166
Simon Huntc0f20c12016-05-09 09:30:20 -0700167
168 // === MASTERSHIP CHANGES
169
170 // invoked from UiSharedTopologyModel mastership listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700171 void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700172 // To think about:: do we need to store mastership info?
173 // or can we rely on looking it up live?
Simon Hunt338a3b42016-04-14 09:43:52 -0700174 // TODO: store the updated mastership information
175 // TODO: post event
176 }
177
Simon Huntb1ce2602016-07-23 14:04:31 -0700178 // === THE NULL REGION
179
180 UiRegion nullRegion() {
181 return uiTopology.nullRegion();
182 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700183
184 // === REGIONS
185
186 private UiRegion addNewRegion(Region r) {
187 UiRegion region = new UiRegion(uiTopology, r);
188 uiTopology.add(region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700189 log.debug("Region {} added to topology", region);
Simon Huntc0f20c12016-05-09 09:30:20 -0700190 return region;
191 }
192
193 private void updateRegion(UiRegion region) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700194 RegionId rid = region.id();
195 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500196 Set<HostId> hostIds = services.region().getRegionHosts(rid);
Simon Huntb1ce2602016-07-23 14:04:31 -0700197
198 // Make sure device objects refer to their region
199 deviceIds.forEach(d -> {
200 UiDevice dev = uiTopology.findDevice(d);
201 if (dev != null) {
202 dev.setRegionId(rid);
203 } else {
204 // if we don't have the UiDevice in the topology, what can we do?
205 log.warn("Region device {}, but we don't have UiDevice in topology", d);
206 }
207 });
208
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500209 hostIds.forEach(d -> {
210 UiHost host = uiTopology.findHost(d);
211 if (host != null) {
212 host.setRegionId(rid);
213 } else {
214 // if we don't have the UiDevice in the topology, what can we do?
215 log.warn("Region host {}, but we don't have UiHost in topology", d);
216 }
217 });
218
Simon Huntb1ce2602016-07-23 14:04:31 -0700219 // Make sure the region object refers to the devices
220 region.reconcileDevices(deviceIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500221 region.reconcileHosts(hostIds);
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700222
223 fixupContainmentHierarchy(region);
224 }
225
226 private void fixupContainmentHierarchy(UiRegion region) {
227 UiTopoLayoutService ls = services.layout();
228 RegionId regionId = region.id();
229
230 UiTopoLayout layout = ls.getLayout(regionId);
231 if (layout == null) {
232 // no layout backed by this region
233 log.warn("No layout backed by region {}", regionId);
234 return;
235 }
236
237 UiTopoLayoutId layoutId = layout.id();
238
239 if (!layout.isRoot()) {
240 UiTopoLayoutId parentId = layout.parent();
241 UiTopoLayout parentLayout = ls.getLayout(parentId);
242 RegionId parentRegionId = parentLayout.regionId();
243 region.setParent(parentRegionId);
244 }
245
246 Set<UiTopoLayout> kids = ls.getChildren(layoutId);
247 Set<RegionId> kidRegionIds = new HashSet<>(kids.size());
248 kids.forEach(k -> kidRegionIds.add(k.regionId()));
249 region.setChildren(kidRegionIds);
Simon Huntc0f20c12016-05-09 09:30:20 -0700250 }
251
252 private void loadRegions() {
253 for (Region r : services.region().getRegions()) {
254 UiRegion region = addNewRegion(r);
255 updateRegion(region);
256 }
257 }
258
259 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700260 void addOrUpdateRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700261 RegionId id = region.id();
262 UiRegion uiRegion = uiTopology.findRegion(id);
263 if (uiRegion == null) {
264 uiRegion = addNewRegion(region);
265 }
266 updateRegion(uiRegion);
267
268 postEvent(REGION_ADDED_OR_UPDATED, uiRegion);
Simon Hunt338a3b42016-04-14 09:43:52 -0700269 }
270
Simon Hunt58a0dd02016-05-17 11:54:23 -0700271 // package private for unit test access
272 UiRegion accessRegion(RegionId id) {
Simon Huntd5b96732016-07-08 13:22:27 -0700273 return id == null ? null : uiTopology.findRegion(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700274 }
275
Simon Huntc0f20c12016-05-09 09:30:20 -0700276 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700277 void removeRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700278 RegionId id = region.id();
279 UiRegion uiRegion = uiTopology.findRegion(id);
280 if (uiRegion != null) {
281 uiTopology.remove(uiRegion);
282 postEvent(REGION_REMOVED, uiRegion);
283 } else {
284 log.warn(E_NO_ELEMENT, "region", id);
285 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700286 }
287
Simon Hunt10973dd2016-08-01 15:50:35 -0700288 Set<UiRegion> getAllRegions() {
289 return uiTopology.allRegions();
290 }
291
Simon Huntc0f20c12016-05-09 09:30:20 -0700292
293 // === DEVICES
294
295 private UiDevice addNewDevice(Device d) {
296 UiDevice device = new UiDevice(uiTopology, d);
Simon Huntb1ce2602016-07-23 14:04:31 -0700297 updateDevice(device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700298 uiTopology.add(device);
Simon Huntb1ce2602016-07-23 14:04:31 -0700299 log.debug("Device {} added to topology", device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700300 return device;
301 }
302
Simon Huntb1ce2602016-07-23 14:04:31 -0700303 // make sure the UiDevice is tagged with the region it belongs to
Simon Huntc0f20c12016-05-09 09:30:20 -0700304 private void updateDevice(UiDevice device) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700305 Region r = services.region().getRegionForDevice(device.id());
306 RegionId rid = r == null ? UiRegion.NULL_ID : r.id();
307 device.setRegionId(rid);
Simon Huntc0f20c12016-05-09 09:30:20 -0700308 }
309
310 private void loadDevices() {
311 for (Device d : services.device().getDevices()) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700312 addNewDevice(d);
Simon Huntc0f20c12016-05-09 09:30:20 -0700313 }
314 }
315
316 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700317 void addOrUpdateDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700318 DeviceId id = device.id();
319 UiDevice uiDevice = uiTopology.findDevice(id);
320 if (uiDevice == null) {
321 uiDevice = addNewDevice(device);
Simon Huntb1ce2602016-07-23 14:04:31 -0700322 } else {
323 updateDevice(uiDevice);
Simon Huntc0f20c12016-05-09 09:30:20 -0700324 }
Simon Huntcda9c032016-04-11 10:32:54 -0700325
Simon Huntc0f20c12016-05-09 09:30:20 -0700326 postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice);
Simon Huntcda9c032016-04-11 10:32:54 -0700327 }
328
Simon Hunt58a0dd02016-05-17 11:54:23 -0700329 // package private for unit test access
330 UiDevice accessDevice(DeviceId id) {
331 return uiTopology.findDevice(id);
332 }
333
Simon Huntc0f20c12016-05-09 09:30:20 -0700334 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700335 void removeDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700336 DeviceId id = device.id();
337 UiDevice uiDevice = uiTopology.findDevice(id);
338 if (uiDevice != null) {
339 uiTopology.remove(uiDevice);
340 postEvent(DEVICE_REMOVED, uiDevice);
341 } else {
342 log.warn(E_NO_ELEMENT, "device", id);
343 }
Simon Huntcda9c032016-04-11 10:32:54 -0700344 }
345
Simon Hunt4854f3d2016-08-02 18:13:15 -0700346 Set<UiDevice> getAllDevices() {
347 return uiTopology.allDevices();
348 }
349
Simon Huntc0f20c12016-05-09 09:30:20 -0700350
Simon Huntc13082f2016-08-03 21:20:23 -0700351 // === LINKS ===
Simon Huntc0f20c12016-05-09 09:30:20 -0700352
Simon Huntc13082f2016-08-03 21:20:23 -0700353 private UiDeviceLink addNewDeviceLink(UiLinkId id) {
354 UiDeviceLink uiDeviceLink = new UiDeviceLink(uiTopology, id);
355 uiTopology.add(uiDeviceLink);
356 return uiDeviceLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700357 }
358
Simon Huntc13082f2016-08-03 21:20:23 -0700359 private UiEdgeLink addNewEdgeLink(UiLinkId id) {
360 UiEdgeLink uiEdgeLink = new UiEdgeLink(uiTopology, id);
361 uiTopology.add(uiEdgeLink);
362 return uiEdgeLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700363 }
364
Simon Huntc13082f2016-08-03 21:20:23 -0700365 private void updateDeviceLink(UiDeviceLink uiDeviceLink, Link link) {
366 uiDeviceLink.attachBackingLink(link);
367 }
368
369 private void loadDeviceLinks() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700370 for (Link link : services.link().getLinks()) {
371 UiLinkId id = uiLinkId(link);
372
Simon Huntc13082f2016-08-03 21:20:23 -0700373 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
374 if (uiDeviceLink == null) {
375 uiDeviceLink = addNewDeviceLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700376 }
Simon Huntc13082f2016-08-03 21:20:23 -0700377 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700378 }
379 }
380
381 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700382 void addOrUpdateDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700383 UiLinkId id = uiLinkId(link);
Simon Huntc13082f2016-08-03 21:20:23 -0700384 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
385 if (uiDeviceLink == null) {
386 uiDeviceLink = addNewDeviceLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700387 }
Simon Huntc13082f2016-08-03 21:20:23 -0700388 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700389
Simon Huntc13082f2016-08-03 21:20:23 -0700390 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink);
Simon Hunt23fb1352016-04-11 12:15:19 -0700391 }
392
Simon Hunt58a0dd02016-05-17 11:54:23 -0700393 // package private for unit test access
Simon Huntc13082f2016-08-03 21:20:23 -0700394 UiDeviceLink accessDeviceLink(UiLinkId id) {
395 return uiTopology.findDeviceLink(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700396 }
397
Simon Huntc0f20c12016-05-09 09:30:20 -0700398 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700399 void removeDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700400 UiLinkId id = uiLinkId(link);
Simon Huntc13082f2016-08-03 21:20:23 -0700401 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
402 if (uiDeviceLink != null) {
403 boolean remaining = uiDeviceLink.detachBackingLink(link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700404 if (remaining) {
Simon Huntc13082f2016-08-03 21:20:23 -0700405 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink);
Simon Huntc0f20c12016-05-09 09:30:20 -0700406 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700407 uiTopology.remove(uiDeviceLink);
408 postEvent(LINK_REMOVED, uiDeviceLink);
Simon Huntc0f20c12016-05-09 09:30:20 -0700409 }
410 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700411 log.warn(E_NO_ELEMENT, "Device link", id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700412 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700413 }
414
Simon Huntc13082f2016-08-03 21:20:23 -0700415 Set<UiDeviceLink> getAllDeviceLinks() {
416 return uiTopology.allDeviceLinks();
Simon Hunt4854f3d2016-08-02 18:13:15 -0700417 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700418
419 // === HOSTS
420
Simon Hunt58a0dd02016-05-17 11:54:23 -0700421 private EdgeLink synthesizeLink(Host h) {
422 return createEdgeLink(h, true);
423 }
424
Simon Huntc0f20c12016-05-09 09:30:20 -0700425 private UiHost addNewHost(Host h) {
426 UiHost host = new UiHost(uiTopology, h);
427 uiTopology.add(host);
428
Simon Hunt58a0dd02016-05-17 11:54:23 -0700429 EdgeLink elink = synthesizeLink(h);
430 UiLinkId elinkId = uiLinkId(elink);
431 host.setEdgeLinkId(elinkId);
432
433 // add synthesized edge link to the topology
Simon Huntb7fd0802016-10-27 12:21:40 -0700434 addNewEdgeLink(elinkId);
Simon Huntc0f20c12016-05-09 09:30:20 -0700435
436 return host;
437 }
438
Simon Huntb7fd0802016-10-27 12:21:40 -0700439 private void insertNewUiEdgeLink(UiLinkId id) {
440 addNewEdgeLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700441 }
442
443 private void updateHost(UiHost uiHost, Host h) {
Simon Huntc13082f2016-08-03 21:20:23 -0700444 UiEdgeLink existing = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700445
Simon Huntb7fd0802016-10-27 12:21:40 -0700446 // TODO: review - do we need EdgeLink now that we are creating from id only?
Simon Hunt58a0dd02016-05-17 11:54:23 -0700447 EdgeLink currentElink = synthesizeLink(h);
448 UiLinkId currentElinkId = uiLinkId(currentElink);
449
450 if (existing != null) {
451 if (!currentElinkId.equals(existing.id())) {
452 // edge link has changed
Simon Huntb7fd0802016-10-27 12:21:40 -0700453 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700454 uiHost.setEdgeLinkId(currentElinkId);
455
456 uiTopology.remove(existing);
457 }
458
459 } else {
460 // no previously existing edge link
Simon Huntb7fd0802016-10-27 12:21:40 -0700461 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700462 uiHost.setEdgeLinkId(currentElinkId);
463
464 }
465
Simon Huntc0f20c12016-05-09 09:30:20 -0700466 HostLocation hloc = h.location();
467 uiHost.setLocation(hloc.deviceId(), hloc.port());
Simon Huntc0f20c12016-05-09 09:30:20 -0700468 }
469
470 private void loadHosts() {
471 for (Host h : services.host().getHosts()) {
472 UiHost host = addNewHost(h);
473 updateHost(host, h);
474 }
475 }
476
477 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700478 void addOrUpdateHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700479 HostId id = host.id();
480 UiHost uiHost = uiTopology.findHost(id);
481 if (uiHost == null) {
482 uiHost = addNewHost(host);
483 }
484 updateHost(uiHost, host);
485
486 postEvent(HOST_ADDED_OR_UPDATED, uiHost);
Simon Hunt23fb1352016-04-11 12:15:19 -0700487 }
488
Simon Huntc0f20c12016-05-09 09:30:20 -0700489 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700490 void moveHost(Host host, Host prevHost) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700491 UiHost uiHost = uiTopology.findHost(prevHost.id());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700492 if (uiHost != null) {
493 updateHost(uiHost, host);
494 postEvent(HOST_MOVED, uiHost);
495 } else {
496 log.warn(E_NO_ELEMENT, "host", prevHost.id());
497 }
498 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700499
Simon Hunt58a0dd02016-05-17 11:54:23 -0700500 // package private for unit test access
501 UiHost accessHost(HostId id) {
502 return uiTopology.findHost(id);
Simon Hunt23fb1352016-04-11 12:15:19 -0700503 }
504
Simon Huntc0f20c12016-05-09 09:30:20 -0700505 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700506 void removeHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700507 HostId id = host.id();
508 UiHost uiHost = uiTopology.findHost(id);
509 if (uiHost != null) {
Simon Huntc13082f2016-08-03 21:20:23 -0700510 UiEdgeLink edgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700511 uiTopology.remove(edgeLink);
Simon Huntc0f20c12016-05-09 09:30:20 -0700512 uiTopology.remove(uiHost);
Simon Huntc0f20c12016-05-09 09:30:20 -0700513 postEvent(HOST_REMOVED, uiHost);
514 } else {
515 log.warn(E_NO_ELEMENT, "host", id);
516 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700517 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700518
Simon Hunt4854f3d2016-08-02 18:13:15 -0700519 Set<UiHost> getAllHosts() {
520 return uiTopology.allHosts();
521 }
522
Simon Huntc0f20c12016-05-09 09:30:20 -0700523
Simon Huntc13082f2016-08-03 21:20:23 -0700524 // === SYNTHETIC LINKS
525
526 List<UiSynthLink> getSynthLinks(RegionId regionId) {
527 return uiTopology.findSynthLinks(regionId);
528 }
529
Simon Huntb1ce2602016-07-23 14:04:31 -0700530 /**
531 * Refreshes the internal state.
532 */
533 public void refresh() {
Simon Huntc13082f2016-08-03 21:20:23 -0700534 // fix up internal linkages to ensure they are correct
Simon Huntb1ce2602016-07-23 14:04:31 -0700535
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700536 // make sure regions reflect layout containment hierarchy
537 fixupContainmentHierarchy(uiTopology.nullRegion());
538 uiTopology.allRegions().forEach(this::fixupContainmentHierarchy);
539
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500540 // make sure devices and hosts are in the correct region
Simon Huntb1ce2602016-07-23 14:04:31 -0700541 Set<UiDevice> allDevices = uiTopology.allDevices();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500542 Set<UiHost> allHosts = uiTopology.allHosts();
Simon Huntb1ce2602016-07-23 14:04:31 -0700543
544 services.region().getRegions().forEach(r -> {
545 RegionId rid = r.id();
546 UiRegion region = uiTopology.findRegion(rid);
547 if (region != null) {
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500548 reconcileDevicesWithRegion(allDevices, r, rid, region);
549 reconcileHostsWithRegion(allHosts, r, rid, region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700550
Simon Huntb1ce2602016-07-23 14:04:31 -0700551 } else {
552 log.warn("No UiRegion in topology for ID {}", rid);
553 }
554 });
555
556 // what is left over, must belong to the null-region
557 Set<DeviceId> leftOver = new HashSet<>(allDevices.size());
558 allDevices.forEach(d -> leftOver.add(d.id()));
559 uiTopology.nullRegion().reconcileDevices(leftOver);
Simon Huntc13082f2016-08-03 21:20:23 -0700560
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500561 Set<HostId> leftOverHosts = new HashSet<>(allHosts.size());
562 allHosts.forEach(h -> leftOverHosts.add(h.id()));
563 uiTopology.nullRegion().reconcileHosts(leftOverHosts);
564
Simon Huntc13082f2016-08-03 21:20:23 -0700565 // now that we have correct region hierarchy, and devices are in their
566 // respective regions, we can compute synthetic links for each region.
567 uiTopology.computeSynthLinks();
Simon Huntb1ce2602016-07-23 14:04:31 -0700568 }
569
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500570 private void reconcileHostsWithRegion(Set<UiHost> allHosts, Region r,
571 RegionId rid, UiRegion region) {
572 Set<HostId> hostIds = services.region().getRegionHosts(rid);
573 region.reconcileHosts(hostIds);
574
Simon Hunt1bee5292016-10-14 11:02:33 -0700575 hostIds.forEach(hid -> {
576 UiHost h = uiTopology.findHost(hid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500577 if (h != null) {
578 h.setRegionId(r.id());
579 allHosts.remove(h);
580 } else {
Simon Hunt1bee5292016-10-14 11:02:33 -0700581 log.warn("Region host ID {} but no UiHost in topology", hid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500582 }
583 });
584 }
585
586 private void reconcileDevicesWithRegion(Set<UiDevice> allDevices, Region r,
587 RegionId rid, UiRegion region) {
588 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
589 region.reconcileDevices(deviceIds);
590
591 deviceIds.forEach(devId -> {
592 UiDevice dev = uiTopology.findDevice(devId);
593 if (dev != null) {
594 dev.setRegionId(r.id());
595 allDevices.remove(dev);
596 } else {
597 log.warn("Region device ID {} but no UiDevice in topology",
598 devId);
599 }
600 });
601 }
602
Simon Huntc13082f2016-08-03 21:20:23 -0700603
Simon Huntc0f20c12016-05-09 09:30:20 -0700604 // === CACHE STATISTICS
605
Simon Hunt338a3b42016-04-14 09:43:52 -0700606 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700607 * Returns a detailed (multi-line) string showing the contents of the cache.
608 *
609 * @return detailed string
610 */
611 public String dumpString() {
612 return uiTopology.dumpString();
613 }
614
615 /**
Simon Hunt338a3b42016-04-14 09:43:52 -0700616 * Returns the number of members in the cluster.
617 *
618 * @return number of cluster members
619 */
620 public int clusterMemberCount() {
621 return uiTopology.clusterMemberCount();
622 }
623
624 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700625 * Returns the number of regions in the topology.
Simon Hunt338a3b42016-04-14 09:43:52 -0700626 *
627 * @return number of regions
628 */
629 public int regionCount() {
630 return uiTopology.regionCount();
631 }
Simon Hunt58a0dd02016-05-17 11:54:23 -0700632
633 /**
634 * Returns the number of devices in the topology.
635 *
636 * @return number of devices
637 */
638 public int deviceCount() {
639 return uiTopology.deviceCount();
640 }
641
642 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700643 * Returns the number of device links in the topology.
Simon Hunt58a0dd02016-05-17 11:54:23 -0700644 *
Simon Huntc13082f2016-08-03 21:20:23 -0700645 * @return number of device links
Simon Hunt58a0dd02016-05-17 11:54:23 -0700646 */
Simon Huntc13082f2016-08-03 21:20:23 -0700647 public int deviceLinkCount() {
648 return uiTopology.deviceLinkCount();
649 }
650
651 /**
652 * Returns the number of edge links in the topology.
653 *
654 * @return number of edge links
655 */
656 public int edgeLinkCount() {
657 return uiTopology.edgeLinkCount();
Simon Hunt58a0dd02016-05-17 11:54:23 -0700658 }
659
660 /**
661 * Returns the number of hosts in the topology.
662 *
663 * @return number of hosts
664 */
665 public int hostCount() {
666 return uiTopology.hostCount();
667 }
Simon Huntd5b96732016-07-08 13:22:27 -0700668
Simon Huntc13082f2016-08-03 21:20:23 -0700669 /**
670 * Returns the number of synthetic links in the topology.
671 *
672 * @return the number of synthetic links
673 */
674 public int synthLinkCount() {
675 return uiTopology.synthLinkCount();
676 }
Simon Huntcda9c032016-04-11 10:32:54 -0700677}