blob: 75142500127b2bf2cadfcd3ce4c06ad1b2f35341 [file] [log] [blame]
Ray Milkey661c38c2016-02-26 17:12:17 -08001/*
2 * Copyright 2016 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
18import java.util.Collections;
19import java.util.HashMap;
20import java.util.Iterator;
21import java.util.List;
22import java.util.Map;
23import java.util.Optional;
24import java.util.Set;
25import java.util.stream.Collectors;
26import java.util.stream.Stream;
27
28import org.onlab.packet.VlanId;
29import org.onosproject.net.ConnectPoint;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.Link;
32import org.onosproject.net.LinkKey;
33import org.onosproject.net.flow.DefaultTrafficSelector;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.TrafficTreatment;
37import org.onosproject.net.flow.criteria.Criterion;
38import org.onosproject.net.flow.criteria.VlanIdCriterion;
39import org.onosproject.net.flow.instructions.L2ModificationInstruction;
40import org.onosproject.net.intent.PathIntent;
41import org.onosproject.net.intent.constraint.EncapsulationConstraint;
42import org.onosproject.net.intent.impl.IntentCompilationException;
43import org.onosproject.net.newresource.Resource;
44import org.onosproject.net.newresource.ResourceService;
45import org.onosproject.net.newresource.Resources;
46import org.slf4j.Logger;
47
48import com.google.common.collect.ImmutableList;
49import com.google.common.collect.Sets;
50
51import static org.onosproject.net.LinkKey.linkKey;
52
53/**
54 * Shared APIs and implementations for path compilers.
55 */
56
57public class PathCompiler<T> {
58
59 /**
60 * Defines methods used to create objects representing flows.
61 */
62 public interface PathCompilerCreateFlow<T> {
63
64 void createFlow(TrafficSelector originalSelector,
65 TrafficTreatment originalTreatment,
66 ConnectPoint ingress, ConnectPoint egress,
67 int priority,
68 boolean applyTreatment,
69 List<T> flows,
70 List<DeviceId> devices);
71
72 Logger log();
73
74 ResourceService resourceService();
75 }
76
77 private boolean isLast(List<Link> links, int i) {
78 return i == links.size() - 2;
79 }
80
81 private Map<LinkKey, VlanId> assignVlanId(PathCompilerCreateFlow creator, PathIntent intent) {
82 Set<LinkKey> linkRequest =
83 Sets.newHashSetWithExpectedSize(intent.path()
84 .links().size() - 2);
85 for (int i = 1; i <= intent.path().links().size() - 2; i++) {
86 LinkKey link = linkKey(intent.path().links().get(i));
87 linkRequest.add(link);
88 // add the inverse link. I want that the VLANID is reserved both for
89 // the direct and inverse link
90 linkRequest.add(linkKey(link.dst(), link.src()));
91 }
92
93 Map<LinkKey, VlanId> vlanIds = findVlanIds(creator, linkRequest);
94 if (vlanIds.isEmpty()) {
95 creator.log().warn("No VLAN IDs available");
96 return Collections.emptyMap();
97 }
98
99 //same VLANID is used for both directions
100 Set<Resource> resources = vlanIds.entrySet().stream()
101 .flatMap(x -> Stream.of(
102 Resources.discrete(x.getKey().src().deviceId(), x.getKey().src().port(), x.getValue())
103 .resource(),
104 Resources.discrete(x.getKey().dst().deviceId(), x.getKey().dst().port(), x.getValue())
105 .resource()
106 ))
107 .collect(Collectors.toSet());
108 List<org.onosproject.net.newresource.ResourceAllocation> allocations =
109 creator.resourceService().allocate(intent.id(), ImmutableList.copyOf(resources));
110 if (allocations.isEmpty()) {
111 Collections.emptyMap();
112 }
113
114 return vlanIds;
115 }
116
117 private Map<LinkKey, VlanId> findVlanIds(PathCompilerCreateFlow creator, Set<LinkKey> links) {
118 Map<LinkKey, VlanId> vlanIds = new HashMap<>();
119 for (LinkKey link : links) {
120 Set<VlanId> forward = findVlanId(creator, link.src());
121 Set<VlanId> backward = findVlanId(creator, link.dst());
122 Set<VlanId> common = Sets.intersection(forward, backward);
123 if (common.isEmpty()) {
124 continue;
125 }
126 vlanIds.put(link, common.iterator().next());
127 }
128 return vlanIds;
129 }
130
131 private Set<VlanId> findVlanId(PathCompilerCreateFlow creator, ConnectPoint cp) {
132 return creator.resourceService().getAvailableResourceValues(
133 Resources.discrete(cp.deviceId(), cp.port()).id(),
134 VlanId.class);
135 }
136
137 private void manageVlanEncap(PathCompilerCreateFlow<T> creator, List<T> flows,
138 List<DeviceId> devices,
139 PathIntent intent) {
140 Map<LinkKey, VlanId> vlanIds = assignVlanId(creator, intent);
141
142 Iterator<Link> links = intent.path().links().iterator();
143 Link srcLink = links.next();
144
145 Link link = links.next();
146
147 // Ingress traffic
148 VlanId vlanId = vlanIds.get(linkKey(link));
149 if (vlanId == null) {
150 throw new IntentCompilationException("No available VLAN ID for " + link);
151 }
152 VlanId prevVlanId = vlanId;
153
154 Optional<VlanIdCriterion> vlanCriterion = intent.selector().criteria()
155 .stream().filter(criterion -> criterion.type() == Criterion.Type.VLAN_VID)
156 .map(criterion -> (VlanIdCriterion) criterion)
157 .findAny();
158
159 //Push VLAN if selector does not include VLAN
160 TrafficTreatment.Builder treatBuilder = DefaultTrafficTreatment.builder();
161 if (!vlanCriterion.isPresent()) {
162 treatBuilder.pushVlan();
163 }
164 //Tag the traffic with the new encapsulation VLAN
165 treatBuilder.setVlanId(vlanId);
166 creator.createFlow(intent.selector(), treatBuilder.build(),
167 srcLink.dst(), link.src(), intent.priority(), true,
168 flows, devices);
169
170 ConnectPoint prev = link.dst();
171
172 while (links.hasNext()) {
173
174 link = links.next();
175
176 if (links.hasNext()) {
177 // Transit traffic
178 VlanId egressVlanId = vlanIds.get(linkKey(link));
179 if (egressVlanId == null) {
180 throw new IntentCompilationException("No available VLAN ID for " + link);
181 }
182 prevVlanId = egressVlanId;
183
184 TrafficSelector transitSelector = DefaultTrafficSelector.builder()
185 .matchInPort(prev.port())
186 .matchVlanId(prevVlanId).build();
187
188 TrafficTreatment.Builder transitTreat = DefaultTrafficTreatment.builder();
189
190 // Set the new vlanId only if the previous one is different
191 if (!prevVlanId.equals(egressVlanId)) {
192 transitTreat.setVlanId(egressVlanId);
193 }
194 creator.createFlow(transitSelector,
195 transitTreat.build(), prev, link.src(),
196 intent.priority(), true, flows, devices);
197 prev = link.dst();
198 } else {
199 // Egress traffic
200 TrafficSelector egressSelector = DefaultTrafficSelector.builder()
201 .matchInPort(prev.port())
202 .matchVlanId(prevVlanId).build();
203 TrafficTreatment.Builder egressTreat = DefaultTrafficTreatment.builder(intent.treatment());
204
205 Optional<L2ModificationInstruction.ModVlanIdInstruction> modVlanIdInstruction = intent.treatment()
206 .allInstructions().stream().filter(
207 instruction -> instruction instanceof L2ModificationInstruction.ModVlanIdInstruction)
208 .map(x -> (L2ModificationInstruction.ModVlanIdInstruction) x).findAny();
209
210 Optional<L2ModificationInstruction.PopVlanInstruction> popVlanInstruction = intent.treatment()
211 .allInstructions().stream().filter(
212 instruction -> instruction instanceof L2ModificationInstruction.PopVlanInstruction)
213 .map(x -> (L2ModificationInstruction.PopVlanInstruction) x).findAny();
214
215 if (!modVlanIdInstruction.isPresent() && !popVlanInstruction.isPresent()) {
216 if (vlanCriterion.isPresent()) {
217 egressTreat.setVlanId(vlanCriterion.get().vlanId());
218 } else {
219 egressTreat.popVlan();
220 }
221 }
222
223 creator.createFlow(egressSelector,
224 egressTreat.build(), prev, link.src(),
225 intent.priority(), true, flows, devices);
226 }
227 }
228 }
229
230 /**
231 * Compiles an intent down to flows.
232 *
233 * @param creator how to create the flows
234 * @param intent intent to process
235 * @param flows list of generated flows
236 * @param devices list of devices that correspond to the flows
237 */
238 public void compile(PathCompilerCreateFlow<T> creator,
239 PathIntent intent,
240 List<T> flows,
241 List<DeviceId> devices) {
242 // Note: right now recompile is not considered
243 // TODO: implement recompile behavior
244
245 List<Link> links = intent.path().links();
246
247 Optional<EncapsulationConstraint> encapConstraint = intent.constraints().stream()
248 .filter(constraint -> constraint instanceof EncapsulationConstraint)
249 .map(x -> (EncapsulationConstraint) x).findAny();
250 //if no encapsulation or is involved only a single switch use the default behaviour
251 if (!encapConstraint.isPresent() || links.size() == 1) {
252 for (int i = 0; i < links.size() - 1; i++) {
253 ConnectPoint ingress = links.get(i).dst();
254 ConnectPoint egress = links.get(i + 1).src();
255 creator.createFlow(intent.selector(), intent.treatment(),
256 ingress, egress, intent.priority(),
257 isLast(links, i), flows, devices);
258 }
259 }
260
261 encapConstraint.map(EncapsulationConstraint::encapType)
262 .map(type -> {
263 switch (type) {
264 case VLAN:
265 manageVlanEncap(creator, flows, devices, intent);
266 // TODO: implement MPLS case here
267 default:
268 // Nothing to do
269 }
270 return 0;
271 });
272 }
273
274}