blob: 363c4b53b0a638fcea0c6b7fe349b72f7244aaba [file] [log] [blame]
Simon Hunted804d52016-03-30 09:51:40 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Simon Hunted804d52016-03-30 09:51:40 -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
Simon Huntf679c4e2016-04-01 17:02:24 -070017package org.onosproject.ui.impl.topo;
Simon Hunted804d52016-03-30 09:51:40 -070018
Sean Condona3ce00b2019-04-10 11:44:01 +010019import org.onosproject.net.DeviceId;
Simon Huntb1ce2602016-07-23 14:04:31 -070020import org.onosproject.net.region.RegionId;
Simon Huntf679c4e2016-04-01 17:02:24 -070021import org.onosproject.ui.UiTopoLayoutService;
22import org.onosproject.ui.impl.UiWebSocket;
Simon Hunt0e161092017-05-08 17:41:38 -070023import org.onosproject.ui.model.topo.UiLinkId;
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -070024import org.onosproject.ui.model.topo.UiModelEvent;
Simon Huntcda9c032016-04-11 10:32:54 -070025import org.onosproject.ui.impl.topo.model.UiModelListener;
Simon Huntf679c4e2016-04-01 17:02:24 -070026import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
Simon Huntd5b96732016-07-08 13:22:27 -070027import org.onosproject.ui.model.topo.UiClusterMember;
Simon Hunt98189192016-07-29 19:02:27 -070028import org.onosproject.ui.model.topo.UiNode;
Simon Huntd5b96732016-07-08 13:22:27 -070029import org.onosproject.ui.model.topo.UiRegion;
Simon Huntc13082f2016-08-03 21:20:23 -070030import org.onosproject.ui.model.topo.UiSynthLink;
Simon Huntf679c4e2016-04-01 17:02:24 -070031import org.onosproject.ui.model.topo.UiTopoLayout;
Simon Hunted804d52016-03-30 09:51:40 -070032import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34
Simon Huntf836a872016-08-10 17:37:36 -070035import java.util.ArrayList;
Simon Huntb1ce2602016-07-23 14:04:31 -070036import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070037import java.util.List;
Simon Hunt0e161092017-05-08 17:41:38 -070038import java.util.Map;
Simon Hunt977aa052016-07-20 17:08:29 -070039import java.util.Set;
Simon Huntd5b96732016-07-08 13:22:27 -070040
Simon Hunted804d52016-03-30 09:51:40 -070041/**
Simon Huntf679c4e2016-04-01 17:02:24 -070042 * Coordinates with the {@link UiTopoLayoutService} to access
43 * {@link UiTopoLayout}s, and with the {@link UiSharedTopologyModel} which
Simon Huntcda9c032016-04-11 10:32:54 -070044 * maintains a local model of the network entities, tailored specifically
45 * for displaying on the UI.
Simon Hunted804d52016-03-30 09:51:40 -070046 * <p>
47 * Note that an instance of this class will be created for each
Simon Huntf679c4e2016-04-01 17:02:24 -070048 * {@link UiWebSocket} connection, and will contain
Simon Hunted804d52016-03-30 09:51:40 -070049 * the state of how the topology is laid out for the logged-in user.
Simon Huntd5b96732016-07-08 13:22:27 -070050 * <p>
51 * The expected pattern is for the {@link Topo2ViewMessageHandler} to obtain
52 * a reference to the session instance (via the {@link UiWebSocket}), and
53 * interact with it when topo-related events come in from the client.
Simon Hunted804d52016-03-30 09:51:40 -070054 */
Simon Huntcda9c032016-04-11 10:32:54 -070055public class UiTopoSession implements UiModelListener {
Simon Hunt977aa052016-07-20 17:08:29 -070056
Simon Hunt537bc762016-12-20 12:15:13 -080057 private static final String TOPO2_UI_MODEL_EVENT = "topo2UiModelEvent";
58
Simon Hunted804d52016-03-30 09:51:40 -070059 private final Logger log = LoggerFactory.getLogger(getClass());
60
Simon Huntf679c4e2016-04-01 17:02:24 -070061 private final UiWebSocket webSocket;
Simon Hunt7092cc42016-04-06 18:40:17 -070062 private final String username;
Simon Hunt537bc762016-12-20 12:15:13 -080063 private final Topo2Jsonifier t2json;
Simon Hunt7092cc42016-04-06 18:40:17 -070064
65 final UiSharedTopologyModel sharedModel;
Simon Hunted804d52016-03-30 09:51:40 -070066
67 private boolean registered = false;
68
Thomas Vachuska92b016b2016-05-20 11:37:57 -070069 private UiTopoLayoutService layoutService;
Simon Hunt7092cc42016-04-06 18:40:17 -070070 private UiTopoLayout currentLayout;
Simon Huntbbd0f462017-01-10 14:50:22 -080071 private boolean messagesEnabled = true;
Simon Huntf679c4e2016-04-01 17:02:24 -070072
Simon Hunted804d52016-03-30 09:51:40 -070073 /**
Simon Hunt537bc762016-12-20 12:15:13 -080074 * Creates a new topology session for the specified web socket connection,
75 * and references to JSONifier, shared model, and layout service.
Simon Hunt7092cc42016-04-06 18:40:17 -070076 *
Thomas Vachuska92b016b2016-05-20 11:37:57 -070077 * @param webSocket web socket
Simon Hunt537bc762016-12-20 12:15:13 -080078 * @param jsonifier JSONifier instance
Thomas Vachuska92b016b2016-05-20 11:37:57 -070079 * @param model share topology model
80 * @param layoutService topology layout service
Simon Hunted804d52016-03-30 09:51:40 -070081 */
Thomas Vachuska92b016b2016-05-20 11:37:57 -070082 public UiTopoSession(UiWebSocket webSocket,
Simon Hunt537bc762016-12-20 12:15:13 -080083 Topo2Jsonifier jsonifier,
Thomas Vachuska92b016b2016-05-20 11:37:57 -070084 UiSharedTopologyModel model,
85 UiTopoLayoutService layoutService) {
Simon Huntf679c4e2016-04-01 17:02:24 -070086 this.webSocket = webSocket;
Simon Hunt7092cc42016-04-06 18:40:17 -070087 this.username = webSocket.userName();
Simon Hunt537bc762016-12-20 12:15:13 -080088 this.t2json = jsonifier;
Simon Huntcda9c032016-04-11 10:32:54 -070089 this.sharedModel = model;
Thomas Vachuska92b016b2016-05-20 11:37:57 -070090 this.layoutService = layoutService;
Simon Hunted804d52016-03-30 09:51:40 -070091 }
92
Simon Hunt977aa052016-07-20 17:08:29 -070093 // constructs a neutered instance, for unit testing
94 UiTopoSession() {
95 webSocket = null;
96 username = null;
Simon Hunt537bc762016-12-20 12:15:13 -080097 t2json = null;
Simon Hunt977aa052016-07-20 17:08:29 -070098 sharedModel = null;
99 }
100
Simon Hunted804d52016-03-30 09:51:40 -0700101 /**
Simon Hunt7092cc42016-04-06 18:40:17 -0700102 * Initializes the session; registering with the shared model.
Simon Hunted804d52016-03-30 09:51:40 -0700103 */
104 public void init() {
105 if (!registered) {
Simon Hunt7092cc42016-04-06 18:40:17 -0700106 log.debug("{} : Registering with shared model", this);
Simon Hunted804d52016-03-30 09:51:40 -0700107 sharedModel.register(this);
Thomas Vachuska92b016b2016-05-20 11:37:57 -0700108 currentLayout = layoutService.getRootLayout();
Simon Hunted804d52016-03-30 09:51:40 -0700109 registered = true;
110 } else {
111 log.warn("already registered");
112 }
113 }
114
115 /**
Simon Hunt7092cc42016-04-06 18:40:17 -0700116 * Destroys the session; unregistering from the shared model.
Simon Hunted804d52016-03-30 09:51:40 -0700117 */
118 public void destroy() {
Simon Hunt7092cc42016-04-06 18:40:17 -0700119 if (registered) {
120 log.debug("{} : Unregistering from shared model", this);
Simon Hunted804d52016-03-30 09:51:40 -0700121 sharedModel.unregister(this);
Simon Huntf679c4e2016-04-01 17:02:24 -0700122 registered = false;
Simon Hunted804d52016-03-30 09:51:40 -0700123 } else {
124 log.warn("already unregistered");
125 }
126 }
127
128 @Override
129 public String toString() {
Simon Huntf679c4e2016-04-01 17:02:24 -0700130 return String.format("{UiTopoSession for user <%s>}", username);
Simon Hunted804d52016-03-30 09:51:40 -0700131 }
Simon Huntcda9c032016-04-11 10:32:54 -0700132
133 @Override
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700134 public boolean isRelevant(UiModelEvent event) {
135 if (!messagesEnabled) {
136 return false;
Simon Huntbbd0f462017-01-10 14:50:22 -0800137 }
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700138 UiRegion uiRegion = sharedModel.getRegion(currentLayout.regionId());
139 return uiRegion.isRelevant(event);
140 }
141
142 @Override
143 public void event(UiModelEvent event) {
Sean Condona3ce00b2019-04-10 11:44:01 +0100144 // To ensure link can be created devices have to be added to the list as
145 // they are created
146 if (event.type() == UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED &&
147 event.memo() == "added") {
148 UiRegion uiRegion = sharedModel.getRegion(currentLayout.regionId());
149 uiRegion.newDeviceAdded(DeviceId.deviceId(event.subject().idAsString()));
150 } else if (event.type() == UiModelEvent.Type.DEVICE_REMOVED) {
151 UiRegion uiRegion = sharedModel.getRegion(currentLayout.regionId());
152 uiRegion.deviceRemoved(DeviceId.deviceId(event.subject().idAsString()));
153 }
154
Thomas Vachuska8c0b18a2017-04-14 16:27:33 -0700155 webSocket.sendMessage(TOPO2_UI_MODEL_EVENT, t2json.jsonEvent(event));
Simon Huntcda9c032016-04-11 10:32:54 -0700156 }
Thomas Vachuska92b016b2016-05-20 11:37:57 -0700157
158 /**
159 * Returns the current layout context.
160 *
161 * @return current topology layout
162 */
163 public UiTopoLayout currentLayout() {
164 return currentLayout;
165 }
166
167 /**
Simon Huntf836a872016-08-10 17:37:36 -0700168 * Returns the breadcrumb trail from current layout to root. That is,
169 * element 0 of the list will be the current layout; the last element
170 * of the list will be the root layout. This list is guaranteed to have
171 * size of at least 1.
172 *
173 * @return breadcrumb trail
174 */
175 public List<UiTopoLayout> breadCrumbs() {
176 UiTopoLayout current = currentLayout;
177 List<UiTopoLayout> crumbs = new ArrayList<>();
178 crumbs.add(current);
179 while (!current.isRoot()) {
180 current = layoutService.getLayout(current.parent());
181 crumbs.add(current);
182 }
183 return crumbs;
184 }
185
186 /**
Thomas Vachuska92b016b2016-05-20 11:37:57 -0700187 * Changes the current layout context to the specified layout.
188 *
189 * @param topoLayout new topology layout context
190 */
191 public void setCurrentLayout(UiTopoLayout topoLayout) {
192 currentLayout = topoLayout;
193 }
194
195 /**
196 * Enables or disables the transmission of topology event update messages.
197 *
198 * @param enabled true if messages should be sent
199 */
200 public void enableEvent(boolean enabled) {
201 messagesEnabled = enabled;
202 }
Simon Huntd5b96732016-07-08 13:22:27 -0700203
204 /**
205 * Returns the list of ONOS instances (cluster members).
206 *
207 * @return the list of ONOS instances
208 */
209 public List<UiClusterMember> getAllInstances() {
210 return sharedModel.getClusterMembers();
211 }
212
213 /**
214 * Returns the region for the specified layout.
215 *
216 * @param layout layout filter
217 * @return region that the layout is based upon
218 */
219 public UiRegion getRegion(UiTopoLayout layout) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700220 RegionId rid = layout.regionId();
221 return rid == null ? sharedModel.getNullRegion() : sharedModel.getRegion(rid);
Simon Huntd5b96732016-07-08 13:22:27 -0700222 }
Simon Hunt977aa052016-07-20 17:08:29 -0700223
224 /**
Simon Hunt98189192016-07-29 19:02:27 -0700225 * Returns the regions/devices that are "peers" to this region. That is,
226 * based on the layout the user is viewing, all the regions/devices that
227 * are associated with layouts that share the same parent layout as this
228 * layout, AND that are linked to an element within this region.
Simon Hunt977aa052016-07-20 17:08:29 -0700229 *
230 * @param layout the layout being viewed
Simon Hunt98189192016-07-29 19:02:27 -0700231 * @return all regions/devices that are "siblings" to this layout's region
Simon Hunt977aa052016-07-20 17:08:29 -0700232 */
Simon Hunt98189192016-07-29 19:02:27 -0700233 public Set<UiNode> getPeerNodes(UiTopoLayout layout) {
234 Set<UiNode> peers = new HashSet<>();
235
236 // first, get the peer regions
237 Set<UiTopoLayout> peerLayouts = layoutService.getPeerLayouts(layout.id());
238 peerLayouts.forEach(l -> {
239 RegionId peerRegion = l.regionId();
240 peers.add(sharedModel.getRegion(peerRegion));
241 });
242
243 // now add the devices that reside in the parent region
244 if (!layout.isRoot()) {
245 UiTopoLayout parentLayout = layoutService.getLayout(layout.parent());
Simon Hunt8f60ff82017-04-24 17:19:30 -0700246 peers.addAll(getRegion(parentLayout).devices());
Simon Hunt98189192016-07-29 19:02:27 -0700247 }
248
249 // TODO: Finally, filter out regions / devices that are not connected
250 // directly to this region by an implicit link
Simon Huntb1ce2602016-07-23 14:04:31 -0700251 return peers;
Simon Hunt977aa052016-07-20 17:08:29 -0700252 }
253
254 /**
255 * Returns the subregions of the region in the specified layout.
256 *
257 * @param layout the layout being viewed
258 * @return all regions that are "contained within" this layout's region
259 */
260 public Set<UiRegion> getSubRegions(UiTopoLayout layout) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700261 Set<UiTopoLayout> kidLayouts = layoutService.getChildren(layout.id());
262 Set<UiRegion> kids = new HashSet<>();
263 kidLayouts.forEach(l -> kids.add(sharedModel.getRegion(l.regionId())));
264 return kids;
Simon Hunt977aa052016-07-20 17:08:29 -0700265 }
266
267 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700268 * Returns the (synthetic) links of the region in the specified layout.
269 *
270 * @param layout the layout being viewed
271 * @return all links that are contained by this layout's region
272 */
273 public List<UiSynthLink> getLinks(UiTopoLayout layout) {
274 return sharedModel.getSynthLinks(layout.regionId());
275 }
276
277 /**
Simon Huntb1ce2602016-07-23 14:04:31 -0700278 * Refreshes the model's internal state.
Simon Hunt977aa052016-07-20 17:08:29 -0700279 */
Simon Huntb1ce2602016-07-23 14:04:31 -0700280 public void refreshModel() {
281 sharedModel.refresh();
Simon Hunt977aa052016-07-20 17:08:29 -0700282 }
Simon Hunt377f5d22016-09-01 16:27:21 -0700283
284 /**
285 * Navigates to the specified region by setting the associated layout as
286 * current.
287 *
288 * @param regionId region identifier
289 */
290 public void navToRegion(String regionId) {
291 // 1. find the layout corresponding to the region ID
292 // 2. set this layout to be "current"
293 RegionId r = RegionId.regionId(regionId);
294 UiTopoLayout layout = layoutService.getLayout(r);
295 setCurrentLayout(layout);
296 }
Simon Hunt0e161092017-05-08 17:41:38 -0700297
298 /**
299 * Returns synthetic links that are in the current region, mapped by
300 * original link ID.
301 *
302 * @return map of synth links
303 */
304 public Map<UiLinkId, UiSynthLink> relevantSynthLinks() {
305 return sharedModel.relevantSynthLinks(currentLayout.regionId());
306 }
Simon Hunted804d52016-03-30 09:51:40 -0700307}