blob: 36629f26d11d2cd02a38e91874b7f0dfba17b71e [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;
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -070033import static org.onosproject.net.DeviceId.deviceId;
Simon Huntb1ce2602016-07-23 14:04:31 -070034import static org.onosproject.net.region.RegionId.regionId;
Simon Hunt23fb1352016-04-11 12:15:19 -070035
Simon Hunt5f6dbf82016-03-30 08:53:33 -070036/**
37 * Represents a region.
38 */
39public class UiRegion extends UiNode {
Simon Hunt23fb1352016-04-11 12:15:19 -070040
Simon Huntf836a872016-08-10 17:37:36 -070041 private static final String NULL_NAME = "(root)";
42 private static final String NO_NAME = "???";
Sean Condonee545762019-03-09 10:43:58 +000043 private static final String MEMO_ADDED = "added";
Simon Huntb1ce2602016-07-23 14:04:31 -070044
45 /**
46 * The identifier for the null-region. That is, a container for devices,
47 * hosts, and links for those that belong to no region.
48 */
49 public static final RegionId NULL_ID = regionId(NULL_NAME);
50
51 private static final String[] DEFAULT_LAYER_TAGS = {
52 UiNode.LAYER_OPTICAL,
53 UiNode.LAYER_PACKET,
54 UiNode.LAYER_DEFAULT
55 };
56
Simon Huntc0f20c12016-05-09 09:30:20 -070057 // loose bindings to things in this region
58 private final Set<DeviceId> deviceIds = new HashSet<>();
59 private final Set<HostId> hostIds = new HashSet<>();
Simon Hunt23fb1352016-04-11 12:15:19 -070060
Simon Huntd5b96732016-07-08 13:22:27 -070061 private final List<String> layerOrder = new ArrayList<>();
62
Simon Huntc0f20c12016-05-09 09:30:20 -070063 private final UiTopology topology;
Simon Hunt23fb1352016-04-11 12:15:19 -070064
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070065 private final RegionId regionId;
Simon Huntc0f20c12016-05-09 09:30:20 -070066
Simon Hunt4f4ffc32016-08-03 18:30:47 -070067 // keep track of hierarchy (inferred from UiTopoLayoutService)
68 private RegionId parent;
69 private final Set<RegionId> kids = new HashSet<>();
70
Simon Huntc0f20c12016-05-09 09:30:20 -070071 /**
72 * Constructs a UI region, with a reference to the specified backing region.
73 *
74 * @param topology parent topology
75 * @param region backing region
76 */
77 public UiRegion(UiTopology topology, Region region) {
Simon Huntb1ce2602016-07-23 14:04:31 -070078 // Implementation Note: if region is null, this UiRegion is being used
79 // as a container for devices, hosts, links that belong to no region.
Simon Huntc0f20c12016-05-09 09:30:20 -070080 this.topology = topology;
Thomas Vachuskab877a6f2017-04-14 11:43:30 -070081 this.regionId = region == null ? NULL_ID : region.id();
Simon Huntb1ce2602016-07-23 14:04:31 -070082
83 setLayerOrder(DEFAULT_LAYER_TAGS);
Simon Huntc0f20c12016-05-09 09:30:20 -070084 }
Simon Hunt23fb1352016-04-11 12:15:19 -070085
86 @Override
87 protected void destroy() {
Simon Huntc0f20c12016-05-09 09:30:20 -070088 deviceIds.clear();
89 hostIds.clear();
Simon Hunt23fb1352016-04-11 12:15:19 -070090 }
91
Simon Hunt642bc452016-05-04 19:34:45 -070092 /**
Simon Huntd5b96732016-07-08 13:22:27 -070093 * Sets the layer order for this region.
94 * Typically, the {@code UiNode.LAYER_*} constants will be used here.
95 *
96 * @param layers the layers
97 */
98 public void setLayerOrder(String... layers) {
99 layerOrder.clear();
100 Collections.addAll(layerOrder, layers);
101 }
102
103 /**
Simon Hunt642bc452016-05-04 19:34:45 -0700104 * Returns the identity of the region.
105 *
106 * @return region ID
107 */
108 public RegionId id() {
Thomas Vachuskab877a6f2017-04-14 11:43:30 -0700109 return regionId;
Simon Hunt642bc452016-05-04 19:34:45 -0700110 }
111
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700112 /**
113 * Returns the identity of the parent region.
114 *
115 * @return parent region ID
116 */
117 public RegionId parent() {
118 return parent;
119 }
120
121 /**
122 * Returns true if this is the root (default) region.
123 *
124 * @return true if root region
125 */
126 public boolean isRoot() {
Simon Hunt8f60ff82017-04-24 17:19:30 -0700127 return parent == null;
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700128 }
129
130 /**
131 * Returns the identities of the child regions.
132 *
133 * @return child region IDs
134 */
135 public Set<RegionId> children() {
136 return ImmutableSet.copyOf(kids);
137 }
138
139 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700140 * Returns the UI region that is the parent of this region.
141 *
142 * @return the parent region
143 */
144 public UiRegion parentRegion() {
145 return topology.findRegion(parent);
146 }
147
148 /**
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700149 * Sets the parent ID for this region.
150 *
151 * @param parentId parent ID
152 */
153 public void setParent(RegionId parentId) {
154 parent = parentId;
155 }
156
157 /**
158 * Sets the children IDs for this region.
159 *
160 * @param children children IDs
161 */
162 public void setChildren(Set<RegionId> children) {
163 kids.clear();
164 kids.addAll(children);
165 }
166
Simon Hunt642bc452016-05-04 19:34:45 -0700167 @Override
168 public String idAsString() {
169 return id().toString();
170 }
Simon Huntc0f20c12016-05-09 09:30:20 -0700171
172 @Override
173 public String name() {
Thomas Vachuskab877a6f2017-04-14 11:43:30 -0700174 Region region = backingRegion();
Simon Huntb1ce2602016-07-23 14:04:31 -0700175 return region == null ? NULL_NAME : region.name();
Simon Huntc0f20c12016-05-09 09:30:20 -0700176 }
177
178 /**
Simon Huntb1ce2602016-07-23 14:04:31 -0700179 * Returns the region instance backing this UI region. If this instance
180 * represents the "null-region", the value returned will be null.
Simon Huntc0f20c12016-05-09 09:30:20 -0700181 *
182 * @return the backing region instance
183 */
184 public Region backingRegion() {
Simon Hunt8f60ff82017-04-24 17:19:30 -0700185 return isRoot() ? null : topology.services.region().getRegion(regionId);
Simon Huntc0f20c12016-05-09 09:30:20 -0700186 }
187
188 /**
189 * Make sure we have only these devices in the region.
190 *
191 * @param devices devices in the region
192 */
193 public void reconcileDevices(Set<DeviceId> devices) {
194 deviceIds.clear();
195 deviceIds.addAll(devices);
196 }
197
198 @Override
199 public String toString() {
200 return toStringHelper(this)
201 .add("id", id())
202 .add("name", name())
Simon Hunt4f4ffc32016-08-03 18:30:47 -0700203 .add("parent", parent)
204 .add("kids", kids)
Simon Huntc0f20c12016-05-09 09:30:20 -0700205 .add("devices", deviceIds)
206 .add("#hosts", hostIds.size())
Simon Huntc0f20c12016-05-09 09:30:20 -0700207 .toString();
208 }
209
210 /**
211 * Returns the region's type.
212 *
213 * @return region type
214 */
215 public Region.Type type() {
Thomas Vachuskab877a6f2017-04-14 11:43:30 -0700216 Region region = backingRegion();
Simon Huntb1ce2602016-07-23 14:04:31 -0700217 return region == null ? null : region.type();
218 }
219
220
221 /**
222 * Returns the count of devices in this region.
223 *
224 * @return the device count
225 */
226 public int deviceCount() {
227 return deviceIds.size();
Simon Huntc0f20c12016-05-09 09:30:20 -0700228 }
229
230 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700231 * Returns the set of device identifiers for this region.
232 *
233 * @return device identifiers for this region
234 */
235 public Set<DeviceId> deviceIds() {
236 return ImmutableSet.copyOf(deviceIds);
237 }
238
239 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700240 * Returns the devices in this region.
241 *
242 * @return the devices in this region
243 */
244 public Set<UiDevice> devices() {
245 return topology.deviceSet(deviceIds);
246 }
247
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500248
249 /**
250 * Make sure we have only these hosts in the region.
251 *
252 * @param hosts hosts in the region
253 */
254 public void reconcileHosts(Set<HostId> hosts) {
255 hostIds.clear();
256 hostIds.addAll(hosts);
257 }
258
Simon Huntc0f20c12016-05-09 09:30:20 -0700259 /**
Simon Hunt58a0dd02016-05-17 11:54:23 -0700260 * Returns the set of host identifiers for this region.
261 *
262 * @return host identifiers for this region
263 */
264 public Set<HostId> hostIds() {
265 return ImmutableSet.copyOf(hostIds);
266 }
267
268 /**
Simon Huntc0f20c12016-05-09 09:30:20 -0700269 * Returns the hosts in this region.
270 *
271 * @return the hosts in this region
272 */
273 public Set<UiHost> hosts() {
274 return topology.hostSet(hostIds);
275 }
276
277 /**
Steven Burrows19e6e4f2016-10-05 13:27:07 -0500278 * Returns the count of devices in this region.
279 *
280 * @return the device count
281 */
282 public int hostCount() {
283 return hostIds.size();
284 }
285
286 /**
Simon Huntd5b96732016-07-08 13:22:27 -0700287 * Returns the order in which layers should be rendered. Lower layers
288 * come earlier in the list. For example, to indicate that nodes in the
289 * optical layer should be rendered "below" nodes in the packet layer,
290 * this method should return:
291 * <pre>
Simon Huntb1ce2602016-07-23 14:04:31 -0700292 * [UiNode.LAYER_OPTICAL, UiNode.LAYER_PACKET, UiNode.LAYER_DEFAULT]
Simon Huntd5b96732016-07-08 13:22:27 -0700293 * </pre>
294 *
295 * @return layer ordering
296 */
297 public List<String> layerOrder() {
298 return Collections.unmodifiableList(layerOrder);
299 }
Simon Huntf836a872016-08-10 17:37:36 -0700300
301 /**
302 * Guarantees to return a string for the name of the specified region.
303 * If region is null, we return the null region name, else we return
304 * the name as configured on the region.
305 *
306 * @param region the region whose name we require
307 * @return the region's name
308 */
309 public static String safeName(Region region) {
310 if (region == null) {
311 return NULL_NAME;
312 }
313 String name = region.name();
314 return isNullOrEmpty(name) ? NO_NAME : name;
315 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700316
317 /**
318 * Determins whether the specified event is relevant to the view
319 * constrained to this region.
320 *
321 * @param event UI model event
322 * @return true if relevant
323 */
324 public boolean isRelevant(UiModelEvent event) {
325 switch (event.type()) {
326 case CLUSTER_MEMBER_ADDED_OR_UPDATED:
327 case CLUSTER_MEMBER_REMOVED:
328 return true;
329
330 case REGION_ADDED_OR_UPDATED:
Sean Condonee545762019-03-09 10:43:58 +0000331 if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
332 regionId.toString().equalsIgnoreCase(
333 ((UiRegion) event.subject()).backingRegion().toString())) {
334 return true;
335 } else {
336 return isDeviceRelevant(((UiDevice) event.subject()).id());
337 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700338 case REGION_REMOVED:
339 return isRegionRelevant(((UiRegion) event.subject()).id());
340
341 case DEVICE_ADDED_OR_UPDATED:
Sean Condonee545762019-03-09 10:43:58 +0000342 if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
343 regionId.toString().equalsIgnoreCase(
344 ((UiDevice) event.subject()).regionId().toString())) {
345 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:
Sean Condonee545762019-03-09 10:43:58 +0000357 if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
358 regionId.toString().equalsIgnoreCase(
359 ((UiHost) event.subject()).regionId().toString())) {
360 return true;
361 } else {
362 return isDeviceRelevant(((UiDevice) event.subject()).id());
363 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700364 case HOST_MOVED:
365 case HOST_REMOVED:
366 return isDeviceRelevant(((UiHost) event.subject()).locationDevice());
367
368 default:
369 return true;
370 }
371 }
372
373 private boolean isDeviceRelevant(DeviceId deviceId) {
374 return deviceIds.contains(deviceId);
375 }
376
377 private boolean isLinkRelevant(UiLink uiLink) {
378 if (uiLink instanceof UiDeviceLink) {
379 UiDeviceLink uiDeviceLink = (UiDeviceLink) uiLink;
380 return isDeviceRelevant(uiDeviceLink.deviceA()) ||
381 isDeviceRelevant(uiDeviceLink.deviceB());
382
383 } else if (uiLink instanceof UiRegionLink) {
384 UiRegionLink uiRegionLink = (UiRegionLink) uiLink;
385 return isRegionRelevant(uiRegionLink.regionA()) ||
386 isRegionRelevant(uiRegionLink.regionB());
387
388 } else if (uiLink instanceof UiRegionDeviceLink) {
389 UiRegionDeviceLink uiRegionDeviceLink = (UiRegionDeviceLink) uiLink;
390 return isRegionRelevant(uiRegionDeviceLink.region()) ||
391 isDeviceRelevant(uiRegionDeviceLink.device());
392
393 } else if (uiLink instanceof UiEdgeLink) {
394 UiEdgeLink uiEdgeLink = (UiEdgeLink) uiLink;
395 return isDeviceRelevant(uiEdgeLink.deviceId());
396 }
397 return false;
398 }
399
400 private boolean isRegionRelevant(RegionId regionId) {
401 return kids.contains(regionId);
402 }
Simon Hunt5f6dbf82016-03-30 08:53:33 -0700403}