Add path intent compiler that generates flow objective intents

Change-Id: I11bee398d927f0e3f32b7cf81d98cfe5816db477
This commit is contained in:
Ray Milkey 2016-02-26 17:12:17 -08:00 committed by Brian O'Connor
parent 00f481629a
commit 661c38cad5
5 changed files with 482 additions and 230 deletions

View File

@ -17,19 +17,25 @@
package org.onosproject.net.intent; package org.onosproject.net.intent;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import org.onosproject.core.ApplicationId; import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.NetworkResource; import org.onosproject.net.NetworkResource;
import org.onosproject.net.flowobjective.Objective; import org.onosproject.net.flowobjective.Objective;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
/** /**
* Intent expressed as (and backed by) a collection of flow objectives through * Intent expressed as (and backed by) a collection of flow objectives through
* which the intent is to be accomplished. * which the intent is to be accomplished.
*/ */
public class FlowObjectiveIntent extends Intent { public final class FlowObjectiveIntent extends Intent {
private final Collection<Objective> objectives; private final List<Objective> objectives;
private final List<DeviceId> devices;
/** /**
* Constructor for serialization. * Constructor for serialization.
@ -37,6 +43,7 @@ public class FlowObjectiveIntent extends Intent {
protected FlowObjectiveIntent() { protected FlowObjectiveIntent() {
super(); super();
this.objectives = null; this.objectives = null;
this.devices = null;
} }
/** /**
@ -44,13 +51,15 @@ public class FlowObjectiveIntent extends Intent {
* resources. * resources.
* *
* @param appId application id * @param appId application id
* @param devices list of target devices; in same order as the objectives
* @param objectives backing flow objectives * @param objectives backing flow objectives
* @param resources backing network resources * @param resources backing network resources
*/ */
public FlowObjectiveIntent(ApplicationId appId, public FlowObjectiveIntent(ApplicationId appId,
Collection<Objective> objectives, List<DeviceId> devices,
List<Objective> objectives,
Collection<NetworkResource> resources) { Collection<NetworkResource> resources) {
this(appId, null, objectives, resources); this(appId, null, devices, objectives, resources);
} }
/** /**
@ -59,14 +68,20 @@ public class FlowObjectiveIntent extends Intent {
* *
* @param appId application id * @param appId application id
* @param key intent key * @param key intent key
* @param devices list of target devices; in same order as the objectives
* @param objectives backing flow objectives * @param objectives backing flow objectives
* @param resources backing network resources * @param resources backing network resources
*/ */
public FlowObjectiveIntent(ApplicationId appId, Key key, public FlowObjectiveIntent(ApplicationId appId,
Collection<Objective> objectives, Key key,
List<DeviceId> devices,
List<Objective> objectives,
Collection<NetworkResource> resources) { Collection<NetworkResource> resources) {
super(appId, key, resources, DEFAULT_INTENT_PRIORITY); super(appId, key, resources, DEFAULT_INTENT_PRIORITY);
this.objectives = objectives; checkArgument(devices.size() == objectives.size(),
"Number of devices and objectives does not match");
this.objectives = ImmutableList.copyOf(objectives);
this.devices = ImmutableList.copyOf(devices);
} }
/** /**
@ -74,10 +89,19 @@ public class FlowObjectiveIntent extends Intent {
* *
* @return flow objectives * @return flow objectives
*/ */
Collection<Objective> objectives() { public List<Objective> objectives() {
return objectives; return objectives;
} }
/**
* Returns the list of devices for the flow objectives.
*
* @return devices
*/
public List<DeviceId> devices() {
return devices;
}
@Override @Override
public boolean isInstallable() { public boolean isInstallable() {
@ -91,7 +115,8 @@ public class FlowObjectiveIntent extends Intent {
.add("key", key()) .add("key", key())
.add("appId", appId()) .add("appId", appId())
.add("resources", resources()) .add("resources", resources())
.add("objectives", objectives) .add("device", devices())
.add("objectives", objectives())
.toString(); .toString();
} }
} }

View File

@ -16,11 +16,13 @@
package org.onosproject.net.intent; package org.onosproject.net.intent;
import com.google.common.collect.ImmutableSet; import java.util.Collection;
import com.google.common.testing.EqualsTester; import java.util.List;
import org.junit.Test; import org.junit.Test;
import org.onosproject.core.ApplicationId; import org.onosproject.core.ApplicationId;
import org.onosproject.core.DefaultApplicationId; import org.onosproject.core.DefaultApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.NetworkResource; import org.onosproject.net.NetworkResource;
import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.DefaultTrafficTreatment;
@ -30,7 +32,9 @@ import org.onosproject.net.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.ForwardingObjective; import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flowobjective.Objective; import org.onosproject.net.flowobjective.Objective;
import java.util.Collection; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.testing.EqualsTester;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -52,8 +56,9 @@ public class FlowObjectiveIntentTest extends IntentTest {
.withSelector(DefaultTrafficSelector.builder().matchEthType((short) 123).build()) .withSelector(DefaultTrafficSelector.builder().matchEthType((short) 123).build())
.withTreatment(DefaultTrafficTreatment.emptyTreatment()) .withTreatment(DefaultTrafficTreatment.emptyTreatment())
.withFlag(ForwardingObjective.Flag.VERSATILE).add(); .withFlag(ForwardingObjective.Flag.VERSATILE).add();
private static final Collection<Objective> OBJECTIVES = ImmutableSet.of(FO1, FO2); private static final List<Objective> OBJECTIVES = ImmutableList.of(FO1, FO2);
private static final Collection<NetworkResource> RESOURCES = ImmutableSet.of(); private static final Collection<NetworkResource> RESOURCES = ImmutableSet.of();
private static final List<DeviceId> DEVICE = ImmutableList.of(DeviceId.NONE, DeviceId.NONE);
/** /**
* Tests basics of construction and getters. * Tests basics of construction and getters.
@ -61,7 +66,7 @@ public class FlowObjectiveIntentTest extends IntentTest {
@Test @Test
public void basics() { public void basics() {
FlowObjectiveIntent intent = FlowObjectiveIntent intent =
new FlowObjectiveIntent(APP_ID, KEY, OBJECTIVES, RESOURCES); new FlowObjectiveIntent(APP_ID, KEY, DEVICE, OBJECTIVES, RESOURCES);
assertEquals("incorrect app id", APP_ID, intent.appId()); assertEquals("incorrect app id", APP_ID, intent.appId());
assertEquals("incorrect key", KEY, intent.key()); assertEquals("incorrect key", KEY, intent.key());
assertEquals("incorrect objectives", OBJECTIVES, intent.objectives()); assertEquals("incorrect objectives", OBJECTIVES, intent.objectives());
@ -89,11 +94,11 @@ public class FlowObjectiveIntentTest extends IntentTest {
@Override @Override
protected Intent createOne() { protected Intent createOne() {
return new FlowObjectiveIntent(APP_ID, OBJECTIVES, RESOURCES); return new FlowObjectiveIntent(APP_ID, DEVICE, OBJECTIVES, RESOURCES);
} }
@Override @Override
protected Intent createAnother() { protected Intent createAnother() {
return new FlowObjectiveIntent(APP_ID, OBJECTIVES, RESOURCES); return new FlowObjectiveIntent(APP_ID, DEVICE, OBJECTIVES, RESOURCES);
} }
} }

View File

@ -0,0 +1,274 @@
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.intent.impl.compiler;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
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 org.onlab.packet.VlanId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
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.Criterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.flow.instructions.L2ModificationInstruction;
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.Resource;
import org.onosproject.net.newresource.ResourceService;
import org.onosproject.net.newresource.Resources;
import org.slf4j.Logger;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import static org.onosproject.net.LinkKey.linkKey;
/**
* Shared APIs and implementations for path compilers.
*/
public class PathCompiler<T> {
/**
* Defines methods used to create objects representing flows.
*/
public interface PathCompilerCreateFlow<T> {
void createFlow(TrafficSelector originalSelector,
TrafficTreatment originalTreatment,
ConnectPoint ingress, ConnectPoint egress,
int priority,
boolean applyTreatment,
List<T> flows,
List<DeviceId> devices);
Logger log();
ResourceService resourceService();
}
private boolean isLast(List<Link> links, int i) {
return i == links.size() - 2;
}
private Map<LinkKey, VlanId> assignVlanId(PathCompilerCreateFlow creator, 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(creator, linkRequest);
if (vlanIds.isEmpty()) {
creator.log().warn("No VLAN IDs available");
return Collections.emptyMap();
}
//same VLANID is used for both directions
Set<Resource> resources = vlanIds.entrySet().stream()
.flatMap(x -> Stream.of(
Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
.resource(),
Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
.resource()
))
.collect(Collectors.toSet());
List<org.onosproject.net.newresource.ResourceAllocation> allocations =
creator.resourceService().allocate(intent.id(), ImmutableList.copyOf(resources));
if (allocations.isEmpty()) {
Collections.emptyMap();
}
return vlanIds;
}
private Map<LinkKey, VlanId> findVlanIds(PathCompilerCreateFlow creator, Set<LinkKey> links) {
Map<LinkKey, VlanId> vlanIds = new HashMap<>();
for (LinkKey link : links) {
Set<VlanId> forward = findVlanId(creator, link.src());
Set<VlanId> backward = findVlanId(creator, 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(PathCompilerCreateFlow creator, ConnectPoint cp) {
return creator.resourceService().getAvailableResourceValues(
Resources.discrete(cp.deviceId(), cp.port()).id(),
VlanId.class);
}
private void manageVlanEncap(PathCompilerCreateFlow<T> creator, List<T> flows,
List<DeviceId> devices,
PathIntent intent) {
Map<LinkKey, VlanId> vlanIds = assignVlanId(creator, intent);
Iterator<Link> links = intent.path().links().iterator();
Link srcLink = links.next();
Link link = links.next();
// Ingress traffic
VlanId vlanId = vlanIds.get(linkKey(link));
if (vlanId == null) {
throw new IntentCompilationException("No available VLAN ID for " + link);
}
VlanId prevVlanId = vlanId;
Optional<VlanIdCriterion> vlanCriterion = intent.selector().criteria()
.stream().filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
.map(criterion -> (VlanIdCriterion) criterion)
.findAny();
//Push VLAN if selector does not include VLAN
TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
if (!vlanCriterion.isPresent()) {
treatBuilder.pushVlan();
}
//Tag the traffic with the new encapsulation VLAN
treatBuilder.setVlanId(vlanId);
creator.createFlow(intent.selector(), treatBuilder.build(),
srcLink.dst(), link.src(), intent.priority(), true,
flows, devices);
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);
}
creator.createFlow(transitSelector,
transitTreat.build(), prev, link.src(),
intent.priority(), true, flows, devices);
prev = link.dst();
} else {
// Egress traffic
TrafficSelector egressSelector = DefaultTrafficSelector.builder()
.matchInPort(prev.port())
.matchVlanId(prevVlanId).build();
TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
Optional<L2ModificationInstruction.ModVlanIdInstruction> modVlanIdInstruction = intent.treatment()
.allInstructions().stream().filter(
instruction -> instruction instanceof L2ModificationInstruction.ModVlanIdInstruction)
.map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x).findAny();
Optional<L2ModificationInstruction.PopVlanInstruction> popVlanInstruction = intent.treatment()
.allInstructions().stream().filter(
instruction -> instruction instanceof L2ModificationInstruction.PopVlanInstruction)
.map(x -> (L2ModificationInstruction.PopVlanInstruction) x).findAny();
if (!modVlanIdInstruction.isPresent() && !popVlanInstruction.isPresent()) {
if (vlanCriterion.isPresent()) {
egressTreat.setVlanId(vlanCriterion.get().vlanId());
} else {
egressTreat.popVlan();
}
}
creator.createFlow(egressSelector,
egressTreat.build(), prev, link.src(),
intent.priority(), true, flows, devices);
}
}
}
/**
* Compiles an intent down to flows.
*
* @param creator how to create the flows
* @param intent intent to process
* @param flows list of generated flows
* @param devices list of devices that correspond to the flows
*/
public void compile(PathCompilerCreateFlow<T> creator,
PathIntent intent,
List<T> flows,
List<DeviceId> devices) {
// Note: right now recompile is not considered
// TODO: implement recompile behavior
List<Link> links = intent.path().links();
Optional<EncapsulationConstraint> encapConstraint = 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 (!encapConstraint.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();
creator.createFlow(intent.selector(), intent.treatment(),
ingress, egress, intent.priority(),
isLast(links, i), flows, devices);
}
}
encapConstraint.map(EncapsulationConstraint::encapType)
.map(type -> {
switch (type) {
case VLAN:
manageVlanEncap(creator, flows, devices, intent);
// TODO: implement MPLS case here
default:
// Nothing to do
}
return 0;
});
}
}

View File

@ -15,57 +15,43 @@
*/ */
package org.onosproject.net.intent.impl.compiler; package org.onosproject.net.intent.impl.compiler;
import com.google.common.collect.ImmutableList; import java.util.LinkedList;
import com.google.common.collect.Sets; import java.util.List;
import java.util.Set;
import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId; import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService; import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint; import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Link; import org.onosproject.net.DeviceId;
import org.onosproject.net.LinkKey;
import org.onosproject.net.flow.DefaultFlowRule; import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule; import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.VlanIdCriterion;
import org.onosproject.net.flow.instructions.L2ModificationInstruction;
import org.onosproject.net.intent.FlowRuleIntent; import org.onosproject.net.intent.FlowRuleIntent;
import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.Intent;
import org.onosproject.net.intent.IntentCompiler; import org.onosproject.net.intent.IntentCompiler;
import org.onosproject.net.intent.IntentExtensionService; import org.onosproject.net.intent.IntentExtensionService;
import org.onosproject.net.intent.PathIntent; 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.Resource;
import org.onosproject.net.newresource.ResourceService; import org.onosproject.net.newresource.ResourceService;
import org.onosproject.net.newresource.Resources;
import org.onosproject.net.resource.link.LinkResourceAllocations; import org.onosproject.net.resource.link.LinkResourceAllocations;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.util.Collections; import com.google.common.collect.ImmutableList;
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; import static org.slf4j.LoggerFactory.getLogger;
@Component(immediate = true) @Component(immediate = true)
public class PathIntentCompiler implements IntentCompiler<PathIntent> { public class PathIntentCompiler
extends PathCompiler<FlowRule>
implements IntentCompiler<PathIntent>,
PathCompiler.PathCompilerCreateFlow<FlowRule> {
private final Logger log = getLogger(getClass()); private final Logger log = getLogger(getClass());
@ -94,47 +80,30 @@ public class PathIntentCompiler implements IntentCompiler<PathIntent> {
@Override @Override
public List<Intent> compile(PathIntent intent, List<Intent> installable, public List<Intent> compile(PathIntent intent, List<Intent> installable,
Set<LinkResourceAllocations> resources) { Set<LinkResourceAllocations> resources) {
// Note: right now recompile is not considered
// TODO: implement recompile behavior
List<Link> links = intent.path().links();
Optional<EncapsulationConstraint> encapConstraint = 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 (!encapConstraint.isPresent() || links.size() == 1) {
List<FlowRule> rules = new LinkedList<>(); List<FlowRule> rules = new LinkedList<>();
for (int i = 0; i < links.size() - 1; i++) { List<DeviceId> devices = new LinkedList<>();
ConnectPoint ingress = links.get(i).dst(); compile(this, intent, rules, devices);
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())); return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
} }
List<FlowRule> rules = encapConstraint.map(EncapsulationConstraint::encapType) @Override
.map(type -> { public Logger log() {
switch (type) { return log;
case VLAN:
return manageVlanEncap(intent);
// TODO: implement MPLS case here
default:
return Collections.<FlowRule>emptyList();
}
})
.orElse(Collections.emptyList());
return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
} }
private FlowRule createFlowRule(TrafficSelector originalSelector, TrafficTreatment originalTreatment, @Override
public ResourceService resourceService() {
return resourceService;
}
@Override
public void createFlow(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
ConnectPoint ingress, ConnectPoint egress, ConnectPoint ingress, ConnectPoint egress,
int priority, boolean applyTreatment) { int priority, boolean applyTreatment,
List<FlowRule> rules,
List<DeviceId> devices) {
TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector) TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector)
.matchInPort(ingress.port()) .matchInPort(ingress.port())
.build(); .build();
@ -147,164 +116,13 @@ public class PathIntentCompiler implements IntentCompiler<PathIntent> {
} }
TrafficTreatment treatment = treatmentBuilder.setOutput(egress.port()).build(); TrafficTreatment treatment = treatmentBuilder.setOutput(egress.port()).build();
return DefaultFlowRule.builder() rules.add(DefaultFlowRule.builder()
.forDevice(ingress.deviceId()) .forDevice(ingress.deviceId())
.withSelector(selector) .withSelector(selector)
.withTreatment(treatment) .withTreatment(treatment)
.withPriority(priority) .withPriority(priority)
.fromApp(appId) .fromApp(appId)
.makePermanent() .makePermanent()
.build(); .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;
Optional<VlanIdCriterion> vlanCriterion = intent.selector().criteria()
.stream().filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
.map(criterion -> (VlanIdCriterion) criterion)
.findAny();
//Push VLAN if selector does not include VLAN
TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
if (!vlanCriterion.isPresent()) {
treatBuilder.pushVlan();
}
//Tag the traffic with the new encapsulation VLAN
treatBuilder.setVlanId(vlanId);
rules.add(createFlowRule(intent.selector(), treatBuilder.build(),
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();
TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
Optional<L2ModificationInstruction.ModVlanIdInstruction> modVlanIdInstruction = intent.treatment()
.allInstructions().stream().filter(
instruction -> instruction instanceof L2ModificationInstruction.ModVlanIdInstruction)
.map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x).findAny();
Optional<L2ModificationInstruction.PopVlanInstruction> popVlanInstruction = intent.treatment()
.allInstructions().stream().filter(
instruction -> instruction instanceof L2ModificationInstruction.PopVlanInstruction)
.map(x -> (L2ModificationInstruction.PopVlanInstruction) x).findAny();
if (!modVlanIdInstruction.isPresent() && !popVlanInstruction.isPresent()) {
if (vlanCriterion.isPresent()) {
egressTreat.setVlanId(vlanCriterion.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<Resource> resources = vlanIds.entrySet().stream()
.flatMap(x -> Stream.of(
Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
.resource(),
Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
.resource()
))
.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.getAvailableResourceValues(
Resources.discrete(cp.deviceId(), cp.port()).id(),
VlanId.class);
}
private boolean isLast(List<Link> links, int i) {
return i == links.size() - 2;
} }
} }

View File

@ -0,0 +1,130 @@
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.net.intent.impl.compiler;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
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.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flowobjective.Objective;
import org.onosproject.net.intent.FlowObjectiveIntent;
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.newresource.ResourceService;
import org.onosproject.net.resource.link.LinkResourceAllocations;
import org.slf4j.Logger;
import com.google.common.collect.ImmutableList;
import static org.slf4j.LoggerFactory.getLogger;
@Component(immediate = true)
public class PathIntentFlowObjectiveCompiler
extends PathCompiler<Objective>
implements IntentCompiler<PathIntent>,
PathCompiler.PathCompilerCreateFlow<Objective> {
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
public void activate() {
appId = coreService.registerApplication("org.onosproject.net.intent");
//intentManager.registerCompiler(PathIntent.class, this);
}
@Deactivate
public void deactivate() {
//intentManager.unregisterCompiler(PathIntent.class);
}
@Override
public List<Intent> compile(PathIntent intent, List<Intent> installable,
Set<LinkResourceAllocations> resources) {
List<Objective> objectives = new LinkedList<>();
List<DeviceId> devices = new LinkedList<>();
compile(this, intent, objectives, devices);
return ImmutableList.of(new FlowObjectiveIntent(appId, devices, objectives, intent.resources()));
}
@Override
public Logger log() {
return log;
}
@Override
public ResourceService resourceService() {
return resourceService;
}
@Override
public void createFlow(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
ConnectPoint ingress, ConnectPoint egress,
int priority, boolean applyTreatment,
List<Objective> objectives,
List<DeviceId> devices) {
TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector)
.matchInPort(ingress.port())
.build();
TrafficTreatment.Builder treatmentBuilder;
if (applyTreatment) {
treatmentBuilder = DefaultTrafficTreatment.builder(originalTreatment);
} else {
treatmentBuilder = DefaultTrafficTreatment.builder();
}
TrafficTreatment treatment = treatmentBuilder.setOutput(egress.port()).build();
objectives.add(DefaultForwardingObjective.builder()
.withSelector(selector)
.withTreatment(treatment)
.withPriority(priority)
.fromApp(appId)
.makePermanent()
.withFlag(ForwardingObjective.Flag.SPECIFIC)
.add());
devices.add(ingress.deviceId());
}
}