blob: 8b334ea6825464ce4879e2e492131fe49836fb1d [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.segmentrouting.pwaas;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.RandomUtils;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
import org.onlab.packet.MplsLabel;
import org.onlab.packet.VlanId;
import org.onlab.util.KryoNamespace;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultLink;
import org.onosproject.net.DeviceId;
import org.onosproject.net.DisjointPath;
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criteria;
import org.onosproject.net.flowobjective.DefaultFilteringObjective;
import org.onosproject.net.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.DefaultNextObjective;
import org.onosproject.net.flowobjective.DefaultObjectiveContext;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flowobjective.NextObjective;
import org.onosproject.net.flowobjective.Objective;
import org.onosproject.net.flowobjective.ObjectiveContext;
import org.onosproject.net.flowobjective.ObjectiveError;
import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.SegmentRoutingService;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.PwaasConfig;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.ConsistentMap;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.Versioned;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.TERMINATION;
import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.FWD;
import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.REV;
/**
* Handles pwaas related events.
*/
public class L2TunnelHandler {
private static final Logger log = LoggerFactory.getLogger(L2TunnelHandler.class);
private final SegmentRoutingManager srManager;
/**
* To store the next objectives related to the initiation.
*/
private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
/**
* To store the next objectives related to the termination.
*/
private final ConsistentMap<String, NextObjective> l2TerminationNextObjStore;
/**
* To store policies.
*/
private final ConsistentMap<String, DefaultL2TunnelPolicy> l2PolicyStore;
/**
* To store tunnels.
*/
private final ConsistentMap<String, DefaultL2Tunnel> l2TunnelStore;
private final KryoNamespace.Builder l2TunnelKryo;
/**
* Create a l2 tunnel handler for the deploy and
* for the tear down of pseudo wires.
*
* @param segmentRoutingManager the segment routing manager
*/
public L2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
srManager = segmentRoutingManager;
l2TunnelKryo = new KryoNamespace.Builder()
.register(KryoNamespaces.API)
.register(DefaultL2Tunnel.class,
DefaultL2TunnelPolicy.class,
L2Mode.class,
MplsLabel.class,
VlanId.class,
ConnectPoint.class);
l2InitiationNextObjStore = srManager.
storageService.
<String, NextObjective>consistentMapBuilder().
withName("onos-l2initiation-nextobj-store").
withSerializer(Serializer.using(l2TunnelKryo.build())).
build();
l2TerminationNextObjStore = srManager.storageService.
<String, NextObjective>consistentMapBuilder()
.withName("onos-l2termination-nextobj-store")
.withSerializer(Serializer.using(l2TunnelKryo.build()))
.build();
l2PolicyStore = srManager.storageService
.<String, DefaultL2TunnelPolicy>consistentMapBuilder()
.withName("onos-l2-policy-store")
.withSerializer(Serializer.using(l2TunnelKryo.build()))
.build();
l2TunnelStore = srManager.storageService
.<String, DefaultL2Tunnel>consistentMapBuilder()
.withName("onos-l2-tunnel-store")
.withSerializer(Serializer.using(l2TunnelKryo.build()))
.build();
}
/**
* Returns all L2 Policies.
*
* @return List of policies
*/
public List<DefaultL2TunnelPolicy> getL2Policies() {
return l2PolicyStore
.values()
.stream()
.map(Versioned::value)
.map(DefaultL2TunnelPolicy::new)
.collect(Collectors.toList());
}
/**
* Returns all L2 Tunnels.
*
* @return List of tunnels.
*/
public List<DefaultL2Tunnel> getL2Tunnels() {
return l2TunnelStore
.values()
.stream()
.map(Versioned::value)
.map(DefaultL2Tunnel::new)
.collect(Collectors.toList());
}
/**
* Processes a link removal. Finds affected pseudowires and rewires them.
* TODO: Make it also take into account failures of links that are used for pw
* traffic in the spine.
* @param link The link that failed
*/
public void processLinkDown(Link link) {
List<DefaultL2Tunnel> tunnels = getL2Tunnels();
List<DefaultL2TunnelPolicy> policies = getL2Policies();
// determine affected pseudowires and update them at once
Set<DefaultL2TunnelDescription> pwToUpdate = tunnels
.stream()
.filter(tun -> tun.pathUsed().contains(link))
.map(l2Tunnel -> {
DefaultL2TunnelPolicy policy = null;
for (DefaultL2TunnelPolicy l2Policy : policies) {
if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
policy = l2Policy;
break;
}
}
return new DefaultL2TunnelDescription(l2Tunnel, policy);
})
.collect(Collectors.toSet());
log.info("Pseudowires affected by link failure : {}, rerouting them...", pwToUpdate);
// update all pseudowires
pwToUpdate.forEach(tun -> updatePw(tun, tun));
}
/**
* Processes Pwaas Config added event.
*
* @param event network config add event
*/
public void processPwaasConfigAdded(NetworkConfigEvent event) {
log.info("Network event : Pseudowire configuration added!");
PwaasConfig config = (PwaasConfig) event.config().get();
// gather pseudowires
Set<DefaultL2TunnelDescription> pwToAdd = config
.getPwIds()
.stream()
.map(config::getPwDescription)
.collect(Collectors.toSet());
// deploy pseudowires
deploy(pwToAdd);
}
/**
* Returns the new vlan id for an ingress point of a
* pseudowire. For double tagged, it is the outer,
* For single tagged it is the single tag, and for
* inner it is None.
*
* @param ingressOuter vlanid of ingress outer
* @param ingressInner vlanid of ingress inner
* @param egressOuter vlanid of egress outer
* @param egressInner vlanid of egress inner
* @return returns the vlan id which will be installed at vlan table 1.
*/
public VlanId determineEgressVlan(VlanId ingressOuter, VlanId ingressInner,
VlanId egressOuter, VlanId egressInner) {
// validity of vlan combinations was checked at verifyPseudowire
if (!(ingressOuter.equals(VlanId.NONE))) {
return egressOuter;
} else if (!(ingressInner.equals(VlanId.NONE))) {
return egressInner;
} else {
return VlanId.vlanId("None");
}
}
/**
* Adds a single pseudowire. This method can be called from cli commands
* without configration updates, thus it does not check for mastership
* of the ingress pseudowire device.
*
* @param pw The pseudowire
* @return
*/
private Result deployPseudowire(DefaultL2TunnelDescription pw) {
Result result;
long l2TunnelId;
l2TunnelId = pw.l2Tunnel().tunnelId();
// The tunnel id cannot be 0.
if (l2TunnelId == 0) {
log.warn("Tunnel id id must be > 0");
return Result.ADDITION_ERROR;
}
// get path here, need to use the same for fwd and rev direction
List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
pw.l2TunnelPolicy().cP2());
if (path == null) {
log.info("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
return WRONG_PARAMETERS;
}
pw.l2Tunnel().setPath(path);
// next hops for next objectives
Link fwdNextHop = path.get(0);
Link revNextHop = reverseLink(path.get(1));
log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
// We establish the tunnel.
// result.nextId will be used in fwd
result = deployPseudoWireInit(pw.l2Tunnel(),
pw.l2TunnelPolicy().cP1(),
pw.l2TunnelPolicy().cP2(),
FWD,
fwdNextHop);
if (result != SUCCESS) {
log.info("Deploying process : Error in deploying pseudowire initiation for CP1");
return Result.ADDITION_ERROR;
}
VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
pw.l2TunnelPolicy().cP1InnerTag(),
pw.l2TunnelPolicy().cP2OuterTag(),
pw.l2TunnelPolicy().cP2InnerTag());
// We create the policy.
result = deployPolicy(l2TunnelId,
pw.l2TunnelPolicy().cP1(),
pw.l2TunnelPolicy().cP1InnerTag(),
pw.l2TunnelPolicy().cP1OuterTag(),
egressVlan,
result.nextId);
if (result != SUCCESS) {
log.info("Deploying process : Error in deploying pseudowire policy for CP1");
return Result.ADDITION_ERROR;
}
// We terminate the tunnel
result = deployPseudoWireTerm(pw.l2Tunnel(),
pw.l2TunnelPolicy().cP2(),
pw.l2TunnelPolicy().cP2OuterTag(),
FWD);
if (result != SUCCESS) {
log.info("Deploying process : Error in deploying pseudowire termination for CP1");
return Result.ADDITION_ERROR;
}
log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
// We establish the reverse tunnel.
result = deployPseudoWireInit(pw.l2Tunnel(),
pw.l2TunnelPolicy().cP2(),
pw.l2TunnelPolicy().cP1(),
REV,
revNextHop);
if (result != SUCCESS) {
log.info("Deploying process : Error in deploying pseudowire initiation for CP2");
return Result.ADDITION_ERROR;
}
egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
pw.l2TunnelPolicy().cP2InnerTag(),
pw.l2TunnelPolicy().cP1OuterTag(),
pw.l2TunnelPolicy().cP1InnerTag());
result = deployPolicy(l2TunnelId,
pw.l2TunnelPolicy().cP2(),
pw.l2TunnelPolicy().cP2InnerTag(),
pw.l2TunnelPolicy().cP2OuterTag(),
egressVlan,
result.nextId);
if (result != SUCCESS) {
log.info("Deploying process : Error in deploying policy for CP2");
return Result.ADDITION_ERROR;
}
result = deployPseudoWireTerm(pw.l2Tunnel(),
pw.l2TunnelPolicy().cP1(),
pw.l2TunnelPolicy().cP1OuterTag(),
REV);
if (result != SUCCESS) {
log.info("Deploying process : Error in deploying pseudowire termination for CP2");
return Result.ADDITION_ERROR;
}
log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
// Populate stores
l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
return Result.SUCCESS;
}
/**
* To deploy a number of pseudo wires.
* <p>
* Called ONLY when configuration changes, thus the check
* for the mastership of the device.
* <p>
* Only the master of CP1 will deploy this pseudowire.
*
* @param pwToAdd the set of pseudo wires to add
*/
private void deploy(Set<DefaultL2TunnelDescription> pwToAdd) {
Result result;
for (DefaultL2TunnelDescription currentL2Tunnel : pwToAdd) {
// only the master of CP1 will program this pseudowire
if (!srManager.isMasterOf(currentL2Tunnel.l2TunnelPolicy().cP1())) {
continue;
}
log.info("Deploying pseudowire {}", currentL2Tunnel.l2Tunnel().tunnelId());
result = deployPseudowire(currentL2Tunnel);
switch (result) {
case WRONG_PARAMETERS:
log.warn("Could not deploy pseudowire {}, wrong parameters!",
currentL2Tunnel.l2Tunnel().tunnelId());
break;
case ADDITION_ERROR:
log.warn("Could not deploy pseudowire {}, error in populating rules!",
currentL2Tunnel.l2Tunnel().tunnelId());
break;
default:
log.info("Pseudowire with {} succesfully deployed!",
currentL2Tunnel.l2Tunnel().tunnelId());
break;
}
}
}
/**
* Processes PWaaS Config updated event.
*
* @param event network config updated event
*/
public void processPwaasConfigUpdated(NetworkConfigEvent event) {
log.info("Pseudowire configuration updated.");
// We retrieve the old pseudo wires.
PwaasConfig prevConfig = (PwaasConfig) event.prevConfig().get();
Set<Long> prevPws = prevConfig.getPwIds();
// We retrieve the new pseudo wires.
PwaasConfig config = (PwaasConfig) event.config().get();
Set<Long> newPws = config.getPwIds();
// We compute the pseudo wires to update.
Set<Long> updPws = newPws.stream()
.filter(tunnelId -> prevPws.contains(tunnelId)
&& !config.getPwDescription(tunnelId).equals(prevConfig.getPwDescription(tunnelId)))
.collect(Collectors.toSet());
// The pseudo wires to remove.
Set<Long> rmvPWs = prevPws.stream()
.filter(tunnelId -> !newPws.contains(tunnelId)).collect(Collectors.toSet());
Set<DefaultL2TunnelDescription> pwToRemove = rmvPWs.stream()
.map(prevConfig::getPwDescription)
.collect(Collectors.toSet());
tearDown(pwToRemove);
// The pseudo wires to add.
Set<Long> addedPWs = newPws.stream()
.filter(tunnelId -> !prevPws.contains(tunnelId))
.collect(Collectors.toSet());
Set<DefaultL2TunnelDescription> pwToAdd = addedPWs.stream()
.map(config::getPwDescription)
.collect(Collectors.toSet());
deploy(pwToAdd);
// The pseudo wires to update.
updPws.forEach(tunnelId -> updatePw(prevConfig.getPwDescription(tunnelId),
config.getPwDescription(tunnelId)));
log.info("Pseudowires removed : {}, Pseudowires updated : {}, Pseudowires added : {}", rmvPWs,
updPws, addedPWs);
}
/**
* Helper function to update a pw.
* <p>
* Called upon configuration changes that update existing pseudowires and
* when links fail. Checking of mastership for CP1 is mandatory because it is
* called in multiple instances for both cases.
* <p>
* Meant to call asynchronously for various events, thus this call can not block and need
* to perform asynchronous operations.
* <p>
* For this reason error checking is omitted.
*
* @param oldPw the pseudo wire to remove
* @param newPw the pseudo wire to add
*/
public void updatePw(DefaultL2TunnelDescription oldPw, DefaultL2TunnelDescription newPw) {
long tunnelId = oldPw.l2Tunnel().tunnelId();
// only the master of CP1 will update this pseudowire
if (!srManager.isMasterOf(oldPw.l2TunnelPolicy().cP1())) {
return;
}
log.info("Updating pseudowire {}", oldPw.l2Tunnel().tunnelId());
// The async tasks to orchestrate the next and
// forwarding update.
CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
CompletableFuture<ObjectiveError> fwdPwFuture = new CompletableFuture<>();
CompletableFuture<ObjectiveError> revPwFuture = new CompletableFuture<>();
// First we remove both policy.
log.debug("Start deleting fwd policy for {}", tunnelId);
// first delete all information from our stores
// we can not do it asynchronously
l2PolicyStore.remove(Long.toString(tunnelId));
l2TunnelStore.remove(Long.toString(tunnelId));
VlanId egressVlan = determineEgressVlan(oldPw.l2TunnelPolicy().cP1OuterTag(),
oldPw.l2TunnelPolicy().cP1InnerTag(),
oldPw.l2TunnelPolicy().cP2OuterTag(),
oldPw.l2TunnelPolicy().cP2InnerTag());
deletePolicy(tunnelId,
oldPw.l2TunnelPolicy().cP1(),
oldPw.l2TunnelPolicy().cP1InnerTag(),
oldPw.l2TunnelPolicy().cP1OuterTag(),
egressVlan,
fwdInitNextFuture,
FWD);
log.debug("Update process : Start deleting rev policy for {}", tunnelId);
egressVlan = determineEgressVlan(oldPw.l2TunnelPolicy().cP2OuterTag(),
oldPw.l2TunnelPolicy().cP2InnerTag(),
oldPw.l2TunnelPolicy().cP1OuterTag(),
oldPw.l2TunnelPolicy().cP1InnerTag());
deletePolicy(tunnelId,
oldPw.l2TunnelPolicy().cP2(),
oldPw.l2TunnelPolicy().cP2InnerTag(),
oldPw.l2TunnelPolicy().cP2OuterTag(),
egressVlan, revInitNextFuture,
REV);
// Finally we remove both the tunnels.
fwdInitNextFuture.thenAcceptAsync(status -> {
if (status == null) {
log.debug("Update process : Fwd policy removed. " +
"Now remove fwd {} for {}", INITIATION, tunnelId);
tearDownPseudoWireInit(tunnelId,
oldPw.l2TunnelPolicy().cP1(),
fwdTermNextFuture,
FWD);
}
});
revInitNextFuture.thenAcceptAsync(status -> {
if (status == null) {
log.debug("Update process : Rev policy removed. " +
"Now remove rev {} for {}", INITIATION, tunnelId);
tearDownPseudoWireInit(tunnelId,
oldPw.l2TunnelPolicy().cP2(),
revTermNextFuture,
REV);
}
});
fwdTermNextFuture.thenAcceptAsync(status -> {
if (status == null) {
log.debug("Update process : Fwd {} removed. " +
"Now remove fwd {} for {}", INITIATION, TERMINATION, tunnelId);
tearDownPseudoWireTerm(oldPw.l2Tunnel(),
oldPw.l2TunnelPolicy().cP2(),
fwdPwFuture,
FWD);
}
});
revTermNextFuture.thenAcceptAsync(status -> {
if (status == null) {
log.debug("Update process : Rev {} removed. " +
"Now remove rev {} for {}", INITIATION, TERMINATION, tunnelId);
tearDownPseudoWireTerm(oldPw.l2Tunnel(),
oldPw.l2TunnelPolicy().cP1(),
revPwFuture,
REV);
}
});
// get path here, need to use the same for fwd and rev direction
List<Link> path = getPath(newPw.l2TunnelPolicy().cP1(),
newPw.l2TunnelPolicy().cP2());
if (path == null) {
log.info("Deploying process : " +
"No path between the connection points for pseudowire {}", newPw.l2Tunnel().tunnelId());
return;
}
newPw.l2Tunnel().setPath(path);
// next hops for next objectives
Link fwdNextHop = path.get(0);
Link revNextHop = reverseLink(path.get(1));
// At the end we install the updated PW.
fwdPwFuture.thenAcceptAsync(status -> {
if (status == null) {
// Upgrade stores and book keeping information, need to move this here
// cause this call is asynchronous.
l2PolicyStore.put(Long.toString(tunnelId), newPw.l2TunnelPolicy());
l2TunnelStore.put(Long.toString(tunnelId), newPw.l2Tunnel());
log.debug("Update process : Deploying new fwd pw for {}", tunnelId);
Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(),
newPw.l2TunnelPolicy().cP1(),
newPw.l2TunnelPolicy().cP2(),
FWD,
fwdNextHop);
if (lamdaResult != SUCCESS) {
return;
}
VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP1OuterTag(),
newPw.l2TunnelPolicy().cP1InnerTag(),
newPw.l2TunnelPolicy().cP2OuterTag(),
newPw.l2TunnelPolicy().cP2InnerTag());
lamdaResult = deployPolicy(tunnelId,
newPw.l2TunnelPolicy().cP1(),
newPw.l2TunnelPolicy().cP1InnerTag(),
newPw.l2TunnelPolicy().cP1OuterTag(),
egressVlanId,
lamdaResult.nextId);
if (lamdaResult != SUCCESS) {
return;
}
deployPseudoWireTerm(newPw.l2Tunnel(),
newPw.l2TunnelPolicy().cP2(),
newPw.l2TunnelPolicy().cP2OuterTag(),
FWD);
}
});
revPwFuture.thenAcceptAsync(status -> {
if (status == null) {
log.debug("Update process : Deploying new rev pw for {}", tunnelId);
Result lamdaResult = deployPseudoWireInit(newPw.l2Tunnel(),
newPw.l2TunnelPolicy().cP2(),
newPw.l2TunnelPolicy().cP1(),
REV,
revNextHop);
if (lamdaResult != SUCCESS) {
return;
}
VlanId egressVlanId = determineEgressVlan(newPw.l2TunnelPolicy().cP2OuterTag(),
newPw.l2TunnelPolicy().cP2InnerTag(),
newPw.l2TunnelPolicy().cP1OuterTag(),
newPw.l2TunnelPolicy().cP1InnerTag());
lamdaResult = deployPolicy(tunnelId,
newPw.l2TunnelPolicy().cP2(),
newPw.l2TunnelPolicy().cP2InnerTag(),
newPw.l2TunnelPolicy().cP2OuterTag(),
egressVlanId,
lamdaResult.nextId);
if (lamdaResult != SUCCESS) {
return;
}
deployPseudoWireTerm(newPw.l2Tunnel(),
newPw.l2TunnelPolicy().cP1(),
newPw.l2TunnelPolicy().cP1OuterTag(),
REV);
}
});
}
/**
* Processes Pwaas Config removed event.
*
* @param event network config removed event
*/
public void processPwaasConfigRemoved(NetworkConfigEvent event) {
log.info("Network event : Pseudowire configuration removed!");
PwaasConfig config = (PwaasConfig) event.prevConfig().get();
Set<DefaultL2TunnelDescription> pwToRemove = config
.getPwIds()
.stream()
.map(config::getPwDescription)
.collect(Collectors.toSet());
// We teardown all the pseudo wire deployed
tearDown(pwToRemove);
}
/**
* Helper function for removing a single pseudowire.
* <p>
* No mastership of CP1 is checked, because it can be called from
* the CLI for removal of pseudowires.
*
* @param l2TunnelId the id of the pseudowire to tear down
* @return Returns SUCCESS if no error is obeserved or an appropriate
* error on a failure
*/
public Result tearDownPseudowire(long l2TunnelId) {
CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
if (l2TunnelId == 0) {
log.warn("Removal process : Tunnel id cannot be 0");
return Result.WRONG_PARAMETERS;
}
// check existence of tunnels/policy in the store, if one is missing abort!
Versioned<DefaultL2Tunnel> l2TunnelVersioned = l2TunnelStore.get(Long.toString(l2TunnelId));
Versioned<DefaultL2TunnelPolicy> l2TunnelPolicyVersioned = l2PolicyStore.get(Long.toString(l2TunnelId));
if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
return Result.REMOVAL_ERROR;
}
DefaultL2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
l2TunnelPolicyVersioned.value());
// remove the tunnels and the policies from the store
l2PolicyStore.remove(Long.toString(l2TunnelId));
l2TunnelStore.remove(Long.toString(l2TunnelId));
log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
pwToRemove.l2TunnelPolicy().cP1InnerTag(),
pwToRemove.l2TunnelPolicy().cP2OuterTag(),
pwToRemove.l2TunnelPolicy().cP2InnerTag());
deletePolicy(l2TunnelId,
pwToRemove.l2TunnelPolicy().cP1(),
pwToRemove.l2TunnelPolicy().cP1InnerTag(),
pwToRemove.l2TunnelPolicy().cP1OuterTag(),
egressVlan,
fwdInitNextFuture,
FWD);
fwdInitNextFuture.thenAcceptAsync(status -> {
if (status == null) {
// Finally we will tear down the pseudo wire.
tearDownPseudoWireInit(l2TunnelId,
pwToRemove.l2TunnelPolicy().cP1(),
fwdTermNextFuture,
FWD);
}
});
fwdTermNextFuture.thenAcceptAsync(status -> {
if (status == null) {
tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
pwToRemove.l2TunnelPolicy().cP2(),
null,
FWD);
}
});
log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
pwToRemove.l2TunnelPolicy().cP2InnerTag(),
pwToRemove.l2TunnelPolicy().cP1OuterTag(),
pwToRemove.l2TunnelPolicy().cP1InnerTag());
// We do the same operations on the reverse side.
deletePolicy(l2TunnelId,
pwToRemove.l2TunnelPolicy().cP2(),
pwToRemove.l2TunnelPolicy().cP2InnerTag(),
pwToRemove.l2TunnelPolicy().cP2OuterTag(),
egressVlan,
revInitNextFuture,
REV);
revInitNextFuture.thenAcceptAsync(status -> {
if (status == null) {
tearDownPseudoWireInit(l2TunnelId,
pwToRemove.l2TunnelPolicy().cP2(),
revTermNextFuture,
REV);
}
});
revTermNextFuture.thenAcceptAsync(status -> {
if (status == null) {
tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
pwToRemove.l2TunnelPolicy().cP1(),
null,
REV);
}
});
return Result.SUCCESS;
}
/**
* Helper function to handle the pw removal.
* <p>
* This method checks for the mastership of the device because it is
* used only from network configuration updates, thus we only want
* one instance only to program each pseudowire.
*
* @param pwToRemove the pseudo wires to remove
*/
public void tearDown(Set<DefaultL2TunnelDescription> pwToRemove) {
Result result;
// We remove all the pw in the configuration file.
for (DefaultL2TunnelDescription currentL2Tunnel : pwToRemove) {
// only the master of CP1 will program this pseudowire
if (!srManager.isMasterOf(currentL2Tunnel.l2TunnelPolicy().cP1())) {
continue;
}
log.info("Removing pseudowire {}", currentL2Tunnel.l2Tunnel().tunnelId());
result = tearDownPseudowire(currentL2Tunnel.l2Tunnel().tunnelId());
switch (result) {
case WRONG_PARAMETERS:
log.warn("Error in supplied parameters for the pseudowire removal with tunnel id {}!",
currentL2Tunnel.l2Tunnel().tunnelId());
break;
case REMOVAL_ERROR:
log.warn("Error in pseudowire removal with tunnel id {}!", currentL2Tunnel.l2Tunnel().tunnelId());
break;
default:
log.warn("Pseudowire with tunnel id {} was removed successfully",
currentL2Tunnel.l2Tunnel().tunnelId());
}
}
}
/**
* Handles the policy establishment which consists in
* create the filtering and forwarding objectives related
* to the initiation and termination.
*
* @param tunnelId the tunnel id
* @param ingress the ingress point
* @param ingressInner the ingress inner tag
* @param ingressOuter the ingress outer tag
* @param nextId the next objective id
* @param egressVlan Vlan-id to set, depends on ingress vlan
* combinations. For example, if pw is double tagged
* then this is the value of the outer vlan, if single
* tagged then it is the new value of the single tag.
* Should be None for untagged traffic.
* @return the result of the operation
*/
private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
VlanId ingressOuter, VlanId egressVlan, int nextId) {
List<Objective> objectives = Lists.newArrayList();
// We create the forwarding objective for supporting
// the l2 tunnel.
ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
// We create and add objective context.
ObjectiveContext context = new DefaultObjectiveContext((objective) ->
log.debug("FwdObj for tunnel {} populated", tunnelId),
(objective, error) ->
log.warn("Failed to populate fwdrObj " +
"for tunnel {}", tunnelId, error));
objectives.add(fwdBuilder.add(context));
// We create the filtering objective to define the
// permit traffic in the switch
FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
// We add the metadata.
TrafficTreatment.Builder treatment = DefaultTrafficTreatment
.builder()
.setTunnelId(tunnelId)
.setVlanId(egressVlan);
filtBuilder.withMeta(treatment.build());
// We create and add objective context.
context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
(objective, error) -> log.warn("Failed to populate filterObj for " +
"tunnel {}", tunnelId, error));
objectives.add(filtBuilder.add(context));
for (Objective objective : objectives) {
if (objective instanceof ForwardingObjective) {
srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
} else {
srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
log.debug("Creating new FiltObj for tunnel {}", tunnelId);
}
}
return SUCCESS;
}
/**
* Handles the tunnel establishment which consists in
* create the next objectives related to the initiation.
*
* @param l2Tunnel the tunnel to deploy
* @param ingress the ingress connect point
* @param egress the egress connect point
* @param direction the direction of the pw
* @return the result of the operation
*/
private Result deployPseudoWireInit(DefaultL2Tunnel l2Tunnel, ConnectPoint ingress,
ConnectPoint egress, Direction direction, Link nextHop) {
if (nextHop == null) {
log.warn("No path between ingress and egress cps for tunnel {}", l2Tunnel.tunnelId());
return WRONG_PARAMETERS;
}
// We create the next objective without the metadata
// context and id. We check if it already exists in the
// store. If not we store as it is in the store.
NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
nextHop.src(),
nextHop.dst(),
l2Tunnel,
egress.deviceId());
if (nextObjectiveBuilder == null) {
return INTERNAL_ERROR;
}
// We set the metadata. We will use this metadata
// to inform the driver we are doing a l2 tunnel.
TrafficSelector metadata = DefaultTrafficSelector
.builder()
.matchTunnelId(l2Tunnel.tunnelId())
.build();
nextObjectiveBuilder.withMeta(metadata);
int nextId = srManager.flowObjectiveService.allocateNextId();
if (nextId < 0) {
log.warn("Not able to allocate a next id for initiation");
return INTERNAL_ERROR;
}
nextObjectiveBuilder.withId(nextId);
String key = generateKey(l2Tunnel.tunnelId(), direction);
l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
ObjectiveContext context = new DefaultObjectiveContext((objective) ->
log.debug("Initiation l2 tunnel rule " +
"for {} populated",
l2Tunnel.tunnelId()),
(objective, error) ->
log.warn("Failed to populate Initiation " +
"l2 tunnel rule for {}: {}",
l2Tunnel.tunnelId(), error));
NextObjective nextObjective = nextObjectiveBuilder.add(context);
srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
l2Tunnel.tunnelId(), nextObjective.id());
Result result = SUCCESS;
result.nextId = nextObjective.id();
return result;
}
/**
* Handles the tunnel termination, which consists in the creation
* of a forwarding objective and a next objective.
*
* @param l2Tunnel the tunnel to terminate
* @param egress the egress point
* @param egressVlan the expected vlan at egress
* @param direction the direction
* @return the result of the operation
*/
private Result deployPseudoWireTerm(DefaultL2Tunnel l2Tunnel, ConnectPoint egress,
VlanId egressVlan, Direction direction) {
// We create the group relative to the termination.
NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
null, egress.deviceId());
if (nextObjectiveBuilder == null) {
return INTERNAL_ERROR;
}
TrafficSelector metadata = DefaultTrafficSelector
.builder()
.matchVlanId(egressVlan)
.build();
nextObjectiveBuilder.withMeta(metadata);
int nextId = srManager.flowObjectiveService.allocateNextId();
if (nextId < 0) {
log.warn("Not able to allocate a next id for initiation");
return INTERNAL_ERROR;
}
nextObjectiveBuilder.withId(nextId);
String key = generateKey(l2Tunnel.tunnelId(), direction);
l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("Termination l2 tunnel rule " +
"for {} populated",
l2Tunnel.tunnelId()),
(objective, error) -> log.warn("Failed to populate " +
"termination l2 tunnel " +
"rule for {}: {}",
l2Tunnel.tunnelId(),
error));
NextObjective nextObjective = nextObjectiveBuilder.add(context);
srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
l2Tunnel.tunnelId(), nextObjective.id());
// We create the flow relative to the termination.
ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
egress.port(), nextObjective.id());
context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
l2Tunnel.tunnelId()),
(objective, error) -> log.warn("Failed to populate fwdrObj" +
" for tunnel termination {}",
l2Tunnel.tunnelId(), error));
srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
nextId, l2Tunnel.tunnelId());
return SUCCESS;
}
/**
* Creates the filtering objective according to a given policy.
*
* @param inPort the in port
* @param innerTag the inner vlan tag
* @param outerTag the outer vlan tag
* @return the filtering objective
*/
private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
log.info("Creating filtering objective for vlans {} / {}", outerTag, innerTag);
return DefaultFilteringObjective
.builder()
.withKey(Criteria.matchInPort(inPort))
.addCondition(Criteria.matchInnerVlanId(innerTag))
.addCondition(Criteria.matchVlanId(outerTag))
.withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
.permit()
.fromApp(srManager.appId());
}
/**
* Creates the forwarding objective for the termination.
*
* @param pwLabel the pseudo wire label
* @param tunnelId the tunnel id
* @param egressPort the egress port
* @param nextId the next step
* @return the forwarding objective to support the termination
*/
private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
PortNumber egressPort, int nextId) {
TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
// The flow has to match on the pw label and bos
trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
trafficSelector.matchMplsLabel(pwLabel);
trafficSelector.matchMplsBos(true);
// The flow has to decrement ttl, restore ttl in
// pop mpls, set tunnel id and port.
trafficTreatment.decMplsTtl();
trafficTreatment.copyTtlIn();
trafficTreatment.popMpls();
trafficTreatment.setTunnelId(tunnelId);
trafficTreatment.setOutput(egressPort);
return DefaultForwardingObjective
.builder()
.fromApp(srManager.appId())
.makePermanent()
.nextStep(nextId)
.withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
.withSelector(trafficSelector.build())
.withTreatment(trafficTreatment.build())
.withFlag(VERSATILE);
}
/**
* Creates the forwarding objective for the initiation.
*
* @param tunnelId the tunnel id
* @param inPort the input port
* @param nextId the next step
* @return the forwarding objective to support the initiation.
*/
private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
// The flow has to match on the mpls logical
// port and the tunnel id.
trafficSelector.matchTunnelId(tunnelId);
trafficSelector.matchInPort(inPort);
return DefaultForwardingObjective
.builder()
.fromApp(srManager.appId())
.makePermanent()
.nextStep(nextId)
.withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
.withSelector(trafficSelector.build())
.withFlag(VERSATILE);
}
/**
* Creates the next objective according to a given
* pipeline. We don't set the next id and we don't
* create the final meta to check if we are re-using
* the same next objective for different tunnels.
*
* @param pipeline the pipeline to support
* @param srcCp the source port
* @param dstCp the destination port
* @param l2Tunnel the tunnel to support
* @param egressId the egress device id
* @return the next objective to support the pipeline
*/
private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
ConnectPoint dstCp, DefaultL2Tunnel l2Tunnel,
DeviceId egressId) {
NextObjective.Builder nextObjBuilder;
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
if (pipeline == INITIATION) {
nextObjBuilder = DefaultNextObjective
.builder()
.withType(NextObjective.Type.SIMPLE)
.fromApp(srManager.appId());
// The pw label is the bottom of stack. It has to
// be different -1.
if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
log.warn("Pw label not configured");
return null;
}
treatmentBuilder.pushMpls();
treatmentBuilder.setMpls(l2Tunnel.pwLabel());
treatmentBuilder.setMplsBos(true);
treatmentBuilder.copyTtlOut();
// If the inter-co label is present we have to set the label.
if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
treatmentBuilder.pushMpls();
treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
treatmentBuilder.setMplsBos(false);
treatmentBuilder.copyTtlOut();
}
// We retrieve the sr label from the config
// specific for pseudowire traffic
// using the egress leaf device id.
MplsLabel srLabel;
try {
srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
} catch (DeviceConfigNotFoundException e) {
log.warn("Sr label for pw traffic not configured");
return null;
}
treatmentBuilder.pushMpls();
treatmentBuilder.setMpls(srLabel);
treatmentBuilder.setMplsBos(false);
treatmentBuilder.copyTtlOut();
// We have to rewrite the src and dst mac address.
MacAddress ingressMac;
try {
ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
} catch (DeviceConfigNotFoundException e) {
log.warn("Was not able to find the ingress mac");
return null;
}
treatmentBuilder.setEthSrc(ingressMac);
MacAddress neighborMac;
try {
neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
} catch (DeviceConfigNotFoundException e) {
log.warn("Was not able to find the neighbor mac");
return null;
}
treatmentBuilder.setEthDst(neighborMac);
} else {
// We create the next objective which
// will be a simple l2 group.
nextObjBuilder = DefaultNextObjective
.builder()
.withType(NextObjective.Type.SIMPLE)
.fromApp(srManager.appId());
}
treatmentBuilder.setOutput(srcCp.port());
nextObjBuilder.addTreatment(treatmentBuilder.build());
return nextObjBuilder;
}
/**
* Reverses a link.
*
* @param link
* @return The reversed link
*/
private Link reverseLink(Link link) {
DefaultLink.Builder linkBuilder = DefaultLink.builder();
linkBuilder.src(link.dst());
linkBuilder.dst(link.src());
linkBuilder.type(link.type());
linkBuilder.providerId(link.providerId());
return linkBuilder.build();
}
/**
* Returns the path betwwen two connect points.
*
* @param srcCp
* @param dstCp
* @return The path
*/
private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
/* We retrieve a set of disjoint paths.
* We perform that in case of a link failure, what happens
* if the PathService gets the link notification AFTER us and
* has not updated the paths?
*/
Set<DisjointPath> paths = srManager
.pathService
.getDisjointPaths(srcCp.elementId(), dstCp.elementId());
// We randomly pick a path.
if (paths.isEmpty()) {
return null;
}
int size = paths.size();
int index = RandomUtils.nextInt(0, size);
return Iterables.get(paths, index).links();
}
/**
* Deletes a given policy using the parameter supplied.
*
* @param tunnelId the tunnel id
* @param ingress the ingress point
* @param ingressInner the ingress inner vlan id
* @param ingressOuter the ingress outer vlan id
* @param future to perform the async operation
* @param direction the direction: forward or reverse
*/
private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
String key = generateKey(tunnelId, direction);
if (!l2InitiationNextObjStore.containsKey(key)) {
log.warn("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
if (future != null) {
future.complete(null);
}
return;
}
NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
int nextId = nextObjective.id();
List<Objective> objectives = Lists.newArrayList();
// We create the forwarding objective.
ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
ObjectiveContext context = new ObjectiveContext() {
@Override
public void onSuccess(Objective objective) {
log.debug("Previous fwdObj for policy {} removed", tunnelId);
if (future != null) {
future.complete(null);
}
}
@Override
public void onError(Objective objective, ObjectiveError error) {
log.warn("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
if (future != null) {
future.complete(error);
}
}
};
objectives.add(fwdBuilder.remove(context));
// We create the filtering objective to define the
// permit traffic in the switch
FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
TrafficTreatment.Builder treatment = DefaultTrafficTreatment
.builder()
.setTunnelId(tunnelId)
.setVlanId(egressVlan);
filtBuilder.withMeta(treatment.build());
context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
(objective, error) ->
log.warn("Failed to revoke filterObj for policy {}",
tunnelId, error));
objectives.add(filtBuilder.remove(context));
for (Objective objective : objectives) {
if (objective instanceof ForwardingObjective) {
srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
} else {
srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
}
}
}
/**
* Deletes the pseudo wire initiation.
*
* @param l2TunnelId the tunnel id
* @param ingress the ingress connect point
* @param future to perform an async operation
* @param direction the direction: reverse of forward
*/
private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
CompletableFuture<ObjectiveError> future, Direction direction) {
String key = generateKey(l2TunnelId, direction);
if (!l2InitiationNextObjStore.containsKey(key)) {
log.info("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
if (future != null) {
future.complete(null);
}
return;
}
NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
// un-comment in case you want to delete groups used by the pw
// however, this will break the update of pseudowires cause the L2 interface group can
// not be deleted (it is referenced by other groups)
/*
ObjectiveContext context = new ObjectiveContext() {
@Override
public void onSuccess(Objective objective) {
log.debug("Previous {} next for {} removed", INITIATION, key);
if (future != null) {
future.complete(null);
}
}
@Override
public void onError(Objective objective, ObjectiveError error) {
log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
if (future != null) {
future.complete(error);
}
}
};
srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
*/
future.complete(null);
l2InitiationNextObjStore.remove(key);
}
/**
* Deletes the pseudo wire termination.
*
* @param l2Tunnel the tunnel
* @param egress the egress connect point
* @param future the async task
* @param direction the direction of the tunnel
*/
private void tearDownPseudoWireTerm(DefaultL2Tunnel l2Tunnel,
ConnectPoint egress,
CompletableFuture<ObjectiveError> future,
Direction direction) {
String key = generateKey(l2Tunnel.tunnelId(), direction);
if (!l2TerminationNextObjStore.containsKey(key)) {
log.info("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
if (future != null) {
future.complete(null);
}
return;
}
NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
l2Tunnel.tunnelId(),
egress.port(),
nextObjective.id());
ObjectiveContext context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for {} {} removed",
TERMINATION,
l2Tunnel.tunnelId()),
(objective, error) ->
log.warn("Failed to remove fwdObj for {} {}",
TERMINATION,
l2Tunnel.tunnelId(),
error));
srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
// un-comment in case you want to delete groups used by the pw
// however, this will break the update of pseudowires cause the L2 interface group can
// not be deleted (it is referenced by other groups)
/*
context = new ObjectiveContext() {
@Override
public void onSuccess(Objective objective) {
log.debug("Previous {} next for {} removed", TERMINATION, key);
if (future != null) {
future.complete(null);
}
}
@Override
public void onError(Objective objective, ObjectiveError error) {
log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
if (future != null) {
future.complete(error);
}
}
};
srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
*/
future.complete(null);
l2TerminationNextObjStore.remove(key);
}
/**
* Utilities to generate pw key.
*
* @param tunnelId the tunnel id
* @param direction the direction of the pw
* @return the key of the store
*/
private String generateKey(long tunnelId, Direction direction) {
return String.format("%s-%s", tunnelId, direction);
}
/**
* Pwaas pipelines.
*/
protected enum Pipeline {
/**
* The initiation pipeline.
*/
INITIATION, /**
* The termination pipeline.
*/
TERMINATION
}
/**
* Enum helper to carry results of various operations.
*/
public enum Result {
/**
* Happy ending scenario.
*/
SUCCESS(0, "No error occurred"),
/**
* We have problems with the supplied parameters.
*/
WRONG_PARAMETERS(1, "Wrong parameters"),
/**
* We have an internal error during the deployment
* or removal phase.
*/
INTERNAL_ERROR(3, "Internal error"),
/**
*
*/
REMOVAL_ERROR(5, "Can not remove pseudowire from network configuration"),
/**
*
*/
ADDITION_ERROR(6, "Can not add pseudowire in network configuration"),
/**
*
*/
CONFIG_NOT_FOUND(7, "Can not find configuration class for pseudowires");
private final int code;
private final String description;
private int nextId;
Result(int code, String description) {
this.code = code;
this.description = description;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return code + ": " + description;
}
}
/**
* Enum helper for handling the direction of the pw.
*/
public enum Direction {
/**
* The forward direction of the pseudo wire.
*/
FWD, /**
* The reverse direction of the pseudo wire.
*/
REV
}
}