Michele Santuari
Committed by Gerrit Code Review

Add MPLS encapsulation behaviour ONOS-3467

- MPLS encapsulation using constraint
- New MPLS encapsulation test
- Fix VLAN encapsulation test

Change-Id: I94670bcd51a95a0272f786681e51d6785a56c4f5
...@@ -31,7 +31,11 @@ import java.util.Optional; ...@@ -31,7 +31,11 @@ import java.util.Optional;
31 31
32 /** 32 /**
33 * Installs MPLS intents. 33 * Installs MPLS intents.
34 + *
35 + * @deprecated in Goldeneye Release, in favour of encapsulation
36 + * constraint {@link org.onosproject.net.intent.constraint.EncapsulationConstraint}
34 */ 37 */
38 +@Deprecated
35 @Command(scope = "onos", name = "add-mpls-intent", description = "Installs mpls connectivity intent") 39 @Command(scope = "onos", name = "add-mpls-intent", description = "Installs mpls connectivity intent")
36 public class AddMplsIntent extends ConnectivityIntentCommand { 40 public class AddMplsIntent extends ConnectivityIntentCommand {
37 41
......
...@@ -34,8 +34,14 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -34,8 +34,14 @@ import static com.google.common.base.Preconditions.checkNotNull;
34 34
35 /** 35 /**
36 * Abstraction of MPLS label-switched connectivity. 36 * Abstraction of MPLS label-switched connectivity.
37 + *
38 + * @deprecated in Goldeneye Release, in favour of encapsulation
39 + * constraint {@link org.onosproject.net.intent.constraint.EncapsulationConstraint}
40 + * with Encasulation type {@link org.onosproject.net.EncapsulationType} MPLS.
41 + *
37 */ 42 */
38 @Beta 43 @Beta
44 +@Deprecated
39 public final class MplsIntent extends ConnectivityIntent { 45 public final class MplsIntent extends ConnectivityIntent {
40 46
41 private final ConnectPoint ingressPoint; 47 private final ConnectPoint ingressPoint;
......
...@@ -30,8 +30,12 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -30,8 +30,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
30 30
31 /** 31 /**
32 * Abstraction of explicit MPLS label-switched path. 32 * Abstraction of explicit MPLS label-switched path.
33 + *
34 + * @deprecated in Goldeneye Release, in favour of encapsulation
35 + * constraint {@link org.onosproject.net.intent.constraint.EncapsulationConstraint}
33 */ 36 */
34 @Beta 37 @Beta
38 +@Deprecated
35 public final class MplsPathIntent extends PathIntent { 39 public final class MplsPathIntent extends PathIntent {
36 40
37 private final Optional<MplsLabel> ingressLabel; 41 private final Optional<MplsLabel> ingressLabel;
......
...@@ -36,7 +36,11 @@ import org.onosproject.net.intent.MplsPathIntent; ...@@ -36,7 +36,11 @@ import org.onosproject.net.intent.MplsPathIntent;
36 import org.onosproject.net.provider.ProviderId; 36 import org.onosproject.net.provider.ProviderId;
37 import org.onosproject.net.resource.link.LinkResourceAllocations; 37 import org.onosproject.net.resource.link.LinkResourceAllocations;
38 38
39 - 39 +/**
40 + * @deprecated in Goldeneye Release, in favour of encapsulation
41 + * constraint {@link org.onosproject.net.intent.constraint.EncapsulationConstraint}
42 + */
43 +@Deprecated
40 @Component(immediate = true) 44 @Component(immediate = true)
41 public class MplsIntentCompiler extends ConnectivityIntentCompiler<MplsIntent> { 45 public class MplsIntentCompiler extends ConnectivityIntentCompiler<MplsIntent> {
42 46
......
...@@ -68,6 +68,11 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -68,6 +68,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
68 import static org.onosproject.net.LinkKey.linkKey; 68 import static org.onosproject.net.LinkKey.linkKey;
69 import static org.slf4j.LoggerFactory.getLogger; 69 import static org.slf4j.LoggerFactory.getLogger;
70 70
71 +/**
72 + * @deprecated in Goldeneye Release, in favour of encapsulation
73 + * constraint {@link org.onosproject.net.intent.constraint.EncapsulationConstraint}
74 + */
75 +@Deprecated
71 @Component(immediate = true) 76 @Component(immediate = true)
72 public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> { 77 public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> {
73 78
......
...@@ -25,6 +25,9 @@ import java.util.Set; ...@@ -25,6 +25,9 @@ import java.util.Set;
25 import java.util.stream.Collectors; 25 import java.util.stream.Collectors;
26 import java.util.stream.Stream; 26 import java.util.stream.Stream;
27 27
28 +import org.onlab.packet.EthType;
29 +import org.onlab.packet.Ethernet;
30 +import org.onlab.packet.MplsLabel;
28 import org.onlab.packet.VlanId; 31 import org.onlab.packet.VlanId;
29 import org.onosproject.net.ConnectPoint; 32 import org.onosproject.net.ConnectPoint;
30 import org.onosproject.net.DeviceId; 33 import org.onosproject.net.DeviceId;
...@@ -35,12 +38,16 @@ import org.onosproject.net.flow.DefaultTrafficTreatment; ...@@ -35,12 +38,16 @@ import org.onosproject.net.flow.DefaultTrafficTreatment;
35 import org.onosproject.net.flow.TrafficSelector; 38 import org.onosproject.net.flow.TrafficSelector;
36 import org.onosproject.net.flow.TrafficTreatment; 39 import org.onosproject.net.flow.TrafficTreatment;
37 import org.onosproject.net.flow.criteria.Criterion; 40 import org.onosproject.net.flow.criteria.Criterion;
41 +import org.onosproject.net.flow.criteria.EthTypeCriterion;
42 +import org.onosproject.net.flow.criteria.MplsCriterion;
38 import org.onosproject.net.flow.criteria.VlanIdCriterion; 43 import org.onosproject.net.flow.criteria.VlanIdCriterion;
44 +import org.onosproject.net.flow.instructions.Instruction;
39 import org.onosproject.net.flow.instructions.L2ModificationInstruction; 45 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
40 import org.onosproject.net.intent.PathIntent; 46 import org.onosproject.net.intent.PathIntent;
41 import org.onosproject.net.intent.constraint.EncapsulationConstraint; 47 import org.onosproject.net.intent.constraint.EncapsulationConstraint;
42 import org.onosproject.net.intent.impl.IntentCompilationException; 48 import org.onosproject.net.intent.impl.IntentCompilationException;
43 import org.onosproject.net.newresource.Resource; 49 import org.onosproject.net.newresource.Resource;
50 +import org.onosproject.net.newresource.ResourceAllocation;
44 import org.onosproject.net.newresource.ResourceService; 51 import org.onosproject.net.newresource.ResourceService;
45 import org.onosproject.net.newresource.Resources; 52 import org.onosproject.net.newresource.Resources;
46 import org.slf4j.Logger; 53 import org.slf4j.Logger;
...@@ -227,6 +234,185 @@ public class PathCompiler<T> { ...@@ -227,6 +234,185 @@ public class PathCompiler<T> {
227 } 234 }
228 } 235 }
229 236
237 + private Map<LinkKey, MplsLabel> assignMplsLabel(PathCompilerCreateFlow creator, PathIntent intent) {
238 + Set<LinkKey> linkRequest =
239 + Sets.newHashSetWithExpectedSize(intent.path()
240 + .links().size() - 2);
241 + for (int i = 1; i <= intent.path().links().size() - 2; i++) {
242 + LinkKey link = linkKey(intent.path().links().get(i));
243 + linkRequest.add(link);
244 + // add the inverse link. I want that the VLANID is reserved both for
245 + // the direct and inverse link
246 + linkRequest.add(linkKey(link.dst(), link.src()));
247 + }
248 +
249 + Map<LinkKey, MplsLabel> labels = findMplsLabels(creator, linkRequest);
250 + if (labels.isEmpty()) {
251 + throw new IntentCompilationException("No available MPLS Label");
252 + }
253 +
254 + // for short term solution: same label is used for both directions
255 + // TODO: introduce the concept of Tx and Rx resources of a port
256 + Set<Resource> resources = labels.entrySet().stream()
257 + .flatMap(x -> Stream.of(
258 + Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
259 + .resource(),
260 + Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
261 + .resource()
262 + ))
263 + .collect(Collectors.toSet());
264 + List<ResourceAllocation> allocations =
265 + creator.resourceService().allocate(intent.id(), ImmutableList.copyOf(resources));
266 + if (allocations.isEmpty()) {
267 + Collections.emptyMap();
268 + }
269 +
270 + return labels;
271 + }
272 +
273 + private Map<LinkKey, MplsLabel> findMplsLabels(PathCompilerCreateFlow creator, Set<LinkKey> links) {
274 + Map<LinkKey, MplsLabel> labels = new HashMap<>();
275 + for (LinkKey link : links) {
276 + Set<MplsLabel> forward = findMplsLabel(creator, link.src());
277 + Set<MplsLabel> backward = findMplsLabel(creator, link.dst());
278 + Set<MplsLabel> common = Sets.intersection(forward, backward);
279 + if (common.isEmpty()) {
280 + continue;
281 + }
282 + labels.put(link, common.iterator().next());
283 + }
284 +
285 + return labels;
286 + }
287 +
288 + private Set<MplsLabel> findMplsLabel(PathCompilerCreateFlow creator, ConnectPoint cp) {
289 + return creator.resourceService().getAvailableResourceValues(
290 + Resources.discrete(cp.deviceId(), cp.port()).id(),
291 + MplsLabel.class);
292 + }
293 +
294 + private void manageMplsEncap(PathCompilerCreateFlow<T> creator, List<T> flows,
295 + List<DeviceId> devices,
296 + PathIntent intent) {
297 + Map<LinkKey, MplsLabel> mplsLabels = assignMplsLabel(creator, intent);
298 +
299 + Iterator<Link> links = intent.path().links().iterator();
300 + Link srcLink = links.next();
301 +
302 + Link link = links.next();
303 + // List of flow rules to be installed
304 +
305 + // Ingress traffic
306 + MplsLabel mplsLabel = mplsLabels.get(linkKey(link));
307 + if (mplsLabel == null) {
308 + throw new IntentCompilationException("No available MPLS Label for " + link);
309 + }
310 + MplsLabel prevMplsLabel = mplsLabel;
311 +
312 + Optional<MplsCriterion> mplsCriterion = intent.selector().criteria()
313 + .stream().filter(criterion -> criterion.type() == Criterion.Type.MPLS_LABEL)
314 + .map(criterion -> (MplsCriterion) criterion)
315 + .findAny();
316 +
317 + //Push MPLS if selector does not include MPLS
318 + TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
319 + if (!mplsCriterion.isPresent()) {
320 + treatBuilder.pushMpls();
321 + }
322 + //Tag the traffic with the new encapsulation MPLS label
323 + treatBuilder.setMpls(mplsLabel);
324 + creator.createFlow(intent.selector(), treatBuilder.build(),
325 + srcLink.dst(), link.src(), intent.priority(), true, flows, devices);
326 +
327 + ConnectPoint prev = link.dst();
328 +
329 + while (links.hasNext()) {
330 +
331 + link = links.next();
332 +
333 + if (links.hasNext()) {
334 + // Transit traffic
335 + MplsLabel transitMplsLabel = mplsLabels.get(linkKey(link));
336 + if (transitMplsLabel == null) {
337 + throw new IntentCompilationException("No available MPLS label for " + link);
338 + }
339 + prevMplsLabel = transitMplsLabel;
340 +
341 + TrafficSelector transitSelector = DefaultTrafficSelector.builder()
342 + .matchInPort(prev.port())
343 + .matchEthType(Ethernet.MPLS_UNICAST)
344 + .matchMplsLabel(prevMplsLabel).build();
345 +
346 + TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
347 +
348 + // Set the new MPLS Label only if the previous one is different
349 + if (!prevMplsLabel.equals(transitMplsLabel)) {
350 + transitTreat.setMpls(transitMplsLabel);
351 + }
352 + creator.createFlow(transitSelector,
353 + transitTreat.build(), prev, link.src(), intent.priority(), true, flows, devices);
354 + prev = link.dst();
355 + } else {
356 + TrafficSelector.Builder egressSelector = DefaultTrafficSelector.builder()
357 + .matchInPort(prev.port())
358 + .matchEthType(Ethernet.MPLS_UNICAST)
359 + .matchMplsLabel(prevMplsLabel);
360 + TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
361 +
362 + // Egress traffic
363 + // check if the treatement is popVlan or setVlan (rewrite),
364 + // than selector needs to match any VlanId
365 + for (Instruction instruct : intent.treatment().allInstructions()) {
366 + if (instruct instanceof L2ModificationInstruction) {
367 + L2ModificationInstruction l2Mod = (L2ModificationInstruction) instruct;
368 + if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
369 + break;
370 + }
371 + if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP ||
372 + l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
373 + egressSelector.matchVlanId(VlanId.ANY);
374 + }
375 + }
376 + }
377 +
378 + if (mplsCriterion.isPresent()) {
379 + egressTreat.setMpls(mplsCriterion.get().label());
380 + } else {
381 + egressTreat.popMpls(outputEthType(intent.selector()));
382 + }
383 +
384 +
385 + if (mplsCriterion.isPresent()) {
386 + egressTreat.setMpls(mplsCriterion.get().label());
387 + } else {
388 + egressTreat.popVlan();
389 + }
390 +
391 + creator.createFlow(egressSelector.build(),
392 + egressTreat.build(), prev, link.src(), intent.priority(), true, flows, devices);
393 + }
394 +
395 + }
396 +
397 + }
398 +
399 + private MplsLabel getMplsLabel(Map<LinkKey, MplsLabel> labels, LinkKey link) {
400 + return labels.get(link);
401 + }
402 +
403 + // if the ingress ethertype is defined, the egress traffic
404 + // will be use that value, otherwise the IPv4 ethertype is used.
405 + private EthType outputEthType(TrafficSelector selector) {
406 + Criterion c = selector.getCriterion(Criterion.Type.ETH_TYPE);
407 + if (c != null && c instanceof EthTypeCriterion) {
408 + EthTypeCriterion ethertype = (EthTypeCriterion) c;
409 + return ethertype.ethType();
410 + } else {
411 + return EthType.EtherType.IPV4.ethType();
412 + }
413 + }
414 +
415 +
230 /** 416 /**
231 * Compiles an intent down to flows. 417 * Compiles an intent down to flows.
232 * 418 *
...@@ -263,7 +449,10 @@ public class PathCompiler<T> { ...@@ -263,7 +449,10 @@ public class PathCompiler<T> {
263 switch (type) { 449 switch (type) {
264 case VLAN: 450 case VLAN:
265 manageVlanEncap(creator, flows, devices, intent); 451 manageVlanEncap(creator, flows, devices, intent);
266 - // TODO: implement MPLS case here 452 + break;
453 + case MPLS:
454 + manageMplsEncap(creator, flows, devices, intent);
455 + break;
267 default: 456 default:
268 // Nothing to do 457 // Nothing to do
269 } 458 }
......
...@@ -15,10 +15,7 @@ ...@@ -15,10 +15,7 @@
15 */ 15 */
16 package org.onosproject.net.intent.impl.compiler; 16 package org.onosproject.net.intent.impl.compiler;
17 17
18 -import java.util.LinkedList; 18 +import com.google.common.collect.ImmutableList;
19 -import java.util.List;
20 -import java.util.Set;
21 -
22 import org.apache.felix.scr.annotations.Activate; 19 import org.apache.felix.scr.annotations.Activate;
23 import org.apache.felix.scr.annotations.Component; 20 import org.apache.felix.scr.annotations.Component;
24 import org.apache.felix.scr.annotations.Deactivate; 21 import org.apache.felix.scr.annotations.Deactivate;
...@@ -42,10 +39,13 @@ import org.onosproject.net.newresource.ResourceService; ...@@ -42,10 +39,13 @@ import org.onosproject.net.newresource.ResourceService;
42 import org.onosproject.net.resource.link.LinkResourceAllocations; 39 import org.onosproject.net.resource.link.LinkResourceAllocations;
43 import org.slf4j.Logger; 40 import org.slf4j.Logger;
44 41
45 -import com.google.common.collect.ImmutableList; 42 +import java.util.LinkedList;
43 +import java.util.List;
44 +import java.util.Set;
46 45
47 import static org.slf4j.LoggerFactory.getLogger; 46 import static org.slf4j.LoggerFactory.getLogger;
48 47
48 +
49 @Component(immediate = true) 49 @Component(immediate = true)
50 public class PathIntentCompiler 50 public class PathIntentCompiler
51 extends PathCompiler<FlowRule> 51 extends PathCompiler<FlowRule>
...@@ -84,6 +84,7 @@ public class PathIntentCompiler ...@@ -84,6 +84,7 @@ public class PathIntentCompiler
84 List<DeviceId> devices = new LinkedList<>(); 84 List<DeviceId> devices = new LinkedList<>();
85 compile(this, intent, rules, devices); 85 compile(this, intent, rules, devices);
86 86
87 +
87 return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources())); 88 return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
88 } 89 }
89 90
...@@ -97,12 +98,14 @@ public class PathIntentCompiler ...@@ -97,12 +98,14 @@ public class PathIntentCompiler
97 return resourceService; 98 return resourceService;
98 } 99 }
99 100
101 +
100 @Override 102 @Override
101 public void createFlow(TrafficSelector originalSelector, TrafficTreatment originalTreatment, 103 public void createFlow(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
102 ConnectPoint ingress, ConnectPoint egress, 104 ConnectPoint ingress, ConnectPoint egress,
103 int priority, boolean applyTreatment, 105 int priority, boolean applyTreatment,
104 List<FlowRule> rules, 106 List<FlowRule> rules,
105 List<DeviceId> devices) { 107 List<DeviceId> devices) {
108 +
106 TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector) 109 TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector)
107 .matchInPort(ingress.port()) 110 .matchInPort(ingress.port())
108 .build(); 111 .build();
...@@ -123,5 +126,6 @@ public class PathIntentCompiler ...@@ -123,5 +126,6 @@ public class PathIntentCompiler
123 .fromApp(appId) 126 .fromApp(appId)
124 .makePermanent() 127 .makePermanent()
125 .build()); 128 .build());
129 +
126 } 130 }
127 } 131 }
......