blob: 7d6edcbfb73c4599c6dda42324274bd37b8a031e [file] [log] [blame]
Sho SHIMIZUee2aa652015-02-25 18:56:43 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16package org.onosproject.net.intent.impl.compiler;
17
Michele Santuari69fc2ff2015-12-03 17:05:35 +010018import com.google.common.collect.ImmutableList;
19import com.google.common.collect.Sets;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010025import org.onlab.packet.VlanId;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080026import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
28import org.onosproject.net.ConnectPoint;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010029import org.onosproject.net.EncapsulationType;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080030import org.onosproject.net.Link;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010031import org.onosproject.net.LinkKey;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080032import org.onosproject.net.flow.DefaultFlowRule;
33import org.onosproject.net.flow.DefaultTrafficSelector;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.FlowRule;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.flow.TrafficTreatment;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010038import org.onosproject.net.flow.criteria.Criterion;
39import org.onosproject.net.flow.criteria.VlanIdCriterion;
Michele Santuari3ea53902016-02-15 11:21:56 +010040import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080041import org.onosproject.net.intent.FlowRuleIntent;
42import org.onosproject.net.intent.Intent;
43import org.onosproject.net.intent.IntentCompiler;
44import org.onosproject.net.intent.IntentExtensionService;
45import org.onosproject.net.intent.PathIntent;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010046import org.onosproject.net.intent.constraint.EncapsulationConstraint;
47import org.onosproject.net.intent.impl.IntentCompilationException;
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -080048import org.onosproject.net.newresource.Resource;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010049import org.onosproject.net.newresource.ResourceService;
Sho SHIMIZU460b9722016-01-28 10:48:26 -080050import org.onosproject.net.newresource.Resources;
Brian O'Connor6de2e202015-05-21 14:30:41 -070051import org.onosproject.net.resource.link.LinkResourceAllocations;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010052import org.slf4j.Logger;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080053
Sho SHIMIZU98ffca82015-05-11 08:39:24 -070054import java.util.Collections;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010055import java.util.HashMap;
56import java.util.Iterator;
57import java.util.LinkedList;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080058import java.util.List;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010059import java.util.Map;
60import java.util.Optional;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080061import java.util.Set;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010062import java.util.stream.Collectors;
63import java.util.stream.Stream;
64
65import static org.onosproject.net.LinkKey.linkKey;
66import static org.slf4j.LoggerFactory.getLogger;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080067
68@Component(immediate = true)
69public class PathIntentCompiler implements IntentCompiler<PathIntent> {
70
Michele Santuari69fc2ff2015-12-03 17:05:35 +010071 private final Logger log = getLogger(getClass());
72
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080073 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected CoreService coreService;
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected IntentExtensionService intentManager;
78
Michele Santuari69fc2ff2015-12-03 17:05:35 +010079 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected ResourceService resourceService;
81
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080082 private ApplicationId appId;
83
84 @Activate
85 public void activate() {
86 appId = coreService.registerApplication("org.onosproject.net.intent");
87 intentManager.registerCompiler(PathIntent.class, this);
88 }
89
90 @Deactivate
91 public void deactivate() {
92 intentManager.unregisterCompiler(PathIntent.class);
93 }
94
95 @Override
96 public List<Intent> compile(PathIntent intent, List<Intent> installable,
97 Set<LinkResourceAllocations> resources) {
98 // Note: right now recompile is not considered
99 // TODO: implement recompile behavior
100
101 List<Link> links = intent.path().links();
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100102 List<FlowRule> rules = new LinkedList<>();
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800103
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100104 Optional<EncapsulationConstraint> enacpConstraint = intent.constraints().stream()
105 .filter(constraint -> constraint instanceof EncapsulationConstraint)
106 .map(x -> (EncapsulationConstraint) x).findAny();
107 //if no encapsulation or is involved only a single switch use the default behaviour
108 if (!enacpConstraint.isPresent() || links.size() == 1) {
109
110 for (int i = 0; i < links.size() - 1; i++) {
111 ConnectPoint ingress = links.get(i).dst();
112 ConnectPoint egress = links.get(i + 1).src();
113 FlowRule rule = createFlowRule(intent.selector(), intent.treatment(),
114 ingress, egress, intent.priority(),
115 isLast(links, i));
116 rules.add(rule);
117 }
118
119 return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
120
121 } else {
122 if (EncapsulationType.VLAN == enacpConstraint.get().encapType()) {
123 rules = manageVlanEncap(intent);
124 }
125 if (EncapsulationType.MPLS == enacpConstraint.get().encapType()) {
126 //TODO: to be implemented
127 rules = Collections.emptyList();
128 }
129
130 return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800131 }
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800132 }
133
134 private FlowRule createFlowRule(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
Brian O'Connor81134662015-06-25 17:23:33 -0400135 ConnectPoint ingress, ConnectPoint egress,
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100136 int priority, boolean applyTreatment) {
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800137 TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector)
138 .matchInPort(ingress.port())
139 .build();
140
141 TrafficTreatment.Builder treatmentBuilder;
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100142 if (applyTreatment) {
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800143 treatmentBuilder = DefaultTrafficTreatment.builder(originalTreatment);
144 } else {
145 treatmentBuilder = DefaultTrafficTreatment.builder();
146 }
147 TrafficTreatment treatment = treatmentBuilder.setOutput(egress.port()).build();
148
Ray Milkeyd13a37b2015-06-12 11:55:17 -0700149 return DefaultFlowRule.builder()
150 .forDevice(ingress.deviceId())
151 .withSelector(selector)
152 .withTreatment(treatment)
Brian O'Connor81134662015-06-25 17:23:33 -0400153 .withPriority(priority)
Ray Milkeyd13a37b2015-06-12 11:55:17 -0700154 .fromApp(appId)
155 .makePermanent()
156 .build();
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800157 }
158
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100159 private List<FlowRule> manageVlanEncap(PathIntent intent) {
160 Map<LinkKey, VlanId> vlanIds = assignVlanId(intent);
161
162 Iterator<Link> links = intent.path().links().iterator();
163 Link srcLink = links.next();
164
165 Link link = links.next();
166 // List of flow rules to be installed
167 List<FlowRule> rules = new LinkedList<>();
168
169 // Ingress traffic
170 VlanId vlanId = vlanIds.get(linkKey(link));
171 if (vlanId == null) {
172 throw new IntentCompilationException("No available VLAN ID for " + link);
173 }
174 VlanId prevVlanId = vlanId;
175
Michele Santuari0ff59c22016-01-15 10:00:32 +0100176 Optional<VlanIdCriterion> vlanCriterion = intent.selector().criteria()
177 .stream().filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
178 .map(criterion -> (VlanIdCriterion) criterion)
179 .findAny();
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100180
Michele Santuari0ff59c22016-01-15 10:00:32 +0100181 //Push VLAN if selector does not include VLAN
182 TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
183 if (!vlanCriterion.isPresent()) {
184 treatBuilder.pushVlan();
185 }
186 //Tag the traffic with the new encapsulation VLAN
187 treatBuilder.setVlanId(vlanId);
188 rules.add(createFlowRule(intent.selector(), treatBuilder.build(),
189 srcLink.dst(), link.src(), intent.priority(), true));
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100190
191 ConnectPoint prev = link.dst();
192
193 while (links.hasNext()) {
194
195 link = links.next();
196
197 if (links.hasNext()) {
198 // Transit traffic
199 VlanId egressVlanId = vlanIds.get(linkKey(link));
200 if (egressVlanId == null) {
201 throw new IntentCompilationException("No available VLAN ID for " + link);
202 }
203 prevVlanId = egressVlanId;
204
205 TrafficSelector transitSelector = DefaultTrafficSelector.builder()
206 .matchInPort(prev.port())
207 .matchVlanId(prevVlanId).build();
208
209 TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
210
211 // Set the new vlanId only if the previous one is different
212 if (!prevVlanId.equals(egressVlanId)) {
213 transitTreat.setVlanId(egressVlanId);
214 }
215 rules.add(createFlowRule(transitSelector,
216 transitTreat.build(), prev, link.src(), intent.priority(), true));
217 prev = link.dst();
218 } else {
219 // Egress traffic
220 TrafficSelector egressSelector = DefaultTrafficSelector.builder()
221 .matchInPort(prev.port())
222 .matchVlanId(prevVlanId).build();
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100223 TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
Michele Santuari3ea53902016-02-15 11:21:56 +0100224
225 Optional<L2ModificationInstruction.ModVlanIdInstruction> modVlanIdInstruction = intent.treatment()
226 .allInstructions().stream().filter(
227 instruction -> instruction instanceof L2ModificationInstruction.ModVlanIdInstruction)
228 .map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x).findAny();
229
230 Optional<L2ModificationInstruction.PopVlanInstruction> popVlanInstruction = intent.treatment()
231 .allInstructions().stream().filter(
232 instruction -> instruction instanceof L2ModificationInstruction.PopVlanInstruction)
233 .map(x -> (L2ModificationInstruction.PopVlanInstruction) x).findAny();
234
235 if (!modVlanIdInstruction.isPresent() && !popVlanInstruction.isPresent()) {
236 if (vlanCriterion.isPresent()) {
237 egressTreat.setVlanId(vlanCriterion.get().vlanId());
238 } else {
239 egressTreat.popVlan();
240 }
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100241 }
242
243 rules.add(createFlowRule(egressSelector,
244 egressTreat.build(), prev, link.src(), intent.priority(), true));
245 }
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100246 }
247 return rules;
248
249 }
250
251 private Map<LinkKey, VlanId> assignVlanId(PathIntent intent) {
252 Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
253 .links().size() - 2);
254 for (int i = 1; i <= intent.path().links().size() - 2; i++) {
255 LinkKey link = linkKey(intent.path().links().get(i));
256 linkRequest.add(link);
257 // add the inverse link. I want that the VLANID is reserved both for
258 // the direct and inverse link
259 linkRequest.add(linkKey(link.dst(), link.src()));
260 }
261
262 Map<LinkKey, VlanId> vlanIds = findVlanIds(linkRequest);
263 if (vlanIds.isEmpty()) {
264 log.warn("No VLAN IDs available");
265 return Collections.emptyMap();
266 }
267
268 //same VLANID is used for both directions
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800269 Set<Resource> resources = vlanIds.entrySet().stream()
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100270 .flatMap(x -> Stream.of(
Sho SHIMIZU460b9722016-01-28 10:48:26 -0800271 Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
Sho SHIMIZUf95b96e2016-01-25 19:35:15 -0800272 .resource(),
Sho SHIMIZU460b9722016-01-28 10:48:26 -0800273 Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
Sho SHIMIZUf95b96e2016-01-25 19:35:15 -0800274 .resource()
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100275 ))
276 .collect(Collectors.toSet());
277 List<org.onosproject.net.newresource.ResourceAllocation> allocations =
278 resourceService.allocate(intent.id(), ImmutableList.copyOf(resources));
279 if (allocations.isEmpty()) {
280 Collections.emptyMap();
281 }
282
283 return vlanIds;
284 }
285
286 private Map<LinkKey, VlanId> findVlanIds(Set<LinkKey> links) {
287 Map<LinkKey, VlanId> vlanIds = new HashMap<>();
288 for (LinkKey link : links) {
289 Set<VlanId> forward = findVlanId(link.src());
290 Set<VlanId> backward = findVlanId(link.dst());
291 Set<VlanId> common = Sets.intersection(forward, backward);
292 if (common.isEmpty()) {
293 continue;
294 }
295 vlanIds.put(link, common.iterator().next());
296 }
297 return vlanIds;
298 }
299
300 private Set<VlanId> findVlanId(ConnectPoint cp) {
Sho SHIMIZU7332fe42016-02-15 14:58:33 -0800301 return resourceService.getAvailableResourceValues(
302 Resources.discrete(cp.deviceId(), cp.port()).id(),
303 VlanId.class);
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100304 }
305
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800306 private boolean isLast(List<Link> links, int i) {
307 return i == links.size() - 2;
308 }
309}