blob: 5d928a048af5e5b3e800b7a98bc704abfda6039f [file] [log] [blame]
/*
* Copyright 2017-present 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;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import org.onosproject.net.DeviceId;
import org.onosproject.net.ElementId;
import org.onosproject.net.HostId;
import org.onosproject.net.Link;
import org.onosproject.net.MarkerResource;
import org.onosproject.net.NetworkResource;
import org.onosproject.net.behaviour.protection.TransportEndpointDescription;
import org.onosproject.net.intent.FlowRuleIntent;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.OpticalConnectivityIntent;
import org.onosproject.net.intent.ProtectionEndpointIntent;
import org.onosproject.ui.impl.topo.util.ServicesBundle;
import org.onosproject.ui.impl.topo.util.TrafficLink;
import org.onosproject.ui.impl.topo.util.TrafficLink.StatsType;
import org.onosproject.ui.impl.topo.util.TrafficLinkMap;
import org.onosproject.ui.topo.AbstractTopoMonitor;
import org.onosproject.ui.topo.DeviceHighlight;
import org.onosproject.ui.topo.Highlights;
import org.onosproject.ui.topo.HostHighlight;
import org.onosproject.ui.topo.LinkHighlight.Flavor;
import org.onosproject.ui.topo.NodeHighlight;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import static org.onosproject.ui.impl.ProtectedIntentMonitor.ProtectedMode.IDLE;
import static org.onosproject.ui.impl.ProtectedIntentMonitor.ProtectedMode.SELECTED_INTENT;
/**
* Encapsulates the behavior of monitoring protected intents.
*/
//TODO refactor duplicated methods from here and the TrafficMonitor to AbstractTopoMonitor
public class ProtectedIntentMonitor extends AbstractTopoMonitor {
private static final Logger log =
LoggerFactory.getLogger(ProtectedIntentMonitor.class);
public static final String PRIMARY_PATH_TAG = "protection1";
/**
* Designates the different modes of operation.
*/
public enum ProtectedMode {
IDLE,
SELECTED_INTENT
}
private final long trafficPeriod;
private final ServicesBundle servicesBundle;
private final TopologyViewMessageHandler msgHandler;
private final Timer timer = new Timer("topo-protected-intents");
private TimerTask trafficTask = null;
private ProtectedMode mode = ProtectedMode.IDLE;
private Intent selectedIntent = null;
/**
* Constructs a protected intent monitor.
*
* @param trafficPeriod traffic task period in ms
* @param servicesBundle bundle of services
* @param msgHandler our message handler
*/
public ProtectedIntentMonitor(long trafficPeriod, ServicesBundle servicesBundle,
TopologyViewMessageHandler msgHandler) {
this.trafficPeriod = trafficPeriod;
this.servicesBundle = servicesBundle;
this.msgHandler = msgHandler;
}
// =======================================================================
// === API ===
// TODO: move this out to the "h2h/multi-intent app"
/**
* Monitor for protected intent data to be sent back to the web client,
* for the given intent.
*
* @param intent the intent to monitor
*/
public synchronized void monitor(Intent intent) {
log.debug("monitor intent: {}", intent.id());
selectedIntent = intent;
mode = SELECTED_INTENT;
scheduleTask();
sendSelectedIntents();
}
/**
* Stop all traffic monitoring.
*/
public synchronized void stopMonitoring() {
log.debug("STOP monitoring");
if (mode != IDLE) {
sendClearAll();
}
}
// =======================================================================
// === Helper methods ===
private void sendClearAll() {
clearAll();
sendClearHighlights();
}
private void clearAll() {
this.mode = IDLE;
clearSelection();
cancelTask();
}
private void clearSelection() {
selectedIntent = null;
}
//TODO duplicate and can be brought in abstract upper class.
private synchronized void scheduleTask() {
if (trafficTask == null) {
log.debug("Starting up background protected intent task...");
trafficTask = new TrafficUpdateTask();
timer.schedule(trafficTask, trafficPeriod, trafficPeriod);
} else {
log.debug("(protected intent task already running)");
}
}
private synchronized void cancelTask() {
if (trafficTask != null) {
trafficTask.cancel();
trafficTask = null;
}
}
private void sendSelectedIntents() {
log.debug("sendSelectedIntents: {}", selectedIntent);
msgHandler.sendHighlights(protectedIntentHighlights());
}
private void sendClearHighlights() {
log.debug("sendClearHighlights");
msgHandler.sendHighlights(new Highlights());
}
// =======================================================================
// === Generate messages in JSON object node format
private Highlights protectedIntentHighlights() {
Highlights highlights = new Highlights();
TrafficLinkMap linkMap = new TrafficLinkMap();
if (selectedIntent != null) {
List<Intent> installables = servicesBundle.intentService()
.getInstallableIntents(selectedIntent.key());
Set<Link> primary = new HashSet<>();
Set<Link> backup = new HashSet<>();
if (installables != null) {
//There should be exactly two FlowRuleIntent and four
//ProtectionEndpointIntent for each ProtectedTransportIntent.
for (Intent installable : installables) {
if (installable instanceof FlowRuleIntent) {
handleFlowRuleIntent(primary, backup, (FlowRuleIntent) installable);
} else if (installable instanceof ProtectionEndpointIntent) {
handleProtectionEndpointIntent(primary, backup,
(ProtectionEndpointIntent) installable);
} else {
log.warn("Intent {} is not an expected installable type {} " +
"related to ProtectedTransportIntent",
installable.id(), installable.getClass().getSimpleName());
stopMonitoring();
}
}
boolean isOptical = selectedIntent instanceof OpticalConnectivityIntent;
//last parameter (traffic) signals if the link is highlited with ants or solid line
//Flavor is swapped so green is primary path.
if (usingBackup(primary)) {
//the backup becomes in use so we have a dotted line
processLinks(linkMap, backup, Flavor.PRIMARY_HIGHLIGHT, isOptical, true);
} else {
processLinks(linkMap, primary, Flavor.SECONDARY_HIGHLIGHT, isOptical, true);
processLinks(linkMap, backup, Flavor.PRIMARY_HIGHLIGHT, isOptical, false);
}
updateHighlights(highlights, primary);
updateHighlights(highlights, backup);
colorLinks(highlights, linkMap);
highlights.subdueAllElse(Highlights.Amount.MINIMALLY);
} else {
log.debug("Selected Intent has no installables intents");
}
} else {
log.debug("Selected Intent is null");
}
return highlights;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
private void handleProtectionEndpointIntent(Set<Link> primary, Set<Link> backup,
ProtectionEndpointIntent peIntent) {
TransportEndpointDescription primaryDesc = peIntent
.description().paths().get(0);
TransportEndpointDescription secondaryDesc = peIntent
.description().paths().get(1);
primary.addAll(servicesBundle.linkService()
.getLinks(primaryDesc.output()
.connectPoint()));
backup.addAll(servicesBundle.linkService()
.getLinks(secondaryDesc.output()
.connectPoint()));
}
private void handleFlowRuleIntent(Set<Link> primary, Set<Link> backup,
FlowRuleIntent frIntent) {
boolean protection1 = frIntent.resources().stream()
.filter(r -> r instanceof MarkerResource)
.map(NetworkResource::toString)
.anyMatch(rstring -> rstring.equals(PRIMARY_PATH_TAG));
if (protection1) {
primary.addAll(linkResources(frIntent));
} else {
backup.addAll(linkResources(frIntent));
}
}
// returns true if the backup path is the one where the traffic is currently flowing
private boolean usingBackup(Set<Link> primary) {
Set<Link> activeLinks = Sets.newHashSet(servicesBundle.linkService().getActiveLinks());
return primary.isEmpty() || !activeLinks.containsAll(primary);
}
private void updateHighlights(Highlights highlights, Iterable<Link> links) {
for (Link link : links) {
ensureNodePresent(highlights, link.src().elementId());
ensureNodePresent(highlights, link.dst().elementId());
}
}
//TODO duplicate and can be brought in abstract upper class.
private void ensureNodePresent(Highlights highlights, ElementId eid) {
String id = eid.toString();
NodeHighlight nh = highlights.getNode(id);
if (nh == null) {
if (eid instanceof DeviceId) {
nh = new DeviceHighlight(id);
highlights.add((DeviceHighlight) nh);
} else if (eid instanceof HostId) {
nh = new HostHighlight(id);
highlights.add((HostHighlight) nh);
}
}
}
private void processLinks(TrafficLinkMap linkMap, Iterable<Link> links,
Flavor flavor, boolean isOptical,
boolean showTraffic) {
if (links != null) {
for (Link link : links) {
TrafficLink tlink = linkMap.add(link);
tlink.tagFlavor(flavor);
tlink.optical(isOptical);
if (showTraffic) {
tlink.antMarch(true);
}
}
}
}
//TODO duplicate and can be brought in abstract upper class.
private void colorLinks(Highlights highlights, TrafficLinkMap linkMap) {
for (TrafficLink tlink : linkMap.biLinks()) {
highlights.add(tlink.highlight(StatsType.TAGGED));
}
}
//TODO duplicate and can be brought in abstract upper class.
// Extracts links from the specified flow rule intent resources
private Collection<Link> linkResources(Intent installable) {
ImmutableList.Builder<Link> builder = ImmutableList.builder();
installable.resources().stream().filter(r -> r instanceof Link)
.forEach(r -> builder.add((Link) r));
return builder.build();
}
// =======================================================================
// === Background Task
// Provides periodic update of traffic information to the client
private class TrafficUpdateTask extends TimerTask {
@Override
public void run() {
try {
switch (mode) {
case SELECTED_INTENT:
sendSelectedIntents();
break;
default:
// RELATED_INTENTS and IDLE modes should never invoke
// the background task, but if they do, they have
// nothing to do
break;
}
} catch (Exception e) {
log.warn("Unable to process protected intent task due to {}", e.getMessage());
log.warn("Boom!", e);
}
}
}
}