blob: ec7cfd7e40849a156bef6746c85daeb1eb2654de [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;
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;
Brian O'Connor6de2e202015-05-21 14:30:41 -070049import org.onosproject.net.resource.link.LinkResourceAllocations;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010050import org.slf4j.Logger;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080051
Sho SHIMIZU98ffca82015-05-11 08:39:24 -070052import java.util.Collections;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010053import java.util.HashMap;
54import java.util.Iterator;
55import java.util.LinkedList;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080056import java.util.List;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010057import java.util.Map;
58import java.util.Optional;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080059import java.util.Set;
Michele Santuari69fc2ff2015-12-03 17:05:35 +010060import java.util.stream.Collectors;
61import java.util.stream.Stream;
62
63import static org.onosproject.net.LinkKey.linkKey;
64import static org.slf4j.LoggerFactory.getLogger;
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080065
66@Component(immediate = true)
67public class PathIntentCompiler implements IntentCompiler<PathIntent> {
68
Michele Santuari69fc2ff2015-12-03 17:05:35 +010069 private final Logger log = getLogger(getClass());
70
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080071 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected CoreService coreService;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected IntentExtensionService intentManager;
76
Michele Santuari69fc2ff2015-12-03 17:05:35 +010077 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected ResourceService resourceService;
79
Sho SHIMIZUee2aa652015-02-25 18:56:43 -080080 private ApplicationId appId;
81
82 @Activate
83 public void activate() {
84 appId = coreService.registerApplication("org.onosproject.net.intent");
85 intentManager.registerCompiler(PathIntent.class, this);
86 }
87
88 @Deactivate
89 public void deactivate() {
90 intentManager.unregisterCompiler(PathIntent.class);
91 }
92
93 @Override
94 public List<Intent> compile(PathIntent intent, List<Intent> installable,
95 Set<LinkResourceAllocations> resources) {
96 // Note: right now recompile is not considered
97 // TODO: implement recompile behavior
98
99 List<Link> links = intent.path().links();
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100100 List<FlowRule> rules = new LinkedList<>();
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800101
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100102 Optional<EncapsulationConstraint> enacpConstraint = intent.constraints().stream()
103 .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
106 if (!enacpConstraint.isPresent() || links.size() == 1) {
107
108 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()));
118
119 } else {
120 if (EncapsulationType.VLAN == enacpConstraint.get().encapType()) {
121 rules = manageVlanEncap(intent);
122 }
123 if (EncapsulationType.MPLS == enacpConstraint.get().encapType()) {
124 //TODO: to be implemented
125 rules = Collections.emptyList();
126 }
127
128 return ImmutableList.of(new FlowRuleIntent(appId, null, rules, intent.resources()));
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800129 }
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
174 //Tag the traffic with the new VLAN
175 TrafficTreatment treat = DefaultTrafficTreatment.builder()
176 .setVlanId(vlanId)
177 .build();
178
179 rules.add(createFlowRule(intent.selector(), treat, srcLink.dst(), link.src(), intent.priority(), true));
180
181 ConnectPoint prev = link.dst();
182
183 while (links.hasNext()) {
184
185 link = links.next();
186
187 if (links.hasNext()) {
188 // Transit traffic
189 VlanId egressVlanId = vlanIds.get(linkKey(link));
190 if (egressVlanId == null) {
191 throw new IntentCompilationException("No available VLAN ID for " + link);
192 }
193 prevVlanId = egressVlanId;
194
195 TrafficSelector transitSelector = DefaultTrafficSelector.builder()
196 .matchInPort(prev.port())
197 .matchVlanId(prevVlanId).build();
198
199 TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
200
201 // Set the new vlanId only if the previous one is different
202 if (!prevVlanId.equals(egressVlanId)) {
203 transitTreat.setVlanId(egressVlanId);
204 }
205 rules.add(createFlowRule(transitSelector,
206 transitTreat.build(), prev, link.src(), intent.priority(), true));
207 prev = link.dst();
208 } else {
209 // Egress traffic
210 TrafficSelector egressSelector = DefaultTrafficSelector.builder()
211 .matchInPort(prev.port())
212 .matchVlanId(prevVlanId).build();
213
214 //TODO: think to other cases for egress packet restoration
215 Optional<VlanIdCriterion> vlanCriteria = intent.selector().criteria()
216 .stream().filter(criteria -> criteria.type() == Criterion.Type.VLAN_VID)
217 .map(criteria -> (VlanIdCriterion) criteria)
218 .findAny();
219 TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
220 if (vlanCriteria.isPresent()) {
221 egressTreat.setVlanId(vlanCriteria.get().vlanId());
222 } else {
223 egressTreat.popVlan();
224 }
225
226 rules.add(createFlowRule(egressSelector,
227 egressTreat.build(), prev, link.src(), intent.priority(), true));
228 }
229
230 }
231 return rules;
232
233 }
234
235 private Map<LinkKey, VlanId> assignVlanId(PathIntent intent) {
236 Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
237 .links().size() - 2);
238 for (int i = 1; i <= intent.path().links().size() - 2; i++) {
239 LinkKey link = linkKey(intent.path().links().get(i));
240 linkRequest.add(link);
241 // add the inverse link. I want that the VLANID is reserved both for
242 // the direct and inverse link
243 linkRequest.add(linkKey(link.dst(), link.src()));
244 }
245
246 Map<LinkKey, VlanId> vlanIds = findVlanIds(linkRequest);
247 if (vlanIds.isEmpty()) {
248 log.warn("No VLAN IDs available");
249 return Collections.emptyMap();
250 }
251
252 //same VLANID is used for both directions
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800253 Set<Resource> resources = vlanIds.entrySet().stream()
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100254 .flatMap(x -> Stream.of(
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800255 Resource.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue()),
256 Resource.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100257 ))
258 .collect(Collectors.toSet());
259 List<org.onosproject.net.newresource.ResourceAllocation> allocations =
260 resourceService.allocate(intent.id(), ImmutableList.copyOf(resources));
261 if (allocations.isEmpty()) {
262 Collections.emptyMap();
263 }
264
265 return vlanIds;
266 }
267
268 private Map<LinkKey, VlanId> findVlanIds(Set<LinkKey> links) {
269 Map<LinkKey, VlanId> vlanIds = new HashMap<>();
270 for (LinkKey link : links) {
271 Set<VlanId> forward = findVlanId(link.src());
272 Set<VlanId> backward = findVlanId(link.dst());
273 Set<VlanId> common = Sets.intersection(forward, backward);
274 if (common.isEmpty()) {
275 continue;
276 }
277 vlanIds.put(link, common.iterator().next());
278 }
279 return vlanIds;
280 }
281
282 private Set<VlanId> findVlanId(ConnectPoint cp) {
Sho SHIMIZU8fa670a2016-01-14 11:17:18 -0800283 return resourceService.getAvailableResources(Resource.discrete(cp.deviceId(), cp.port())).stream()
Michele Santuari69fc2ff2015-12-03 17:05:35 +0100284 .filter(x -> x.last() instanceof VlanId)
285 .map(x -> (VlanId) x.last())
286 .collect(Collectors.toSet());
287 }
288
Sho SHIMIZUee2aa652015-02-25 18:56:43 -0800289 private boolean isLast(List<Link> links, int i) {
290 return i == links.size() - 2;
291 }
292}