blob: 4fe24b6a0530fc3de3261b6c187ee5c1f1e33d9d [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 {
Simon Hunt23fb1352016-04-11 12:15:19 -070039
Sean Condon28884332019-03-21 14:07:00 +000040 public static final String NULL_NAME = "(root)";
Simon Huntf836a872016-08-10 17:37:36 -070041 private static final String NO_NAME = "???";
Sean Condonee545762019-03-09 10:43:58 +000042 private static final String MEMO_ADDED = "added";
Simon Huntb1ce2602016-07-23 14:04:31 -070043
44 /**
45 * The identifier for the null-region. That is, a container for devices,
46 * hosts, and links for those that belong to no region.
47 */
48 public static final RegionId NULL_ID = regionId(NULL_NAME);
49
50 private static final String[] DEFAULT_LAYER_TAGS = {
51 UiNode.LAYER_OPTICAL,
52 UiNode.LAYER_PACKET,
53 UiNode.LAYER_DEFAULT
54 };
55
Simon Huntc0f20c12016-05-09 09:30:20 -070056 // loose bindings to things in this region
57 private final Set<DeviceId> deviceIds = new HashSet<>();
58 private final Set<HostId> hostIds = new HashSet<>();
Simon Hunt23fb1352016-04-11 12:15:19 -070059
Simon Huntd5b96732016-07-08 13:22:27 -070060 private final List<String> layerOrder = new ArrayList<>();
61
Simon Huntc0f20c12016-05-09 09:30:20 -070062 private final UiTopology topology;
Simon Hunt23fb1352016-04-11 12:15:19 -070063
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070064 private final RegionId regionId;
Simon Huntc0f20c12016-05-09 09:30:20 -070065
Simon Hunt4f4ffc32016-08-03 18:30:47 -070066 // keep track of hierarchy (inferred from UiTopoLayoutService)
67 private RegionId parent;
68 private final Set<RegionId> kids = new HashSet<>();
69
Simon Huntc0f20c12016-05-09 09:30:20 -070070 /**
71 * Constructs a UI region, with a reference to the specified backing region.
72 *
73 * @param topology parent topology
74 * @param region backing region
75 */
76 public UiRegion(UiTopology topology, Region region) {
Simon Huntb1ce2602016-07-23 14:04:31 -070077 // Implementation Note: if region is null, this UiRegion is being used
78 // as a container for devices, hosts, links that belong to no region.
Simon Huntc0f20c12016-05-09 09:30:20 -070079 this.topology = topology;
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070080 this.regionId = region == null ? NULL_ID : region.id();
Simon Huntb1ce2602016-07-23 14:04:31 -070081
82 setLayerOrder(DEFAULT_LAYER_TAGS);
Simon Huntc0f20c12016-05-09 09:30:20 -070083 }
Simon Hunt23fb1352016-04-11 12:15:19 -070084
85 @Override
86 protected void destroy() {
Simon Huntc0f20c12016-05-09 09:30:20 -070087 deviceIds.clear();
88 hostIds.clear();
Simon Hunt23fb1352016-04-11 12:15:19 -070089 }
90
Simon Hunt642bc452016-05-04 19:34:45 -070091 /**
Simon Huntd5b96732016-07-08 13:22:27 -070092 * Sets the layer order for this region.
93 * Typically, the {@code UiNode.LAYER_*} constants will be used here.
94 *
95 * @param layers the layers
96 */
97 public void setLayerOrder(String... layers) {
98 layerOrder.clear();
99 Collections.addAll(layerOrder, layers);
100 }
101
102 /**
Simon Hunt642bc452016-05-04 19:34:45 -0700103 * Returns the identity of the region.
104 *
105 * @return region ID
106 */
107 public RegionId id() {
Thomas Vachuskab877a6f2017-04-14 11:43:30 -0700108 return regionId;
Simon Hunt642bc452016-05-04 19:34:45 -0700109 }
110
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700111 /**
112 * Returns the identity of the parent region.
113 *
114 * @return parent region ID
115 */
116 public RegionId parent() {
117 return parent;
118 }
119
120 /**
121 * Returns true if this is the root (default) region.
122 *
123 * @return true if root region
124 */
125 public boolean isRoot() {
Simon Hunt8f60ff82017-04-24 17:19:30 -0700126 return parent == null;
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700127 }
128
129 /**
130 * Returns the identities of the child regions.
131 *
132 * @return child region IDs
133 */
134 public Set<RegionId> children() {
135 return ImmutableSet.copyOf(kids);
136 }
137
138 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700139 * Returns the UI region that is the parent of this region.
140 *
141 * @return the parent region
142 */
143 public UiRegion parentRegion() {
144 return topology.findRegion(parent);
145 }
146
147 /**
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700148 * Sets the parent ID for this region.
149 *
150 * @param parentId parent ID
151 */
152 public void setParent(RegionId parentId) {
153 parent = parentId;
154 }
155
156 /**
157 * Sets the children IDs for this region.
158 *
159 * @param children children IDs
160 */
161 public void setChildren(Set<RegionId> children) {
162 kids.clear();
163 kids.addAll(children);
164 }
165
Simon Hunt642bc452016-05-04 19:34:45 -0700166 @Override
167 public String idAsString() {
168 return id().toString();
169 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700170
171 @Override
172 public String name() {
Thomas Vachuskab877a6f2017-04-14 11:43:30 -0700173 Region region = backingRegion();
Simon Huntb1ce2602016-07-23 14:04:31 -0700174 return region == null ? NULL_NAME : region.name();
Simon Huntc0f20c12016-05-09 09:30:20 -0700175 }
176
177 /**
Simon Huntb1ce2602016-07-23 14:04:31 -0700178 * Returns the region instance backing this UI region. If this instance
179 * represents the "null-region", the value returned will be null.
Simon Huntc0f20c12016-05-09 09:30:20 -0700180 *
181 * @return the backing region instance
182 */
183 public Region backingRegion() {
Simon Hunt8f60ff82017-04-24 17:19:30 -0700184 return isRoot() ? null : topology.services.region().getRegion(regionId);
Simon Huntc0f20c12016-05-09 09:30:20 -0700185 }
186
187 /**
188 * Make sure we have only these devices in the region.
189 *
190 * @param devices devices in the region
191 */
192 public void reconcileDevices(Set<DeviceId> devices) {
193 deviceIds.clear();
194 deviceIds.addAll(devices);
195 }
196
197 @Override
198 public String toString() {
199 return toStringHelper(this)
200 .add("id", id())
201 .add("name", name())
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700202 .add("parent", parent)
203 .add("kids", kids)
Simon Huntc0f20c12016-05-09 09:30:20 -0700204 .add("devices", deviceIds)
205 .add("#hosts", hostIds.size())
Simon Huntc0f20c12016-05-09 09:30:20 -0700206 .toString();
207 }
208
209 /**
210 * Returns the region's type.
211 *
212 * @return region type
213 */
214 public Region.Type type() {
Thomas Vachuskab877a6f2017-04-14 11:43:30 -0700215 Region region = backingRegion();
Simon Huntb1ce2602016-07-23 14:04:31 -0700216 return region == null ? null : region.type();
217 }
218
219
220 /**
221 * Returns the count of devices in this region.
222 *
223 * @return the device count
224 */
225 public int deviceCount() {
226 return deviceIds.size();
Simon Huntc0f20c12016-05-09 09:30:20 -0700227 }
228
229 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700230 * Returns the set of device identifiers for this region.
231 *
232 * @return device identifiers for this region
233 */
234 public Set<DeviceId> deviceIds() {
235 return ImmutableSet.copyOf(deviceIds);
236 }
237
238 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700239 * Returns the devices in this region.
240 *
241 * @return the devices in this region
242 */
243 public Set<UiDevice> devices() {
244 return topology.deviceSet(deviceIds);
245 }
246
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500247
248 /**
249 * Make sure we have only these hosts in the region.
250 *
251 * @param hosts hosts in the region
252 */
253 public void reconcileHosts(Set<HostId> hosts) {
254 hostIds.clear();
255 hostIds.addAll(hosts);
256 }
257
Simon Huntc0f20c12016-05-09 09:30:20 -0700258 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700259 * Returns the set of host identifiers for this region.
260 *
261 * @return host identifiers for this region
262 */
263 public Set<HostId> hostIds() {
264 return ImmutableSet.copyOf(hostIds);
265 }
266
267 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700268 * Returns the hosts in this region.
269 *
270 * @return the hosts in this region
271 */
272 public Set<UiHost> hosts() {
273 return topology.hostSet(hostIds);
274 }
275
276 /**
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500277 * Returns the count of devices in this region.
278 *
279 * @return the device count
280 */
281 public int hostCount() {
282 return hostIds.size();
283 }
284
285 /**
Simon Huntd5b96732016-07-08 13:22:27 -0700286 * Returns the order in which layers should be rendered. Lower layers
287 * come earlier in the list. For example, to indicate that nodes in the
288 * optical layer should be rendered "below" nodes in the packet layer,
289 * this method should return:
290 * <pre>
Simon Huntb1ce2602016-07-23 14:04:31 -0700291 * [UiNode.LAYER_OPTICAL, UiNode.LAYER_PACKET, UiNode.LAYER_DEFAULT]
Simon Huntd5b96732016-07-08 13:22:27 -0700292 * </pre>
293 *
294 * @return layer ordering
295 */
296 public List<String> layerOrder() {
297 return Collections.unmodifiableList(layerOrder);
298 }
Simon Huntf836a872016-08-10 17:37:36 -0700299
300 /**
301 * Guarantees to return a string for the name of the specified region.
302 * If region is null, we return the null region name, else we return
303 * the name as configured on the region.
304 *
305 * @param region the region whose name we require
306 * @return the region's name
307 */
308 public static String safeName(Region region) {
309 if (region == null) {
310 return NULL_NAME;
311 }
312 String name = region.name();
313 return isNullOrEmpty(name) ? NO_NAME : name;
314 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700315
316 /**
317 * Determins whether the specified event is relevant to the view
318 * constrained to this region.
319 *
320 * @param event UI model event
321 * @return true if relevant
322 */
323 public boolean isRelevant(UiModelEvent event) {
324 switch (event.type()) {
325 case CLUSTER_MEMBER_ADDED_OR_UPDATED:
326 case CLUSTER_MEMBER_REMOVED:
327 return true;
328
329 case REGION_ADDED_OR_UPDATED:
Sean Condonee545762019-03-09 10:43:58 +0000330 if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
331 regionId.toString().equalsIgnoreCase(
332 ((UiRegion) event.subject()).backingRegion().toString())) {
333 return true;
334 } else {
335 return isDeviceRelevant(((UiDevice) event.subject()).id());
336 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700337 case REGION_REMOVED:
338 return isRegionRelevant(((UiRegion) event.subject()).id());
339
340 case DEVICE_ADDED_OR_UPDATED:
Sean Condon28884332019-03-21 14:07:00 +0000341 final UiDevice uiDevice = (UiDevice) event.subject();
Sean Condonee545762019-03-09 10:43:58 +0000342 if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
Sean Condon28884332019-03-21 14:07:00 +0000343 uiDevice.regionId() != null &&
344 regionId.equals(
345 ((UiDevice) event.subject()).regionId())) {
Sean Condonee545762019-03-09 10:43:58 +0000346 return true;
347 } else {
348 return isDeviceRelevant(((UiDevice) event.subject()).id());
349 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700350 case DEVICE_REMOVED:
351 return isDeviceRelevant(((UiDevice) event.subject()).id());
352
353 case LINK_ADDED_OR_UPDATED:
354 case LINK_REMOVED:
355 return isLinkRelevant((UiLink) event.subject());
356
357 case HOST_ADDED_OR_UPDATED:
Carmelo Cascone975a3432019-03-21 17:33:15 -0700358 final UiHost uiHost = (UiHost) event.subject();
Sean Condonee545762019-03-09 10:43:58 +0000359 if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
Carmelo Cascone975a3432019-03-21 17:33:15 -0700360 uiHost.regionId() != null &&
Sean Condonee545762019-03-09 10:43:58 +0000361 regionId.toString().equalsIgnoreCase(
Carmelo Cascone975a3432019-03-21 17:33:15 -0700362 uiHost.regionId().toString())) {
Sean Condonee545762019-03-09 10:43:58 +0000363 return true;
364 } else {
Sean Condon28884332019-03-21 14:07:00 +0000365 return isHostRelevant(((UiHost) event.subject()).id());
Sean Condonee545762019-03-09 10:43:58 +0000366 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700367 case HOST_MOVED:
368 case HOST_REMOVED:
369 return isDeviceRelevant(((UiHost) event.subject()).locationDevice());
370
371 default:
372 return true;
373 }
374 }
375
376 private boolean isDeviceRelevant(DeviceId deviceId) {
377 return deviceIds.contains(deviceId);
378 }
379
Sean Condon28884332019-03-21 14:07:00 +0000380 private boolean isHostRelevant(HostId hostId) {
381 return hostIds.contains(hostId);
382 }
383
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700384 private boolean isLinkRelevant(UiLink uiLink) {
385 if (uiLink instanceof UiDeviceLink) {
386 UiDeviceLink uiDeviceLink = (UiDeviceLink) uiLink;
387 return isDeviceRelevant(uiDeviceLink.deviceA()) ||
388 isDeviceRelevant(uiDeviceLink.deviceB());
389
390 } else if (uiLink instanceof UiRegionLink) {
391 UiRegionLink uiRegionLink = (UiRegionLink) uiLink;
392 return isRegionRelevant(uiRegionLink.regionA()) ||
393 isRegionRelevant(uiRegionLink.regionB());
394
395 } else if (uiLink instanceof UiRegionDeviceLink) {
396 UiRegionDeviceLink uiRegionDeviceLink = (UiRegionDeviceLink) uiLink;
397 return isRegionRelevant(uiRegionDeviceLink.region()) ||
398 isDeviceRelevant(uiRegionDeviceLink.device());
399
400 } else if (uiLink instanceof UiEdgeLink) {
401 UiEdgeLink uiEdgeLink = (UiEdgeLink) uiLink;
402 return isDeviceRelevant(uiEdgeLink.deviceId());
403 }
404 return false;
405 }
406
407 private boolean isRegionRelevant(RegionId regionId) {
408 return kids.contains(regionId);
409 }
Simon Hunt5f6dbf82016-03-30 08:53:33 -0700410}