| /* |
| * Copyright 2016 Open Networking Laboratory |
| * |
| * 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.model; |
| |
| import org.onosproject.cluster.ControllerNode; |
| import org.onosproject.cluster.NodeId; |
| import org.onosproject.cluster.RoleInfo; |
| import org.onosproject.event.EventDispatcher; |
| import org.onosproject.net.Device; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.EdgeLink; |
| import org.onosproject.net.Host; |
| import org.onosproject.net.HostId; |
| import org.onosproject.net.HostLocation; |
| import org.onosproject.net.Link; |
| import org.onosproject.net.region.Region; |
| import org.onosproject.net.region.RegionId; |
| import org.onosproject.ui.model.ServiceBundle; |
| import org.onosproject.ui.model.topo.UiClusterMember; |
| import org.onosproject.ui.model.topo.UiDevice; |
| import org.onosproject.ui.model.topo.UiElement; |
| import org.onosproject.ui.model.topo.UiHost; |
| import org.onosproject.ui.model.topo.UiLink; |
| import org.onosproject.ui.model.topo.UiLinkId; |
| import org.onosproject.ui.model.topo.UiRegion; |
| import org.onosproject.ui.model.topo.UiTopology; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.util.Set; |
| |
| import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; |
| import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_ADDED_OR_UPDATED; |
| import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.CLUSTER_MEMBER_REMOVED; |
| import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_ADDED_OR_UPDATED; |
| import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.DEVICE_REMOVED; |
| import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_ADDED_OR_UPDATED; |
| import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_MOVED; |
| import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.HOST_REMOVED; |
| import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.LINK_ADDED_OR_UPDATED; |
| import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.LINK_REMOVED; |
| import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.REGION_ADDED_OR_UPDATED; |
| import static org.onosproject.ui.impl.topo.model.UiModelEvent.Type.REGION_REMOVED; |
| import static org.onosproject.ui.model.topo.UiLinkId.uiLinkId; |
| |
| /** |
| * UI Topology Model cache. |
| */ |
| class ModelCache { |
| |
| private static final String E_NO_ELEMENT = "Tried to remove non-member {}: {}"; |
| |
| private static final Logger log = LoggerFactory.getLogger(ModelCache.class); |
| |
| private final ServiceBundle services; |
| private final EventDispatcher dispatcher; |
| private final UiTopology uiTopology = new UiTopology(); |
| |
| ModelCache(ServiceBundle services, EventDispatcher eventDispatcher) { |
| this.services = services; |
| this.dispatcher = eventDispatcher; |
| } |
| |
| @Override |
| public String toString() { |
| return "ModelCache{" + uiTopology + "}"; |
| } |
| |
| private void postEvent(UiModelEvent.Type type, UiElement subject) { |
| dispatcher.post(new UiModelEvent(type, subject)); |
| } |
| |
| void clear() { |
| uiTopology.clear(); |
| } |
| |
| /** |
| * Create our internal model of the global topology. An assumption we are |
| * making is that the topology is empty to start. |
| */ |
| void load() { |
| loadClusterMembers(); |
| loadRegions(); |
| loadDevices(); |
| loadLinks(); |
| loadHosts(); |
| } |
| |
| |
| // === CLUSTER MEMBERS |
| |
| private UiClusterMember addNewClusterMember(ControllerNode n) { |
| UiClusterMember member = new UiClusterMember(uiTopology, n); |
| uiTopology.add(member); |
| return member; |
| } |
| |
| private void updateClusterMember(UiClusterMember member) { |
| ControllerNode.State state = services.cluster().getState(member.id()); |
| member.setState(state); |
| member.setMastership(services.mastership().getDevicesOf(member.id())); |
| // NOTE: 'UI-attached' is session-based data, not global, so will |
| // be set elsewhere |
| } |
| |
| private void loadClusterMembers() { |
| for (ControllerNode n : services.cluster().getNodes()) { |
| UiClusterMember member = addNewClusterMember(n); |
| updateClusterMember(member); |
| } |
| } |
| |
| // invoked from UiSharedTopologyModel cluster event listener |
| void addOrUpdateClusterMember(ControllerNode cnode) { |
| NodeId id = cnode.id(); |
| UiClusterMember member = uiTopology.findClusterMember(id); |
| if (member == null) { |
| member = addNewClusterMember(cnode); |
| } |
| updateClusterMember(member); |
| |
| postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member); |
| } |
| |
| // package private for unit test access |
| UiClusterMember accessClusterMember(NodeId id) { |
| return uiTopology.findClusterMember(id); |
| } |
| |
| // invoked from UiSharedTopologyModel cluster event listener |
| void removeClusterMember(ControllerNode cnode) { |
| NodeId id = cnode.id(); |
| UiClusterMember member = uiTopology.findClusterMember(id); |
| if (member != null) { |
| uiTopology.remove(member); |
| postEvent(CLUSTER_MEMBER_REMOVED, member); |
| } else { |
| log.warn(E_NO_ELEMENT, "cluster node", id); |
| } |
| } |
| |
| |
| // === MASTERSHIP CHANGES |
| |
| // invoked from UiSharedTopologyModel mastership listener |
| void updateMasterships(DeviceId deviceId, RoleInfo roleInfo) { |
| // To think about:: do we need to store mastership info? |
| // or can we rely on looking it up live? |
| // TODO: store the updated mastership information |
| // TODO: post event |
| } |
| |
| |
| // === REGIONS |
| |
| private UiRegion addNewRegion(Region r) { |
| UiRegion region = new UiRegion(uiTopology, r); |
| uiTopology.add(region); |
| return region; |
| } |
| |
| private void updateRegion(UiRegion region) { |
| Set<DeviceId> devs = services.region().getRegionDevices(region.id()); |
| region.reconcileDevices(devs); |
| } |
| |
| private void loadRegions() { |
| for (Region r : services.region().getRegions()) { |
| UiRegion region = addNewRegion(r); |
| updateRegion(region); |
| } |
| } |
| |
| // invoked from UiSharedTopologyModel region listener |
| void addOrUpdateRegion(Region region) { |
| RegionId id = region.id(); |
| UiRegion uiRegion = uiTopology.findRegion(id); |
| if (uiRegion == null) { |
| uiRegion = addNewRegion(region); |
| } |
| updateRegion(uiRegion); |
| |
| postEvent(REGION_ADDED_OR_UPDATED, uiRegion); |
| } |
| |
| // invoked from UiSharedTopologyModel region listener |
| void removeRegion(Region region) { |
| RegionId id = region.id(); |
| UiRegion uiRegion = uiTopology.findRegion(id); |
| if (uiRegion != null) { |
| uiTopology.remove(uiRegion); |
| postEvent(REGION_REMOVED, uiRegion); |
| } else { |
| log.warn(E_NO_ELEMENT, "region", id); |
| } |
| } |
| |
| |
| // === DEVICES |
| |
| private UiDevice addNewDevice(Device d) { |
| UiDevice device = new UiDevice(uiTopology, d); |
| uiTopology.add(device); |
| return device; |
| } |
| |
| private void updateDevice(UiDevice device) { |
| device.setRegionId(services.region().getRegionForDevice(device.id()).id()); |
| } |
| |
| private void loadDevices() { |
| for (Device d : services.device().getDevices()) { |
| UiDevice device = addNewDevice(d); |
| updateDevice(device); |
| } |
| } |
| |
| // invoked from UiSharedTopologyModel device listener |
| void addOrUpdateDevice(Device device) { |
| DeviceId id = device.id(); |
| UiDevice uiDevice = uiTopology.findDevice(id); |
| if (uiDevice == null) { |
| uiDevice = addNewDevice(device); |
| } |
| updateDevice(uiDevice); |
| |
| postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice); |
| } |
| |
| // invoked from UiSharedTopologyModel device listener |
| void removeDevice(Device device) { |
| DeviceId id = device.id(); |
| UiDevice uiDevice = uiTopology.findDevice(id); |
| if (uiDevice != null) { |
| uiTopology.remove(uiDevice); |
| postEvent(DEVICE_REMOVED, uiDevice); |
| } else { |
| log.warn(E_NO_ELEMENT, "device", id); |
| } |
| } |
| |
| |
| // === LINKS |
| |
| private UiLink addNewLink(UiLinkId id) { |
| UiLink uiLink = new UiLink(uiTopology, id); |
| uiTopology.add(uiLink); |
| return uiLink; |
| } |
| |
| private void updateLink(UiLink uiLink, Link link) { |
| uiLink.attachBackingLink(link); |
| } |
| |
| private void loadLinks() { |
| for (Link link : services.link().getLinks()) { |
| UiLinkId id = uiLinkId(link); |
| |
| UiLink uiLink = uiTopology.findLink(id); |
| if (uiLink == null) { |
| uiLink = addNewLink(id); |
| } |
| updateLink(uiLink, link); |
| } |
| } |
| |
| // invoked from UiSharedTopologyModel link listener |
| void addOrUpdateLink(Link link) { |
| UiLinkId id = uiLinkId(link); |
| UiLink uiLink = uiTopology.findLink(id); |
| if (uiLink == null) { |
| uiLink = addNewLink(id); |
| } |
| updateLink(uiLink, link); |
| |
| postEvent(LINK_ADDED_OR_UPDATED, uiLink); |
| } |
| |
| // invoked from UiSharedTopologyModel link listener |
| void removeLink(Link link) { |
| UiLinkId id = uiLinkId(link); |
| UiLink uiLink = uiTopology.findLink(id); |
| if (uiLink != null) { |
| boolean remaining = uiLink.detachBackingLink(link); |
| if (remaining) { |
| postEvent(LINK_ADDED_OR_UPDATED, uiLink); |
| } else { |
| uiTopology.remove(uiLink); |
| postEvent(LINK_REMOVED, uiLink); |
| } |
| } else { |
| log.warn(E_NO_ELEMENT, "link", id); |
| } |
| } |
| |
| |
| // === HOSTS |
| |
| private UiHost addNewHost(Host h) { |
| UiHost host = new UiHost(uiTopology, h); |
| uiTopology.add(host); |
| |
| UiLink edgeLink = addNewEdgeLink(host); |
| host.setEdgeLinkId(edgeLink.id()); |
| |
| return host; |
| } |
| |
| private void removeOldEdgeLink(UiHost uiHost) { |
| UiLink old = uiTopology.findLink(uiHost.edgeLinkId()); |
| if (old != null) { |
| uiTopology.remove(old); |
| } |
| } |
| |
| private UiLink addNewEdgeLink(UiHost uiHost) { |
| EdgeLink elink = createEdgeLink(uiHost.backingHost(), true); |
| UiLinkId elinkId = UiLinkId.uiLinkId(elink); |
| UiLink uiLink = addNewLink(elinkId); |
| uiLink.attachEdgeLink(elink); |
| return uiLink; |
| } |
| |
| private void updateHost(UiHost uiHost, Host h) { |
| removeOldEdgeLink(uiHost); |
| HostLocation hloc = h.location(); |
| uiHost.setLocation(hloc.deviceId(), hloc.port()); |
| addNewEdgeLink(uiHost); |
| } |
| |
| private void loadHosts() { |
| for (Host h : services.host().getHosts()) { |
| UiHost host = addNewHost(h); |
| updateHost(host, h); |
| } |
| } |
| |
| // invoked from UiSharedTopologyModel host listener |
| void addOrUpdateHost(Host host) { |
| HostId id = host.id(); |
| UiHost uiHost = uiTopology.findHost(id); |
| if (uiHost == null) { |
| uiHost = addNewHost(host); |
| } |
| updateHost(uiHost, host); |
| |
| postEvent(HOST_ADDED_OR_UPDATED, uiHost); |
| } |
| |
| // invoked from UiSharedTopologyModel host listener |
| void moveHost(Host host, Host prevHost) { |
| UiHost uiHost = uiTopology.findHost(prevHost.id()); |
| updateHost(uiHost, host); |
| |
| postEvent(HOST_MOVED, uiHost); |
| } |
| |
| // invoked from UiSharedTopologyModel host listener |
| void removeHost(Host host) { |
| HostId id = host.id(); |
| UiHost uiHost = uiTopology.findHost(id); |
| if (uiHost != null) { |
| uiTopology.remove(uiHost); |
| removeOldEdgeLink(uiHost); |
| postEvent(HOST_REMOVED, uiHost); |
| } else { |
| log.warn(E_NO_ELEMENT, "host", id); |
| } |
| } |
| |
| |
| // === CACHE STATISTICS |
| |
| /** |
| * Returns the number of members in the cluster. |
| * |
| * @return number of cluster members |
| */ |
| public int clusterMemberCount() { |
| return uiTopology.clusterMemberCount(); |
| } |
| |
| /** |
| * Returns the number of regions in the topology. |
| * |
| * @return number of regions |
| */ |
| public int regionCount() { |
| return uiTopology.regionCount(); |
| } |
| } |