blob: cad9cb7bd9801f023dc6a9a03dc44fea6f778cf3 [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;
29import org.onosproject.net.Link;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010030import org.onosproject.net.LinkKey;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080031import org.onosproject.net.flow.DefaultFlowRule;
32import org.onosproject.net.flow.DefaultTrafficSelector;
33import org.onosproject.net.flow.DefaultTrafficTreatment;
34import org.onosproject.net.flow.FlowRule;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.TrafficTreatment;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010037import org.onosproject.net.flow.criteria.Criterion;
38import org.onosproject.net.flow.criteria.VlanIdCriterion;
Michele Santuari3ea53902016-02-15 11:21:56 +010039import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080040import org.onosproject.net.intent.FlowRuleIntent;
41import org.onosproject.net.intent.Intent;
42import org.onosproject.net.intent.IntentCompiler;
43import org.onosproject.net.intent.IntentExtensionService;
44import org.onosproject.net.intent.PathIntent;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010045import org.onosproject.net.intent.constraint.EncapsulationConstraint;
46import org.onosproject.net.intent.impl.IntentCompilationException;
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -080047import org.onosproject.net.newresource.Resource;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010048import org.onosproject.net.newresource.ResourceService;
Sho SHIMIZU460b9722016-01-28 10:48:26 -080049import org.onosproject.net.newresource.Resources;
Brian O'Connor6de2e202015-05-21 14:30:41 -070050import org.onosproject.net.resource.link.LinkResourceAllocations;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010051import org.slf4j.Logger;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080052
Sho SHIMIZU98ffca82015-05-11 08:39:24 -070053import java.util.Collections;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010054import java.util.HashMap;
55import java.util.Iterator;
56import java.util.LinkedList;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080057import java.util.List;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010058import java.util.Map;
59import java.util.Optional;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080060import java.util.Set;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010061import java.util.stream.Collectors;
62import java.util.stream.Stream;
63
64import static org.onosproject.net.LinkKey.linkKey;
65import static org.slf4j.LoggerFactory.getLogger;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080066
67@Component(immediate = true)
68public class PathIntentCompiler implements IntentCompiler<PathIntent> {
69
Michele Santuari69fc2ff2015-12-03 17:05:35 +010070 private final Logger log = getLogger(getClass());
71
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080072 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected CoreService coreService;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected IntentExtensionService intentManager;
77
Michele Santuari69fc2ff2015-12-03 17:05:35 +010078 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected ResourceService resourceService;
80
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080081 private ApplicationId appId;
82
83 @Activate
84 public void activate() {
85 appId = coreService.registerApplication("org.onosproject.net.intent");
86 intentManager.registerCompiler(PathIntent.class, this);
87 }
88
89 @Deactivate
90 public void deactivate() {
91 intentManager.unregisterCompiler(PathIntent.class);
92 }
93
94 @Override
95 public List<Intent> compile(PathIntent intent, List<Intent> installable,
96 Set<LinkResourceAllocations> resources) {
97 // Note: right now recompile is not considered
98 // TODO: implement recompile behavior
99
100 List<Link> links = intent.path().links();
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800101
Sho SHIMIZUa5e9beb2016-02-24 14:07:35 -0800102 Optional<EncapsulationConstraint> encapConstraint = intent.constraints().stream()
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100103 .filter(constraint -> constraint instanceof EncapsulationConstraint)
104 .map(x -> (EncapsulationConstraint) x).findAny();
105 //if no encapsulation or is involved only a single switch use the default behaviour
Sho SHIMIZUa5e9beb2016-02-24 14:07:35 -0800106 if (!encapConstraint.isPresent() || links.size() == 1) {
Sho SHIMIZU4dab5612016-02-24 14:24:34 -0800107 List<FlowRule> rules = new LinkedList<>();
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100108 for (int i = 0; i < links.size() - 1; i++) {
109 ConnectPoint ingress = links.get(i).dst();
110 ConnectPoint egress = links.get(i + 1).src();
111 FlowRule rule = createFlowRule(intent.selector(), intent.treatment(),
112 ingress, egress, intent.priority(),
113 isLast(links, i));
114 rules.add(rule);
115 }
116
117 return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800118 }
Sho SHIMIZUe8f656c2016-02-24 14:05:17 -0800119
Sho SHIMIZU53f4c892016-02-24 14:17:13 -0800120 List<FlowRule> rules = encapConstraint.map(EncapsulationConstraint::encapType)
121 .map(type -> {
122 switch (type) {
123 case VLAN:
124 return manageVlanEncap(intent);
125 // TODO: implement MPLS case here
126 default:
127 return Collections.<FlowRule>emptyList();
128 }
129 })
130 .orElse(Collections.emptyList());
Sho SHIMIZUe8f656c2016-02-24 14:05:17 -0800131
132 return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800133 }
134
135 private FlowRule createFlowRule(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
Brian O'Connor81134662015-06-25 17:23:33 -0400136 ConnectPoint ingress, ConnectPoint egress,
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100137 int priority, boolean applyTreatment) {
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800138 TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector)
139 .matchInPort(ingress.port())
140 .build();
141
142 TrafficTreatment.Builder treatmentBuilder;
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100143 if (applyTreatment) {
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800144 treatmentBuilder = DefaultTrafficTreatment.builder(originalTreatment);
145 } else {
146 treatmentBuilder = DefaultTrafficTreatment.builder();
147 }
148 TrafficTreatment treatment = treatmentBuilder.setOutput(egress.port()).build();
149
Ray Milkeyd13a37b2015-06-12 11:55:17 -0700150 return DefaultFlowRule.builder()
151 .forDevice(ingress.deviceId())
152 .withSelector(selector)
153 .withTreatment(treatment)
Brian O'Connor81134662015-06-25 17:23:33 -0400154 .withPriority(priority)
Ray Milkeyd13a37b2015-06-12 11:55:17 -0700155 .fromApp(appId)
156 .makePermanent()
157 .build();
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800158 }
159
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100160 private List<FlowRule> manageVlanEncap(PathIntent intent) {
161 Map<LinkKey, VlanId> vlanIds = assignVlanId(intent);
162
163 Iterator<Link> links = intent.path().links().iterator();
164 Link srcLink = links.next();
165
166 Link link = links.next();
167 // List of flow rules to be installed
168 List<FlowRule> rules = new LinkedList<>();
169
170 // Ingress traffic
171 VlanId vlanId = vlanIds.get(linkKey(link));
172 if (vlanId == null) {
173 throw new IntentCompilationException("No available VLAN ID for " + link);
174 }
175 VlanId prevVlanId = vlanId;
176
Michele Santuari0ff59c22016-01-15 10:00:32 +0100177 Optional<VlanIdCriterion> vlanCriterion = intent.selector().criteria()
178 .stream().filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
179 .map(criterion -> (VlanIdCriterion) criterion)
180 .findAny();
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100181
Michele Santuari0ff59c22016-01-15 10:00:32 +0100182 //Push VLAN if selector does not include VLAN
183 TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
184 if (!vlanCriterion.isPresent()) {
185 treatBuilder.pushVlan();
186 }
187 //Tag the traffic with the new encapsulation VLAN
188 treatBuilder.setVlanId(vlanId);
189 rules.add(createFlowRule(intent.selector(), treatBuilder.build(),
190 srcLink.dst(), link.src(), intent.priority(), true));
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100191
192 ConnectPoint prev = link.dst();
193
194 while (links.hasNext()) {
195
196 link = links.next();
197
198 if (links.hasNext()) {
199 // Transit traffic
200 VlanId egressVlanId = vlanIds.get(linkKey(link));
201 if (egressVlanId == null) {
202 throw new IntentCompilationException("No available VLAN ID for " + link);
203 }
204 prevVlanId = egressVlanId;
205
206 TrafficSelector transitSelector = DefaultTrafficSelector.builder()
207 .matchInPort(prev.port())
208 .matchVlanId(prevVlanId).build();
209
210 TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
211
212 // Set the new vlanId only if the previous one is different
213 if (!prevVlanId.equals(egressVlanId)) {
214 transitTreat.setVlanId(egressVlanId);
215 }
216 rules.add(createFlowRule(transitSelector,
217 transitTreat.build(), prev, link.src(), intent.priority(), true));
218 prev = link.dst();
219 } else {
220 // Egress traffic
221 TrafficSelector egressSelector = DefaultTrafficSelector.builder()
222 .matchInPort(prev.port())
223 .matchVlanId(prevVlanId).build();
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100224 TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
Michele Santuari3ea53902016-02-15 11:21:56 +0100225
226 Optional<L2ModificationInstruction.ModVlanIdInstruction> modVlanIdInstruction = intent.treatment()
227 .allInstructions().stream().filter(
228 instruction -> instruction instanceof L2ModificationInstruction.ModVlanIdInstruction)
229 .map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x).findAny();
230
231 Optional<L2ModificationInstruction.PopVlanInstruction> popVlanInstruction = intent.treatment()
232 .allInstructions().stream().filter(
233 instruction -> instruction instanceof L2ModificationInstruction.PopVlanInstruction)
234 .map(x -> (L2ModificationInstruction.PopVlanInstruction) x).findAny();
235
236 if (!modVlanIdInstruction.isPresent() && !popVlanInstruction.isPresent()) {
237 if (vlanCriterion.isPresent()) {
238 egressTreat.setVlanId(vlanCriterion.get().vlanId());
239 } else {
240 egressTreat.popVlan();
241 }
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100242 }
243
244 rules.add(createFlowRule(egressSelector,
245 egressTreat.build(), prev, link.src(), intent.priority(), true));
246 }
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100247 }
248 return rules;
249
250 }
251
252 private Map<LinkKey, VlanId> assignVlanId(PathIntent intent) {
253 Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
254 .links().size() - 2);
255 for (int i = 1; i <= intent.path().links().size() - 2; i++) {
256 LinkKey link = linkKey(intent.path().links().get(i));
257 linkRequest.add(link);
258 // add the inverse link. I want that the VLANID is reserved both for
259 // the direct and inverse link
260 linkRequest.add(linkKey(link.dst(), link.src()));
261 }
262
263 Map<LinkKey, VlanId> vlanIds = findVlanIds(linkRequest);
264 if (vlanIds.isEmpty()) {
265 log.warn("No VLAN IDs available");
266 return Collections.emptyMap();
267 }
268
269 //same VLANID is used for both directions
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800270 Set<Resource> resources = vlanIds.entrySet().stream()
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100271 .flatMap(x -> Stream.of(
Sho SHIMIZU460b9722016-01-28 10:48:26 -0800272 Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
Sho SHIMIZUf95b96e2016-01-25 19:35:15 -0800273 .resource(),
Sho SHIMIZU460b9722016-01-28 10:48:26 -0800274 Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
Sho SHIMIZUf95b96e2016-01-25 19:35:15 -0800275 .resource()
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100276 ))
277 .collect(Collectors.toSet());
278 List<org.onosproject.net.newresource.ResourceAllocation> allocations =
279 resourceService.allocate(intent.id(), ImmutableList.copyOf(resources));
280 if (allocations.isEmpty()) {
281 Collections.emptyMap();
282 }
283
284 return vlanIds;
285 }
286
287 private Map<LinkKey, VlanId> findVlanIds(Set<LinkKey> links) {
288 Map<LinkKey, VlanId> vlanIds = new HashMap<>();
289 for (LinkKey link : links) {
290 Set<VlanId> forward = findVlanId(link.src());
291 Set<VlanId> backward = findVlanId(link.dst());
292 Set<VlanId> common = Sets.intersection(forward, backward);
293 if (common.isEmpty()) {
294 continue;
295 }
296 vlanIds.put(link, common.iterator().next());
297 }
298 return vlanIds;
299 }
300
301 private Set<VlanId> findVlanId(ConnectPoint cp) {
Sho SHIMIZU7332fe42016-02-15 14:58:33 -0800302 return resourceService.getAvailableResourceValues(
303 Resources.discrete(cp.deviceId(), cp.port()).id(),
304 VlanId.class);
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100305 }
306
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800307 private boolean isLast(List<Link> links, int i) {
308 return i == links.size() - 2;
309 }
310}