Refactored pseudowire code.
This refactoring is needed in order to be able to further
refactor the sr application in api / app / web, where api contains
the interfaces, used by other modules, app contains the implementations
and web contains rest related code.
Change-Id: I147442e5d1248941a6c3c4df2715b01e72c40b3f
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 525f84a..d951b53 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -102,8 +102,11 @@
import org.onosproject.segmentrouting.grouphandler.DestinationSet;
import org.onosproject.segmentrouting.grouphandler.NextNeighbors;
import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
+import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelHandler;
import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
+import org.onosproject.segmentrouting.pwaas.L2Tunnel;
import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
+import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
import org.onosproject.segmentrouting.storekey.McastStoreKey;
import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
@@ -420,7 +423,7 @@
linkHandler = new LinkHandler(this);
routeHandler = new RouteHandler(this);
neighbourHandler = new SegmentRoutingNeighbourDispatcher(this);
- l2TunnelHandler = new L2TunnelHandler(this);
+ l2TunnelHandler = new DefaultL2TunnelHandler(this);
cfgService.addListener(cfgListener);
cfgService.registerConfigFactory(deviceConfigFactory);
@@ -447,18 +450,20 @@
return new KryoNamespace.Builder()
.register(KryoNamespaces.API)
.register(DestinationSetNextObjectiveStoreKey.class,
- VlanNextObjectiveStoreKey.class,
- DestinationSet.class,
- NextNeighbors.class,
- Tunnel.class,
- DefaultTunnel.class,
- Policy.class,
- TunnelPolicy.class,
- Policy.Type.class,
- PortNextObjectiveStoreKey.class,
- XConnectStoreKey.class,
- DefaultL2Tunnel.class,
- DefaultL2TunnelPolicy.class
+ VlanNextObjectiveStoreKey.class,
+ DestinationSet.class,
+ NextNeighbors.class,
+ Tunnel.class,
+ DefaultTunnel.class,
+ Policy.class,
+ TunnelPolicy.class,
+ Policy.Type.class,
+ PortNextObjectiveStoreKey.class,
+ XConnectStoreKey.class,
+ L2Tunnel.class,
+ L2TunnelPolicy.class,
+ DefaultL2Tunnel.class,
+ DefaultL2TunnelPolicy.class
);
}
@@ -552,20 +557,20 @@
}
@Override
- public List<DefaultL2Tunnel> getL2Tunnels() {
+ public List<L2Tunnel> getL2Tunnels() {
return l2TunnelHandler.getL2Tunnels();
}
@Override
- public List<DefaultL2TunnelPolicy> getL2Policies() {
+ public List<L2TunnelPolicy> getL2Policies() {
return l2TunnelHandler.getL2Policies();
}
@Override
public L2TunnelHandler.Result addPseudowire(String tunnelId, String pwLabel, String cP1,
- String cP1InnerVlan, String cP1OuterVlan, String cP2,
- String cP2InnerVlan, String cP2OuterVlan,
- String mode, String sdTag) {
+ String cP1InnerVlan, String cP1OuterVlan, String cP2,
+ String cP2InnerVlan, String cP2OuterVlan,
+ String mode, String sdTag) {
// Try to inject an empty Pwaas config if it is not found for the first time
PwaasConfig config = cfgService.getConfig(appId(), PwaasConfig.class);
if (config == null) {
@@ -1407,7 +1412,7 @@
break;
}
} else if (event.configClass().equals(PwaasConfig.class)) {
- checkState(l2TunnelHandler != null, "L2TunnelHandler is not initialized");
+ checkState(l2TunnelHandler != null, "DefaultL2TunnelHandler is not initialized");
switch (event.type()) {
case CONFIG_ADDED:
l2TunnelHandler.processPwaasConfigAdded(event);
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
index 00e7a6a..6f115e4 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
@@ -22,9 +22,9 @@
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
import org.onosproject.segmentrouting.grouphandler.NextNeighbors;
-import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
-import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
+import org.onosproject.segmentrouting.pwaas.L2Tunnel;
import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
+import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
import com.google.common.collect.ImmutableMap;
@@ -91,18 +91,18 @@
*
* @return list of l2 tunnels
*/
- List<DefaultL2Tunnel> getL2Tunnels();
+ List<L2Tunnel> getL2Tunnels();
/**
* Returns all l2 policie of pseudowires.
*
* @return list of l2 policies.
*/
- List<DefaultL2TunnelPolicy> getL2Policies();
+ List<L2TunnelPolicy> getL2Policies();
/**
* Removes pw. Essentially updates configuration for PwaasConfig
- * and sends event for removal. The rest are handled by L2TunnelHandler
+ * and sends event for removal. The rest are handled by DefaultL2TunnelHandler
*
* @param pwId The pseudowire id
* @return SUCCESS if operation successful or a descriptive error otherwise.
@@ -125,9 +125,9 @@
* @return SUCCESS if operation is successful or a descriptive error otherwise.
*/
L2TunnelHandler.Result addPseudowire(String tunnelId, String pwLabel, String cP1,
- String cP1InnerVlan, String cP1OuterVlan, String cP2,
- String cP2InnerVlan, String cP2OuterVlan,
- String mode, String sdTag);
+ String cP1InnerVlan, String cP1OuterVlan, String cP2,
+ String cP2InnerVlan, String cP2OuterVlan,
+ String mode, String sdTag);
/**
* Creates a policy.
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PseudowireIdCompleter.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PseudowireIdCompleter.java
index 9022bec..b44ca76 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PseudowireIdCompleter.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PseudowireIdCompleter.java
@@ -19,7 +19,7 @@
import org.apache.karaf.shell.console.completer.StringsCompleter;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
+import org.onosproject.segmentrouting.pwaas.L2Tunnel;
import java.util.Iterator;
import java.util.List;
@@ -39,7 +39,7 @@
AbstractShellCommand.get(SegmentRoutingService.class);
- List<DefaultL2Tunnel> tunnels = srService.getL2Tunnels();
+ List<L2Tunnel> tunnels = srService.getL2Tunnels();
// combine polices and tunnels to pseudowires
Iterator<String> pseudowires = tunnels.stream()
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java
index 70ae32f..c48f633 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java
@@ -19,9 +19,10 @@
import org.onlab.packet.VlanId;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
-import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
+import org.onosproject.segmentrouting.pwaas.L2Tunnel;
+import org.onosproject.segmentrouting.pwaas.L2TunnelDescription;
+import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
import java.util.List;
import java.util.stream.Collectors;
@@ -46,14 +47,14 @@
SegmentRoutingService srService =
AbstractShellCommand.get(SegmentRoutingService.class);
- List<DefaultL2Tunnel> tunnels = srService.getL2Tunnels();
- List<DefaultL2TunnelPolicy> policies = srService.getL2Policies();
+ List<L2Tunnel> tunnels = srService.getL2Tunnels();
+ List<L2TunnelPolicy> policies = srService.getL2Policies();
// combine polices and tunnels to pseudowires
- List<DefaultL2TunnelDescription> pseudowires = tunnels.stream()
+ List<L2TunnelDescription> pseudowires = tunnels.stream()
.map(l2Tunnel -> {
- DefaultL2TunnelPolicy policy = null;
- for (DefaultL2TunnelPolicy l2Policy : policies) {
+ L2TunnelPolicy policy = null;
+ for (L2TunnelPolicy l2Policy : policies) {
if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
policy = l2Policy;
break;
@@ -67,7 +68,7 @@
pseudowires.forEach(pw -> printPseudowire(pw));
}
- private void printPseudowire(DefaultL2TunnelDescription pseudowire) {
+ private void printPseudowire(L2TunnelDescription pseudowire) {
VlanId vlan = pseudowire.l2Tunnel().transportVlan().equals(VlanId.vlanId((short) 4094)) ?
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java
index feb0049..31cad7e 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java
@@ -30,6 +30,9 @@
import org.onosproject.net.config.Config;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.intf.InterfaceService;
+import org.onosproject.segmentrouting.pwaas.L2Tunnel;
+import org.onosproject.segmentrouting.pwaas.L2TunnelDescription;
+import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
@@ -116,7 +119,7 @@
@Override
public boolean isValid() {
- Set<DefaultL2TunnelDescription> pseudowires;
+ Set<L2TunnelDescription> pseudowires;
try {
pseudowires = getPwIds().stream()
.map(this::getPwDescription)
@@ -138,7 +141,7 @@
* @param l2Tunnel the tunnel to verify
* @return the result of the verification
*/
- private void verifyTunnel(DefaultL2Tunnel l2Tunnel) {
+ private void verifyTunnel(L2Tunnel l2Tunnel) {
// Service delimiting tag not supported yet.
if (!l2Tunnel.sdTag().equals(VlanId.NONE)) {
@@ -274,8 +277,8 @@
* @param vlanSet Vlan set used with this configuration
* @param tunnelSet Tunnel set used with this configuration
*/
- private void verifyGlobalValidity(DefaultL2Tunnel tunnel,
- DefaultL2TunnelPolicy policy,
+ private void verifyGlobalValidity(L2Tunnel tunnel,
+ L2TunnelPolicy policy,
Set<MplsLabel> labelSet,
Map<ConnectPoint, Set<VlanId>> vlanSet,
Set<Long> tunnelSet) {
@@ -412,13 +415,13 @@
* @param l2TunnelDescription the pseudo wire description
* @return the result of the check
*/
- private void verifyPseudoWire(DefaultL2TunnelDescription l2TunnelDescription,
+ private void verifyPseudoWire(L2TunnelDescription l2TunnelDescription,
Set<MplsLabel> labelSet,
Map<ConnectPoint, Set<VlanId>> vlanset,
Set<Long> tunnelSet) {
- DefaultL2Tunnel l2Tunnel = l2TunnelDescription.l2Tunnel();
- DefaultL2TunnelPolicy l2TunnelPolicy = l2TunnelDescription.l2TunnelPolicy();
+ L2Tunnel l2Tunnel = l2TunnelDescription.l2Tunnel();
+ L2TunnelPolicy l2TunnelPolicy = l2TunnelDescription.l2TunnelPolicy();
verifyTunnel(l2Tunnel);
@@ -447,7 +450,7 @@
* @param pseudowires Set of pseudowries to validate
* @return returns true if everything goes well.
*/
- public boolean configurationValidity(Set<DefaultL2TunnelDescription> pseudowires) {
+ public boolean configurationValidity(Set<L2TunnelDescription> pseudowires) {
// structures to keep pw information
// in order to see if instantiating them will create
@@ -459,7 +462,7 @@
// check that pseudowires can be instantiated in the network
// we try to guarantee that all the pws will work before
// instantiating any of them
- for (DefaultL2TunnelDescription pw : pseudowires) {
+ for (L2TunnelDescription pw : pseudowires) {
verifyPseudoWire(pw, labelsUsed, vlanIds, tunIds);
}
@@ -543,7 +546,7 @@
* @return set of l2 tunnel descriptions
* @throws IllegalArgumentException if wrong format
*/
- public DefaultL2TunnelDescription getPwDescription(Long tunnelId) {
+ public L2TunnelDescription getPwDescription(Long tunnelId) {
JsonNode pwDescription = object.get(tunnelId.toString());
if (!hasFields((ObjectNode) pwDescription,
SRC_CP, SRC_INNER_TAG, SRC_OUTER_TAG,
@@ -580,14 +583,14 @@
tempString = pwDescription.get(PW_LABEL).asText();
MplsLabel pwLabel = parsePWLabel(tempString);
- DefaultL2Tunnel l2Tunnel = new DefaultL2Tunnel(
+ L2Tunnel l2Tunnel = new DefaultL2Tunnel(
l2Mode,
sdTag,
tunnelId,
pwLabel
);
- DefaultL2TunnelPolicy l2TunnelPolicy = new DefaultL2TunnelPolicy(
+ L2TunnelPolicy l2TunnelPolicy = new DefaultL2TunnelPolicy(
tunnelId,
srcCp,
srcInnerTag,
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2Tunnel.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2Tunnel.java
index 64923bb..417a795 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2Tunnel.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2Tunnel.java
@@ -32,7 +32,7 @@
/**
* Implementation of the default l2 tunnel.
*/
-public class DefaultL2Tunnel {
+public class DefaultL2Tunnel implements L2Tunnel {
/**
* Mode of the pseudo wire.
@@ -132,6 +132,7 @@
*
* @return the pseudo wire mode
*/
+ @Override
public L2Mode pwMode() {
return pwMode;
}
@@ -142,6 +143,7 @@
*
* @return the service delimitation vlan id
*/
+ @Override
public VlanId sdTag() {
return sdTag;
}
@@ -151,6 +153,7 @@
*
* @return the pseudo wire tunnel id
*/
+ @Override
public long tunnelId() {
return tunnelId;
}
@@ -160,6 +163,7 @@
*
* @return the mpls pw label
*/
+ @Override
public MplsLabel pwLabel() {
return pwLabel;
}
@@ -169,6 +173,7 @@
*
* @param path The path to set
*/
+ @Override
public void setPath(List<Link> path) {
pathUsed = new ArrayList<>(path);
}
@@ -178,6 +183,7 @@
*
* @param vlan the vlan to use.
*/
+ @Override
public void setTransportVlan(VlanId vlan) {
transportVlan = vlan;
}
@@ -187,10 +193,12 @@
*
* @return pathUsed
*/
+ @Override
public List<Link> pathUsed() {
return pathUsed;
}
+ @Override
public VlanId transportVlan() {
return transportVlan;
}
@@ -201,6 +209,7 @@
*
* @return the mpls inter-co label
*/
+ @Override
public MplsLabel interCoLabel() {
return interCoLabel;
}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelDescription.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelDescription.java
index b5206a3..7a47972 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelDescription.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelDescription.java
@@ -26,17 +26,17 @@
* Helper class to carry the l2 tunnel
* and its policy.
*/
-public class DefaultL2TunnelDescription {
+public class DefaultL2TunnelDescription implements L2TunnelDescription {
/**
* The l2 tunnel.
*/
- private DefaultL2Tunnel l2Tunnel;
+ private L2Tunnel l2Tunnel;
/**
* The l2 tunnel policy.
*/
- private DefaultL2TunnelPolicy l2TunnelPolicy;
+ private L2TunnelPolicy l2TunnelPolicy;
/**
* Creates a l2 tunnel description using the given info.
@@ -44,8 +44,8 @@
* @param l2Tunnel the l2 tunnel
* @param l2TunnelPolicy the l2 tunnel description
*/
- public DefaultL2TunnelDescription(DefaultL2Tunnel l2Tunnel,
- DefaultL2TunnelPolicy l2TunnelPolicy) {
+ public DefaultL2TunnelDescription(L2Tunnel l2Tunnel,
+ L2TunnelPolicy l2TunnelPolicy) {
checkNotNull(l2Tunnel);
checkNotNull(l2TunnelPolicy);
@@ -61,39 +61,23 @@
this.l2TunnelPolicy = null;
}
- /**
- * Returns the l2 tunnel.
- *
- * @return the l2 tunnel
- */
- public DefaultL2Tunnel l2Tunnel() {
+ @Override
+ public L2Tunnel l2Tunnel() {
return l2Tunnel;
}
- /**
- * Returns the l2 tunnel policy.
- *
- * @return the l2 tunnel policy.
- */
- public DefaultL2TunnelPolicy l2TunnelPolicy() {
+ @Override
+ public L2TunnelPolicy l2TunnelPolicy() {
return l2TunnelPolicy;
}
- /**
- * Sets the l2 tunnel.
- *
- * @param tunnel the l2 tunnel to set.
- */
- public void setL2Tunnel(DefaultL2Tunnel tunnel) {
+ @Override
+ public void setL2Tunnel(L2Tunnel tunnel) {
l2Tunnel = tunnel;
}
- /**
- * Sets the l2 policy.
- *
- * @param policy the policy to set.
- */
- public void setL2TunnelPolicy(DefaultL2TunnelPolicy policy) {
+ @Override
+ public void setL2TunnelPolicy(L2TunnelPolicy policy) {
l2TunnelPolicy = policy;
}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java
new file mode 100644
index 0000000..df02417
--- /dev/null
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java
@@ -0,0 +1,1725 @@
+/*
+ * 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.Link;
+import org.onosproject.net.Path;
+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.DistributedSet;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.Versioned;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+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 DefaultL2TunnelHandler implements L2TunnelHandler {
+
+ private static final Logger log = LoggerFactory.getLogger(DefaultL2TunnelHandler.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, L2TunnelPolicy> l2PolicyStore;
+
+ /**
+ * To store tunnels.
+ */
+ private final ConsistentMap<String, L2Tunnel> l2TunnelStore;
+
+ private final KryoNamespace.Builder l2TunnelKryo;
+
+ /**
+ * Contains transport vlans used for spine-leaf pseudowires.
+ */
+ private final DistributedSet<VlanId> vlanStore;
+
+ /**
+ * Used for determining transport vlans for leaf-spine.
+ */
+ private short transportVlanUpper = 4093, transportVlanLower = 3500;
+
+ private static final VlanId UNTAGGED_TRANSPORT_VLAN = VlanId.vlanId((short) 4094);
+
+ /**
+ * Create a l2 tunnel handler for the deploy and
+ * for the tear down of pseudo wires.
+ *
+ * @param segmentRoutingManager the segment routing manager
+ */
+ public DefaultL2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
+ srManager = segmentRoutingManager;
+ l2TunnelKryo = new KryoNamespace.Builder()
+ .register(KryoNamespaces.API)
+ .register(L2Tunnel.class,
+ L2TunnelPolicy.class,
+ 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, L2TunnelPolicy>consistentMapBuilder()
+ .withName("onos-l2-policy-store")
+ .withSerializer(Serializer.using(l2TunnelKryo.build()))
+ .build();
+
+ l2TunnelStore = srManager.storageService
+ .<String, L2Tunnel>consistentMapBuilder()
+ .withName("onos-l2-tunnel-store")
+ .withSerializer(Serializer.using(l2TunnelKryo.build()))
+ .build();
+
+ vlanStore = srManager.storageService.<VlanId>setBuilder()
+ .withName("onos-transport-vlan-store")
+ .withSerializer(Serializer.using(
+ new KryoNamespace.Builder()
+ .register(KryoNamespaces.API)
+ .build()))
+ .build()
+ .asDistributedSet();
+ }
+
+ /**
+ * Deploys any pre-existing pseudowires in the configuration.
+ * Used by manager only in initialization.
+ */
+ @Override
+ public void init() {
+
+ PwaasConfig config = srManager.cfgService.getConfig(srManager.appId(), PwaasConfig.class);
+ if (config == null) {
+ return;
+ }
+
+ log.info("Deploying existing pseudowires");
+
+ // gather pseudowires
+ Set<L2TunnelDescription> pwToAdd = config
+ .getPwIds()
+ .stream()
+ .map(config::getPwDescription)
+ .collect(Collectors.toSet());
+
+ // deploy pseudowires
+ deploy(pwToAdd);
+ }
+
+ /**
+ * Returns all L2 Policies.
+ *
+ * @return List of policies
+ */
+ @Override
+ public List<L2TunnelPolicy> getL2Policies() {
+
+ return new ArrayList<>(l2PolicyStore
+ .values()
+ .stream()
+ .map(Versioned::value)
+ .collect(Collectors.toList()));
+
+ }
+
+ /**
+ * Returns all L2 Tunnels.
+ *
+ * @return List of tunnels.
+ */
+ @Override
+ public List<L2Tunnel> getL2Tunnels() {
+
+ return new ArrayList<>(l2TunnelStore
+ .values()
+ .stream()
+ .map(Versioned::value)
+ .collect(Collectors.toList()));
+
+ }
+
+ @Override
+ public void processLinkDown(Link link) {
+
+ List<L2Tunnel> tunnels = getL2Tunnels();
+ List<L2TunnelPolicy> policies = getL2Policies();
+
+ // determine affected pseudowires and update them at once
+ Set<L2TunnelDescription> pwToUpdate = tunnels
+ .stream()
+ .filter(tun -> tun.pathUsed().contains(link))
+ .map(l2Tunnel -> {
+ L2TunnelPolicy policy = null;
+ for (L2TunnelPolicy 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));
+ }
+
+ @Override
+ public void processPwaasConfigAdded(NetworkConfigEvent event) {
+ checkArgument(event.config().isPresent(),
+ "Config is not presented in PwaasConfigAdded event {}", event);
+
+ log.info("Network event : Pseudowire configuration added!");
+ PwaasConfig config = (PwaasConfig) event.config().get();
+
+ // gather pseudowires
+ Set<L2TunnelDescription> 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.
+ */
+ private 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");
+ }
+ }
+
+ /**
+ * Determines vlan used for transporting the pw traffic.
+ *
+ * Leaf-Leaf traffic is transferred untagged, thus we choose the UNTAGGED_TRANSPORT_VLAN
+ * and also make sure to add the popVlan instruction.
+ * For spine-leaf pws we choose the highest vlan value available from a certain range.
+ *
+ * @param spinePw if the pw is leaf-spine.
+ * @return The vlan id chossen to transport this pseudowire. If vlan is UNTAGGED_TRANSPORT_VLAN
+ * then the pw is transported untagged.
+ */
+ private VlanId determineTransportVlan(boolean spinePw) {
+
+ if (!spinePw) {
+
+ log.info("Untagged transport with internal vlan {} for pseudowire!", UNTAGGED_TRANSPORT_VLAN);
+ return UNTAGGED_TRANSPORT_VLAN;
+ } else {
+ for (short i = transportVlanUpper; i > transportVlanLower; i--) {
+
+ VlanId vlanToUse = VlanId.vlanId((short) i);
+ if (!vlanStore.contains(vlanToUse)) {
+
+ vlanStore.add(vlanToUse);
+ log.info("Transport vlan {} for pseudowire!", vlanToUse);
+ return vlanToUse;
+ }
+ }
+
+ log.info("No available transport vlan found, pseudowire traffic will be carried untagged " +
+ "with internal vlan {}!", UNTAGGED_TRANSPORT_VLAN);
+ return UNTAGGED_TRANSPORT_VLAN;
+ }
+ }
+
+ /**
+ * Adds a single pseudowire from leaf to a leaf.
+ * This method can be called from cli commands
+ * without configuration updates, thus it does not check for mastership
+ * of the ingress pseudowire device.
+ *
+ * @param pw The pseudowire
+ * @param spinePw True if pseudowire is from leaf to spine
+ * @return result of pseudowire deployment
+ */
+ private Result deployPseudowire(L2TunnelDescription pw, boolean spinePw) {
+
+ 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;
+ }
+
+ Link fwdNextHop;
+ Link revNextHop;
+ if (!spinePw) {
+ if (path.size() != 2) {
+ log.info("Deploying process : Path between two leafs should have size of 2, for pseudowire {}",
+ l2TunnelId);
+ return INTERNAL_ERROR;
+ }
+
+ fwdNextHop = path.get(0);
+ revNextHop = reverseLink(path.get(1));
+ } else {
+ if (path.size() != 1) {
+ log.info("Deploying process : Path between leaf spine should equal to 1, for pseudowire {}",
+ l2TunnelId);
+ return INTERNAL_ERROR;
+ }
+
+ fwdNextHop = path.get(0);
+ revNextHop = reverseLink(path.get(0));
+ }
+
+ pw.l2Tunnel().setPath(path);
+ pw.l2Tunnel().setTransportVlan(determineTransportVlan(spinePw));
+
+ // next hops for next objectives
+
+ 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,
+ spinePw);
+ 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(),
+ VlanId.NONE,
+ FWD,
+ spinePw);
+
+ 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,
+ spinePw);
+ 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(),
+ VlanId.NONE,
+ REV,
+ spinePw);
+
+ 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<L2TunnelDescription> pwToAdd) {
+
+ Result result;
+
+ for (L2TunnelDescription currentL2Tunnel : pwToAdd) {
+ ConnectPoint cp1 = currentL2Tunnel.l2TunnelPolicy().cP1();
+ ConnectPoint cp2 = currentL2Tunnel.l2TunnelPolicy().cP2();
+ long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
+
+ // only the master of CP1 will program this pseudowire
+ if (!srManager.isMasterOf(cp1)) {
+ log.debug("Not the master of {}. Ignore pseudo wire deployment id={}", cp1, tunnelId);
+ continue;
+ }
+
+ try {
+ // differentiate between leaf-leaf pseudowires and leaf-spine
+ // and pass the appropriate flag in them.
+ if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
+ !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
+ log.warn("Can not deploy pseudowire from spine to spine!");
+ result = Result.INTERNAL_ERROR;
+ } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
+ srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
+ log.info("Deploying a leaf-leaf pseudowire {}", tunnelId);
+ result = deployPseudowire(currentL2Tunnel, false);
+ } else {
+ log.info("Deploying a leaf-spine pseudowire {}", tunnelId);
+ result = deployPseudowire(currentL2Tunnel, true);
+ }
+ } catch (DeviceConfigNotFoundException e) {
+ log.error("Exception caught when deploying pseudowire", e.toString());
+ result = Result.INTERNAL_ERROR;
+ }
+
+ switch (result) {
+ case INTERNAL_ERROR:
+ log.warn("Could not deploy pseudowire {}, internal error!", tunnelId);
+ break;
+ case WRONG_PARAMETERS:
+ log.warn("Could not deploy pseudowire {}, wrong parameters!", tunnelId);
+ break;
+ case ADDITION_ERROR:
+ log.warn("Could not deploy pseudowire {}, error in populating rules!", tunnelId);
+ break;
+ default:
+ log.info("Pseudowire with {} succesfully deployed!", tunnelId);
+ break;
+ }
+ }
+ }
+
+
+ @Override
+ public void processPwaasConfigUpdated(NetworkConfigEvent event) {
+ checkArgument(event.config().isPresent(),
+ "Config is not presented in PwaasConfigUpdated event {}", event);
+ checkArgument(event.prevConfig().isPresent(),
+ "PrevConfig is not presented in PwaasConfigUpdated event {}", 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<L2TunnelDescription> 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<L2TunnelDescription> 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
+ */
+ private void updatePw(L2TunnelDescription oldPw,
+ L2TunnelDescription newPw) {
+ ConnectPoint oldCp1 = oldPw.l2TunnelPolicy().cP1();
+ long tunnelId = oldPw.l2Tunnel().tunnelId();
+
+ // only the master of CP1 will update this pseudowire
+ if (!srManager.isMasterOf(oldPw.l2TunnelPolicy().cP1())) {
+ log.debug("Not the master of {}. Ignore pseudo wire update id={}", oldCp1, tunnelId);
+ return;
+ }
+ // only determine if the new pseudowire is leaf-spine, because
+ // removal process is the same for both leaf-leaf and leaf-spine pws
+ boolean newPwSpine;
+ try {
+ newPwSpine = !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP1().deviceId()) ||
+ !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP2().deviceId());
+ } catch (DeviceConfigNotFoundException e) {
+ // if exception is caught treat the new pw as leaf-leaf
+ newPwSpine = false;
+ }
+
+ // copy the variable here because we need to use it in lambda thus it needs to be final
+ boolean finalNewPwSpine = newPwSpine;
+
+ 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 delete all information from our stores, we can not do it asynchronously
+ l2PolicyStore.remove(Long.toString(tunnelId));
+
+ // grab the old l2 tunnel from the store, since it carries information which is not exposed
+ // to the user configuration and set it to oldPw.
+ oldPw.setL2Tunnel(l2TunnelStore.get(Long.toString(tunnelId)).value());
+ VlanId transportVlan = l2TunnelStore.get(Long.toString(tunnelId)).value().transportVlan();
+ l2TunnelStore.remove(Long.toString(tunnelId));
+
+ // remove the reserved transport vlan, if one is used
+ if (!transportVlan.equals(UNTAGGED_TRANSPORT_VLAN)) {
+ vlanStore.remove(transportVlan);
+ }
+
+ // First we remove both policy.
+ log.debug("Start deleting fwd policy for {}", 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.error("Update process : " +
+ "No path between the connection points for pseudowire {}", newPw.l2Tunnel().tunnelId());
+ return;
+ }
+
+ Link fwdNextHop, revNextHop;
+ if (!finalNewPwSpine) {
+ if (path.size() != 2) {
+ log.error("Update process : Error, path between two leafs should have size of 2, for pseudowire {}",
+ newPw.l2Tunnel().tunnelId());
+ return;
+ }
+ fwdNextHop = path.get(0);
+ revNextHop = reverseLink(path.get(1));
+ } else {
+ if (path.size() != 1) {
+ log.error("Update process : Error, path between leaf spine should equal to 1, for pseudowire {}",
+ newPw.l2Tunnel().tunnelId());
+ return;
+ }
+ fwdNextHop = path.get(0);
+ revNextHop = reverseLink(path.get(0));
+ }
+
+ // set new path and transport vlan.
+ newPw.l2Tunnel().setPath(path);
+ newPw.l2Tunnel().setTransportVlan(determineTransportVlan(newPwSpine));
+
+ // 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, finalNewPwSpine);
+ 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(),
+ VlanId.NONE, FWD, finalNewPwSpine);
+
+ }
+ });
+ 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, finalNewPwSpine);
+ 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(),
+ VlanId.NONE,
+ REV, finalNewPwSpine);
+ }
+ });
+ }
+
+ @Override
+ public void processPwaasConfigRemoved(NetworkConfigEvent event) {
+ checkArgument(event.prevConfig().isPresent(),
+ "PrevConfig is not presented in PwaasConfigRemoved event {}", event);
+
+ log.info("Network event : Pseudowire configuration removed!");
+ PwaasConfig config = (PwaasConfig) event.prevConfig().get();
+
+ Set<L2TunnelDescription> 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
+ */
+ private 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<L2Tunnel> l2TunnelVersioned = l2TunnelStore.get(Long.toString(l2TunnelId));
+ Versioned<L2TunnelPolicy> 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;
+ }
+
+ L2TunnelDescription 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));
+
+ // remove the reserved transport vlan
+ if (!pwToRemove.l2Tunnel().transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
+ vlanStore.remove(pwToRemove.l2Tunnel().transportVlan());
+ }
+
+ 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;
+ }
+
+ @Override
+ public void tearDown(Set<L2TunnelDescription> pwToRemove) {
+
+ Result result;
+
+ // We remove all the pw in the configuration file.
+ for (L2TunnelDescription currentL2Tunnel : pwToRemove) {
+ ConnectPoint cp1 = currentL2Tunnel.l2TunnelPolicy().cP1();
+ ConnectPoint cp2 = currentL2Tunnel.l2TunnelPolicy().cP2();
+ long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
+
+ // only the master of CP1 will program this pseudowire
+ if (!srManager.isMasterOf(cp1)) {
+ log.debug("Not the master of {}. Ignore pseudo wire removal id={}", cp1, tunnelId);
+ continue;
+ }
+
+ // no need to differentiate here between leaf-leaf and leaf-spine, because
+ // the only change is in the groups, which we do not remove either way
+ log.info("Removing pseudowire {}", tunnelId);
+
+ result = tearDownPseudowire(tunnelId);
+ switch (result) {
+ case WRONG_PARAMETERS:
+ log.warn("Error in supplied parameters for the pseudowire removal with tunnel id {}!",
+ tunnelId);
+ break;
+ case REMOVAL_ERROR:
+ log.warn("Error in pseudowire removal with tunnel id {}!", tunnelId);
+ break;
+ default:
+ log.warn("Pseudowire with tunnel id {} was removed successfully", 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
+ * @param spinePw if the pseudowire involves a spine switch
+ * @return the result of the operation
+ */
+ private Result deployPseudoWireInit(L2Tunnel l2Tunnel, ConnectPoint ingress,
+ ConnectPoint egress, Direction direction, Link nextHop, boolean spinePw) {
+
+ 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(),
+ spinePw);
+
+ 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
+ * @param spinePw if the pseudowire involves a spine switch
+ * @return the result of the operation
+ */
+ private Result deployPseudoWireTerm(L2Tunnel l2Tunnel, ConnectPoint egress,
+ VlanId egressVlan, Direction direction, boolean spinePw) {
+
+ // We create the group relative to the termination.
+ NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
+ l2Tunnel, egress.deviceId(),
+ spinePw);
+ 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());
+
+ if (spinePw) {
+
+ // determine the input port at the
+ PortNumber inPort;
+
+ if (egress.deviceId().
+ equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
+ inPort = l2Tunnel.pathUsed().get(0).dst().port();
+ } else {
+ inPort = l2Tunnel.pathUsed().get(0).src().port();
+ }
+
+ MacAddress dstMac;
+ try {
+ dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
+ } catch (Exception e) {
+ log.info("Device not found in configuration, no programming of MAC address");
+ dstMac = null;
+ }
+
+ log.info("Populating filtering objective for pseudowire transport" +
+ " with vlan = {}, port = {}, mac = {}",
+ l2Tunnel.transportVlan(),
+ inPort,
+ dstMac);
+ FilteringObjective.Builder filteringObjectiveBuilder =
+ createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
+ context = new DefaultObjectiveContext(( objective ) ->
+ log.debug("Special filtObj for " + "for {} populated",
+ l2Tunnel.tunnelId()),
+ ( objective, error ) ->
+ log.warn("Failed to populate " +
+ "special filtObj " +
+ "rule for {}: {}",
+ l2Tunnel.tunnelId(), error));
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+ filteringObjectiveBuilder.withMeta(treatment.build());
+ srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.add(context));
+ log.debug("Creating new special FiltObj for termination point with tunnel {} for port {}",
+ l2Tunnel.tunnelId(),
+ inPort);
+ }
+
+ return SUCCESS;
+ }
+
+
+ /**
+ * Creates the filtering objective according to a given port and vlanid.
+ *
+ * @param inPort the in port
+ * @param vlanId the inner vlan tag
+ * @return the filtering objective
+ */
+ private FilteringObjective.Builder createNormalPipelineFiltObjective(PortNumber inPort,
+ VlanId vlanId,
+ MacAddress dstMac) {
+
+ log.info("Creating filtering objective for pseudowire transport with vlan={}, port={}, mac={}",
+ vlanId,
+ inPort,
+ dstMac);
+ FilteringObjective.Builder fwdBuilder = DefaultFilteringObjective
+ .builder()
+ .withKey(Criteria.matchInPort(inPort))
+ .addCondition(Criteria.matchVlanId(vlanId))
+ .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
+ .permit()
+ .fromApp(srManager.appId());
+
+ if (dstMac != null) {
+ fwdBuilder.addCondition(Criteria.matchEthDst(dstMac));
+ }
+
+ return fwdBuilder;
+ }
+
+ /**
+ * 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
+ * @param spinePw if the pw involves a spine switch
+ * @return the next objective to support the pipeline
+ */
+ private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
+ ConnectPoint dstCp, L2Tunnel l2Tunnel,
+ DeviceId egressId, boolean spinePw) {
+ 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();
+ }
+
+ // if pw is leaf-to-leaf we need to
+ // add the routing label also
+ if (!spinePw) {
+ // 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);
+
+ // if not a leaf-spine pw we need to POP the vlan at the output
+ // since we carry this traffic untagged.
+ if (!spinePw) {
+ treatmentBuilder.popVlan();
+ }
+ } else {
+ // We create the next objective which
+ // will be a simple l2 group.
+ nextObjBuilder = DefaultNextObjective
+ .builder()
+ .withType(NextObjective.Type.SIMPLE)
+ .fromApp(srManager.appId());
+ }
+
+ // set the appropriate transport vlan
+ treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
+ treatmentBuilder.setOutput(srcCp.port());
+ nextObjBuilder.addTreatment(treatmentBuilder.build());
+ return nextObjBuilder;
+ }
+
+ /**
+ * Reverses a link.
+ *
+ * @param link link to be reversed
+ * @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 source connect point
+ * @param dstCp destination connect point
+ * @return the path
+ */
+ private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
+ /* TODO We retrieve a set of paths in case of a link failure, what happens
+ * if the TopologyService gets the link notification AFTER us and has not updated the paths?
+ *
+ * TODO This has the potential to act on old topology.
+ * Maybe we should make SRManager be a listener on topology events instead raw link events.
+ */
+ Set<Path> paths = srManager.topologyService.getPaths(
+ srManager.topologyService.currentTopology(),
+ srcCp.deviceId(), dstCp.deviceId());
+
+ log.debug("Paths obtained from topology service {}", paths);
+
+ // We randomly pick a path.
+ if (paths.isEmpty()) {
+ return null;
+ }
+ int size = paths.size();
+ int index = RandomUtils.nextInt(0, size);
+
+ List<Link> result = Iterables.get(paths, index).links();
+ log.debug("Randomly picked a path {}", result);
+
+ return result;
+ }
+
+ /**
+ * 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(L2Tunnel 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 {} {}, " +
+ "direction {} removed",
+ TERMINATION,
+ l2Tunnel.tunnelId(),
+ direction),
+ (objective, error) ->
+ log.warn("Failed to remove fwdObj " +
+ "for {} {}" +
+ ", direction {}",
+ TERMINATION,
+ l2Tunnel.tunnelId(),
+ error,
+ direction));
+ 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));
+ */
+
+ // delete the extra filtering objective for terminating
+ // spine-spine pws
+ if (!l2Tunnel.transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
+
+ // determine the input port at the
+ PortNumber inPort;
+
+ if (egress.deviceId().
+ equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
+ inPort = l2Tunnel.pathUsed().get(0).dst().port();
+ } else {
+ inPort = l2Tunnel.pathUsed().get(0).src().port();
+ }
+
+ MacAddress dstMac;
+ try {
+ dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
+ } catch (Exception e) {
+ log.info("Device not found in configuration, no programming of MAC address");
+ dstMac = null;
+ }
+
+ log.info("Removing filtering objective for pseudowire transport" +
+ " with vlan = {}, port = {}, mac = {}",
+ l2Tunnel.transportVlan(),
+ inPort,
+ dstMac);
+ FilteringObjective.Builder filteringObjectiveBuilder =
+ createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
+ context = new DefaultObjectiveContext(( objective ) ->
+ log.debug("Special filtObj for " + "for {} removed",
+ l2Tunnel.tunnelId()), ( objective, error ) ->
+ log.warn("Failed to populate " + "special filtObj " +
+ "rule for {}: {}", l2Tunnel.tunnelId(), error));
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+ filteringObjectiveBuilder.withMeta(treatment.build());
+ srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.remove(context));
+ log.debug("Removing special FiltObj for termination point with tunnel {} for port {}",
+ l2Tunnel.tunnelId(),
+ inPort);
+ }
+
+ l2TerminationNextObjStore.remove(key);
+ future.complete(null);
+ }
+
+ /**
+ * 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);
+ }
+
+}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelPolicy.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelPolicy.java
index ac52f3e..4738744 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelPolicy.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelPolicy.java
@@ -27,7 +27,7 @@
/**
* Implementation of the default l2 tunnel policy.
*/
-public class DefaultL2TunnelPolicy {
+public class DefaultL2TunnelPolicy implements L2TunnelPolicy {
/**
* Id of the tunnel associated to this policy.
@@ -97,65 +97,37 @@
this.cP2OuterTag = policy.cP2OuterTag;
}
- /**
- * Returns the first connect point of the policy.
- *
- * @return first connect point
- */
+ @Override
public ConnectPoint cP1() {
return cP1;
}
- /**
- * Returns the second connect point of the policy.
- *
- * @return second connect point
- */
+ @Override
public ConnectPoint cP2() {
return cP2;
}
- /**
- * Returns the cP1 inner vlan tag of the policy.
- *
- * @return cP1 inner vlan tag
- */
+ @Override
public VlanId cP1InnerTag() {
return cP1InnerTag;
}
- /**
- * Returns the cP1 outer vlan tag of the policy.
- *
- * @return cP1 outer vlan tag
- */
+ @Override
public VlanId cP1OuterTag() {
return cP1OuterTag;
}
- /**
- * Returns the cP2 inner vlan tag of the policy.
- *
- * @return cP2 inner vlan tag
- */
+ @Override
public VlanId cP2InnerTag() {
return cP2InnerTag;
}
- /**
- * Returns the cP2 outer vlan tag of the policy.
- *
- * @return cP2 outer vlan tag
- */
+ @Override
public VlanId cP2OuterTag() {
return cP2OuterTag;
}
- /**
- * Returns the tunnel ID of the policy.
- *
- * @return Tunnel ID
- */
+ @Override
public long tunnelId() {
return this.tunnelId;
}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2Tunnel.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2Tunnel.java
new file mode 100644
index 0000000..3e98480
--- /dev/null
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2Tunnel.java
@@ -0,0 +1,89 @@
+/*
+ * 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 org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.Link;
+
+import java.util.List;
+
+public interface L2Tunnel {
+
+ /**
+ * Return the mode of the l2 tunnel.
+ *
+ * @return The pw mode.
+ */
+ L2Mode pwMode();
+
+ /**
+ * Returns the service delimiting tag.
+ *
+ * @return the sd tag
+ */
+ VlanId sdTag();
+
+ /**
+ * Returns the id of the tunnel.
+ *
+ * @return the tunnel id
+ */
+ long tunnelId();
+
+ /**
+ * Return the label of the pseudowire.
+ *
+ * @return the pw label.
+ */
+ MplsLabel pwLabel();
+
+ /**
+ * Returns the path used by the pseudowire.
+ *
+ * @return The path that is used
+ */
+ List<Link> pathUsed();
+
+ /**
+ * Returns the transport vlan used by the pseudowire.
+ *
+ * @return The transport vlan
+ */
+ VlanId transportVlan();
+
+ /**
+ * Returns the inter-co label used by the pseudowire.
+ *
+ * @return The inter CO label.
+ */
+ MplsLabel interCoLabel();
+
+ /**
+ * Sets the path that this pw uses.
+ *
+ * @param path The apth to use
+ */
+ void setPath(List<Link> path);
+
+ /**
+ * Set the transport vlan that this pw will use.
+ *
+ * @param vlan The vlan to use.
+ */
+ void setTransportVlan(VlanId vlan);
+}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelDescription.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelDescription.java
new file mode 100644
index 0000000..7be187a
--- /dev/null
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelDescription.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+public interface L2TunnelDescription {
+
+ /**
+ * Returns the l2 tunnel.
+ *
+ * @return the l2 tunnel
+ */
+ L2Tunnel l2Tunnel();
+
+ /**
+ * Returns the l2 tunnel policy.
+ *
+ * @return the l2 tunnel policy.
+ */
+ L2TunnelPolicy l2TunnelPolicy();
+
+ /**
+ * Sets the l2 tunnel.
+ *
+ * @param tunnel the l2 tunnel to set.
+ */
+ void setL2Tunnel(L2Tunnel tunnel);
+
+ /**
+ * Sets the l2 policy.
+ *
+ * @param policy the policy to set.
+ */
+ void setL2TunnelPolicy(L2TunnelPolicy policy);
+}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
index 133de0c..3a89a21 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
@@ -16,209 +16,28 @@
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.Link;
-import org.onosproject.net.Path;
-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.DistributedSet;
-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 com.google.common.base.Preconditions.checkArgument;
-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;
+public interface L2TunnelHandler {
+ void init();
/**
- * To store policies.
- */
- private final ConsistentMap<String, DefaultL2TunnelPolicy> l2PolicyStore;
-
- /**
- * To store tunnels.
- */
- private final ConsistentMap<String, DefaultL2Tunnel> l2TunnelStore;
-
- private final KryoNamespace.Builder l2TunnelKryo;
-
- /**
- * Contains transport vlans used for spine-leaf pseudowires.
- */
- private final DistributedSet<VlanId> vlanStore;
-
- /**
- * Used for determining transport vlans for leaf-spine.
- */
- private short transportVlanUpper = 4093, transportVlanLower = 3500;
-
- private static final VlanId UNTAGGED_TRANSPORT_VLAN = VlanId.vlanId((short) 4094);
-
- /**
- * Create a l2 tunnel handler for the deploy and
- * for the tear down of pseudo wires.
+ * Returns a copy of the l2 policies that exist in the store.
*
- * @param segmentRoutingManager the segment routing manager
+ * @return The l2 policies
*/
- 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();
-
- vlanStore = srManager.storageService.<VlanId>setBuilder()
- .withName("onos-transport-vlan-store")
- .withSerializer(Serializer.using(
- new KryoNamespace.Builder()
- .register(KryoNamespaces.API)
- .build()))
- .build()
- .asDistributedSet();
- }
+ List<L2TunnelPolicy> getL2Policies();
/**
- * Deploys any pre-existing pseudowires in the configuration.
- * Used by manager only in initialization.
- */
- public void init() {
-
- PwaasConfig config = srManager.cfgService.getConfig(srManager.appId(), PwaasConfig.class);
- if (config == null) {
- return;
- }
-
- log.info("Deploying existing pseudowires");
-
- // gather pseudowires
- Set<DefaultL2TunnelDescription> pwToAdd = config
- .getPwIds()
- .stream()
- .map(config::getPwDescription)
- .collect(Collectors.toSet());
-
- // deploy pseudowires
- deploy(pwToAdd);
- }
-
- /**
- * Returns all L2 Policies.
+ * Returns a copy of the l2 tunnels that exist in the store.
*
- * @return List of policies
+ * @return The l2 tunnels.
*/
- 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());
-
- }
+ List<L2Tunnel> getL2Tunnels();
/**
* Processes a link removal. Finds affected pseudowires and rewires them.
@@ -226,1526 +45,44 @@
* 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));
- }
+ void processLinkDown(Link link);
/**
* Processes Pwaas Config added event.
*
* @param event network config add event
*/
- public void processPwaasConfigAdded(NetworkConfigEvent event) {
- checkArgument(event.config().isPresent(),
- "Config is not presented in PwaasConfigAdded event {}", 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.
- */
- private 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");
- }
- }
-
- /**
- * Determines vlan used for transporting the pw traffic.
- *
- * Leaf-Leaf traffic is transferred untagged, thus we choose the UNTAGGED_TRANSPORT_VLAN
- * and also make sure to add the popVlan instruction.
- * For spine-leaf pws we choose the highest vlan value available from a certain range.
- *
- * @param spinePw if the pw is leaf-spine.
- * @return The vlan id chossen to transport this pseudowire. If vlan is UNTAGGED_TRANSPORT_VLAN
- * then the pw is transported untagged.
- */
- private VlanId determineTransportVlan(boolean spinePw) {
-
- if (!spinePw) {
-
- log.info("Untagged transport with internal vlan {} for pseudowire!", UNTAGGED_TRANSPORT_VLAN);
- return UNTAGGED_TRANSPORT_VLAN;
- } else {
- for (short i = transportVlanUpper; i > transportVlanLower; i--) {
-
- VlanId vlanToUse = VlanId.vlanId((short) i);
- if (!vlanStore.contains(vlanToUse)) {
-
- vlanStore.add(vlanToUse);
- log.info("Transport vlan {} for pseudowire!", vlanToUse);
- return vlanToUse;
- }
- }
-
- log.info("No available transport vlan found, pseudowire traffic will be carried untagged " +
- "with internal vlan {}!", UNTAGGED_TRANSPORT_VLAN);
- return UNTAGGED_TRANSPORT_VLAN;
- }
- }
-
- /**
- * Adds a single pseudowire from leaf to a leaf.
- * This method can be called from cli commands
- * without configuration updates, thus it does not check for mastership
- * of the ingress pseudowire device.
- *
- * @param pw The pseudowire
- * @param spinePw True if pseudowire is from leaf to spine
- * @return result of pseudowire deployment
- */
- private Result deployPseudowire(DefaultL2TunnelDescription pw, boolean spinePw) {
-
- 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;
- }
-
- Link fwdNextHop;
- Link revNextHop;
- if (!spinePw) {
- if (path.size() != 2) {
- log.info("Deploying process : Path between two leafs should have size of 2, for pseudowire {}",
- l2TunnelId);
- return INTERNAL_ERROR;
- }
-
- fwdNextHop = path.get(0);
- revNextHop = reverseLink(path.get(1));
- } else {
- if (path.size() != 1) {
- log.info("Deploying process : Path between leaf spine should equal to 1, for pseudowire {}",
- l2TunnelId);
- return INTERNAL_ERROR;
- }
-
- fwdNextHop = path.get(0);
- revNextHop = reverseLink(path.get(0));
- }
-
- pw.l2Tunnel().setPath(path);
- pw.l2Tunnel().setTransportVlan(determineTransportVlan(spinePw));
-
- // next hops for next objectives
-
- 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,
- spinePw);
- 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(),
- VlanId.NONE,
- FWD,
- spinePw);
-
- 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,
- spinePw);
- 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(),
- VlanId.NONE,
- REV,
- spinePw);
-
- 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) {
- ConnectPoint cp1 = currentL2Tunnel.l2TunnelPolicy().cP1();
- ConnectPoint cp2 = currentL2Tunnel.l2TunnelPolicy().cP2();
- long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
-
- // only the master of CP1 will program this pseudowire
- if (!srManager.isMasterOf(cp1)) {
- log.debug("Not the master of {}. Ignore pseudo wire deployment id={}", cp1, tunnelId);
- continue;
- }
-
- try {
- // differentiate between leaf-leaf pseudowires and leaf-spine
- // and pass the appropriate flag in them.
- if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
- !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
- log.warn("Can not deploy pseudowire from spine to spine!");
- result = Result.INTERNAL_ERROR;
- } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
- srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
- log.info("Deploying a leaf-leaf pseudowire {}", tunnelId);
- result = deployPseudowire(currentL2Tunnel, false);
- } else {
- log.info("Deploying a leaf-spine pseudowire {}", tunnelId);
- result = deployPseudowire(currentL2Tunnel, true);
- }
- } catch (DeviceConfigNotFoundException e) {
- log.error("Exception caught when deploying pseudowire", e.toString());
- result = Result.INTERNAL_ERROR;
- }
-
- switch (result) {
- case INTERNAL_ERROR:
- log.warn("Could not deploy pseudowire {}, internal error!", tunnelId);
- break;
- case WRONG_PARAMETERS:
- log.warn("Could not deploy pseudowire {}, wrong parameters!", tunnelId);
- break;
- case ADDITION_ERROR:
- log.warn("Could not deploy pseudowire {}, error in populating rules!", tunnelId);
- break;
- default:
- log.info("Pseudowire with {} succesfully deployed!", tunnelId);
- break;
- }
- }
- }
+ void processPwaasConfigAdded(NetworkConfigEvent event);
/**
* Processes PWaaS Config updated event.
*
* @param event network config updated event
*/
- public void processPwaasConfigUpdated(NetworkConfigEvent event) {
- checkArgument(event.config().isPresent(),
- "Config is not presented in PwaasConfigUpdated event {}", event);
- checkArgument(event.prevConfig().isPresent(),
- "PrevConfig is not presented in PwaasConfigUpdated event {}", 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
- */
- private void updatePw(DefaultL2TunnelDescription oldPw,
- DefaultL2TunnelDescription newPw) {
- ConnectPoint oldCp1 = oldPw.l2TunnelPolicy().cP1();
- long tunnelId = oldPw.l2Tunnel().tunnelId();
-
- // only the master of CP1 will update this pseudowire
- if (!srManager.isMasterOf(oldPw.l2TunnelPolicy().cP1())) {
- log.debug("Not the master of {}. Ignore pseudo wire update id={}", oldCp1, tunnelId);
- return;
- }
- // only determine if the new pseudowire is leaf-spine, because
- // removal process is the same for both leaf-leaf and leaf-spine pws
- boolean newPwSpine;
- try {
- newPwSpine = !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP1().deviceId()) ||
- !srManager.deviceConfiguration().isEdgeDevice(newPw.l2TunnelPolicy().cP2().deviceId());
- } catch (DeviceConfigNotFoundException e) {
- // if exception is caught treat the new pw as leaf-leaf
- newPwSpine = false;
- }
-
- // copy the variable here because we need to use it in lambda thus it needs to be final
- boolean finalNewPwSpine = newPwSpine;
-
- 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 delete all information from our stores, we can not do it asynchronously
- l2PolicyStore.remove(Long.toString(tunnelId));
-
- // grab the old l2 tunnel from the store, since it carries information which is not exposed
- // to the user configuration and set it to oldPw.
- oldPw.setL2Tunnel(l2TunnelStore.get(Long.toString(tunnelId)).value());
- VlanId transportVlan = l2TunnelStore.get(Long.toString(tunnelId)).value().transportVlan();
- l2TunnelStore.remove(Long.toString(tunnelId));
-
- // remove the reserved transport vlan, if one is used
- if (!transportVlan.equals(UNTAGGED_TRANSPORT_VLAN)) {
- vlanStore.remove(transportVlan);
- }
-
- // First we remove both policy.
- log.debug("Start deleting fwd policy for {}", 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.error("Update process : " +
- "No path between the connection points for pseudowire {}", newPw.l2Tunnel().tunnelId());
- return;
- }
-
- Link fwdNextHop, revNextHop;
- if (!finalNewPwSpine) {
- if (path.size() != 2) {
- log.error("Update process : Error, path between two leafs should have size of 2, for pseudowire {}",
- newPw.l2Tunnel().tunnelId());
- return;
- }
- fwdNextHop = path.get(0);
- revNextHop = reverseLink(path.get(1));
- } else {
- if (path.size() != 1) {
- log.error("Update process : Error, path between leaf spine should equal to 1, for pseudowire {}",
- newPw.l2Tunnel().tunnelId());
- return;
- }
- fwdNextHop = path.get(0);
- revNextHop = reverseLink(path.get(0));
- }
-
- // set new path and transport vlan.
- newPw.l2Tunnel().setPath(path);
- newPw.l2Tunnel().setTransportVlan(determineTransportVlan(newPwSpine));
-
- // 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, finalNewPwSpine);
- 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(),
- VlanId.NONE, FWD, finalNewPwSpine);
-
- }
- });
- 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, finalNewPwSpine);
- 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(),
- VlanId.NONE,
- REV, finalNewPwSpine);
- }
- });
- }
+ void processPwaasConfigUpdated(NetworkConfigEvent event);
/**
* Processes Pwaas Config removed event.
*
* @param event network config removed event
*/
- public void processPwaasConfigRemoved(NetworkConfigEvent event) {
- checkArgument(event.prevConfig().isPresent(),
- "PrevConfig is not presented in PwaasConfigRemoved event {}", 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
- */
- private 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));
-
- // remove the reserved transport vlan
- if (!pwToRemove.l2Tunnel().transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
- vlanStore.remove(pwToRemove.l2Tunnel().transportVlan());
- }
-
- 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;
- }
+ void processPwaasConfigRemoved(NetworkConfigEvent event);
/**
* Helper function to handle the pw removal.
* <p>
- * This method checks for the mastership of the device because it is
+ * This method should 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) {
- ConnectPoint cp1 = currentL2Tunnel.l2TunnelPolicy().cP1();
- ConnectPoint cp2 = currentL2Tunnel.l2TunnelPolicy().cP2();
- long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
-
- // only the master of CP1 will program this pseudowire
- if (!srManager.isMasterOf(cp1)) {
- log.debug("Not the master of {}. Ignore pseudo wire removal id={}", cp1, tunnelId);
- continue;
- }
-
- // no need to differentiate here between leaf-leaf and leaf-spine, because
- // the only change is in the groups, which we do not remove either way
- log.info("Removing pseudowire {}", tunnelId);
-
- result = tearDownPseudowire(tunnelId);
- switch (result) {
- case WRONG_PARAMETERS:
- log.warn("Error in supplied parameters for the pseudowire removal with tunnel id {}!",
- tunnelId);
- break;
- case REMOVAL_ERROR:
- log.warn("Error in pseudowire removal with tunnel id {}!", tunnelId);
- break;
- default:
- log.warn("Pseudowire with tunnel id {} was removed successfully", 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
- * @param spinePw if the pseudowire involves a spine switch
- * @return the result of the operation
- */
- private Result deployPseudoWireInit(DefaultL2Tunnel l2Tunnel, ConnectPoint ingress,
- ConnectPoint egress, Direction direction, Link nextHop, boolean spinePw) {
-
- 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(),
- spinePw);
-
- 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
- * @param spinePw if the pseudowire involves a spine switch
- * @return the result of the operation
- */
- private Result deployPseudoWireTerm(DefaultL2Tunnel l2Tunnel, ConnectPoint egress,
- VlanId egressVlan, Direction direction, boolean spinePw) {
-
- // We create the group relative to the termination.
- NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
- l2Tunnel, egress.deviceId(),
- spinePw);
- 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());
-
- if (spinePw) {
-
- // determine the input port at the
- PortNumber inPort;
-
- if (egress.deviceId().
- equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
- inPort = l2Tunnel.pathUsed().get(0).dst().port();
- } else {
- inPort = l2Tunnel.pathUsed().get(0).src().port();
- }
-
- MacAddress dstMac;
- try {
- dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
- } catch (Exception e) {
- log.info("Device not found in configuration, no programming of MAC address");
- dstMac = null;
- }
-
- log.info("Populating filtering objective for pseudowire transport" +
- " with vlan = {}, port = {}, mac = {}",
- l2Tunnel.transportVlan(),
- inPort,
- dstMac);
- FilteringObjective.Builder filteringObjectiveBuilder =
- createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
- context = new DefaultObjectiveContext(( objective ) ->
- log.debug("Special filtObj for " + "for {} populated",
- l2Tunnel.tunnelId()),
- ( objective, error ) ->
- log.warn("Failed to populate " +
- "special filtObj " +
- "rule for {}: {}",
- l2Tunnel.tunnelId(), error));
- TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
- filteringObjectiveBuilder.withMeta(treatment.build());
- srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.add(context));
- log.debug("Creating new special FiltObj for termination point with tunnel {} for port {}",
- l2Tunnel.tunnelId(),
- inPort);
- }
-
- return SUCCESS;
- }
-
-
- /**
- * Creates the filtering objective according to a given port and vlanid.
- *
- * @param inPort the in port
- * @param vlanId the inner vlan tag
- * @return the filtering objective
- */
- private FilteringObjective.Builder createNormalPipelineFiltObjective(PortNumber inPort,
- VlanId vlanId,
- MacAddress dstMac) {
-
- log.info("Creating filtering objective for pseudowire transport with vlan={}, port={}, mac={}",
- vlanId,
- inPort,
- dstMac);
- FilteringObjective.Builder fwdBuilder = DefaultFilteringObjective
- .builder()
- .withKey(Criteria.matchInPort(inPort))
- .addCondition(Criteria.matchVlanId(vlanId))
- .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
- .permit()
- .fromApp(srManager.appId());
-
- if (dstMac != null) {
- fwdBuilder.addCondition(Criteria.matchEthDst(dstMac));
- }
-
- return fwdBuilder;
- }
-
- /**
- * 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
- * @param spinePw if the pw involves a spine switch
- * @return the next objective to support the pipeline
- */
- private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
- ConnectPoint dstCp, DefaultL2Tunnel l2Tunnel,
- DeviceId egressId, boolean spinePw) {
- 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();
- }
-
- // if pw is leaf-to-leaf we need to
- // add the routing label also
- if (!spinePw) {
- // 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);
-
- // if not a leaf-spine pw we need to POP the vlan at the output
- // since we carry this traffic untagged.
- if (!spinePw) {
- treatmentBuilder.popVlan();
- }
- } else {
- // We create the next objective which
- // will be a simple l2 group.
- nextObjBuilder = DefaultNextObjective
- .builder()
- .withType(NextObjective.Type.SIMPLE)
- .fromApp(srManager.appId());
- }
-
- // set the appropriate transport vlan
- treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
- treatmentBuilder.setOutput(srcCp.port());
- nextObjBuilder.addTreatment(treatmentBuilder.build());
- return nextObjBuilder;
- }
-
- /**
- * Reverses a link.
- *
- * @param link link to be reversed
- * @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 source connect point
- * @param dstCp destination connect point
- * @return the path
- */
- private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
- /* TODO We retrieve a set of paths in case of a link failure, what happens
- * if the TopologyService gets the link notification AFTER us and has not updated the paths?
- *
- * TODO This has the potential to act on old topology.
- * Maybe we should make SRManager be a listener on topology events instead raw link events.
- */
- Set<Path> paths = srManager.topologyService.getPaths(
- srManager.topologyService.currentTopology(),
- srcCp.deviceId(), dstCp.deviceId());
-
- log.debug("Paths obtained from topology service {}", paths);
-
- // We randomly pick a path.
- if (paths.isEmpty()) {
- return null;
- }
- int size = paths.size();
- int index = RandomUtils.nextInt(0, size);
-
- List<Link> result = Iterables.get(paths, index).links();
- log.debug("Randomly picked a path {}", result);
-
- return result;
- }
-
- /**
- * 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 {} {}, " +
- "direction {} removed",
- TERMINATION,
- l2Tunnel.tunnelId(),
- direction),
- (objective, error) ->
- log.warn("Failed to remove fwdObj " +
- "for {} {}" +
- ", direction {}",
- TERMINATION,
- l2Tunnel.tunnelId(),
- error,
- direction));
- 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));
- */
-
- // delete the extra filtering objective for terminating
- // spine-spine pws
- if (!l2Tunnel.transportVlan().equals(UNTAGGED_TRANSPORT_VLAN)) {
-
- // determine the input port at the
- PortNumber inPort;
-
- if (egress.deviceId().
- equals(l2Tunnel.pathUsed().get(0).dst().deviceId())) {
- inPort = l2Tunnel.pathUsed().get(0).dst().port();
- } else {
- inPort = l2Tunnel.pathUsed().get(0).src().port();
- }
-
- MacAddress dstMac;
- try {
- dstMac = srManager.deviceConfiguration().getDeviceMac(egress.deviceId());
- } catch (Exception e) {
- log.info("Device not found in configuration, no programming of MAC address");
- dstMac = null;
- }
-
- log.info("Removing filtering objective for pseudowire transport" +
- " with vlan = {}, port = {}, mac = {}",
- l2Tunnel.transportVlan(),
- inPort,
- dstMac);
- FilteringObjective.Builder filteringObjectiveBuilder =
- createNormalPipelineFiltObjective(inPort, l2Tunnel.transportVlan(), dstMac);
- context = new DefaultObjectiveContext(( objective ) ->
- log.debug("Special filtObj for " + "for {} removed",
- l2Tunnel.tunnelId()), ( objective, error ) ->
- log.warn("Failed to populate " + "special filtObj " +
- "rule for {}: {}", l2Tunnel.tunnelId(), error));
- TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
- filteringObjectiveBuilder.withMeta(treatment.build());
- srManager.flowObjectiveService.filter(egress.deviceId(), filteringObjectiveBuilder.remove(context));
- log.debug("Removing special FiltObj for termination point with tunnel {} for port {}",
- l2Tunnel.tunnelId(),
- inPort);
- }
-
- l2TerminationNextObjStore.remove(key);
- future.complete(null);
- }
-
- /**
- * 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);
- }
+ void tearDown(Set<L2TunnelDescription> pwToRemove);
/**
* Pwaas pipelines.
*/
- protected enum Pipeline {
+ enum Pipeline {
/**
* The initiation pipeline.
*/
@@ -1758,7 +95,7 @@
/**
* Enum helper to carry results of various operations.
*/
- public enum Result {
+ enum Result {
/**
* Happy ending scenario.
*/
@@ -1792,7 +129,7 @@
private final int code;
private final String description;
- private int nextId;
+ public int nextId;
Result(int code, String description) {
this.code = code;
@@ -1812,7 +149,7 @@
/**
* Enum helper for handling the direction of the pw.
*/
- public enum Direction {
+ enum Direction {
/**
* The forward direction of the pseudo wire.
*/
@@ -1821,5 +158,4 @@
*/
REV
}
-
}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelPolicy.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelPolicy.java
new file mode 100644
index 0000000..17be45b
--- /dev/null
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelPolicy.java
@@ -0,0 +1,72 @@
+/*
+ * 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 org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+
+public interface L2TunnelPolicy {
+
+ /**
+ * Returns the first connect point of the policy.
+ *
+ * @return first connect point
+ */
+ ConnectPoint cP1();
+
+ /**
+ * Returns the second connect point of the policy.
+ *
+ * @return second connect point
+ */
+ ConnectPoint cP2();
+
+ /**
+ * Returns the cP1 inner vlan tag of the policy.
+ *
+ * @return cP1 inner vlan tag
+ */
+ VlanId cP1InnerTag();
+
+ /**
+ * Returns the cP1 outer vlan tag of the policy.
+ *
+ * @return cP1 outer vlan tag
+ */
+ VlanId cP1OuterTag();
+
+ /**
+ * Returns the cP2 inner vlan tag of the policy.
+ *
+ * @return cP2 inner vlan tag
+ */
+ VlanId cP2InnerTag();
+
+ /**
+ * Returns the cP2 outer vlan tag of the policy.
+ *
+ * @return cP2 outer vlan tag
+ */
+ VlanId cP2OuterTag();
+
+ /**
+ * Returns the tunnel ID of the policy.
+ *
+ * @return Tunnel ID
+ */
+ long tunnelId();
+}
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/PwaasConfigTest.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/PwaasConfigTest.java
index 60163eb..23d1233 100644
--- a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/PwaasConfigTest.java
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/PwaasConfigTest.java
@@ -46,8 +46,11 @@
import org.onosproject.net.intf.InterfaceService;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.segmentrouting.config.PwaasConfig;
+
+import org.onosproject.segmentrouting.pwaas.L2Tunnel;
+import org.onosproject.segmentrouting.pwaas.L2TunnelDescription;
+import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
-import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
import org.onosproject.segmentrouting.pwaas.L2Mode;
@@ -326,15 +329,15 @@
*/
@Test
public void testGetPwDescription() {
- DefaultL2TunnelDescription l2TunnelDescription = null;
+ L2TunnelDescription l2TunnelDescription = null;
- DefaultL2Tunnel l2Tunnel = new DefaultL2Tunnel(
+ L2Tunnel l2Tunnel = new DefaultL2Tunnel(
L2Mode.valueOf(MODE_1),
SD_TAG_1,
Long.parseLong(TUNNEL_ID_1),
PW_LABEL_1
);
- DefaultL2TunnelPolicy l2TunnelPolicy = new DefaultL2TunnelPolicy(
+ L2TunnelPolicy l2TunnelPolicy = new DefaultL2TunnelPolicy(
Long.parseLong(TUNNEL_ID_1),
INGRESS_1,
INGRESS_INNER_TAG_1,