blob: c8a9e05edfc1e6402e21ebfc02d07fda1812568e [file] [log] [blame]
Simon Hunt5f6dbf82016-03-30 08:53:33 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Simon Hunt5f6dbf82016-03-30 08:53:33 -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.model.topo;
18
Simon Hunt58a0dd02016-05-17 11:54:23 -070019import com.google.common.collect.ImmutableSet;
Simon Huntc0f20c12016-05-09 09:30:20 -070020import org.onosproject.net.DeviceId;
21import org.onosproject.net.HostId;
Simon Hunt23fb1352016-04-11 12:15:19 -070022import org.onosproject.net.region.Region;
Simon Hunt642bc452016-05-04 19:34:45 -070023import org.onosproject.net.region.RegionId;
Simon Hunt23fb1352016-04-11 12:15:19 -070024
Simon Huntd5b96732016-07-08 13:22:27 -070025import java.util.ArrayList;
26import java.util.Collections;
Simon Huntc0f20c12016-05-09 09:30:20 -070027import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070028import java.util.List;
Simon Hunt23fb1352016-04-11 12:15:19 -070029import java.util.Set;
Simon Huntc0f20c12016-05-09 09:30:20 -070030
31import static com.google.common.base.MoreObjects.toStringHelper;
Simon Huntf836a872016-08-10 17:37:36 -070032import static com.google.common.base.Strings.isNullOrEmpty;
Simon Huntb1ce2602016-07-23 14:04:31 -070033import static org.onosproject.net.region.RegionId.regionId;
Simon Hunt23fb1352016-04-11 12:15:19 -070034
Simon Hunt5f6dbf82016-03-30 08:53:33 -070035/**
36 * Represents a region.
37 */
38public class UiRegion extends UiNode {
Sean Condon28884332019-03-21 14:07:00 +000039 public static final String NULL_NAME = "(root)";
Simon Huntf836a872016-08-10 17:37:36 -070040 private static final String NO_NAME = "???";
Sean Condonee545762019-03-09 10:43:58 +000041 private static final String MEMO_ADDED = "added";
Simon Huntb1ce2602016-07-23 14:04:31 -070042
43 /**
44 * The identifier for the null-region. That is, a container for devices,
45 * hosts, and links for those that belong to no region.
46 */
47 public static final RegionId NULL_ID = regionId(NULL_NAME);
48
49 private static final String[] DEFAULT_LAYER_TAGS = {
50 UiNode.LAYER_OPTICAL,
51 UiNode.LAYER_PACKET,
52 UiNode.LAYER_DEFAULT
53 };
54
Simon Huntc0f20c12016-05-09 09:30:20 -070055 // loose bindings to things in this region
56 private final Set<DeviceId> deviceIds = new HashSet<>();
57 private final Set<HostId> hostIds = new HashSet<>();
Simon Hunt23fb1352016-04-11 12:15:19 -070058
Simon Huntd5b96732016-07-08 13:22:27 -070059 private final List<String> layerOrder = new ArrayList<>();
60
Simon Huntc0f20c12016-05-09 09:30:20 -070061 private final UiTopology topology;
Simon Hunt23fb1352016-04-11 12:15:19 -070062
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070063 private final RegionId regionId;
Simon Huntc0f20c12016-05-09 09:30:20 -070064
Simon Hunt4f4ffc32016-08-03 18:30:47 -070065 // keep track of hierarchy (inferred from UiTopoLayoutService)
66 private RegionId parent;
67 private final Set<RegionId> kids = new HashSet<>();
68
Simon Huntc0f20c12016-05-09 09:30:20 -070069 /**
70 * Constructs a UI region, with a reference to the specified backing region.
71 *
72 * @param topology parent topology
73 * @param region backing region
74 */
75 public UiRegion(UiTopology topology, Region region) {
Simon Huntb1ce2602016-07-23 14:04:31 -070076 // Implementation Note: if region is null, this UiRegion is being used
77 // as a container for devices, hosts, links that belong to no region.
Simon Huntc0f20c12016-05-09 09:30:20 -070078 this.topology = topology;
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070079 this.regionId = region == null ? NULL_ID : region.id();
Simon Huntb1ce2602016-07-23 14:04:31 -070080
81 setLayerOrder(DEFAULT_LAYER_TAGS);
Simon Huntc0f20c12016-05-09 09:30:20 -070082 }
Simon Hunt23fb1352016-04-11 12:15:19 -070083
84 @Override
85 protected void destroy() {
Simon Huntc0f20c12016-05-09 09:30:20 -070086 deviceIds.clear();
87 hostIds.clear();
Simon Hunt23fb1352016-04-11 12:15:19 -070088 }
89
Simon Hunt642bc452016-05-04 19:34:45 -070090 /**
Simon Huntd5b96732016-07-08 13:22:27 -070091 * Sets the layer order for this region.
92 * Typically, the {@code UiNode.LAYER_*} constants will be used here.
93 *
94 * @param layers the layers
95 */
96 public void setLayerOrder(String... layers) {
97 layerOrder.clear();
98 Collections.addAll(layerOrder, layers);
99 }
100
101 /**
Simon Hunt642bc452016-05-04 19:34:45 -0700102 * Returns the identity of the region.
103 *
104 * @return region ID
105 */
106 public RegionId id() {
Thomas Vachuskab877a6f2017-04-14 11:43:30 -0700107 return regionId;
Simon Hunt642bc452016-05-04 19:34:45 -0700108 }
109
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700110 /**
111 * Returns the identity of the parent region.
112 *
113 * @return parent region ID
114 */
115 public RegionId parent() {
116 return parent;
117 }
118
119 /**
120 * Returns true if this is the root (default) region.
121 *
122 * @return true if root region
123 */
124 public boolean isRoot() {
Simon Hunt8f60ff82017-04-24 17:19:30 -0700125 return parent == null;
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700126 }
127
128 /**
129 * Returns the identities of the child regions.
130 *
131 * @return child region IDs
132 */
133 public Set<RegionId> children() {
134 return ImmutableSet.copyOf(kids);
135 }
136
137 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700138 * Returns the UI region that is the parent of this region.
139 *
140 * @return the parent region
141 */
142 public UiRegion parentRegion() {
143 return topology.findRegion(parent);
144 }
145
146 /**
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700147 * Sets the parent ID for this region.
148 *
149 * @param parentId parent ID
150 */
151 public void setParent(RegionId parentId) {
152 parent = parentId;
153 }
154
155 /**
156 * Sets the children IDs for this region.
157 *
158 * @param children children IDs
159 */
160 public void setChildren(Set<RegionId> children) {
161 kids.clear();
162 kids.addAll(children);
163 }
164
Simon Hunt642bc452016-05-04 19:34:45 -0700165 @Override
166 public String idAsString() {
167 return id().toString();
168 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700169
170 @Override
171 public String name() {
Thomas Vachuskab877a6f2017-04-14 11:43:30 -0700172 Region region = backingRegion();
Simon Huntb1ce2602016-07-23 14:04:31 -0700173 return region == null ? NULL_NAME : region.name();
Simon Huntc0f20c12016-05-09 09:30:20 -0700174 }
175
176 /**
Simon Huntb1ce2602016-07-23 14:04:31 -0700177 * Returns the region instance backing this UI region. If this instance
178 * represents the "null-region", the value returned will be null.
Simon Huntc0f20c12016-05-09 09:30:20 -0700179 *
180 * @return the backing region instance
181 */
182 public Region backingRegion() {
Simon Hunt8f60ff82017-04-24 17:19:30 -0700183 return isRoot() ? null : topology.services.region().getRegion(regionId);
Simon Huntc0f20c12016-05-09 09:30:20 -0700184 }
185
186 /**
187 * Make sure we have only these devices in the region.
188 *
189 * @param devices devices in the region
190 */
191 public void reconcileDevices(Set<DeviceId> devices) {
192 deviceIds.clear();
193 deviceIds.addAll(devices);
194 }
195
196 @Override
197 public String toString() {
198 return toStringHelper(this)
199 .add("id", id())
200 .add("name", name())
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700201 .add("parent", parent)
202 .add("kids", kids)
Simon Huntc0f20c12016-05-09 09:30:20 -0700203 .add("devices", deviceIds)
204 .add("#hosts", hostIds.size())
Simon Huntc0f20c12016-05-09 09:30:20 -0700205 .toString();
206 }
207
208 /**
209 * Returns the region's type.
210 *
211 * @return region type
212 */
213 public Region.Type type() {
Thomas Vachuskab877a6f2017-04-14 11:43:30 -0700214 Region region = backingRegion();
Simon Huntb1ce2602016-07-23 14:04:31 -0700215 return region == null ? null : region.type();
216 }
217
218
219 /**
220 * Returns the count of devices in this region.
221 *
222 * @return the device count
223 */
224 public int deviceCount() {
225 return deviceIds.size();
Simon Huntc0f20c12016-05-09 09:30:20 -0700226 }
227
228 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700229 * Returns the set of device identifiers for this region.
230 *
231 * @return device identifiers for this region
232 */
233 public Set<DeviceId> deviceIds() {
234 return ImmutableSet.copyOf(deviceIds);
235 }
236
237 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700238 * Returns the devices in this region.
239 *
240 * @return the devices in this region
241 */
242 public Set<UiDevice> devices() {
243 return topology.deviceSet(deviceIds);
244 }
245
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500246
247 /**
248 * Make sure we have only these hosts in the region.
249 *
250 * @param hosts hosts in the region
251 */
252 public void reconcileHosts(Set<HostId> hosts) {
253 hostIds.clear();
254 hostIds.addAll(hosts);
255 }
256
Simon Huntc0f20c12016-05-09 09:30:20 -0700257 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700258 * Returns the set of host identifiers for this region.
259 *
260 * @return host identifiers for this region
261 */
262 public Set<HostId> hostIds() {
263 return ImmutableSet.copyOf(hostIds);
264 }
265
266 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700267 * Returns the hosts in this region.
268 *
269 * @return the hosts in this region
270 */
271 public Set<UiHost> hosts() {
272 return topology.hostSet(hostIds);
273 }
274
275 /**
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500276 * Returns the count of devices in this region.
277 *
278 * @return the device count
279 */
280 public int hostCount() {
281 return hostIds.size();
282 }
283
284 /**
Simon Huntd5b96732016-07-08 13:22:27 -0700285 * Returns the order in which layers should be rendered. Lower layers
286 * come earlier in the list. For example, to indicate that nodes in the
287 * optical layer should be rendered "below" nodes in the packet layer,
288 * this method should return:
289 * <pre>
Simon Huntb1ce2602016-07-23 14:04:31 -0700290 * [UiNode.LAYER_OPTICAL, UiNode.LAYER_PACKET, UiNode.LAYER_DEFAULT]
Simon Huntd5b96732016-07-08 13:22:27 -0700291 * </pre>
292 *
293 * @return layer ordering
294 */
295 public List<String> layerOrder() {
296 return Collections.unmodifiableList(layerOrder);
297 }
Simon Huntf836a872016-08-10 17:37:36 -0700298
299 /**
300 * Guarantees to return a string for the name of the specified region.
301 * If region is null, we return the null region name, else we return
302 * the name as configured on the region.
303 *
304 * @param region the region whose name we require
305 * @return the region's name
306 */
307 public static String safeName(Region region) {
308 if (region == null) {
309 return NULL_NAME;
310 }
311 String name = region.name();
312 return isNullOrEmpty(name) ? NO_NAME : name;
313 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700314
315 /**
316 * Determins whether the specified event is relevant to the view
317 * constrained to this region.
318 *
319 * @param event UI model event
320 * @return true if relevant
321 */
322 public boolean isRelevant(UiModelEvent event) {
323 switch (event.type()) {
324 case CLUSTER_MEMBER_ADDED_OR_UPDATED:
325 case CLUSTER_MEMBER_REMOVED:
326 return true;
327
328 case REGION_ADDED_OR_UPDATED:
Sean Condonee545762019-03-09 10:43:58 +0000329 if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
330 regionId.toString().equalsIgnoreCase(
331 ((UiRegion) event.subject()).backingRegion().toString())) {
332 return true;
333 } else {
334 return isDeviceRelevant(((UiDevice) event.subject()).id());
335 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700336 case REGION_REMOVED:
337 return isRegionRelevant(((UiRegion) event.subject()).id());
338
339 case DEVICE_ADDED_OR_UPDATED:
Sean Condon28884332019-03-21 14:07:00 +0000340 final UiDevice uiDevice = (UiDevice) event.subject();
Sean Condonee545762019-03-09 10:43:58 +0000341 if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
Sean Condon28884332019-03-21 14:07:00 +0000342 uiDevice.regionId() != null &&
343 regionId.equals(
344 ((UiDevice) event.subject()).regionId())) {
Sean Condonee545762019-03-09 10:43:58 +0000345 return true;
346 } else {
347 return isDeviceRelevant(((UiDevice) event.subject()).id());
348 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700349 case DEVICE_REMOVED:
350 return isDeviceRelevant(((UiDevice) event.subject()).id());
351
352 case LINK_ADDED_OR_UPDATED:
353 case LINK_REMOVED:
354 return isLinkRelevant((UiLink) event.subject());
355
356 case HOST_ADDED_OR_UPDATED:
Carmelo Cascone975a3432019-03-21 17:33:15 -0700357 final UiHost uiHost = (UiHost) event.subject();
Sean Condonee545762019-03-09 10:43:58 +0000358 if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
Carmelo Cascone975a3432019-03-21 17:33:15 -0700359 uiHost.regionId() != null &&
Sean Condonee545762019-03-09 10:43:58 +0000360 regionId.toString().equalsIgnoreCase(
Carmelo Cascone975a3432019-03-21 17:33:15 -0700361 uiHost.regionId().toString())) {
Sean Condonee545762019-03-09 10:43:58 +0000362 return true;
363 } else {
Sean Condon28884332019-03-21 14:07:00 +0000364 return isHostRelevant(((UiHost) event.subject()).id());
Sean Condonee545762019-03-09 10:43:58 +0000365 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700366 case HOST_MOVED:
367 case HOST_REMOVED:
368 return isDeviceRelevant(((UiHost) event.subject()).locationDevice());
369
370 default:
371 return true;
372 }
373 }
374
Sean Condona3ce00b2019-04-10 11:44:01 +0100375 public void newDeviceAdded(DeviceId deviceId) {
376 deviceIds.add(deviceId);
377 }
378
379 public void deviceRemoved(DeviceId deviceId) {
380 deviceIds.remove(deviceId);
381 }
382
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700383 private boolean isDeviceRelevant(DeviceId deviceId) {
384 return deviceIds.contains(deviceId);
385 }
386
Sean Condon28884332019-03-21 14:07:00 +0000387 private boolean isHostRelevant(HostId hostId) {
388 return hostIds.contains(hostId);
389 }
390
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700391 private boolean isLinkRelevant(UiLink uiLink) {
392 if (uiLink instanceof UiDeviceLink) {
393 UiDeviceLink uiDeviceLink = (UiDeviceLink) uiLink;
394 return isDeviceRelevant(uiDeviceLink.deviceA()) ||
395 isDeviceRelevant(uiDeviceLink.deviceB());
396
397 } else if (uiLink instanceof UiRegionLink) {
398 UiRegionLink uiRegionLink = (UiRegionLink) uiLink;
399 return isRegionRelevant(uiRegionLink.regionA()) ||
400 isRegionRelevant(uiRegionLink.regionB());
401
402 } else if (uiLink instanceof UiRegionDeviceLink) {
403 UiRegionDeviceLink uiRegionDeviceLink = (UiRegionDeviceLink) uiLink;
404 return isRegionRelevant(uiRegionDeviceLink.region()) ||
405 isDeviceRelevant(uiRegionDeviceLink.device());
406
407 } else if (uiLink instanceof UiEdgeLink) {
408 UiEdgeLink uiEdgeLink = (UiEdgeLink) uiLink;
409 return isDeviceRelevant(uiEdgeLink.deviceId());
410 }
411 return false;
412 }
413
414 private boolean isRegionRelevant(RegionId regionId) {
415 return kids.contains(regionId);
416 }
Simon Hunt5f6dbf82016-03-30 08:53:33 -0700417}