| /* |
| * Copyright 2016-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.onosproject.ui.impl.topo; |
| |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.region.RegionId; |
| import org.onosproject.ui.UiTopoLayoutService; |
| import org.onosproject.ui.impl.UiWebSocket; |
| import org.onosproject.ui.model.topo.UiLinkId; |
| import org.onosproject.ui.model.topo.UiModelEvent; |
| import org.onosproject.ui.impl.topo.model.UiModelListener; |
| import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel; |
| import org.onosproject.ui.model.topo.UiClusterMember; |
| import org.onosproject.ui.model.topo.UiNode; |
| import org.onosproject.ui.model.topo.UiRegion; |
| import org.onosproject.ui.model.topo.UiSynthLink; |
| import org.onosproject.ui.model.topo.UiTopoLayout; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Coordinates with the {@link UiTopoLayoutService} to access |
| * {@link UiTopoLayout}s, and with the {@link UiSharedTopologyModel} which |
| * maintains a local model of the network entities, tailored specifically |
| * for displaying on the UI. |
| * <p> |
| * Note that an instance of this class will be created for each |
| * {@link UiWebSocket} connection, and will contain |
| * the state of how the topology is laid out for the logged-in user. |
| * <p> |
| * The expected pattern is for the {@link Topo2ViewMessageHandler} to obtain |
| * a reference to the session instance (via the {@link UiWebSocket}), and |
| * interact with it when topo-related events come in from the client. |
| */ |
| public class UiTopoSession implements UiModelListener { |
| |
| private static final String TOPO2_UI_MODEL_EVENT = "topo2UiModelEvent"; |
| |
| private final Logger log = LoggerFactory.getLogger(getClass()); |
| |
| private final UiWebSocket webSocket; |
| private final String username; |
| private final Topo2Jsonifier t2json; |
| |
| final UiSharedTopologyModel sharedModel; |
| |
| private boolean registered = false; |
| |
| private UiTopoLayoutService layoutService; |
| private UiTopoLayout currentLayout; |
| private boolean messagesEnabled = true; |
| |
| /** |
| * Creates a new topology session for the specified web socket connection, |
| * and references to JSONifier, shared model, and layout service. |
| * |
| * @param webSocket web socket |
| * @param jsonifier JSONifier instance |
| * @param model share topology model |
| * @param layoutService topology layout service |
| */ |
| public UiTopoSession(UiWebSocket webSocket, |
| Topo2Jsonifier jsonifier, |
| UiSharedTopologyModel model, |
| UiTopoLayoutService layoutService) { |
| this.webSocket = webSocket; |
| this.username = webSocket.userName(); |
| this.t2json = jsonifier; |
| this.sharedModel = model; |
| this.layoutService = layoutService; |
| } |
| |
| // constructs a neutered instance, for unit testing |
| UiTopoSession() { |
| webSocket = null; |
| username = null; |
| t2json = null; |
| sharedModel = null; |
| } |
| |
| /** |
| * Initializes the session; registering with the shared model. |
| */ |
| public void init() { |
| if (!registered) { |
| log.debug("{} : Registering with shared model", this); |
| sharedModel.register(this); |
| currentLayout = layoutService.getRootLayout(); |
| registered = true; |
| } else { |
| log.warn("already registered"); |
| } |
| } |
| |
| /** |
| * Destroys the session; unregistering from the shared model. |
| */ |
| public void destroy() { |
| if (registered) { |
| log.debug("{} : Unregistering from shared model", this); |
| sharedModel.unregister(this); |
| registered = false; |
| } else { |
| log.warn("already unregistered"); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return String.format("{UiTopoSession for user <%s>}", username); |
| } |
| |
| @Override |
| public boolean isRelevant(UiModelEvent event) { |
| if (!messagesEnabled) { |
| return false; |
| } |
| UiRegion uiRegion = sharedModel.getRegion(currentLayout.regionId()); |
| return uiRegion.isRelevant(event); |
| } |
| |
| @Override |
| public void event(UiModelEvent event) { |
| // To ensure link can be created devices have to be added to the list as |
| // they are created |
| if (event.type() == UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED && |
| event.memo().equals("added")) { |
| UiRegion uiRegion = sharedModel.getRegion(currentLayout.regionId()); |
| uiRegion.newDeviceAdded(DeviceId.deviceId(event.subject().idAsString())); |
| } else if (event.type() == UiModelEvent.Type.DEVICE_REMOVED) { |
| UiRegion uiRegion = sharedModel.getRegion(currentLayout.regionId()); |
| uiRegion.deviceRemoved(DeviceId.deviceId(event.subject().idAsString())); |
| } |
| |
| webSocket.sendMessage(TOPO2_UI_MODEL_EVENT, t2json.jsonEvent(event)); |
| } |
| |
| /** |
| * Returns the current layout context. |
| * |
| * @return current topology layout |
| */ |
| public UiTopoLayout currentLayout() { |
| return currentLayout; |
| } |
| |
| /** |
| * Returns the breadcrumb trail from current layout to root. That is, |
| * element 0 of the list will be the current layout; the last element |
| * of the list will be the root layout. This list is guaranteed to have |
| * size of at least 1. |
| * |
| * @return breadcrumb trail |
| */ |
| public List<UiTopoLayout> breadCrumbs() { |
| UiTopoLayout current = currentLayout; |
| List<UiTopoLayout> crumbs = new ArrayList<>(); |
| crumbs.add(current); |
| while (!current.isRoot()) { |
| current = layoutService.getLayout(current.parent()); |
| crumbs.add(current); |
| } |
| return crumbs; |
| } |
| |
| /** |
| * Changes the current layout context to the specified layout. |
| * |
| * @param topoLayout new topology layout context |
| */ |
| public void setCurrentLayout(UiTopoLayout topoLayout) { |
| currentLayout = topoLayout; |
| } |
| |
| /** |
| * Enables or disables the transmission of topology event update messages. |
| * |
| * @param enabled true if messages should be sent |
| */ |
| public void enableEvent(boolean enabled) { |
| messagesEnabled = enabled; |
| } |
| |
| /** |
| * Returns the list of ONOS instances (cluster members). |
| * |
| * @return the list of ONOS instances |
| */ |
| public List<UiClusterMember> getAllInstances() { |
| return sharedModel.getClusterMembers(); |
| } |
| |
| /** |
| * Returns the region for the specified layout. |
| * |
| * @param layout layout filter |
| * @return region that the layout is based upon |
| */ |
| public UiRegion getRegion(UiTopoLayout layout) { |
| RegionId rid = layout.regionId(); |
| return rid == null ? sharedModel.getNullRegion() : sharedModel.getRegion(rid); |
| } |
| |
| /** |
| * Returns the regions/devices that are "peers" to this region. That is, |
| * based on the layout the user is viewing, all the regions/devices that |
| * are associated with layouts that share the same parent layout as this |
| * layout, AND that are linked to an element within this region. |
| * |
| * @param layout the layout being viewed |
| * @return all regions/devices that are "siblings" to this layout's region |
| */ |
| public Set<UiNode> getPeerNodes(UiTopoLayout layout) { |
| Set<UiNode> peers = new HashSet<>(); |
| |
| // first, get the peer regions |
| Set<UiTopoLayout> peerLayouts = layoutService.getPeerLayouts(layout.id()); |
| peerLayouts.forEach(l -> { |
| RegionId peerRegion = l.regionId(); |
| peers.add(sharedModel.getRegion(peerRegion)); |
| }); |
| |
| // now add the devices that reside in the parent region |
| if (!layout.isRoot()) { |
| UiTopoLayout parentLayout = layoutService.getLayout(layout.parent()); |
| peers.addAll(getRegion(parentLayout).devices()); |
| } |
| |
| // TODO: Finally, filter out regions / devices that are not connected |
| // directly to this region by an implicit link |
| return peers; |
| } |
| |
| /** |
| * Returns the subregions of the region in the specified layout. |
| * |
| * @param layout the layout being viewed |
| * @return all regions that are "contained within" this layout's region |
| */ |
| public Set<UiRegion> getSubRegions(UiTopoLayout layout) { |
| Set<UiTopoLayout> kidLayouts = layoutService.getChildren(layout.id()); |
| Set<UiRegion> kids = new HashSet<>(); |
| kidLayouts.forEach(l -> kids.add(sharedModel.getRegion(l.regionId()))); |
| return kids; |
| } |
| |
| /** |
| * Returns the (synthetic) links of the region in the specified layout. |
| * |
| * @param layout the layout being viewed |
| * @return all links that are contained by this layout's region |
| */ |
| public List<UiSynthLink> getLinks(UiTopoLayout layout) { |
| return sharedModel.getSynthLinks(layout.regionId()); |
| } |
| |
| /** |
| * Refreshes the model's internal state. |
| */ |
| public void refreshModel() { |
| sharedModel.refresh(); |
| } |
| |
| /** |
| * Navigates to the specified region by setting the associated layout as |
| * current. |
| * |
| * @param regionId region identifier |
| */ |
| public void navToRegion(String regionId) { |
| // 1. find the layout corresponding to the region ID |
| // 2. set this layout to be "current" |
| RegionId r = RegionId.regionId(regionId); |
| UiTopoLayout layout = layoutService.getLayout(r); |
| setCurrentLayout(layout); |
| } |
| |
| /** |
| * Returns synthetic links that are in the current region, mapped by |
| * original link ID. |
| * |
| * @return map of synth links |
| */ |
| public Map<UiLinkId, UiSynthLink> relevantSynthLinks() { |
| return sharedModel.relevantSynthLinks(currentLayout.regionId()); |
| } |
| } |