blob: 288a343590a913ca03fb992112a2216f94d525d3 [file] [log] [blame]
Simon Hunted804d52016-03-30 09:51:40 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
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
Simon Hunt537bc762016-12-20 12:15:13 -080019import com.fasterxml.jackson.databind.node.ObjectNode;
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 Huntcda9c032016-04-11 10:32:54 -070023import org.onosproject.ui.impl.topo.model.UiModelEvent;
24import org.onosproject.ui.impl.topo.model.UiModelListener;
Simon Huntf679c4e2016-04-01 17:02:24 -070025import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
Simon Huntd5b96732016-07-08 13:22:27 -070026import org.onosproject.ui.model.topo.UiClusterMember;
Simon Hunt98189192016-07-29 19:02:27 -070027import org.onosproject.ui.model.topo.UiNode;
Simon Huntd5b96732016-07-08 13:22:27 -070028import org.onosproject.ui.model.topo.UiRegion;
Simon Huntc13082f2016-08-03 21:20:23 -070029import org.onosproject.ui.model.topo.UiSynthLink;
Simon Huntf679c4e2016-04-01 17:02:24 -070030import org.onosproject.ui.model.topo.UiTopoLayout;
Simon Hunted804d52016-03-30 09:51:40 -070031import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33
Simon Huntf836a872016-08-10 17:37:36 -070034import java.util.ArrayList;
Simon Huntb1ce2602016-07-23 14:04:31 -070035import java.util.HashSet;
Simon Huntd5b96732016-07-08 13:22:27 -070036import java.util.List;
Simon Hunt977aa052016-07-20 17:08:29 -070037import java.util.Set;
Simon Huntd5b96732016-07-08 13:22:27 -070038
Simon Hunted804d52016-03-30 09:51:40 -070039/**
Simon Huntf679c4e2016-04-01 17:02:24 -070040 * Coordinates with the {@link UiTopoLayoutService} to access
41 * {@link UiTopoLayout}s, and with the {@link UiSharedTopologyModel} which
Simon Huntcda9c032016-04-11 10:32:54 -070042 * maintains a local model of the network entities, tailored specifically
43 * for displaying on the UI.
Simon Hunted804d52016-03-30 09:51:40 -070044 * <p>
45 * Note that an instance of this class will be created for each
Simon Huntf679c4e2016-04-01 17:02:24 -070046 * {@link UiWebSocket} connection, and will contain
Simon Hunted804d52016-03-30 09:51:40 -070047 * the state of how the topology is laid out for the logged-in user.
Simon Huntd5b96732016-07-08 13:22:27 -070048 * <p>
49 * The expected pattern is for the {@link Topo2ViewMessageHandler} to obtain
50 * a reference to the session instance (via the {@link UiWebSocket}), and
51 * interact with it when topo-related events come in from the client.
Simon Hunted804d52016-03-30 09:51:40 -070052 */
Simon Huntcda9c032016-04-11 10:32:54 -070053public class UiTopoSession implements UiModelListener {
Simon Hunt977aa052016-07-20 17:08:29 -070054
Simon Hunt537bc762016-12-20 12:15:13 -080055 private static final String TOPO2_UI_MODEL_EVENT = "topo2UiModelEvent";
56
Simon Hunted804d52016-03-30 09:51:40 -070057 private final Logger log = LoggerFactory.getLogger(getClass());
58
Simon Huntf679c4e2016-04-01 17:02:24 -070059 private final UiWebSocket webSocket;
Simon Hunt7092cc42016-04-06 18:40:17 -070060 private final String username;
Simon Hunt537bc762016-12-20 12:15:13 -080061 private final Topo2Jsonifier t2json;
Simon Hunt7092cc42016-04-06 18:40:17 -070062
63 final UiSharedTopologyModel sharedModel;
Simon Hunted804d52016-03-30 09:51:40 -070064
65 private boolean registered = false;
66
Thomas Vachuska92b016b2016-05-20 11:37:57 -070067 private UiTopoLayoutService layoutService;
Simon Hunt7092cc42016-04-06 18:40:17 -070068 private UiTopoLayout currentLayout;
Simon Huntbbd0f462017-01-10 14:50:22 -080069 private boolean messagesEnabled = true;
Simon Huntf679c4e2016-04-01 17:02:24 -070070
Simon Hunted804d52016-03-30 09:51:40 -070071 /**
Simon Hunt537bc762016-12-20 12:15:13 -080072 * Creates a new topology session for the specified web socket connection,
73 * and references to JSONifier, shared model, and layout service.
Simon Hunt7092cc42016-04-06 18:40:17 -070074 *
Thomas Vachuska92b016b2016-05-20 11:37:57 -070075 * @param webSocket web socket
Simon Hunt537bc762016-12-20 12:15:13 -080076 * @param jsonifier JSONifier instance
Thomas Vachuska92b016b2016-05-20 11:37:57 -070077 * @param model share topology model
78 * @param layoutService topology layout service
Simon Hunted804d52016-03-30 09:51:40 -070079 */
Thomas Vachuska92b016b2016-05-20 11:37:57 -070080 public UiTopoSession(UiWebSocket webSocket,
Simon Hunt537bc762016-12-20 12:15:13 -080081 Topo2Jsonifier jsonifier,
Thomas Vachuska92b016b2016-05-20 11:37:57 -070082 UiSharedTopologyModel model,
83 UiTopoLayoutService layoutService) {
Simon Huntf679c4e2016-04-01 17:02:24 -070084 this.webSocket = webSocket;
Simon Hunt7092cc42016-04-06 18:40:17 -070085 this.username = webSocket.userName();
Simon Hunt537bc762016-12-20 12:15:13 -080086 this.t2json = jsonifier;
Simon Huntcda9c032016-04-11 10:32:54 -070087 this.sharedModel = model;
Thomas Vachuska92b016b2016-05-20 11:37:57 -070088 this.layoutService = layoutService;
Simon Hunted804d52016-03-30 09:51:40 -070089 }
90
Simon Hunt977aa052016-07-20 17:08:29 -070091 // constructs a neutered instance, for unit testing
92 UiTopoSession() {
93 webSocket = null;
94 username = null;
Simon Hunt537bc762016-12-20 12:15:13 -080095 t2json = null;
Simon Hunt977aa052016-07-20 17:08:29 -070096 sharedModel = null;
97 }
98
Simon Hunted804d52016-03-30 09:51:40 -070099 /**
Simon Hunt7092cc42016-04-06 18:40:17 -0700100 * Initializes the session; registering with the shared model.
Simon Hunted804d52016-03-30 09:51:40 -0700101 */
102 public void init() {
103 if (!registered) {
Simon Hunt7092cc42016-04-06 18:40:17 -0700104 log.debug("{} : Registering with shared model", this);
Simon Hunted804d52016-03-30 09:51:40 -0700105 sharedModel.register(this);
Thomas Vachuska92b016b2016-05-20 11:37:57 -0700106 currentLayout = layoutService.getRootLayout();
Simon Hunted804d52016-03-30 09:51:40 -0700107 registered = true;
108 } else {
109 log.warn("already registered");
110 }
111 }
112
113 /**
Simon Hunt7092cc42016-04-06 18:40:17 -0700114 * Destroys the session; unregistering from the shared model.
Simon Hunted804d52016-03-30 09:51:40 -0700115 */
116 public void destroy() {
Simon Hunt7092cc42016-04-06 18:40:17 -0700117 if (registered) {
118 log.debug("{} : Unregistering from shared model", this);
Simon Hunted804d52016-03-30 09:51:40 -0700119 sharedModel.unregister(this);
Simon Huntf679c4e2016-04-01 17:02:24 -0700120 registered = false;
Simon Hunted804d52016-03-30 09:51:40 -0700121 } else {
122 log.warn("already unregistered");
123 }
124 }
125
126 @Override
127 public String toString() {
Simon Huntf679c4e2016-04-01 17:02:24 -0700128 return String.format("{UiTopoSession for user <%s>}", username);
Simon Hunted804d52016-03-30 09:51:40 -0700129 }
Simon Huntcda9c032016-04-11 10:32:54 -0700130
131 @Override
132 public void event(UiModelEvent event) {
Simon Huntbbd0f462017-01-10 14:50:22 -0800133 String msg = messagesEnabled
134 ? "Event received: {}"
135 : "Event received: {}, but not transmitted";
136 log.debug(msg, event);
Simon Hunt537bc762016-12-20 12:15:13 -0800137
Simon Huntbbd0f462017-01-10 14:50:22 -0800138 if (messagesEnabled) {
139 ObjectNode payload = t2json.jsonEvent(event);
Simon Hunt537bc762016-12-20 12:15:13 -0800140
Simon Huntbbd0f462017-01-10 14:50:22 -0800141 // TODO: add filtering for relevant objects only...
142 // TO Decide: Since the session holds the state of what is being
143 // displayed on the client, we should filter out any model events
144 // that are not relevant, and only send up events for objects that
145 // are currently being viewed by the user.
146
147 webSocket.sendMessage(TOPO2_UI_MODEL_EVENT, payload);
148 }
Simon Huntcda9c032016-04-11 10:32:54 -0700149 }
Thomas Vachuska92b016b2016-05-20 11:37:57 -0700150
151 /**
152 * Returns the current layout context.
153 *
154 * @return current topology layout
155 */
156 public UiTopoLayout currentLayout() {
157 return currentLayout;
158 }
159
160 /**
Simon Huntf836a872016-08-10 17:37:36 -0700161 * Returns the breadcrumb trail from current layout to root. That is,
162 * element 0 of the list will be the current layout; the last element
163 * of the list will be the root layout. This list is guaranteed to have
164 * size of at least 1.
165 *
166 * @return breadcrumb trail
167 */
168 public List<UiTopoLayout> breadCrumbs() {
169 UiTopoLayout current = currentLayout;
170 List<UiTopoLayout> crumbs = new ArrayList<>();
171 crumbs.add(current);
172 while (!current.isRoot()) {
173 current = layoutService.getLayout(current.parent());
174 crumbs.add(current);
175 }
176 return crumbs;
177 }
178
179 /**
Thomas Vachuska92b016b2016-05-20 11:37:57 -0700180 * Changes the current layout context to the specified layout.
181 *
182 * @param topoLayout new topology layout context
183 */
184 public void setCurrentLayout(UiTopoLayout topoLayout) {
185 currentLayout = topoLayout;
186 }
187
188 /**
189 * Enables or disables the transmission of topology event update messages.
190 *
191 * @param enabled true if messages should be sent
192 */
193 public void enableEvent(boolean enabled) {
194 messagesEnabled = enabled;
195 }
Simon Huntd5b96732016-07-08 13:22:27 -0700196
197 /**
198 * Returns the list of ONOS instances (cluster members).
199 *
200 * @return the list of ONOS instances
201 */
202 public List<UiClusterMember> getAllInstances() {
203 return sharedModel.getClusterMembers();
204 }
205
206 /**
207 * Returns the region for the specified layout.
208 *
209 * @param layout layout filter
210 * @return region that the layout is based upon
211 */
212 public UiRegion getRegion(UiTopoLayout layout) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700213 RegionId rid = layout.regionId();
214 return rid == null ? sharedModel.getNullRegion() : sharedModel.getRegion(rid);
Simon Huntd5b96732016-07-08 13:22:27 -0700215 }
Simon Hunt977aa052016-07-20 17:08:29 -0700216
217 /**
Simon Hunt98189192016-07-29 19:02:27 -0700218 * Returns the regions/devices that are "peers" to this region. That is,
219 * based on the layout the user is viewing, all the regions/devices that
220 * are associated with layouts that share the same parent layout as this
221 * layout, AND that are linked to an element within this region.
Simon Hunt977aa052016-07-20 17:08:29 -0700222 *
223 * @param layout the layout being viewed
Simon Hunt98189192016-07-29 19:02:27 -0700224 * @return all regions/devices that are "siblings" to this layout's region
Simon Hunt977aa052016-07-20 17:08:29 -0700225 */
Simon Hunt98189192016-07-29 19:02:27 -0700226 public Set<UiNode> getPeerNodes(UiTopoLayout layout) {
227 Set<UiNode> peers = new HashSet<>();
228
229 // first, get the peer regions
230 Set<UiTopoLayout> peerLayouts = layoutService.getPeerLayouts(layout.id());
231 peerLayouts.forEach(l -> {
232 RegionId peerRegion = l.regionId();
233 peers.add(sharedModel.getRegion(peerRegion));
234 });
235
236 // now add the devices that reside in the parent region
237 if (!layout.isRoot()) {
238 UiTopoLayout parentLayout = layoutService.getLayout(layout.parent());
239 getRegion(parentLayout).devices().forEach(peers::add);
240 }
241
242 // TODO: Finally, filter out regions / devices that are not connected
243 // directly to this region by an implicit link
Simon Huntb1ce2602016-07-23 14:04:31 -0700244 return peers;
Simon Hunt977aa052016-07-20 17:08:29 -0700245 }
246
247 /**
248 * Returns the subregions of the region in the specified layout.
249 *
250 * @param layout the layout being viewed
251 * @return all regions that are "contained within" this layout's region
252 */
253 public Set<UiRegion> getSubRegions(UiTopoLayout layout) {
Simon Huntb1ce2602016-07-23 14:04:31 -0700254 Set<UiTopoLayout> kidLayouts = layoutService.getChildren(layout.id());
255 Set<UiRegion> kids = new HashSet<>();
256 kidLayouts.forEach(l -> kids.add(sharedModel.getRegion(l.regionId())));
257 return kids;
Simon Hunt977aa052016-07-20 17:08:29 -0700258 }
259
260 /**
Simon Huntc13082f2016-08-03 21:20:23 -0700261 * Returns the (synthetic) links of the region in the specified layout.
262 *
263 * @param layout the layout being viewed
264 * @return all links that are contained by this layout's region
265 */
266 public List<UiSynthLink> getLinks(UiTopoLayout layout) {
267 return sharedModel.getSynthLinks(layout.regionId());
268 }
269
270 /**
Simon Huntb1ce2602016-07-23 14:04:31 -0700271 * Refreshes the model's internal state.
Simon Hunt977aa052016-07-20 17:08:29 -0700272 */
Simon Huntb1ce2602016-07-23 14:04:31 -0700273 public void refreshModel() {
274 sharedModel.refresh();
Simon Hunt977aa052016-07-20 17:08:29 -0700275 }
Simon Hunt377f5d22016-09-01 16:27:21 -0700276
277 /**
278 * Navigates to the specified region by setting the associated layout as
279 * current.
280 *
281 * @param regionId region identifier
282 */
283 public void navToRegion(String regionId) {
284 // 1. find the layout corresponding to the region ID
285 // 2. set this layout to be "current"
286 RegionId r = RegionId.regionId(regionId);
287 UiTopoLayout layout = layoutService.getLayout(r);
288 setCurrentLayout(layout);
289 }
Simon Hunted804d52016-03-30 09:51:40 -0700290}