blob: bb38fa5b28e85da1e9e7fac7367413288b875222 [file] [log] [blame]
package org.onosproject.net.intent.impl.installer;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
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.Ethernet;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
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.FlowRuleOperation;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criteria.EthTypeCriterion;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.Criterion.Type;
import org.onosproject.net.intent.IntentExtensionService;
import org.onosproject.net.intent.IntentInstaller;
import org.onosproject.net.intent.MplsPathIntent;
import org.onosproject.net.link.LinkStore;
import org.onosproject.net.resource.DefaultLinkResourceRequest;
import org.onosproject.net.resource.LinkResourceAllocations;
import org.onosproject.net.resource.LinkResourceRequest;
import org.onosproject.net.resource.LinkResourceService;
import org.onosproject.net.resource.MplsLabel;
import org.onosproject.net.resource.MplsLabelResourceAllocation;
import org.onosproject.net.resource.ResourceAllocation;
import org.onosproject.net.resource.ResourceType;
import org.slf4j.Logger;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Installer for {@link MplsPathIntent packet path connectivity intents}.
*/
@Component(immediate = true)
public class MplsPathIntentInstaller implements IntentInstaller<MplsPathIntent> {
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentExtensionService intentManager;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkResourceService resourceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkStore linkStore;
protected ApplicationId appId;
@Activate
public void activate() {
appId = coreService.registerApplication("org.onosproject.net.intent");
intentManager.registerInstaller(MplsPathIntent.class, this);
}
@Deactivate
public void deactivate() {
intentManager.unregisterInstaller(MplsPathIntent.class);
}
@Override
public List<Collection<FlowRuleOperation>> install(MplsPathIntent intent) {
LinkResourceAllocations allocations = assignMplsLabel(intent);
return generateRules(intent, allocations, FlowRuleOperation.Type.ADD);
}
@Override
public List<Collection<FlowRuleOperation>> uninstall(MplsPathIntent intent) {
LinkResourceAllocations allocations = resourceService
.getAllocations(intent.id());
resourceService.releaseResources(allocations);
return generateRules(intent, allocations, FlowRuleOperation.Type.REMOVE);
}
@Override
public List<Collection<FlowRuleOperation>> replace(MplsPathIntent oldIntent,
MplsPathIntent newIntent) {
//FIXME this is brute force
List<Collection<FlowRuleOperation>> batches = Lists.newArrayList();
batches.addAll(uninstall(oldIntent));
batches.addAll(install(newIntent));
return batches;
}
private LinkResourceAllocations assignMplsLabel(MplsPathIntent intent) {
// TODO: do it better... Suggestions?
Set<Link> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
.links().size() - 2);
for (int i = 1; i <= intent.path().links().size() - 2; i++) {
Link link = intent.path().links().get(i);
linkRequest.add(link);
// add the inverse link. I want that the label is reserved both for
// the direct and inverse link
linkRequest.add(linkStore.getLink(link.dst(), link.src()));
}
LinkResourceRequest.Builder request = DefaultLinkResourceRequest
.builder(intent.id(), linkRequest).addMplsRequest();
LinkResourceAllocations reqMpls = resourceService
.requestResources(request.build());
return reqMpls;
}
private MplsLabel getMplsLabel(LinkResourceAllocations allocations,
Link link) {
for (ResourceAllocation allocation : allocations
.getResourceAllocation(link)) {
if (allocation.type() == ResourceType.MPLS_LABEL) {
return ((MplsLabelResourceAllocation) allocation).mplsLabel();
}
}
log.warn("MPLS label was not assigned successfully");
return null;
}
private List<Collection<FlowRuleOperation>> generateRules(MplsPathIntent intent,
LinkResourceAllocations allocations,
FlowRuleOperation.Type operation) {
Iterator<Link> links = intent.path().links().iterator();
Link srcLink = links.next();
ConnectPoint prev = srcLink.dst();
Link link = links.next();
// List of flow rules to be installed
List<FlowRuleOperation> rules = Lists.newLinkedList();
// Ingress traffic
// Get the new MPLS label
MplsLabel mpls = getMplsLabel(allocations, link);
checkNotNull(mpls);
MplsLabel prevLabel = mpls;
rules.add(ingressFlow(prev.port(), link, intent, mpls, operation));
prev = link.dst();
while (links.hasNext()) {
link = links.next();
if (links.hasNext()) {
// Transit traffic
// Get the new MPLS label
mpls = getMplsLabel(allocations, link);
checkNotNull(mpls);
rules.add(transitFlow(prev.port(), link, intent,
prevLabel, mpls, operation));
prevLabel = mpls;
} else {
// Egress traffic
rules.add(egressFlow(prev.port(), link, intent,
prevLabel, operation));
}
prev = link.dst();
}
return Lists.newArrayList(ImmutableSet.of(rules));
}
private FlowRuleOperation ingressFlow(PortNumber inPort, Link link,
MplsPathIntent intent,
MplsLabel label,
FlowRuleOperation.Type operation) {
TrafficSelector.Builder ingressSelector = DefaultTrafficSelector
.builder(intent.selector());
TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
ingressSelector.matchInPort(inPort);
if (intent.ingressLabel().isPresent()) {
ingressSelector.matchEthType(Ethernet.MPLS_UNICAST)
.matchMplsLabel(intent.ingressLabel().get());
// Swap the MPLS label
treat.setMpls(label.label());
} else {
// Push and set the MPLS label
treat.pushMpls().setMpls(label.label());
}
// Add the output action
treat.setOutput(link.src().port());
return flowRuleOperation(intent, link.src().deviceId(),
ingressSelector.build(), treat.build(),
operation);
}
private FlowRuleOperation transitFlow(PortNumber inPort, Link link,
MplsPathIntent intent,
MplsLabel prevLabel,
MplsLabel outLabel,
FlowRuleOperation.Type operation) {
// Ignore the ingress Traffic Selector and use only the MPLS label
// assigned in the previous link
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST)
.matchMplsLabel(prevLabel.label());
TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
// Set the new label only if the label on the packet is
// different
if (prevLabel.equals(outLabel)) {
treat.setMpls(outLabel.label());
}
treat.setOutput(link.src().port());
return flowRuleOperation(intent, link.src().deviceId(),
selector.build(), treat.build(), operation);
}
private FlowRuleOperation egressFlow(PortNumber inPort, Link link,
MplsPathIntent intent,
MplsLabel prevLabel,
FlowRuleOperation.Type operation) {
// egress point: either set the egress MPLS label or pop the
// MPLS label based on the intent annotations
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST)
.matchMplsLabel(prevLabel.label());
// apply the intent's treatments
TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder(intent
.treatment());
if (intent.egressLabel().isPresent()) {
treat.setMpls(intent.egressLabel().get());
} else {
// if the ingress ethertype is defined, the egress traffic
// will be use that value, otherwise the IPv4 ethertype is used.
Criterion c = intent.selector().getCriterion(Type.ETH_TYPE);
if (c != null && c instanceof EthTypeCriterion) {
EthTypeCriterion ethertype = (EthTypeCriterion) c;
treat.popMpls((short) ethertype.ethType());
} else {
treat.popMpls(Ethernet.TYPE_IPV4);
}
}
treat.setOutput(link.src().port());
return flowRuleOperation(intent, link.src().deviceId(),
selector.build(), treat.build(), operation);
}
protected FlowRuleOperation flowRuleOperation(MplsPathIntent intent,
DeviceId deviceId,
TrafficSelector selector,
TrafficTreatment treat,
FlowRuleOperation.Type operation) {
FlowRule rule = new DefaultFlowRule(
deviceId,
selector,
treat,
intent.priority(),
appId,
0,
true);
return new FlowRuleOperation(rule, operation);
}
}