2 * Copyright 2015 Open Networking Laboratory
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.onosproject.net.intent.impl.compiler;
18 import com.google.common.collect.Sets;
20 import org.apache.felix.scr.annotations.Activate;
21 import org.apache.felix.scr.annotations.Component;
22 import org.apache.felix.scr.annotations.Deactivate;
23 import org.apache.felix.scr.annotations.Reference;
24 import org.apache.felix.scr.annotations.ReferenceCardinality;
25 import org.onlab.packet.EthType;
26 import org.onlab.packet.Ethernet;
27 import org.onlab.packet.MplsLabel;
28 import org.onlab.packet.VlanId;
29 import org.onosproject.core.ApplicationId;
30 import org.onosproject.core.CoreService;
31 import org.onosproject.net.ConnectPoint;
32 import org.onosproject.net.DeviceId;
33 import org.onosproject.net.Link;
34 import org.onosproject.net.LinkKey;
35 import org.onosproject.net.PortNumber;
36 import org.onosproject.net.flow.DefaultFlowRule;
37 import org.onosproject.net.flow.DefaultTrafficSelector;
38 import org.onosproject.net.flow.DefaultTrafficTreatment;
39 import org.onosproject.net.flow.FlowRule;
40 import org.onosproject.net.flow.TrafficSelector;
41 import org.onosproject.net.flow.TrafficTreatment;
42 import org.onosproject.net.flow.criteria.Criterion;
43 import org.onosproject.net.flow.criteria.EthTypeCriterion;
44 import org.onosproject.net.flow.instructions.Instruction;
45 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
46 import org.onosproject.net.intent.FlowRuleIntent;
47 import org.onosproject.net.intent.Intent;
48 import org.onosproject.net.intent.IntentCompiler;
49 import org.onosproject.net.intent.IntentExtensionService;
50 import org.onosproject.net.intent.MplsPathIntent;
51 import org.onosproject.net.newresource.ResourcePath;
52 import org.onosproject.net.newresource.ResourceService;
53 import org.onosproject.net.resource.link.LinkResourceAllocations;
54 import org.slf4j.Logger;
56 import java.util.Collections;
57 import java.util.HashMap;
58 import java.util.Iterator;
59 import java.util.LinkedList;
60 import java.util.List;
62 import java.util.Optional;
64 import java.util.stream.Collectors;
66 import static com.google.common.base.Preconditions.checkNotNull;
67 import static org.onosproject.net.LinkKey.linkKey;
68 import static org.slf4j.LoggerFactory.getLogger;
70 @Component(immediate = true)
71 public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> {
73 private final Logger log = getLogger(getClass());
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected IntentExtensionService intentExtensionService;
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected CoreService coreService;
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected ResourceService resourceService;
84 protected ApplicationId appId;
87 public List<Intent> compile(MplsPathIntent intent, List<Intent> installable,
88 Set<LinkResourceAllocations> resources) {
89 Map<LinkKey, MplsLabel> labels = assignMplsLabel(intent);
90 List<FlowRule> rules = generateRules(intent, labels);
92 return Collections.singletonList(new FlowRuleIntent(appId, rules, intent.resources()));
96 public void activate() {
97 appId = coreService.registerApplication("org.onosproject.net.intent");
98 intentExtensionService.registerCompiler(MplsPathIntent.class, this);
102 public void deactivate() {
103 intentExtensionService.unregisterCompiler(MplsPathIntent.class);
106 private Map<LinkKey, MplsLabel> assignMplsLabel(MplsPathIntent intent) {
107 // TODO: do it better... Suggestions?
108 Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
109 .links().size() - 2);
110 for (int i = 1; i <= intent.path().links().size() - 2; i++) {
111 LinkKey link = linkKey(intent.path().links().get(i));
112 linkRequest.add(link);
113 // add the inverse link. I want that the label is reserved both for
114 // the direct and inverse link
115 linkRequest.add(linkKey(link.dst(), link.src()));
118 Map<LinkKey, MplsLabel> labels = findMplsLabels(linkRequest);
119 if (labels.isEmpty()) {
120 return Collections.emptyMap();
123 List<ResourcePath> resources = labels.entrySet().stream()
124 .map(x -> ResourcePath.discrete(linkKey(x.getKey().src(), x.getKey().src()), x.getValue()))
125 .collect(Collectors.toList());
126 List<org.onosproject.net.newresource.ResourceAllocation> allocations =
127 resourceService.allocate(intent.id(), resources);
128 if (allocations.isEmpty()) {
129 Collections.emptyMap();
135 private Map<LinkKey, MplsLabel> findMplsLabels(Set<LinkKey> links) {
136 Map<LinkKey, MplsLabel> labels = new HashMap<>();
137 for (LinkKey link : links) {
138 Optional<MplsLabel> label = findMplsLabel(link);
139 if (label.isPresent()) {
140 labels.put(link, label.get());
147 private Optional<MplsLabel> findMplsLabel(LinkKey link) {
148 return resourceService.getAvailableResources(ResourcePath.discrete(link)).stream()
149 .filter(x -> x.last() instanceof MplsLabel)
150 .map(x -> (MplsLabel) x.last())
154 private MplsLabel getMplsLabel(Map<LinkKey, MplsLabel> labels, LinkKey link) {
155 return labels.get(link);
158 private List<FlowRule> generateRules(MplsPathIntent intent,
159 Map<LinkKey, MplsLabel> labels) {
161 Iterator<Link> links = intent.path().links().iterator();
162 Link srcLink = links.next();
163 ConnectPoint prev = srcLink.dst();
165 Link link = links.next();
166 // List of flow rules to be installed
167 List<FlowRule> rules = new LinkedList<>();
170 // Get the new MPLS label
171 MplsLabel mpls = getMplsLabel(labels, linkKey(link));
173 MplsLabel prevLabel = mpls;
174 rules.add(ingressFlow(prev.port(), link, intent, mpls));
178 while (links.hasNext()) {
182 if (links.hasNext()) {
184 // Get the new MPLS label
185 mpls = getMplsLabel(labels, linkKey(link));
187 rules.add(transitFlow(prev.port(), link, intent,
193 rules.add(egressFlow(prev.port(), link, intent,
202 private FlowRule ingressFlow(PortNumber inPort, Link link,
203 MplsPathIntent intent,
206 TrafficSelector.Builder ingressSelector = DefaultTrafficSelector
207 .builder(intent.selector());
208 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
209 ingressSelector.matchInPort(inPort);
211 if (intent.ingressLabel().isPresent()) {
212 ingressSelector.matchEthType(Ethernet.MPLS_UNICAST)
213 .matchMplsLabel(intent.ingressLabel().get());
215 // Swap the MPLS label
216 treat.setMpls(label);
218 // Push and set the MPLS label
219 treat.pushMpls().setMpls(label);
221 // Add the output action
222 treat.setOutput(link.src().port());
224 return createFlowRule(intent, link.src().deviceId(), ingressSelector.build(), treat.build());
227 private FlowRule transitFlow(PortNumber inPort, Link link,
228 MplsPathIntent intent,
230 MplsLabel outLabel) {
232 // Ignore the ingress Traffic Selector and use only the MPLS label
233 // assigned in the previous link
234 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
235 selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST)
236 .matchMplsLabel(prevLabel);
237 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
239 // Set the new label only if the label on the packet is
241 if (!prevLabel.equals(outLabel)) {
242 treat.setMpls(outLabel);
245 treat.setOutput(link.src().port());
246 return createFlowRule(intent, link.src().deviceId(), selector.build(), treat.build());
249 private FlowRule egressFlow(PortNumber inPort, Link link,
250 MplsPathIntent intent,
251 MplsLabel prevLabel) {
252 // egress point: either set the egress MPLS label or pop the
253 // MPLS label based on the intent annotations
255 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
256 selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST)
257 .matchMplsLabel(prevLabel);
259 // apply the intent's treatments
260 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder(intent
263 // check if the treatement is popVlan or setVlan (rewrite),
264 // than selector needs to match any VlanId
265 for (Instruction instruct : intent.treatment().allInstructions()) {
266 if (instruct instanceof L2ModificationInstruction) {
267 L2ModificationInstruction l2Mod = (L2ModificationInstruction) instruct;
268 if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
271 if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP ||
272 l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
273 selector.matchVlanId(VlanId.ANY);
278 if (intent.egressLabel().isPresent()) {
279 treat.setMpls(intent.egressLabel().get());
281 treat.popMpls(outputEthType(intent.selector()));
283 treat.setOutput(link.src().port());
284 return createFlowRule(intent, link.src().deviceId(),
285 selector.build(), treat.build());
288 protected FlowRule createFlowRule(MplsPathIntent intent, DeviceId deviceId,
289 TrafficSelector selector, TrafficTreatment treat) {
290 return DefaultFlowRule.builder()
292 .withSelector(selector)
293 .withTreatment(treat)
294 .withPriority(intent.priority())
300 // if the ingress ethertype is defined, the egress traffic
301 // will be use that value, otherwise the IPv4 ethertype is used.
302 private EthType outputEthType(TrafficSelector selector) {
303 Criterion c = selector.getCriterion(Criterion.Type.ETH_TYPE);
304 if (c != null && c instanceof EthTypeCriterion) {
305 EthTypeCriterion ethertype = (EthTypeCriterion) c;
306 return ethertype.ethType();
308 return EthType.EtherType.IPV4.ethType();