blob: 3a3bf68f02b34be9310da5f71b2aa1680114d52f [file] [log] [blame]
/*
* Copyright 2015 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;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onosproject.cluster.ClusterEvent;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.event.AbstractListenerRegistry;
import org.onosproject.event.EventDeliveryService;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Device;
import org.onosproject.net.Host;
import org.onosproject.net.Link;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostService;
import org.onosproject.net.intent.IntentService;
import org.onosproject.net.link.LinkEvent;
import org.onosproject.net.link.LinkService;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
import static org.onosproject.ui.impl.topo.TopoUiEvent.Type.SUMMARY_UPDATE;
/**
* Maintains a UI-centric model of the topology, as inferred from interactions
* with the different (device, host, link, ...) services. Will serve up this
* model to anyone who cares to {@link TopoUiListener listen}.
*/
@Component(immediate = true)
@Service
public class TopoUiModelManager implements TopoUiModelService {
// TODO: put back to 30,000 ms for production
private static final long SUMMARY_PERIOD = 15_000;
private final Logger log = LoggerFactory.getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterService clusterService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkService linkService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentService intentService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected EventDeliveryService eventDispatcher;
private AbstractListenerRegistry<TopoUiEvent, TopoUiListener>
listenerRegistry = new AbstractListenerRegistry<>();
private final TopoMessageFactory messageFactory = new TopoMessageFactory();
private final MetaDb metaDb = new MetaDb();
private final Timer timer = new Timer("topology-view");
private TimerTask summaryTask = null;
private boolean summaryRunning = false;
@Activate
public void activate() {
eventDispatcher.addSink(TopoUiEvent.class, listenerRegistry);
messageFactory.injectServices(
metaDb,
clusterService,
deviceService,
linkService,
hostService,
mastershipService
// TODO: others??
);
log.info("Started");
}
@Deactivate
public void deactivate() {
eventDispatcher.removeSink(TopoUiEvent.class);
log.info("Stopped");
}
// TODO: figure out how to cull zombie listeners
// The problem is when one refreshes the GUI (topology view)
// a new instance of AltTopoViewMessageHandler is created and added
// as a listener, but we never got a TopoStop event, which is what
// causes the listener (for an AltTopoViewMessageHandler instance) to
// be removed.
// ==== Somehow need to tie this in to the GUI-disconnected event.
// This probably requires client-generated heartbeat messages to
// Keep the connection alive.
@Override
public void addListener(TopoUiListener listener) {
listenerRegistry.addListener(listener);
}
@Override
public void removeListener(TopoUiListener listener) {
listenerRegistry.removeListener(listener);
}
@Override
public List<ObjectNode> getInitialState() {
List<ObjectNode> results = new ArrayList<>();
addInstances(results);
addDevices(results);
addLinks(results);
addHosts(results);
return results;
}
@Override
public synchronized void startSummaryMonitoring() {
// first, cancel previous task if not canceled already
stopSummaryMonitoring();
// create and start a summary task, to execute with no delay, and
// every SUMMARY_PERIOD milliseconds thereafter.
summaryTask = new TimerTask() {
@Override
public void run() {
if (summaryRunning) {
post(new TopoUiEvent(SUMMARY_UPDATE, null));
}
}
};
timer.schedule(summaryTask, 0, SUMMARY_PERIOD);
summaryRunning = true;
}
@Override
public synchronized void stopSummaryMonitoring() {
if (summaryTask != null) {
summaryTask.cancel();
summaryTask = null;
}
summaryRunning = false;
}
@Override
public SummaryData getSummaryData() {
return new SummaryDataImpl();
}
// =====================================================================
private final class SummaryDataImpl implements SummaryData {
private final Topology topology = topologyService.currentTopology();
@Override
public int deviceCount() {
return topology.deviceCount();
}
@Override
public int linkCount() {
return topology.linkCount();
}
@Override
public int hostCount() {
return hostService.getHostCount();
}
@Override
public int clusterCount() {
return topology.clusterCount();
}
@Override
public long intentCount() {
return intentService.getIntentCount();
}
@Override
public int flowRuleCount() {
return flowRuleService.getFlowRuleCount();
}
}
// =====================================================================
private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
(o1, o2) -> o1.id().toString().compareTo(o2.id().toString());
// =====================================================================
private void addInstances(List<ObjectNode> results) {
List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
Collections.sort(nodes, NODE_COMPARATOR);
for (ControllerNode node : nodes) {
ClusterEvent ev = new ClusterEvent(INSTANCE_ADDED, node);
results.add(messageFactory.instanceMessage(ev));
}
}
private void addDevices(List<ObjectNode> results) {
// Send optical first, others later -- for layered rendering
List<DeviceEvent> deferred = new ArrayList<>();
for (Device device : deviceService.getDevices()) {
DeviceEvent ev = new DeviceEvent(DEVICE_ADDED, device);
if (device.type() == Device.Type.ROADM) {
results.add(messageFactory.deviceMessage(ev));
} else {
deferred.add(ev);
}
}
for (DeviceEvent ev : deferred) {
results.add(messageFactory.deviceMessage(ev));
}
}
private void addLinks(List<ObjectNode> results) {
// Send optical first, others later -- for layered rendering
List<LinkEvent> deferred = new ArrayList<>();
for (Link link : linkService.getLinks()) {
LinkEvent ev = new LinkEvent(LINK_ADDED, link);
if (link.type() == Link.Type.OPTICAL) {
results.add(messageFactory.linkMessage(ev));
} else {
deferred.add(ev);
}
}
for (LinkEvent ev : deferred) {
results.add(messageFactory.linkMessage(ev));
}
}
private void addHosts(List<ObjectNode> results) {
for (Host host : hostService.getHosts()) {
HostEvent ev = new HostEvent(HOST_ADDED, host);
results.add(messageFactory.hostMessage(ev));
}
}
// =====================================================================
private void post(TopoUiEvent event) {
if (event != null) {
eventDispatcher.post(event);
}
}
// NOTE: session-independent state only
// private inner classes to listen to device/host/link events
// TODO..
}