blob: 569f095fcbb4326668c1080e5f4a1a682fa30238 [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 SHIMIZUf08cb4c2016-02-11 18:35:59 -080026import org.onlab.util.Tools;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080027import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.net.ConnectPoint;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010030import org.onosproject.net.EncapsulationType;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080031import org.onosproject.net.Link;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010032import org.onosproject.net.LinkKey;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080033import org.onosproject.net.flow.DefaultFlowRule;
34import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.FlowRule;
37import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010039import org.onosproject.net.flow.criteria.Criterion;
40import org.onosproject.net.flow.criteria.VlanIdCriterion;
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 Santuari0ff59c22016-01-15 10:00:32 +0100224 if (vlanCriterion.isPresent()) {
225 egressTreat.setVlanId(vlanCriterion.get().vlanId());
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100226 } else {
227 egressTreat.popVlan();
228 }
229
230 rules.add(createFlowRule(egressSelector,
231 egressTreat.build(), prev, link.src(), intent.priority(), true));
232 }
233
234 }
235 return rules;
236
237 }
238
239 private Map<LinkKey, VlanId> assignVlanId(PathIntent intent) {
240 Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
241 .links().size() - 2);
242 for (int i = 1; i <= intent.path().links().size() - 2; i++) {
243 LinkKey link = linkKey(intent.path().links().get(i));
244 linkRequest.add(link);
245 // add the inverse link. I want that the VLANID is reserved both for
246 // the direct and inverse link
247 linkRequest.add(linkKey(link.dst(), link.src()));
248 }
249
250 Map<LinkKey, VlanId> vlanIds = findVlanIds(linkRequest);
251 if (vlanIds.isEmpty()) {
252 log.warn("No VLAN IDs available");
253 return Collections.emptyMap();
254 }
255
256 //same VLANID is used for both directions
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800257 Set<Resource> resources = vlanIds.entrySet().stream()
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100258 .flatMap(x -> Stream.of(
Sho SHIMIZU460b9722016-01-28 10:48:26 -0800259 Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
Sho SHIMIZUf95b96e2016-01-25 19:35:15 -0800260 .resource(),
Sho SHIMIZU460b9722016-01-28 10:48:26 -0800261 Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
Sho SHIMIZUf95b96e2016-01-25 19:35:15 -0800262 .resource()
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100263 ))
264 .collect(Collectors.toSet());
265 List<org.onosproject.net.newresource.ResourceAllocation> allocations =
266 resourceService.allocate(intent.id(), ImmutableList.copyOf(resources));
267 if (allocations.isEmpty()) {
268 Collections.emptyMap();
269 }
270
271 return vlanIds;
272 }
273
274 private Map<LinkKey, VlanId> findVlanIds(Set<LinkKey> links) {
275 Map<LinkKey, VlanId> vlanIds = new HashMap<>();
276 for (LinkKey link : links) {
277 Set<VlanId> forward = findVlanId(link.src());
278 Set<VlanId> backward = findVlanId(link.dst());
279 Set<VlanId> common = Sets.intersection(forward, backward);
280 if (common.isEmpty()) {
281 continue;
282 }
283 vlanIds.put(link, common.iterator().next());
284 }
285 return vlanIds;
286 }
287
288 private Set<VlanId> findVlanId(ConnectPoint cp) {
Sho SHIMIZUdd3750c2016-02-01 11:37:04 -0800289 return resourceService.getAvailableResources(Resources.discrete(cp.deviceId(), cp.port()).id()).stream()
Sho SHIMIZUf08cb4c2016-02-11 18:35:59 -0800290 .flatMap(x -> Tools.stream(x.valueAs(VlanId.class)))
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100291 .collect(Collectors.toSet());
292 }
293
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800294 private boolean isLast(List<Link> links, int i) {
295 return i == links.size() - 2;
296 }
297}