blob: 330af049ce66a0884b387802713b16a3ac1983fb [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()));
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800120 }
Sho SHIMIZUe8f656c2016-02-24 14:05:17 -0800121
122 if (EncapsulationType.VLAN == enacpConstraint.get().encapType()) {
123 rules = manageVlanEncap(intent);
124 } else if (EncapsulationType.MPLS == enacpConstraint.get().encapType()) {
125 //TODO: to be implemented
126 rules = Collections.emptyList();
127 }
128
129 return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800130 }
131
132 private FlowRule createFlowRule(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
Brian O'Connor81134662015-06-25 17:23:33 -0400133 ConnectPoint ingress, ConnectPoint egress,
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100134 int priority, boolean applyTreatment) {
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800135 TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector)
136 .matchInPort(ingress.port())
137 .build();
138
139 TrafficTreatment.Builder treatmentBuilder;
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100140 if (applyTreatment) {
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800141 treatmentBuilder = DefaultTrafficTreatment.builder(originalTreatment);
142 } else {
143 treatmentBuilder = DefaultTrafficTreatment.builder();
144 }
145 TrafficTreatment treatment = treatmentBuilder.setOutput(egress.port()).build();
146
Ray Milkeyd13a37b2015-06-12 11:55:17 -0700147 return DefaultFlowRule.builder()
148 .forDevice(ingress.deviceId())
149 .withSelector(selector)
150 .withTreatment(treatment)
Brian O'Connor81134662015-06-25 17:23:33 -0400151 .withPriority(priority)
Ray Milkeyd13a37b2015-06-12 11:55:17 -0700152 .fromApp(appId)
153 .makePermanent()
154 .build();
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800155 }
156
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100157 private List<FlowRule> manageVlanEncap(PathIntent intent) {
158 Map<LinkKey, VlanId> vlanIds = assignVlanId(intent);
159
160 Iterator<Link> links = intent.path().links().iterator();
161 Link srcLink = links.next();
162
163 Link link = links.next();
164 // List of flow rules to be installed
165 List<FlowRule> rules = new LinkedList<>();
166
167 // Ingress traffic
168 VlanId vlanId = vlanIds.get(linkKey(link));
169 if (vlanId == null) {
170 throw new IntentCompilationException("No available VLAN ID for " + link);
171 }
172 VlanId prevVlanId = vlanId;
173
Michele Santuari0ff59c22016-01-15 10:00:32 +0100174 Optional<VlanIdCriterion> vlanCriterion = intent.selector().criteria()
175 .stream().filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
176 .map(criterion -> (VlanIdCriterion) criterion)
177 .findAny();
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100178
Michele Santuari0ff59c22016-01-15 10:00:32 +0100179 //Push VLAN if selector does not include VLAN
180 TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
181 if (!vlanCriterion.isPresent()) {
182 treatBuilder.pushVlan();
183 }
184 //Tag the traffic with the new encapsulation VLAN
185 treatBuilder.setVlanId(vlanId);
186 rules.add(createFlowRule(intent.selector(), treatBuilder.build(),
187 srcLink.dst(), link.src(), intent.priority(), true));
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100188
189 ConnectPoint prev = link.dst();
190
191 while (links.hasNext()) {
192
193 link = links.next();
194
195 if (links.hasNext()) {
196 // Transit traffic
197 VlanId egressVlanId = vlanIds.get(linkKey(link));
198 if (egressVlanId == null) {
199 throw new IntentCompilationException("No available VLAN ID for " + link);
200 }
201 prevVlanId = egressVlanId;
202
203 TrafficSelector transitSelector = DefaultTrafficSelector.builder()
204 .matchInPort(prev.port())
205 .matchVlanId(prevVlanId).build();
206
207 TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
208
209 // Set the new vlanId only if the previous one is different
210 if (!prevVlanId.equals(egressVlanId)) {
211 transitTreat.setVlanId(egressVlanId);
212 }
213 rules.add(createFlowRule(transitSelector,
214 transitTreat.build(), prev, link.src(), intent.priority(), true));
215 prev = link.dst();
216 } else {
217 // Egress traffic
218 TrafficSelector egressSelector = DefaultTrafficSelector.builder()
219 .matchInPort(prev.port())
220 .matchVlanId(prevVlanId).build();
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100221 TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
Michele Santuari3ea53902016-02-15 11:21:56 +0100222
223 Optional<L2ModificationInstruction.ModVlanIdInstruction> modVlanIdInstruction = intent.treatment()
224 .allInstructions().stream().filter(
225 instruction -> instruction instanceof L2ModificationInstruction.ModVlanIdInstruction)
226 .map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x).findAny();
227
228 Optional<L2ModificationInstruction.PopVlanInstruction> popVlanInstruction = intent.treatment()
229 .allInstructions().stream().filter(
230 instruction -> instruction instanceof L2ModificationInstruction.PopVlanInstruction)
231 .map(x -> (L2ModificationInstruction.PopVlanInstruction) x).findAny();
232
233 if (!modVlanIdInstruction.isPresent() && !popVlanInstruction.isPresent()) {
234 if (vlanCriterion.isPresent()) {
235 egressTreat.setVlanId(vlanCriterion.get().vlanId());
236 } else {
237 egressTreat.popVlan();
238 }
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100239 }
240
241 rules.add(createFlowRule(egressSelector,
242 egressTreat.build(), prev, link.src(), intent.priority(), true));
243 }
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100244 }
245 return rules;
246
247 }
248
249 private Map<LinkKey, VlanId> assignVlanId(PathIntent intent) {
250 Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
251 .links().size() - 2);
252 for (int i = 1; i <= intent.path().links().size() - 2; i++) {
253 LinkKey link = linkKey(intent.path().links().get(i));
254 linkRequest.add(link);
255 // add the inverse link. I want that the VLANID is reserved both for
256 // the direct and inverse link
257 linkRequest.add(linkKey(link.dst(), link.src()));
258 }
259
260 Map<LinkKey, VlanId> vlanIds = findVlanIds(linkRequest);
261 if (vlanIds.isEmpty()) {
262 log.warn("No VLAN IDs available");
263 return Collections.emptyMap();
264 }
265
266 //same VLANID is used for both directions
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800267 Set<Resource> resources = vlanIds.entrySet().stream()
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100268 .flatMap(x -> Stream.of(
Sho SHIMIZU460b9722016-01-28 10:48:26 -0800269 Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
Sho SHIMIZUf95b96e2016-01-25 19:35:15 -0800270 .resource(),
Sho SHIMIZU460b9722016-01-28 10:48:26 -0800271 Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
Sho SHIMIZUf95b96e2016-01-25 19:35:15 -0800272 .resource()
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100273 ))
274 .collect(Collectors.toSet());
275 List<org.onosproject.net.newresource.ResourceAllocation> allocations =
276 resourceService.allocate(intent.id(), ImmutableList.copyOf(resources));
277 if (allocations.isEmpty()) {
278 Collections.emptyMap();
279 }
280
281 return vlanIds;
282 }
283
284 private Map<LinkKey, VlanId> findVlanIds(Set<LinkKey> links) {
285 Map<LinkKey, VlanId> vlanIds = new HashMap<>();
286 for (LinkKey link : links) {
287 Set<VlanId> forward = findVlanId(link.src());
288 Set<VlanId> backward = findVlanId(link.dst());
289 Set<VlanId> common = Sets.intersection(forward, backward);
290 if (common.isEmpty()) {
291 continue;
292 }
293 vlanIds.put(link, common.iterator().next());
294 }
295 return vlanIds;
296 }
297
298 private Set<VlanId> findVlanId(ConnectPoint cp) {
Sho SHIMIZU7332fe42016-02-15 14:58:33 -0800299 return resourceService.getAvailableResourceValues(
300 Resources.discrete(cp.deviceId(), cp.port()).id(),
301 VlanId.class);
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100302 }
303
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800304 private boolean isLast(List<Link> links, int i) {
305 return i == links.size() - 2;
306 }
307}