blob: 77961ed7e68079d85f2e1f4231a6875347e5ae67 [file] [log] [blame]
/*
* 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.actn.mdsc.tetunnelctl;
import com.google.common.collect.Lists;
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.onosproject.actn.mdsc.pce.TeTunnelPceService;
import org.onosproject.incubator.net.tunnel.Tunnel;
import org.onosproject.incubator.net.tunnel.TunnelAdminService;
import org.onosproject.incubator.net.tunnel.TunnelEvent;
import org.onosproject.incubator.net.tunnel.TunnelId;
import org.onosproject.incubator.net.tunnel.TunnelListener;
import org.onosproject.incubator.net.tunnel.TunnelService;
import org.onosproject.tetopology.management.api.TeTopology;
import org.onosproject.tetopology.management.api.TeTopologyKey;
import org.onosproject.tetopology.management.api.TeTopologyService;
import org.onosproject.tetopology.management.api.node.TeNodeKey;
import org.onosproject.tetopology.management.api.node.TtpKey;
import org.onosproject.tetunnel.api.TeTunnelAdminService;
import org.onosproject.tetunnel.api.TeTunnelService;
import org.onosproject.tetunnel.api.tunnel.DefaultTeTunnel;
import org.onosproject.tetunnel.api.tunnel.TeTunnel;
import org.onosproject.tetunnel.api.tunnel.TeTunnelKey;
import org.onosproject.tetunnel.api.tunnel.path.TePath;
import org.onosproject.tetunnel.api.tunnel.path.TeRouteSubobject;
import org.onosproject.tetunnel.api.tunnel.path.TeRouteUnnumberedLink;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
/**
* TE Tunnel controller/processor which manages TE tunnel processing.
* <p>
* For example, when creating a cross-domain tunnel from a MDSC, the
* processor will call a relevant PCE to get an end-to-end cross-domain path,
* then spits the path into segment tunnels(domain tunnels), and then informs
* PNCs to setup domain tunnels respectively.
*/
@Component(immediate = true)
public class TeTunnelCtl {
private static final Logger log = LoggerFactory.getLogger(TeTunnelCtl.class);
private final TunnelListener tunnelListener = new InternalTunnelListener();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TunnelService tunnelService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TunnelAdminService tunnelAdminService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TeTunnelService teTunnelService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TeTunnelAdminService teTunnelAdminService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TeTopologyService teTopologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TeTunnelPceService teTunnelPceService;
@Activate
protected void activate() {
tunnelService.addListener(tunnelListener);
log.info("Started");
}
@Deactivate
protected void deactivate() {
tunnelService.removeListener(tunnelListener);
log.info("Stopped");
}
private void addTeTunnel(TeTunnel teTunnel) {
if (teTunnel == null) {
return;
}
Tunnel tunnel = tunnelService.queryTunnel(
teTunnelService.getTunnelId(teTunnel.teTunnelKey()));
if (tunnel == null) {
log.error("tunnel does not exist, {}", teTunnel.teTunnelKey());
return;
}
if (tunnel.state() != Tunnel.State.INIT) {
log.error("tunnel state error, {}, {}", teTunnel.teTunnelKey(),
tunnel.state());
return;
}
tunnelAdminService.updateTunnelState(tunnel, Tunnel.State.ESTABLISHING);
//TODO support multi-thread
if (isTeTunnelCrossDomain(teTunnel)) {
if (!addCrossDomainTeTunnel(teTunnel)) {
tunnelAdminService.updateTunnelState(tunnel, Tunnel.State.FAILED);
}
}
/*
* "else" is to do nothing.
* When adding a single domain tunnel, the TunnelManager will call
* tunnel providers, then the providers will pass the request to
* the domain controller. Nothing to do here.
*/
}
private boolean isTeTunnelCrossDomain(TeTunnel teTunnel) {
TeTopology srcTopo = teTopologyService.teTopology(
teTopologyService.teNode(teTunnel.srcNode())
.underlayTeTopologyId());
TeTopology dstTopo = teTopologyService.teTopology(
teTopologyService.teNode(teTunnel.dstNode())
.underlayTeTopologyId());
return (srcTopo != null && dstTopo != null
&& srcTopo.ownerId().equals(dstTopo.ownerId()));
}
private boolean addCrossDomainTeTunnel(TeTunnel teTunnel) {
List<TeRouteSubobject> route = null;
TePath primaryPath = teTunnel.primaryPaths().get(0);
if (primaryPath != null &&
primaryPath.type() == TePath.Type.EXPLICIT) {
route = primaryPath.explicitRoute();
} else {
Collection<List<TeRouteSubobject>> routes =
teTunnelPceService.computePaths(teTunnel);
if (routes == null || routes.isEmpty()) {
log.error("no available route for {}",
teTunnel.teTunnelKey());
return false;
}
//FIXME: try other pce when failed?
route = routes.iterator().next();
}
if (route == null) {
log.error("no available route for {}",
teTunnel.teTunnelKey());
return false;
}
return spitRoute(teTunnel, route);
}
//spits route to segment tunnels
private boolean spitRoute(TeTunnel teTunnel, List<TeRouteSubobject> route) {
List<TeTunnelKey> segmentTunnels = Lists.newArrayList();
boolean success = true;
TeNodeKey srcNode = teTunnel.srcNode();
TtpKey srcTp = teTunnel.srcTp();
TeNodeKey dstNode = null;
TtpKey dstTp = null;
for (TeRouteSubobject teRouteSubobject : route) {
if (!(teRouteSubobject instanceof TeRouteUnnumberedLink)) {
log.error("unsupported type {}", teRouteSubobject.type());
success = false;
break;
}
TeRouteUnnumberedLink teRouteUnnumberedLink =
(TeRouteUnnumberedLink) teRouteSubobject;
dstNode = teRouteUnnumberedLink.node();
dstTp = teRouteUnnumberedLink.ttp();
if (Objects.equals(srcNode, dstNode) &&
Objects.equals(srcTp, dstTp)) {
continue;
}
if (Objects.equals(srcNode, dstNode)) {
if (!addSegmentTunnel(segmentTunnels, teTunnel,
srcNode, srcTp, dstNode, dstTp)) {
success = false;
break;
}
}
srcNode = dstNode;
srcTp = dstTp;
}
if (success && !(Objects.equals(dstNode, teTunnel.dstNode()) &&
Objects.equals(dstTp, teTunnel.dstTp()))) {
srcNode = dstNode;
srcTp = dstTp;
dstNode = teTunnel.dstNode();
dstTp = teTunnel.dstTp();
if (!addSegmentTunnel(segmentTunnels, teTunnel,
srcNode, srcTp, dstNode, dstTp)) {
success = false;
}
}
if (!success) {
// roll back segment tunnels
for (TeTunnelKey key : segmentTunnels) {
teTunnelAdminService.removeTeTunnel(key);
}
} else {
teTunnelAdminService.setSegmentTunnel(teTunnel.teTunnelKey(),
segmentTunnels);
}
return success;
}
private boolean addSegmentTunnel(List<TeTunnelKey> segmentTunnels,
TeTunnel teTunnel,
TeNodeKey srcNode, TtpKey srcTp,
TeNodeKey dstNode, TtpKey dstTp) {
TeTunnelKey teTunnelKey = getNextTeTunnelKey(srcNode.teTopologyKey());
TeTunnel teTunnelSegment = DefaultTeTunnel.builder()
.teTunnelKey(teTunnelKey)
.srcNode(srcNode)
.dstNode(dstNode)
.srcTp(srcTp)
.dstTp(dstTp)
.adminState(teTunnel.adminStatus())
.lspProtectionType(teTunnel.lspProtectionType())
.type(teTunnel.type())
.build();
TunnelId tunnelId =
teTunnelAdminService.createTeTunnel(teTunnelSegment);
if (tunnelId == null) {
log.error("failed to create segment tunnel: {},{},{},{}",
srcNode, srcTp, dstNode, dstTp);
return false;
}
segmentTunnels.add(teTunnelKey);
return true;
}
private TeTunnelKey getNextTeTunnelKey(TeTopologyKey key) {
//FIXME need a better way to get a te tunnel id
long teTunnelId = teTunnelService.getTeTunnels(key).size() + 1L;
return new TeTunnelKey(key, teTunnelId);
}
private void updateTeTunnel(TeTunnel teTunnel, Tunnel tunnel) {
if (teTunnel == null) {
return;
}
if (tunnel.state() == Tunnel.State.ESTABLISHED) {
tunnelEstablished(teTunnel);
} else if (tunnel.state() == Tunnel.State.REMOVING) {
removingTunnel(teTunnel);
}
//TODO update TE tunnel content
}
private void tunnelEstablished(TeTunnel teTunnel) {
TeTunnel e2eTeTunnel = retriveE2eTunnel(teTunnel);
if (e2eTeTunnel != null) {
boolean goodToContinue = true;
for (TeTunnelKey key : e2eTeTunnel.segmentTunnels()) {
goodToContinue = checkSegmentTunnel(key);
if (!goodToContinue) {
break;
}
}
if (goodToContinue) {
tunnelAdminService.updateTunnelState(
tunnelService.queryTunnel(
teTunnelService.getTunnelId(
teTunnel.teTunnelKey())),
Tunnel.State.ESTABLISHED
);
}
}
}
private TeTunnel retriveE2eTunnel(TeTunnel segmentTunnel) {
return teTunnelService.getTeTunnel(segmentTunnel.e2eTunnelKey());
}
private boolean checkSegmentTunnel(TeTunnelKey key) {
Tunnel segmentTunnel = tunnelService.queryTunnel(
teTunnelService.getTunnelId(key));
if (segmentTunnel == null ||
segmentTunnel.state() != Tunnel.State.ESTABLISHED) {
return false;
}
return true;
}
private void removingTunnel(TeTunnel teTunnel) {
List<TeTunnelKey> segmentTunnels = teTunnel.segmentTunnels();
if (segmentTunnels != null && !segmentTunnels.isEmpty()) {
for (TeTunnelKey key : segmentTunnels) {
teTunnelAdminService.removeTeTunnel(key);
}
}
}
// Listens on tunnel events.
private class InternalTunnelListener implements TunnelListener {
@Override
public void event(TunnelEvent event) {
switch (event.type()) {
case TUNNEL_ADDED:
addTunnel(event.subject());
break;
case TUNNEL_UPDATED:
updateTunnel(event.subject());
break;
//TODO: TE Tunnel remove/... event process
default:
log.warn("unknown event: {}", event.type());
break;
}
}
private void addTunnel(Tunnel tunnel) {
addTeTunnel(teTunnelService.getTeTunnel(tunnel.tunnelId()));
}
private void updateTunnel(Tunnel tunnel) {
updateTeTunnel(teTunnelService.getTeTunnel(tunnel.tunnelId()),
tunnel);
}
}
}