Support encapsulation in PathIntent
- related to ONOS-3467
- unit test work
- depends on ONOS-3507 and also on the advertisement of VLAN resource
for different devices.
Change-Id: Ia852c751135b5ca4a16901c6f3a85ceea11514a3
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
index 7add217..ffced6c 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
@@ -15,42 +15,68 @@
*/
package org.onosproject.net.intent.impl.compiler;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.EncapsulationType;
import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.intent.FlowRuleIntent;
import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentCompiler;
import org.onosproject.net.intent.IntentExtensionService;
import org.onosproject.net.intent.PathIntent;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
+import org.onosproject.net.intent.impl.IntentCompilationException;
+import org.onosproject.net.newresource.ResourcePath;
+import org.onosproject.net.newresource.ResourceService;
import org.onosproject.net.resource.link.LinkResourceAllocations;
+import org.slf4j.Logger;
-import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.onosproject.net.LinkKey.linkKey;
+import static org.slf4j.LoggerFactory.getLogger;
@Component(immediate = true)
public class PathIntentCompiler implements IntentCompiler<PathIntent> {
+ private final Logger log = getLogger(getClass());
+
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentExtensionService intentManager;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ResourceService resourceService;
+
private ApplicationId appId;
@Activate
@@ -71,29 +97,47 @@
// TODO: implement recompile behavior
List<Link> links = intent.path().links();
- List<FlowRule> rules = new ArrayList<>(links.size() - 1);
+ List<FlowRule> rules = new LinkedList<>();
- for (int i = 0; i < links.size() - 1; i++) {
- ConnectPoint ingress = links.get(i).dst();
- ConnectPoint egress = links.get(i + 1).src();
- FlowRule rule = createFlowRule(intent.selector(), intent.treatment(),
- ingress, egress, intent.priority(),
- isLast(links, i));
- rules.add(rule);
+ Optional<EncapsulationConstraint> enacpConstraint = intent.constraints().stream()
+ .filter(constraint -> constraint instanceof EncapsulationConstraint)
+ .map(x -> (EncapsulationConstraint) x).findAny();
+ //if no encapsulation or is involved only a single switch use the default behaviour
+ if (!enacpConstraint.isPresent() || links.size() == 1) {
+
+ for (int i = 0; i < links.size() - 1; i++) {
+ ConnectPoint ingress = links.get(i).dst();
+ ConnectPoint egress = links.get(i + 1).src();
+ FlowRule rule = createFlowRule(intent.selector(), intent.treatment(),
+ ingress, egress, intent.priority(),
+ isLast(links, i));
+ rules.add(rule);
+ }
+
+ return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
+
+ } else {
+ if (EncapsulationType.VLAN == enacpConstraint.get().encapType()) {
+ rules = manageVlanEncap(intent);
+ }
+ if (EncapsulationType.MPLS == enacpConstraint.get().encapType()) {
+ //TODO: to be implemented
+ rules = Collections.emptyList();
+ }
+
+ return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
}
-
- return Collections.singletonList(new FlowRuleIntent(appId, null, rules, intent.resources()));
}
private FlowRule createFlowRule(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
ConnectPoint ingress, ConnectPoint egress,
- int priority, boolean last) {
+ int priority, boolean applyTreatment) {
TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector)
.matchInPort(ingress.port())
.build();
TrafficTreatment.Builder treatmentBuilder;
- if (last) {
+ if (applyTreatment) {
treatmentBuilder = DefaultTrafficTreatment.builder(originalTreatment);
} else {
treatmentBuilder = DefaultTrafficTreatment.builder();
@@ -110,6 +154,138 @@
.build();
}
+ private List<FlowRule> manageVlanEncap(PathIntent intent) {
+ Map<LinkKey, VlanId> vlanIds = assignVlanId(intent);
+
+ Iterator<Link> links = intent.path().links().iterator();
+ Link srcLink = links.next();
+
+ Link link = links.next();
+ // List of flow rules to be installed
+ List<FlowRule> rules = new LinkedList<>();
+
+ // Ingress traffic
+ VlanId vlanId = vlanIds.get(linkKey(link));
+ if (vlanId == null) {
+ throw new IntentCompilationException("No available VLAN ID for " + link);
+ }
+ VlanId prevVlanId = vlanId;
+
+ //Tag the traffic with the new VLAN
+ TrafficTreatment treat = DefaultTrafficTreatment.builder()
+ .setVlanId(vlanId)
+ .build();
+
+ rules.add(createFlowRule(intent.selector(), treat, srcLink.dst(), link.src(), intent.priority(), true));
+
+ ConnectPoint prev = link.dst();
+
+ while (links.hasNext()) {
+
+ link = links.next();
+
+ if (links.hasNext()) {
+ // Transit traffic
+ VlanId egressVlanId = vlanIds.get(linkKey(link));
+ if (egressVlanId == null) {
+ throw new IntentCompilationException("No available VLAN ID for " + link);
+ }
+ prevVlanId = egressVlanId;
+
+ TrafficSelector transitSelector = DefaultTrafficSelector.builder()
+ .matchInPort(prev.port())
+ .matchVlanId(prevVlanId).build();
+
+ TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
+
+ // Set the new vlanId only if the previous one is different
+ if (!prevVlanId.equals(egressVlanId)) {
+ transitTreat.setVlanId(egressVlanId);
+ }
+ rules.add(createFlowRule(transitSelector,
+ transitTreat.build(), prev, link.src(), intent.priority(), true));
+ prev = link.dst();
+ } else {
+ // Egress traffic
+ TrafficSelector egressSelector = DefaultTrafficSelector.builder()
+ .matchInPort(prev.port())
+ .matchVlanId(prevVlanId).build();
+
+ //TODO: think to other cases for egress packet restoration
+ Optional<VlanIdCriterion> vlanCriteria = intent.selector().criteria()
+ .stream().filter(criteria -> criteria.type() == Criterion.Type.VLAN_VID)
+ .map(criteria -> (VlanIdCriterion) criteria)
+ .findAny();
+ TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
+ if (vlanCriteria.isPresent()) {
+ egressTreat.setVlanId(vlanCriteria.get().vlanId());
+ } else {
+ egressTreat.popVlan();
+ }
+
+ rules.add(createFlowRule(egressSelector,
+ egressTreat.build(), prev, link.src(), intent.priority(), true));
+ }
+
+ }
+ return rules;
+
+ }
+
+ private Map<LinkKey, VlanId> assignVlanId(PathIntent intent) {
+ Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
+ .links().size() - 2);
+ for (int i = 1; i <= intent.path().links().size() - 2; i++) {
+ LinkKey link = linkKey(intent.path().links().get(i));
+ linkRequest.add(link);
+ // add the inverse link. I want that the VLANID is reserved both for
+ // the direct and inverse link
+ linkRequest.add(linkKey(link.dst(), link.src()));
+ }
+
+ Map<LinkKey, VlanId> vlanIds = findVlanIds(linkRequest);
+ if (vlanIds.isEmpty()) {
+ log.warn("No VLAN IDs available");
+ return Collections.emptyMap();
+ }
+
+ //same VLANID is used for both directions
+ Set<ResourcePath> resources = vlanIds.entrySet().stream()
+ .flatMap(x -> Stream.of(
+ ResourcePath.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue()),
+ ResourcePath.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
+ ))
+ .collect(Collectors.toSet());
+ List<org.onosproject.net.newresource.ResourceAllocation> allocations =
+ resourceService.allocate(intent.id(), ImmutableList.copyOf(resources));
+ if (allocations.isEmpty()) {
+ Collections.emptyMap();
+ }
+
+ return vlanIds;
+ }
+
+ private Map<LinkKey, VlanId> findVlanIds(Set<LinkKey> links) {
+ Map<LinkKey, VlanId> vlanIds = new HashMap<>();
+ for (LinkKey link : links) {
+ Set<VlanId> forward = findVlanId(link.src());
+ Set<VlanId> backward = findVlanId(link.dst());
+ Set<VlanId> common = Sets.intersection(forward, backward);
+ if (common.isEmpty()) {
+ continue;
+ }
+ vlanIds.put(link, common.iterator().next());
+ }
+ return vlanIds;
+ }
+
+ private Set<VlanId> findVlanId(ConnectPoint cp) {
+ return resourceService.getAvailableResources(ResourcePath.discrete(cp.deviceId(), cp.port())).stream()
+ .filter(x -> x.last() instanceof VlanId)
+ .map(x -> (VlanId) x.last())
+ .collect(Collectors.toSet());
+ }
+
private boolean isLast(List<Link> links, int i) {
return i == links.size() - 2;
}