blob: 2377499c474cb2a1b53cf130d836e9d3860fe969 [file] [log] [blame]
Simon Huntcda9c032016-04-11 10:32:54 -07001/*
Brian O'Connor0a4e6742016-09-15 23:03:10 -07002 * Copyright 2016-present Open Networking Laboratory
Simon Huntcda9c032016-04-11 10:32:54 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.ui.impl.topo.model;
18
Simon Hunt8eac4ae2017-01-20 12:56:45 -080019import com.fasterxml.jackson.databind.node.ObjectNode;
Simon Hunt23fb1352016-04-11 12:15:19 -070020import org.onosproject.cluster.ControllerNode;
Simon Hunt642bc452016-05-04 19:34:45 -070021import org.onosproject.cluster.NodeId;
Simon Hunt23fb1352016-04-11 12:15:19 -070022import org.onosproject.cluster.RoleInfo;
Simon Huntcda9c032016-04-11 10:32:54 -070023import org.onosproject.event.EventDispatcher;
24import org.onosproject.net.Device;
Simon Hunt23fb1352016-04-11 12:15:19 -070025import org.onosproject.net.DeviceId;
Simon Huntc0f20c12016-05-09 09:30:20 -070026import org.onosproject.net.EdgeLink;
Simon Hunt23fb1352016-04-11 12:15:19 -070027import org.onosproject.net.Host;
Simon Huntc0f20c12016-05-09 09:30:20 -070028import org.onosproject.net.HostId;
29import org.onosproject.net.HostLocation;
Simon Hunt23fb1352016-04-11 12:15:19 -070030import org.onosproject.net.Link;
31import org.onosproject.net.region.Region;
Simon Huntc0f20c12016-05-09 09:30:20 -070032import org.onosproject.net.region.RegionId;
Simon Hunt4f4ffc32016-08-03 18:30:47 -070033import org.onosproject.ui.UiTopoLayoutService;
Simon Hunt8eac4ae2017-01-20 12:56:45 -080034import org.onosproject.ui.impl.topo.Topo2Jsonifier;
Simon Hunt642bc452016-05-04 19:34:45 -070035import org.onosproject.ui.model.ServiceBundle;
Simon Hunt338a3b42016-04-14 09:43:52 -070036import org.onosproject.ui.model.topo.UiClusterMember;
Simon Huntcda9c032016-04-11 10:32:54 -070037import org.onosproject.ui.model.topo.UiDevice;
Simon Huntc13082f2016-08-03 21:20:23 -070038import org.onosproject.ui.model.topo.UiDeviceLink;
39import org.onosproject.ui.model.topo.UiEdgeLink;
Simon Huntc0f20c12016-05-09 09:30:20 -070040import org.onosproject.ui.model.topo.UiElement;
41import org.onosproject.ui.model.topo.UiHost;
Simon Huntc0f20c12016-05-09 09:30:20 -070042import org.onosproject.ui.model.topo.UiLinkId;
43import org.onosproject.ui.model.topo.UiRegion;
Simon Huntc13082f2016-08-03 21:20:23 -070044import org.onosproject.ui.model.topo.UiSynthLink;
Simon Hunt4f4ffc32016-08-03 18:30:47 -070045import org.onosproject.ui.model.topo.UiTopoLayout;
46import org.onosproject.ui.model.topo.UiTopoLayoutId;
Simon Hunt23fb1352016-04-11 12:15:19 -070047import org.onosproject.ui.model.topo.UiTopology;
Simon Hunt642bc452016-05-04 19:34:45 -070048import org.slf4j.Logger;
49import org.slf4j.LoggerFactory;
Simon Hunt23fb1352016-04-11 12:15:19 -070050
Simon Huntb1ce2602016-07-23 14:04:31 -070051import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070052import java.util.List;
Simon Huntc0f20c12016-05-09 09:30:20 -070053import java.util.Set;
54
55import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Simon Hunt642bc452016-05-04 19:34:45 -070056import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_ADDED_OR_UPDATED;
57import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_REMOVED;
58import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED;
Simon Hunt23fb1352016-04-11 12:15:19 -070059import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_REMOVED;
Simon Huntc0f20c12016-05-09 09:30:20 -070060import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_ADDED_OR_UPDATED;
61import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_MOVED;
62import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_REMOVED;
63import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.LINK_ADDED_OR_UPDATED;
64import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.LINK_REMOVED;
65import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.REGION_ADDED_OR_UPDATED;
66import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.REGION_REMOVED;
67import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId;
Simon Huntcda9c032016-04-11 10:32:54 -070068
69/**
70 * UI Topology Model cache.
71 */
72class ModelCache {
73
Simon Huntc0f20c12016-05-09 09:30:20 -070074 private static final String E_NO_ELEMENT = "Tried to remove non-member {}: {}";
Simon Hunt8eac4ae2017-01-20 12:56:45 -080075 private static final String MEMO_ADDED = "added";
76 private static final String MEMO_UPDATED = "updated";
77 private static final String MEMO_REMOVED = "removed";
78 private static final String MEMO_MOVED = "moved";
Simon Huntc0f20c12016-05-09 09:30:20 -070079
Simon Hunt642bc452016-05-04 19:34:45 -070080 private static final Logger log = LoggerFactory.getLogger(ModelCache.class);
81
Simon Huntc4ca7102017-04-08 22:28:04 -070082 // TODO: add NetworkConfigService to service bundle
83// private final NetworkConfigService cfgService =
84// DefaultServiceDirectory.getService(NetworkConfigService.class);
85
Simon Hunt642bc452016-05-04 19:34:45 -070086 private final ServiceBundle services;
Simon Huntcda9c032016-04-11 10:32:54 -070087 private final EventDispatcher dispatcher;
Simon Hunt23fb1352016-04-11 12:15:19 -070088 private final UiTopology uiTopology = new UiTopology();
Simon Huntcda9c032016-04-11 10:32:54 -070089
Simon Hunt8eac4ae2017-01-20 12:56:45 -080090 private Topo2Jsonifier t2json;
91
Simon Hunt642bc452016-05-04 19:34:45 -070092 ModelCache(ServiceBundle services, EventDispatcher eventDispatcher) {
93 this.services = services;
Simon Huntcda9c032016-04-11 10:32:54 -070094 this.dispatcher = eventDispatcher;
95 }
96
Simon Hunt338a3b42016-04-14 09:43:52 -070097 @Override
98 public String toString() {
99 return "ModelCache{" + uiTopology + "}";
100 }
101
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800102 private void postEvent(UiModelEvent.Type type, UiElement subject, String memo) {
103 ObjectNode data = t2json != null ? t2json.jsonUiElement(subject) : null;
104 dispatcher.post(new UiModelEvent(type, subject, data, memo));
105 }
106
107 void injectJsonifier(Topo2Jsonifier t2json) {
108 this.t2json = t2json;
Simon Huntc0f20c12016-05-09 09:30:20 -0700109 }
110
Simon Huntcda9c032016-04-11 10:32:54 -0700111 void clear() {
Simon Hunt23fb1352016-04-11 12:15:19 -0700112 uiTopology.clear();
Simon Huntcda9c032016-04-11 10:32:54 -0700113 }
114
115 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700116 * Create our internal model of the global topology. An assumption we are
117 * making is that the topology is empty to start.
Simon Huntcda9c032016-04-11 10:32:54 -0700118 */
119 void load() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700120 loadClusterMembers();
121 loadRegions();
122 loadDevices();
Simon Huntc13082f2016-08-03 21:20:23 -0700123 loadDeviceLinks();
Simon Huntc0f20c12016-05-09 09:30:20 -0700124 loadHosts();
Simon Huntcda9c032016-04-11 10:32:54 -0700125 }
126
127
Simon Huntc0f20c12016-05-09 09:30:20 -0700128 // === CLUSTER MEMBERS
129
130 private UiClusterMember addNewClusterMember(ControllerNode n) {
131 UiClusterMember member = new UiClusterMember(uiTopology, n);
132 uiTopology.add(member);
133 return member;
134 }
135
136 private void updateClusterMember(UiClusterMember member) {
137 ControllerNode.State state = services.cluster().getState(member.id());
138 member.setState(state);
Simon Huntc0f20c12016-05-09 09:30:20 -0700139 }
140
141 private void loadClusterMembers() {
142 for (ControllerNode n : services.cluster().getNodes()) {
143 UiClusterMember member = addNewClusterMember(n);
144 updateClusterMember(member);
145 }
146 }
147
148 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700149 void addOrUpdateClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700150 NodeId id = cnode.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800151 String memo = MEMO_UPDATED;
Simon Hunt642bc452016-05-04 19:34:45 -0700152 UiClusterMember member = uiTopology.findClusterMember(id);
153 if (member == null) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700154 member = addNewClusterMember(cnode);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800155 memo = MEMO_ADDED;
Simon Hunt338a3b42016-04-14 09:43:52 -0700156 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700157 updateClusterMember(member);
Simon Hunt338a3b42016-04-14 09:43:52 -0700158
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800159 postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700160 }
161
Simon Huntc0f20c12016-05-09 09:30:20 -0700162 // package private for unit test access
163 UiClusterMember accessClusterMember(NodeId id) {
164 return uiTopology.findClusterMember(id);
165 }
166
167 // invoked from UiSharedTopologyModel cluster event listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700168 void removeClusterMember(ControllerNode cnode) {
Simon Hunt642bc452016-05-04 19:34:45 -0700169 NodeId id = cnode.id();
170 UiClusterMember member = uiTopology.findClusterMember(id);
171 if (member != null) {
172 uiTopology.remove(member);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800173 postEvent(CLUSTER_MEMBER_REMOVED, member, MEMO_REMOVED);
Simon Hunt642bc452016-05-04 19:34:45 -0700174 } else {
Simon Huntc0f20c12016-05-09 09:30:20 -0700175 log.warn(E_NO_ELEMENT, "cluster node", id);
Simon Hunt642bc452016-05-04 19:34:45 -0700176 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700177 }
178
Simon Huntd5b96732016-07-08 13:22:27 -0700179 List<UiClusterMember> getAllClusterMembers() {
180 return uiTopology.allClusterMembers();
181 }
182
Simon Huntc0f20c12016-05-09 09:30:20 -0700183
184 // === MASTERSHIP CHANGES
185
186 // invoked from UiSharedTopologyModel mastership listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700187 void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700188 // To think about:: do we need to store mastership info?
189 // or can we rely on looking it up live?
Simon Hunt338a3b42016-04-14 09:43:52 -0700190 // TODO: store the updated mastership information
191 // TODO: post event
192 }
193
Simon Huntb1ce2602016-07-23 14:04:31 -0700194 // === THE NULL REGION
195
196 UiRegion nullRegion() {
197 return uiTopology.nullRegion();
198 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700199
200 // === REGIONS
201
202 private UiRegion addNewRegion(Region r) {
203 UiRegion region = new UiRegion(uiTopology, r);
204 uiTopology.add(region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700205 log.debug("Region {} added to topology", region);
Simon Huntc0f20c12016-05-09 09:30:20 -0700206 return region;
207 }
208
209 private void updateRegion(UiRegion region) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700210 RegionId rid = region.id();
211 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500212 Set<HostId> hostIds = services.region().getRegionHosts(rid);
Simon Huntb1ce2602016-07-23 14:04:31 -0700213
214 // Make sure device objects refer to their region
215 deviceIds.forEach(d -> {
216 UiDevice dev = uiTopology.findDevice(d);
217 if (dev != null) {
218 dev.setRegionId(rid);
219 } else {
220 // if we don't have the UiDevice in the topology, what can we do?
221 log.warn("Region device {}, but we don't have UiDevice in topology", d);
222 }
223 });
224
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500225 hostIds.forEach(d -> {
226 UiHost host = uiTopology.findHost(d);
227 if (host != null) {
228 host.setRegionId(rid);
229 } else {
230 // if we don't have the UiDevice in the topology, what can we do?
231 log.warn("Region host {}, but we don't have UiHost in topology", d);
232 }
233 });
234
Simon Huntb1ce2602016-07-23 14:04:31 -0700235 // Make sure the region object refers to the devices
236 region.reconcileDevices(deviceIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500237 region.reconcileHosts(hostIds);
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700238
239 fixupContainmentHierarchy(region);
240 }
241
242 private void fixupContainmentHierarchy(UiRegion region) {
243 UiTopoLayoutService ls = services.layout();
244 RegionId regionId = region.id();
245
246 UiTopoLayout layout = ls.getLayout(regionId);
247 if (layout == null) {
248 // no layout backed by this region
249 log.warn("No layout backed by region {}", regionId);
250 return;
251 }
252
253 UiTopoLayoutId layoutId = layout.id();
254
255 if (!layout.isRoot()) {
256 UiTopoLayoutId parentId = layout.parent();
257 UiTopoLayout parentLayout = ls.getLayout(parentId);
258 RegionId parentRegionId = parentLayout.regionId();
259 region.setParent(parentRegionId);
260 }
261
262 Set<UiTopoLayout> kids = ls.getChildren(layoutId);
263 Set<RegionId> kidRegionIds = new HashSet<>(kids.size());
264 kids.forEach(k -> kidRegionIds.add(k.regionId()));
265 region.setChildren(kidRegionIds);
Simon Huntc0f20c12016-05-09 09:30:20 -0700266 }
267
268 private void loadRegions() {
269 for (Region r : services.region().getRegions()) {
270 UiRegion region = addNewRegion(r);
271 updateRegion(region);
272 }
273 }
274
275 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700276 void addOrUpdateRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700277 RegionId id = region.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800278 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700279 UiRegion uiRegion = uiTopology.findRegion(id);
280 if (uiRegion == null) {
281 uiRegion = addNewRegion(region);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800282 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700283 }
284 updateRegion(uiRegion);
285
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800286 postEvent(REGION_ADDED_OR_UPDATED, uiRegion, memo);
Simon Hunt338a3b42016-04-14 09:43:52 -0700287 }
288
Simon Hunt58a0dd02016-05-17 11:54:23 -0700289 // package private for unit test access
290 UiRegion accessRegion(RegionId id) {
Simon Huntd5b96732016-07-08 13:22:27 -0700291 return id == null ? null : uiTopology.findRegion(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700292 }
293
Simon Huntc0f20c12016-05-09 09:30:20 -0700294 // invoked from UiSharedTopologyModel region listener
Simon Hunt338a3b42016-04-14 09:43:52 -0700295 void removeRegion(Region region) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700296 RegionId id = region.id();
297 UiRegion uiRegion = uiTopology.findRegion(id);
298 if (uiRegion != null) {
299 uiTopology.remove(uiRegion);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800300 postEvent(REGION_REMOVED, uiRegion, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700301 } else {
302 log.warn(E_NO_ELEMENT, "region", id);
303 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700304 }
305
Simon Hunt10973dd2016-08-01 15:50:35 -0700306 Set<UiRegion> getAllRegions() {
307 return uiTopology.allRegions();
308 }
309
Simon Huntc0f20c12016-05-09 09:30:20 -0700310
311 // === DEVICES
312
313 private UiDevice addNewDevice(Device d) {
314 UiDevice device = new UiDevice(uiTopology, d);
Simon Huntb1ce2602016-07-23 14:04:31 -0700315 updateDevice(device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700316 uiTopology.add(device);
Simon Huntb1ce2602016-07-23 14:04:31 -0700317 log.debug("Device {} added to topology", device);
Simon Huntc0f20c12016-05-09 09:30:20 -0700318 return device;
319 }
320
Simon Huntb1ce2602016-07-23 14:04:31 -0700321 // make sure the UiDevice is tagged with the region it belongs to
Simon Huntc0f20c12016-05-09 09:30:20 -0700322 private void updateDevice(UiDevice device) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700323 Region r = services.region().getRegionForDevice(device.id());
324 RegionId rid = r == null ? UiRegion.NULL_ID : r.id();
325 device.setRegionId(rid);
Simon Huntc0f20c12016-05-09 09:30:20 -0700326 }
327
328 private void loadDevices() {
329 for (Device d : services.device().getDevices()) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700330 addNewDevice(d);
Simon Huntc0f20c12016-05-09 09:30:20 -0700331 }
332 }
333
334 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700335 void addOrUpdateDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700336 DeviceId id = device.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800337 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700338 UiDevice uiDevice = uiTopology.findDevice(id);
339 if (uiDevice == null) {
340 uiDevice = addNewDevice(device);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800341 memo = MEMO_ADDED;
Simon Huntb1ce2602016-07-23 14:04:31 -0700342 } else {
343 updateDevice(uiDevice);
Simon Huntc0f20c12016-05-09 09:30:20 -0700344 }
Simon Huntcda9c032016-04-11 10:32:54 -0700345
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800346 postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice, memo);
Simon Huntcda9c032016-04-11 10:32:54 -0700347 }
348
Simon Hunt58a0dd02016-05-17 11:54:23 -0700349 // package private for unit test access
350 UiDevice accessDevice(DeviceId id) {
351 return uiTopology.findDevice(id);
352 }
353
Simon Huntc0f20c12016-05-09 09:30:20 -0700354 // invoked from UiSharedTopologyModel device listener
Simon Huntcda9c032016-04-11 10:32:54 -0700355 void removeDevice(Device device) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700356 DeviceId id = device.id();
357 UiDevice uiDevice = uiTopology.findDevice(id);
358 if (uiDevice != null) {
359 uiTopology.remove(uiDevice);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800360 postEvent(DEVICE_REMOVED, uiDevice, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700361 } else {
362 log.warn(E_NO_ELEMENT, "device", id);
363 }
Simon Huntcda9c032016-04-11 10:32:54 -0700364 }
365
Simon Hunt4854f3d2016-08-02 18:13:15 -0700366 Set<UiDevice> getAllDevices() {
367 return uiTopology.allDevices();
368 }
369
Simon Huntc0f20c12016-05-09 09:30:20 -0700370
Simon Huntc13082f2016-08-03 21:20:23 -0700371 // === LINKS ===
Simon Huntc0f20c12016-05-09 09:30:20 -0700372
Simon Huntc13082f2016-08-03 21:20:23 -0700373 private UiDeviceLink addNewDeviceLink(UiLinkId id) {
374 UiDeviceLink uiDeviceLink = new UiDeviceLink(uiTopology, id);
375 uiTopology.add(uiDeviceLink);
376 return uiDeviceLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700377 }
378
Simon Huntc13082f2016-08-03 21:20:23 -0700379 private UiEdgeLink addNewEdgeLink(UiLinkId id) {
380 UiEdgeLink uiEdgeLink = new UiEdgeLink(uiTopology, id);
381 uiTopology.add(uiEdgeLink);
382 return uiEdgeLink;
Simon Huntc0f20c12016-05-09 09:30:20 -0700383 }
384
Simon Huntc13082f2016-08-03 21:20:23 -0700385 private void updateDeviceLink(UiDeviceLink uiDeviceLink, Link link) {
386 uiDeviceLink.attachBackingLink(link);
387 }
388
389 private void loadDeviceLinks() {
Simon Huntc0f20c12016-05-09 09:30:20 -0700390 for (Link link : services.link().getLinks()) {
391 UiLinkId id = uiLinkId(link);
392
Simon Huntc13082f2016-08-03 21:20:23 -0700393 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
394 if (uiDeviceLink == null) {
395 uiDeviceLink = addNewDeviceLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700396 }
Simon Huntc13082f2016-08-03 21:20:23 -0700397 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700398 }
399 }
400
401 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700402 void addOrUpdateDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700403 UiLinkId id = uiLinkId(link);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800404 String memo = MEMO_UPDATED;
Simon Huntc13082f2016-08-03 21:20:23 -0700405 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
406 if (uiDeviceLink == null) {
407 uiDeviceLink = addNewDeviceLink(id);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800408 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700409 }
Simon Huntc13082f2016-08-03 21:20:23 -0700410 updateDeviceLink(uiDeviceLink, link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700411
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800412 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700413 }
414
Simon Hunt58a0dd02016-05-17 11:54:23 -0700415 // package private for unit test access
Simon Huntc13082f2016-08-03 21:20:23 -0700416 UiDeviceLink accessDeviceLink(UiLinkId id) {
417 return uiTopology.findDeviceLink(id);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700418 }
419
Simon Huntc0f20c12016-05-09 09:30:20 -0700420 // invoked from UiSharedTopologyModel link listener
Simon Huntc13082f2016-08-03 21:20:23 -0700421 void removeDeviceLink(Link link) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700422 UiLinkId id = uiLinkId(link);
Simon Huntc13082f2016-08-03 21:20:23 -0700423 UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
424 if (uiDeviceLink != null) {
425 boolean remaining = uiDeviceLink.detachBackingLink(link);
Simon Huntc0f20c12016-05-09 09:30:20 -0700426 if (remaining) {
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800427 postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, MEMO_UPDATED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700428 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700429 uiTopology.remove(uiDeviceLink);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800430 postEvent(LINK_REMOVED, uiDeviceLink, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700431 }
432 } else {
Simon Huntc13082f2016-08-03 21:20:23 -0700433 log.warn(E_NO_ELEMENT, "Device link", id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700434 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700435 }
436
Simon Huntc13082f2016-08-03 21:20:23 -0700437 Set<UiDeviceLink> getAllDeviceLinks() {
438 return uiTopology.allDeviceLinks();
Simon Hunt4854f3d2016-08-02 18:13:15 -0700439 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700440
441 // === HOSTS
442
Simon Hunt58a0dd02016-05-17 11:54:23 -0700443 private EdgeLink synthesizeLink(Host h) {
444 return createEdgeLink(h, true);
445 }
446
Simon Huntc0f20c12016-05-09 09:30:20 -0700447 private UiHost addNewHost(Host h) {
448 UiHost host = new UiHost(uiTopology, h);
449 uiTopology.add(host);
450
Simon Hunt58a0dd02016-05-17 11:54:23 -0700451 EdgeLink elink = synthesizeLink(h);
452 UiLinkId elinkId = uiLinkId(elink);
453 host.setEdgeLinkId(elinkId);
454
455 // add synthesized edge link to the topology
Simon Huntb7fd0802016-10-27 12:21:40 -0700456 addNewEdgeLink(elinkId);
Simon Huntc0f20c12016-05-09 09:30:20 -0700457
458 return host;
459 }
460
Simon Huntb7fd0802016-10-27 12:21:40 -0700461 private void insertNewUiEdgeLink(UiLinkId id) {
462 addNewEdgeLink(id);
Simon Huntc0f20c12016-05-09 09:30:20 -0700463 }
464
465 private void updateHost(UiHost uiHost, Host h) {
Simon Huntc13082f2016-08-03 21:20:23 -0700466 UiEdgeLink existing = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700467
Simon Huntb7fd0802016-10-27 12:21:40 -0700468 // TODO: review - do we need EdgeLink now that we are creating from id only?
Simon Hunt58a0dd02016-05-17 11:54:23 -0700469 EdgeLink currentElink = synthesizeLink(h);
470 UiLinkId currentElinkId = uiLinkId(currentElink);
471
472 if (existing != null) {
473 if (!currentElinkId.equals(existing.id())) {
474 // edge link has changed
Simon Huntb7fd0802016-10-27 12:21:40 -0700475 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700476 uiHost.setEdgeLinkId(currentElinkId);
477
478 uiTopology.remove(existing);
479 }
480
481 } else {
482 // no previously existing edge link
Simon Huntb7fd0802016-10-27 12:21:40 -0700483 insertNewUiEdgeLink(currentElinkId);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700484 uiHost.setEdgeLinkId(currentElinkId);
485
486 }
487
Simon Huntc0f20c12016-05-09 09:30:20 -0700488 HostLocation hloc = h.location();
489 uiHost.setLocation(hloc.deviceId(), hloc.port());
Simon Huntc0f20c12016-05-09 09:30:20 -0700490 }
491
492 private void loadHosts() {
493 for (Host h : services.host().getHosts()) {
494 UiHost host = addNewHost(h);
495 updateHost(host, h);
496 }
497 }
498
499 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700500 void addOrUpdateHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700501 HostId id = host.id();
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800502 String memo = MEMO_UPDATED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700503 UiHost uiHost = uiTopology.findHost(id);
504 if (uiHost == null) {
505 uiHost = addNewHost(host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800506 memo = MEMO_ADDED;
Simon Huntc0f20c12016-05-09 09:30:20 -0700507 }
508 updateHost(uiHost, host);
509
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800510 postEvent(HOST_ADDED_OR_UPDATED, uiHost, memo);
Simon Hunt23fb1352016-04-11 12:15:19 -0700511 }
512
Simon Huntc0f20c12016-05-09 09:30:20 -0700513 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700514 void moveHost(Host host, Host prevHost) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700515 UiHost uiHost = uiTopology.findHost(prevHost.id());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700516 if (uiHost != null) {
517 updateHost(uiHost, host);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800518 postEvent(HOST_MOVED, uiHost, MEMO_MOVED);
Simon Hunt58a0dd02016-05-17 11:54:23 -0700519 } else {
520 log.warn(E_NO_ELEMENT, "host", prevHost.id());
521 }
522 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700523
Simon Hunt58a0dd02016-05-17 11:54:23 -0700524 // package private for unit test access
525 UiHost accessHost(HostId id) {
526 return uiTopology.findHost(id);
Simon Hunt23fb1352016-04-11 12:15:19 -0700527 }
528
Simon Huntc0f20c12016-05-09 09:30:20 -0700529 // invoked from UiSharedTopologyModel host listener
Simon Hunt23fb1352016-04-11 12:15:19 -0700530 void removeHost(Host host) {
Simon Huntc0f20c12016-05-09 09:30:20 -0700531 HostId id = host.id();
532 UiHost uiHost = uiTopology.findHost(id);
533 if (uiHost != null) {
Simon Huntc13082f2016-08-03 21:20:23 -0700534 UiEdgeLink edgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId());
Simon Hunt58a0dd02016-05-17 11:54:23 -0700535 uiTopology.remove(edgeLink);
Simon Huntc0f20c12016-05-09 09:30:20 -0700536 uiTopology.remove(uiHost);
Simon Hunt8eac4ae2017-01-20 12:56:45 -0800537 postEvent(HOST_REMOVED, uiHost, MEMO_REMOVED);
Simon Huntc0f20c12016-05-09 09:30:20 -0700538 } else {
539 log.warn(E_NO_ELEMENT, "host", id);
540 }
Simon Hunt23fb1352016-04-11 12:15:19 -0700541 }
Simon Hunt338a3b42016-04-14 09:43:52 -0700542
Simon Hunt4854f3d2016-08-02 18:13:15 -0700543 Set<UiHost> getAllHosts() {
544 return uiTopology.allHosts();
545 }
546
Simon Huntc0f20c12016-05-09 09:30:20 -0700547
Simon Huntc13082f2016-08-03 21:20:23 -0700548 // === SYNTHETIC LINKS
549
550 List<UiSynthLink> getSynthLinks(RegionId regionId) {
551 return uiTopology.findSynthLinks(regionId);
552 }
553
Simon Huntb1ce2602016-07-23 14:04:31 -0700554 /**
555 * Refreshes the internal state.
556 */
557 public void refresh() {
Simon Huntc13082f2016-08-03 21:20:23 -0700558 // fix up internal linkages to ensure they are correct
Simon Huntb1ce2602016-07-23 14:04:31 -0700559
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700560 // make sure regions reflect layout containment hierarchy
561 fixupContainmentHierarchy(uiTopology.nullRegion());
562 uiTopology.allRegions().forEach(this::fixupContainmentHierarchy);
563
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500564 // make sure devices and hosts are in the correct region
Simon Huntb1ce2602016-07-23 14:04:31 -0700565 Set<UiDevice> allDevices = uiTopology.allDevices();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500566 Set<UiHost> allHosts = uiTopology.allHosts();
Simon Huntb1ce2602016-07-23 14:04:31 -0700567
568 services.region().getRegions().forEach(r -> {
569 RegionId rid = r.id();
Simon Huntc4ca7102017-04-08 22:28:04 -0700570
571// BasicRegionConfig rcfg = cfgService.getConfig(rid, BasicRegionConfig.class);
572// services.netcfg() ...
573 // TODO: figure out how to include peer-location data in UiRegion instance
574
Simon Huntb1ce2602016-07-23 14:04:31 -0700575 UiRegion region = uiTopology.findRegion(rid);
576 if (region != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500577 reconcileDevicesAndHostsWithRegion(allDevices, allHosts, rid, region);
Simon Huntb1ce2602016-07-23 14:04:31 -0700578 } else {
579 log.warn("No UiRegion in topology for ID {}", rid);
580 }
581 });
582
583 // what is left over, must belong to the null-region
584 Set<DeviceId> leftOver = new HashSet<>(allDevices.size());
585 allDevices.forEach(d -> leftOver.add(d.id()));
586 uiTopology.nullRegion().reconcileDevices(leftOver);
Simon Huntc13082f2016-08-03 21:20:23 -0700587
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500588 Set<HostId> leftOverHosts = new HashSet<>(allHosts.size());
589 allHosts.forEach(h -> leftOverHosts.add(h.id()));
590 uiTopology.nullRegion().reconcileHosts(leftOverHosts);
591
Simon Huntc13082f2016-08-03 21:20:23 -0700592 // now that we have correct region hierarchy, and devices are in their
593 // respective regions, we can compute synthetic links for each region.
594 uiTopology.computeSynthLinks();
Simon Huntb1ce2602016-07-23 14:04:31 -0700595 }
596
Steven Burrows8f45ce22016-10-27 20:04:14 -0500597 private void reconcileDevicesAndHostsWithRegion(Set<UiDevice> allDevices,
598 Set<UiHost> allHosts,
599 RegionId rid,
600 UiRegion region) {
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500601 Set<DeviceId> deviceIds = services.region().getRegionDevices(rid);
Steven Burrows8f45ce22016-10-27 20:04:14 -0500602 Set<HostId> hostIds = new HashSet<>();
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500603 region.reconcileDevices(deviceIds);
604
605 deviceIds.forEach(devId -> {
606 UiDevice dev = uiTopology.findDevice(devId);
607 if (dev != null) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500608 dev.setRegionId(rid);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500609 allDevices.remove(dev);
610 } else {
611 log.warn("Region device ID {} but no UiDevice in topology",
Simon Huntc4ca7102017-04-08 22:28:04 -0700612 devId);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500613 }
Steven Burrows8f45ce22016-10-27 20:04:14 -0500614
615 Set<Host> hosts = services.host().getConnectedHosts(devId);
616 for (Host h : hosts) {
617 HostId hid = h.id();
618 hostIds.add(hid);
619 UiHost host = uiTopology.findHost(hid);
620
621 if (host != null) {
622 host.setRegionId(rid);
623 allHosts.remove(host);
624 } else {
625 log.warn("Region host ID {} but no UiHost in topology",
Simon Huntc4ca7102017-04-08 22:28:04 -0700626 hid);
Steven Burrows8f45ce22016-10-27 20:04:14 -0500627 }
628 }
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500629 });
Steven Burrows8f45ce22016-10-27 20:04:14 -0500630
631 region.reconcileHosts(hostIds);
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500632 }
633
Simon Huntc13082f2016-08-03 21:20:23 -0700634
Simon Huntc0f20c12016-05-09 09:30:20 -0700635 // === CACHE STATISTICS
636
Simon Hunt338a3b42016-04-14 09:43:52 -0700637 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700638 * Returns a detailed (multi-line) string showing the contents of the cache.
639 *
640 * @return detailed string
641 */
642 public String dumpString() {
643 return uiTopology.dumpString();
644 }
645
646 /**
Simon Hunt338a3b42016-04-14 09:43:52 -0700647 * Returns the number of members in the cluster.
648 *
649 * @return number of cluster members
650 */
651 public int clusterMemberCount() {
652 return uiTopology.clusterMemberCount();
653 }
654
655 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700656 * Returns the number of regions in the topology.
Simon Hunt338a3b42016-04-14 09:43:52 -0700657 *
658 * @return number of regions
659 */
660 public int regionCount() {
661 return uiTopology.regionCount();
662 }
Simon Hunt58a0dd02016-05-17 11:54:23 -0700663
664 /**
665 * Returns the number of devices in the topology.
666 *
667 * @return number of devices
668 */
669 public int deviceCount() {
670 return uiTopology.deviceCount();
671 }
672
673 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700674 * Returns the number of device links in the topology.
Simon Hunt58a0dd02016-05-17 11:54:23 -0700675 *
Simon Huntc13082f2016-08-03 21:20:23 -0700676 * @return number of device links
Simon Hunt58a0dd02016-05-17 11:54:23 -0700677 */
Simon Huntc13082f2016-08-03 21:20:23 -0700678 public int deviceLinkCount() {
679 return uiTopology.deviceLinkCount();
680 }
681
682 /**
683 * Returns the number of edge links in the topology.
684 *
685 * @return number of edge links
686 */
687 public int edgeLinkCount() {
688 return uiTopology.edgeLinkCount();
Simon Hunt58a0dd02016-05-17 11:54:23 -0700689 }
690
691 /**
692 * Returns the number of hosts in the topology.
693 *
694 * @return number of hosts
695 */
696 public int hostCount() {
697 return uiTopology.hostCount();
698 }
Simon Huntd5b96732016-07-08 13:22:27 -0700699
Simon Huntc13082f2016-08-03 21:20:23 -0700700 /**
701 * Returns the number of synthetic links in the topology.
702 *
703 * @return the number of synthetic links
704 */
705 public int synthLinkCount() {
706 return uiTopology.synthLinkCount();
707 }
Simon Huntcda9c032016-04-11 10:32:54 -0700708}