blob: 32784a38b8bcaff29cbdf9a80ed84c0e630310bc [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
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 */
Sho SHIMIZU6c28f832015-02-20 16:12:19 -080016package org.onosproject.net.intent.impl.compiler;
Ray Milkeya058c732014-10-08 13:52:34 -070017
Pier Ventreffe88d62016-10-13 14:34:40 -070018import com.google.common.collect.ImmutableSet;
Ray Milkeya058c732014-10-08 13:52:34 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
helenyrwu2a674902016-07-20 09:48:04 -070022import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
Ray Milkeya7cf8c82018-02-08 15:07:06 -080024import org.onlab.graph.ScalarWeight;
Brian O'Connorabafb502014-12-02 22:26:20 -080025import org.onosproject.net.ConnectPoint;
26import org.onosproject.net.DefaultPath;
helenyrwu2a674902016-07-20 09:48:04 -070027import org.onosproject.net.DeviceId;
28import org.onosproject.net.DisjointPath;
29import org.onosproject.net.EdgeLink;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.net.Link;
31import org.onosproject.net.Path;
helenyrwu2a674902016-07-20 09:48:04 -070032import org.onosproject.net.Port;
33import org.onosproject.net.PortNumber;
34import org.onosproject.net.device.DeviceService;
35import org.onosproject.net.flow.DefaultFlowRule;
36import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.FlowRule;
39import org.onosproject.net.flow.TrafficSelector;
40import org.onosproject.net.flow.TrafficTreatment;
41import org.onosproject.net.flow.instructions.Instruction;
42import org.onosproject.net.flow.instructions.Instructions;
43import org.onosproject.net.group.DefaultGroupBucket;
44import org.onosproject.net.group.DefaultGroupDescription;
45import org.onosproject.net.group.DefaultGroupKey;
46import org.onosproject.net.group.Group;
47import org.onosproject.net.group.GroupBucket;
48import org.onosproject.net.group.GroupBuckets;
49import org.onosproject.net.group.GroupDescription;
50import org.onosproject.net.group.GroupKey;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070051import org.onosproject.net.group.GroupListener;
helenyrwu2a674902016-07-20 09:48:04 -070052import org.onosproject.net.group.GroupService;
53import org.onosproject.net.intent.FlowRuleIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080054import org.onosproject.net.intent.Intent;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070055import org.onosproject.net.intent.IntentCompilationException;
helenyrwu2a674902016-07-20 09:48:04 -070056import org.onosproject.net.intent.IntentId;
Pier Ventreffe88d62016-10-13 14:34:40 -070057import org.onosproject.net.intent.LinkCollectionIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080058import org.onosproject.net.intent.PathIntent;
59import org.onosproject.net.intent.PointToPointIntent;
helenyrwu2a674902016-07-20 09:48:04 -070060import org.onosproject.net.intent.constraint.ProtectionConstraint;
61import org.onosproject.net.intent.impl.PathNotFoundException;
62import org.onosproject.net.link.LinkService;
Brian O'Connorabafb502014-12-02 22:26:20 -080063import org.onosproject.net.provider.ProviderId;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070064import org.slf4j.Logger;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080065
helenyrwu2a674902016-07-20 09:48:04 -070066import java.nio.ByteBuffer;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080067import java.util.ArrayList;
helenyrwu2a674902016-07-20 09:48:04 -070068import java.util.Collections;
69import java.util.Iterator;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080070import java.util.List;
helenyrwu2a674902016-07-20 09:48:04 -070071import java.util.ListIterator;
Pier Ventreffe88d62016-10-13 14:34:40 -070072import java.util.Set;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070073import java.util.concurrent.CompletableFuture;
74import java.util.concurrent.ExecutionException;
75import java.util.concurrent.TimeUnit;
76import java.util.concurrent.TimeoutException;
Luca Pretede10c782017-01-05 17:23:08 -080077import java.util.stream.Collectors;
78import java.util.stream.Stream;
Ray Milkeya058c732014-10-08 13:52:34 -070079
Thomas Vachuska425a2d72014-10-29 11:28:28 -070080import static java.util.Arrays.asList;
Brian O'Connorabafb502014-12-02 22:26:20 -080081import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070082import static org.slf4j.LoggerFactory.getLogger;
Thomas Vachuska425a2d72014-10-29 11:28:28 -070083
Ray Milkeya058c732014-10-08 13:52:34 -070084/**
Brian O'Connorabafb502014-12-02 22:26:20 -080085 * An intent compiler for {@link org.onosproject.net.intent.PointToPointIntent}.
Ray Milkeya058c732014-10-08 13:52:34 -070086 */
87@Component(immediate = true)
88public class PointToPointIntentCompiler
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080089 extends ConnectivityIntentCompiler<PointToPointIntent> {
Ray Milkeya058c732014-10-08 13:52:34 -070090
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080091 // TODO: use off-the-shell core provider ID
92 private static final ProviderId PID =
Brian O'Connorabafb502014-12-02 22:26:20 -080093 new ProviderId("core", "org.onosproject.core", true);
Sho SHIMIZU3908fde2014-11-19 16:30:22 -080094 // TODO: consider whether the default cost is appropriate or not
95 public static final int DEFAULT_COST = 1;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070096
helenyrwu2a674902016-07-20 09:48:04 -070097 protected static final int PRIORITY = Intent.DEFAULT_INTENT_PRIORITY;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070098
99 private static final int GROUP_TIMEOUT = 5;
100
101 private final Logger log = getLogger(getClass());
102
helenyrwu2a674902016-07-20 09:48:04 -0700103 protected boolean erasePrimary = false;
104 protected boolean eraseBackup = false;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected GroupService groupService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected LinkService linkService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected DeviceService deviceService;
weibit50eb95b2014-10-25 21:47:54 -0700114
Ray Milkeya058c732014-10-08 13:52:34 -0700115 @Activate
116 public void activate() {
Ray Milkeya058c732014-10-08 13:52:34 -0700117 intentManager.registerCompiler(PointToPointIntent.class, this);
118 }
119
120 @Deactivate
121 public void deactivate() {
122 intentManager.unregisterCompiler(PointToPointIntent.class);
123 }
124
125 @Override
Sho SHIMIZUec07ffd2016-02-22 20:45:21 -0800126 public List<Intent> compile(PointToPointIntent intent, List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700127 log.trace("compiling {} {}", intent, installable);
Luca Pretede10c782017-01-05 17:23:08 -0800128 ConnectPoint ingressPoint = intent.filteredIngressPoint().connectPoint();
129 ConnectPoint egressPoint = intent.filteredEgressPoint().connectPoint();
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800130
131 if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700132 return createZeroHopLinkCollectionIntent(intent);
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800133 }
Ray Milkeya058c732014-10-08 13:52:34 -0700134
helenyrwu2a674902016-07-20 09:48:04 -0700135 // proceed with no protected paths
136 if (!ProtectionConstraint.requireProtectedPath(intent)) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700137 return createUnprotectedLinkCollectionIntent(intent);
helenyrwu2a674902016-07-20 09:48:04 -0700138 }
139
140 try {
141 // attempt to compute and implement backup path
142 return createProtectedIntent(ingressPoint, egressPoint, intent, installable);
143 } catch (PathNotFoundException e) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700144 log.warn("Could not find disjoint Path for {}", intent);
helenyrwu2a674902016-07-20 09:48:04 -0700145 // no disjoint path extant -- maximum one path exists between devices
146 return createSinglePathIntent(ingressPoint, egressPoint, intent, installable);
147 }
148 }
149
150 private List<Intent> createZeroHopIntent(ConnectPoint ingressPoint,
151 ConnectPoint egressPoint,
152 PointToPointIntent intent) {
153 List<Link> links = asList(createEdgeLink(ingressPoint, true), createEdgeLink(egressPoint, false));
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800154 return asList(createPathIntent(new DefaultPath(PID, links, ScalarWeight.toWeight(DEFAULT_COST)),
helenyrwu2a674902016-07-20 09:48:04 -0700155 intent, PathIntent.ProtectionType.PRIMARY));
156 }
157
Pier Ventreffe88d62016-10-13 14:34:40 -0700158 private List<Intent> createZeroHopLinkCollectionIntent(PointToPointIntent intent) {
159 return asList(createLinkCollectionIntent(ImmutableSet.of(), DEFAULT_COST,
160 intent));
161 }
162
Luca Pretede10c782017-01-05 17:23:08 -0800163 /**
164 * Creates an unprotected intent.
165 * @param ingressPoint the ingress connect point
166 * @param egressPoint the egress connect point
167 * @param intent the original intent
168 * @return the compilation result
169 * @deprecated 1.10.0
170 */
171 @Deprecated
helenyrwu2a674902016-07-20 09:48:04 -0700172 private List<Intent> createUnprotectedIntent(ConnectPoint ingressPoint,
173 ConnectPoint egressPoint,
174 PointToPointIntent intent) {
Ray Milkeya058c732014-10-08 13:52:34 -0700175 List<Link> links = new ArrayList<>();
Luca Preted26ea652017-01-03 15:59:30 -0800176 Path path = getPathOrException(intent, ingressPoint.deviceId(),
177 egressPoint.deviceId());
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800178
Sho SHIMIZU3908fde2014-11-19 16:30:22 -0800179 links.add(createEdgeLink(ingressPoint, true));
Ray Milkeya058c732014-10-08 13:52:34 -0700180 links.addAll(path.links());
Sho SHIMIZU3908fde2014-11-19 16:30:22 -0800181 links.add(createEdgeLink(egressPoint, false));
Ray Milkeya058c732014-10-08 13:52:34 -0700182
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800183 return asList(createPathIntent(new DefaultPath(PID, links, path.weight(),
helenyrwu2a674902016-07-20 09:48:04 -0700184 path.annotations()), intent,
185 PathIntent.ProtectionType.PRIMARY));
186 }
187
Pier Ventreffe88d62016-10-13 14:34:40 -0700188 private List<Intent> createUnprotectedLinkCollectionIntent(PointToPointIntent intent) {
Luca Preted26ea652017-01-03 15:59:30 -0800189 Path path = getPathOrException(intent, intent.filteredIngressPoint().connectPoint().deviceId(),
190 intent.filteredEgressPoint().connectPoint().deviceId());
Pier Ventreffe88d62016-10-13 14:34:40 -0700191
Luca Pretede10c782017-01-05 17:23:08 -0800192 // Allocate bandwidth if a bandwidth constraint is set
193 ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
194 ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
195
196 List<ConnectPoint> pathCPs =
197 path.links().stream()
198 .flatMap(l -> Stream.of(l.src(), l.dst()))
199 .collect(Collectors.toList());
200
201 pathCPs.add(ingressCP);
202 pathCPs.add(egressCP);
203
204 allocateBandwidth(intent, pathCPs);
205
Pier Ventreffe88d62016-10-13 14:34:40 -0700206 return asList(createLinkCollectionIntent(ImmutableSet.copyOf(path.links()),
207 path.cost(),
208 intent));
209 }
210
helenyrwu2a674902016-07-20 09:48:04 -0700211 //FIXME: Compatibility with EncapsulationConstraint
212 private List<Intent> createProtectedIntent(ConnectPoint ingressPoint,
213 ConnectPoint egressPoint,
214 PointToPointIntent intent,
215 List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700216 log.trace("createProtectedIntent");
helenyrwu2a674902016-07-20 09:48:04 -0700217 DisjointPath path = getDisjointPath(intent, ingressPoint.deviceId(),
218 egressPoint.deviceId());
219
220 List<Intent> reusableIntents = null;
221 if (installable != null) {
222 reusableIntents = filterInvalidSubIntents(installable, intent);
223 if (reusableIntents.size() == installable.size()) {
224 // all old paths are still viable
225 return installable;
226 }
227 }
228
229 List<Intent> intentList = new ArrayList<>();
230
231 // primary path intent
232 List<Link> links = new ArrayList<>();
233 links.addAll(path.links());
234 links.add(createEdgeLink(egressPoint, false));
235
236 // backup path intent
237 List<Link> backupLinks = new ArrayList<>();
238 backupLinks.addAll(path.backup().links());
239 backupLinks.add(createEdgeLink(egressPoint, false));
240
241 /*
242 * One of the old paths is still entirely intact. This old path has
243 * already been made primary, so we must add a backup path intent
244 * and modify the failover group treatment accordingly.
245 */
246 if (reusableIntents != null && reusableIntents.size() > 1) {
247 /*
248 * Ensures that the egress port on source device is different than
249 * that of existing path so that failover group will be useful
250 * (would not be useful if both output ports in group bucket were
251 * the same). Does not necessarily ensure that the new backup path
252 * is entirely disjoint from the old path.
253 */
254 PortNumber primaryPort = getPrimaryPort(intent);
255 if (primaryPort != null && !links.get(0).src().port().equals(primaryPort)) {
256 reusableIntents.add(createPathIntent(new DefaultPath(PID, links,
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800257 path.weight(), path.annotations()),
helenyrwu2a674902016-07-20 09:48:04 -0700258 intent, PathIntent.ProtectionType.BACKUP));
259 updateFailoverGroup(intent, links);
260 return reusableIntents;
261
262 } else {
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800263 reusableIntents.add(createPathIntent(new DefaultPath(PID, backupLinks,
264 path.backup().weight(),
265 path.backup().annotations()), intent, PathIntent.ProtectionType.BACKUP));
helenyrwu2a674902016-07-20 09:48:04 -0700266 updateFailoverGroup(intent, backupLinks);
267 return reusableIntents;
268 }
269 }
270
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800271 intentList.add(createPathIntent(new DefaultPath(PID, links, path.weight(),
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700272 path.annotations()),
helenyrwu2a674902016-07-20 09:48:04 -0700273 intent, PathIntent.ProtectionType.PRIMARY));
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800274 intentList.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().weight(),
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700275 path.backup().annotations()),
276 intent, PathIntent.ProtectionType.BACKUP));
helenyrwu2a674902016-07-20 09:48:04 -0700277
278 // Create fast failover flow rule intent or, if it already exists,
279 // add contents appropriately.
280 if (groupService.getGroup(ingressPoint.deviceId(),
281 makeGroupKey(intent.id())) == null) {
282 // manufactured fast failover flow rule intent
283 createFailoverTreatmentGroup(path.links(), path.backup().links(), intent);
284
285 FlowRuleIntent frIntent = new FlowRuleIntent(intent.appId(),
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700286 intent.key(),
helenyrwu2a674902016-07-20 09:48:04 -0700287 createFailoverFlowRules(intent),
288 asList(ingressPoint.deviceId()),
Luca Prete670ac5d2017-02-03 15:55:43 -0800289 PathIntent.ProtectionType.FAILOVER,
290 intent.resourceGroup());
helenyrwu2a674902016-07-20 09:48:04 -0700291 intentList.add(frIntent);
292 } else {
293 updateFailoverGroup(intent, links);
294 updateFailoverGroup(intent, backupLinks);
295 }
296
297 return intentList;
298 }
299
300 private List<Intent> createSinglePathIntent(ConnectPoint ingressPoint,
301 ConnectPoint egressPoint,
302 PointToPointIntent intent,
303 List<Intent> installable) {
304 List<Link> links = new ArrayList<>();
Luca Preted26ea652017-01-03 15:59:30 -0800305 Path onlyPath = getPathOrException(intent, ingressPoint.deviceId(),
306 egressPoint.deviceId());
helenyrwu2a674902016-07-20 09:48:04 -0700307
308 List<Intent> reusableIntents = null;
309 if (installable != null) {
310 reusableIntents = filterInvalidSubIntents(installable, intent);
311 if (reusableIntents.size() == installable.size()) {
312 // all old paths are still viable
313 return installable;
314 }
315 }
316
317 // If there exists a full path from old installable intents,
318 // return the intents that comprise it.
319 if (reusableIntents != null && reusableIntents.size() > 1) {
320 return reusableIntents;
321 } else {
Luca Pretede10c782017-01-05 17:23:08 -0800322 // Allocate bandwidth if a bandwidth constraint is set
323 ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
324 ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
325
326 List<ConnectPoint> pathCPs =
327 onlyPath.links().stream()
328 .flatMap(l -> Stream.of(l.src(), l.dst()))
329 .collect(Collectors.toList());
330
331 pathCPs.add(ingressCP);
332 pathCPs.add(egressCP);
333
334 // Allocate bandwidth if a bandwidth constraint is set
335 allocateBandwidth(intent, pathCPs);
336
helenyrwu2a674902016-07-20 09:48:04 -0700337 links.add(createEdgeLink(ingressPoint, true));
338 links.addAll(onlyPath.links());
339 links.add(createEdgeLink(egressPoint, false));
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800340 return asList(createPathIntent(new DefaultPath(PID, links, onlyPath.weight(),
helenyrwu2a674902016-07-20 09:48:04 -0700341 onlyPath.annotations()),
342 intent, PathIntent.ProtectionType.PRIMARY));
343 }
Ray Milkeya058c732014-10-08 13:52:34 -0700344 }
345
346 /**
347 * Creates a path intent from the specified path and original
348 * connectivity intent.
349 *
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700350 * @param path path to create an intent for
Ray Milkeya058c732014-10-08 13:52:34 -0700351 * @param intent original intent
helenyrwu2a674902016-07-20 09:48:04 -0700352 * @param type primary or backup
Ray Milkeya058c732014-10-08 13:52:34 -0700353 */
354 private Intent createPathIntent(Path path,
helenyrwu2a674902016-07-20 09:48:04 -0700355 PointToPointIntent intent,
356 PathIntent.ProtectionType type) {
Ray Milkeyebc5d222015-03-18 15:45:36 -0700357 return PathIntent.builder()
358 .appId(intent.appId())
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700359 .key(intent.key())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700360 .selector(intent.selector())
361 .treatment(intent.treatment())
362 .path(path)
363 .constraints(intent.constraints())
364 .priority(intent.priority())
helenyrwu2a674902016-07-20 09:48:04 -0700365 .setType(type)
Luca Prete670ac5d2017-02-03 15:55:43 -0800366 .resourceGroup(intent.resourceGroup())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700367 .build();
Ray Milkeya058c732014-10-08 13:52:34 -0700368 }
369
Pier Ventreffe88d62016-10-13 14:34:40 -0700370
371 /**
372 * Creates a link collection intent from the specified path and original
373 * point to point intent.
374 *
375 * @param links the links of the packets
376 * @param cost the cost associated to the links
377 * @param intent the point to point intent we are compiling
378 * @return the link collection intent
379 */
380 private Intent createLinkCollectionIntent(Set<Link> links,
381 double cost,
382 PointToPointIntent intent) {
383
384 return LinkCollectionIntent.builder()
385 .key(intent.key())
386 .appId(intent.appId())
387 .selector(intent.selector())
388 .treatment(intent.treatment())
389 .links(ImmutableSet.copyOf(links))
390 .filteredIngressPoints(ImmutableSet.of(
391 intent.filteredIngressPoint()
392 ))
393 .filteredEgressPoints(ImmutableSet.of(
394 intent.filteredEgressPoint()
395 ))
396 .applyTreatmentOnEgress(true)
397 .constraints(intent.constraints())
398 .priority(intent.priority())
399 .cost(cost)
Luca Prete670ac5d2017-02-03 15:55:43 -0800400 .resourceGroup(intent.resourceGroup())
Pier Ventreffe88d62016-10-13 14:34:40 -0700401 .build();
402 }
403
helenyrwu2a674902016-07-20 09:48:04 -0700404 /**
405 * Gets primary port number through failover group associated
406 * with this intent.
407 */
408 private PortNumber getPrimaryPort(PointToPointIntent intent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700409 Group group = groupService.getGroup(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700410 makeGroupKey(intent.id()));
411 PortNumber primaryPort = null;
412 if (group != null) {
413 List<GroupBucket> buckets = group.buckets().buckets();
414 Iterator<GroupBucket> iterator = buckets.iterator();
415 while (primaryPort == null && iterator.hasNext()) {
416 GroupBucket bucket = iterator.next();
417 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
418 if (individualInstruction instanceof Instructions.OutputInstruction) {
419 Instructions.OutputInstruction outInstruction =
420 (Instructions.OutputInstruction) individualInstruction;
421 PortNumber tempPortNum = outInstruction.port();
Pier Ventreffe88d62016-10-13 14:34:40 -0700422 Port port = deviceService.getPort(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700423 tempPortNum);
424 if (port != null && port.isEnabled()) {
425 primaryPort = tempPortNum;
426 }
427 }
428 }
429 }
430 return primaryPort;
431 }
432
433 /**
434 * Creates group key unique to each intent.
Ray Milkeyef794342016-11-09 16:20:29 -0800435 *
436 * @param intentId identifier of intent to get a key for
437 * @return unique group key for the intent identifier
helenyrwu2a674902016-07-20 09:48:04 -0700438 */
439 public static GroupKey makeGroupKey(IntentId intentId) {
440 ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
441 buffer.putLong(intentId.fingerprint());
442 return new DefaultGroupKey(buffer.array());
443 }
444
445 /**
446 * Creates a new failover group with the initial ports of the links
447 * from the primary and backup path.
448 *
449 * @param links links from the primary path
450 * @param backupLinks links from the backup path
451 * @param intent intent from which this call originates
452 */
453 private void createFailoverTreatmentGroup(List<Link> links,
454 List<Link> backupLinks,
455 PointToPointIntent intent) {
456
457 List<GroupBucket> buckets = new ArrayList<>();
458
459 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
460 ConnectPoint src = links.get(0).src();
461 tBuilderIn.setOutput(src.port());
462
463 TrafficTreatment.Builder tBuilderIn2 = DefaultTrafficTreatment.builder();
464 ConnectPoint src2 = backupLinks.get(0).src();
465 tBuilderIn2.setOutput(src2.port());
466
467 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null));
468 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn2.build(), src2.port(), null));
469
470 GroupBuckets groupBuckets = new GroupBuckets(buckets);
471
472 GroupDescription groupDesc = new DefaultGroupDescription(src.deviceId(), Group.Type.FAILOVER,
473 groupBuckets, makeGroupKey(intent.id()), null, intent.appId());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700474 log.trace("adding failover group {}", groupDesc);
helenyrwu2a674902016-07-20 09:48:04 -0700475 groupService.addGroup(groupDesc);
476 }
477
478 /**
479 * Manufactures flow rule with treatment that is defined by failover
480 * group and traffic selector determined by ingress port of the intent.
481 *
482 * @param intent intent which is being compiled (for appId)
483 * @return a list of a singular flow rule with fast failover
484 * outport traffic treatment
485 */
486 private List<FlowRule> createFailoverFlowRules(PointToPointIntent intent) {
487 List<FlowRule> flowRules = new ArrayList<>();
488
489 ConnectPoint ingress = intent.ingressPoint();
490 DeviceId deviceId = ingress.deviceId();
491
492 // flow rule with failover traffic treatment
493 TrafficSelector trafficSelector = DefaultTrafficSelector.builder(intent.selector())
494 .matchInPort(ingress.port()).build();
495
496 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
497 flowRules.add(flowRuleBuilder.withSelector(trafficSelector)
498 .withTreatment(buildFailoverTreatment(deviceId, makeGroupKey(intent.id())))
499 .fromApp(intent.appId())
500 .makePermanent()
501 .forDevice(deviceId)
502 .withPriority(PRIORITY)
503 .build());
504
505 return flowRules;
506 }
507
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700508
509 /**
510 * Waits for specified group to appear maximum of {@value #GROUP_TIMEOUT} seconds.
511 *
512 * @param deviceId {@link DeviceId}
513 * @param groupKey {@link GroupKey} to wait for.
514 * @return {@link Group}
515 * @throws IntentCompilationException on any error.
516 */
517 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey) {
518 return waitForGroup(deviceId, groupKey, GROUP_TIMEOUT, TimeUnit.SECONDS);
519 }
520
521 /**
522 * Waits for specified group to appear until timeout.
523 *
524 * @param deviceId {@link DeviceId}
525 * @param groupKey {@link GroupKey} to wait for.
526 * @param timeout timeout
527 * @param unit unit of timeout
528 * @return {@link Group}
529 * @throws IntentCompilationException on any error.
530 */
531 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey, long timeout, TimeUnit unit) {
532 Group group = groupService.getGroup(deviceId, groupKey);
533 if (group != null) {
534 return group;
535 }
536
537 final CompletableFuture<Group> future = new CompletableFuture<>();
538 final GroupListener listener = event -> {
539 if (event.subject().deviceId() == deviceId &&
540 event.subject().appCookie().equals(groupKey)) {
541 future.complete(event.subject());
542 return;
543 }
544 };
545
546 groupService.addListener(listener);
547 try {
548 group = groupService.getGroup(deviceId, groupKey);
549 if (group != null) {
550 return group;
551 }
552 return future.get(timeout, unit);
553 } catch (InterruptedException e) {
554 log.debug("Interrupted", e);
555 Thread.currentThread().interrupt();
556 throw new IntentCompilationException("Interrupted", e);
557 } catch (ExecutionException e) {
558 log.debug("ExecutionException", e);
559 throw new IntentCompilationException("ExecutionException caught", e);
560 } catch (TimeoutException e) {
561 // one last try
562 group = groupService.getGroup(deviceId, groupKey);
563 if (group != null) {
564 return group;
565 } else {
566 log.debug("Timeout", e);
567 throw new IntentCompilationException("Timeout", e);
568 }
569 } finally {
570 groupService.removeListener(listener);
571 }
572 }
573
helenyrwu2a674902016-07-20 09:48:04 -0700574 private TrafficTreatment buildFailoverTreatment(DeviceId srcDevice,
575 GroupKey groupKey) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700576 Group group = waitForGroup(srcDevice, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700577 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
578 TrafficTreatment trafficTreatment = tBuilder.group(group.id()).build();
579 return trafficTreatment;
580 }
581
582 /**
583 * Deletes intents from the given list if the ports or links the intent
584 * relies on are no longer viable. The failover flow rule intent is never
585 * deleted -- only its contents are updated.
586 *
587 * @param oldInstallables list of intents to examine
588 * @return list of reusable installable intents
589 */
590 private List<Intent> filterInvalidSubIntents(List<Intent> oldInstallables,
591 PointToPointIntent pointIntent) {
592 List<Intent> intentList = new ArrayList<>();
593 intentList.addAll(oldInstallables);
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800594
595 Iterator<Intent> iterator = intentList.iterator();
596 while (iterator.hasNext()) {
597 Intent intent = iterator.next();
598 intent.resources().forEach(resource -> {
599 if (resource instanceof Link) {
600 Link link = (Link) resource;
601 if (link.state() == Link.State.INACTIVE) {
602 setPathsToRemove(intent);
603 } else if (link instanceof EdgeLink) {
604 ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId)
605 ? link.src() : link.dst();
606 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
607 if (port == null || !port.isEnabled()) {
helenyrwu2a674902016-07-20 09:48:04 -0700608 setPathsToRemove(intent);
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800609 }
610 } else {
611 Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port());
612 Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port());
613 if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) {
614 setPathsToRemove(intent);
helenyrwu2a674902016-07-20 09:48:04 -0700615 }
616 }
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800617 }
618 });
helenyrwu2a674902016-07-20 09:48:04 -0700619 }
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800620 removeAndUpdateIntents(intentList, pointIntent);
621
helenyrwu2a674902016-07-20 09:48:04 -0700622 return intentList;
623 }
624
625 /**
626 * Sets instance variables erasePrimary and eraseBackup. If erasePrimary,
627 * the primary path is no longer viable and related intents will be deleted.
628 * If eraseBackup, the backup path is no longer viable and related intents
629 * will be deleted.
630 *
631 * @param intent intent whose resources are found to be disabled/inactive:
632 * if intent is part of primary path, primary path set for removal;
633 * if intent is part of backup path, backup path set for removal;
634 * if bad intent is of type failover, the ingress point is down,
635 * and both paths are rendered inactive.
636 * @return true if both primary and backup paths are to be removed
637 */
638 private boolean setPathsToRemove(Intent intent) {
639 if (intent instanceof FlowRuleIntent) {
640 FlowRuleIntent frIntent = (FlowRuleIntent) intent;
641 PathIntent.ProtectionType type = frIntent.type();
642 if (type == PathIntent.ProtectionType.PRIMARY || type == PathIntent.ProtectionType.FAILOVER) {
643 erasePrimary = true;
644 }
645 if (type == PathIntent.ProtectionType.BACKUP || type == PathIntent.ProtectionType.FAILOVER) {
646 eraseBackup = true;
647 }
648 }
649 return erasePrimary && eraseBackup;
650 }
651
652 /**
653 * Removes intents from installables list, depending on the values
654 * of instance variables erasePrimary and eraseBackup. Flow rule intents
655 * that contain the manufactured fast failover flow rules are never deleted.
656 * The contents are simply modified as necessary. If cleanUpIntents size
657 * is greater than 1 (failover intent), then one whole path from previous
658 * installables must be still viable.
659 *
660 * @param cleanUpIntents list of installable intents
661 */
662 private void removeAndUpdateIntents(List<Intent> cleanUpIntents,
663 PointToPointIntent pointIntent) {
664 ListIterator<Intent> iterator = cleanUpIntents.listIterator();
665 while (iterator.hasNext()) {
666 Intent cIntent = iterator.next();
667 if (cIntent instanceof FlowRuleIntent) {
668 FlowRuleIntent fIntent = (FlowRuleIntent) cIntent;
669 if (fIntent.type() == PathIntent.ProtectionType.PRIMARY && erasePrimary) {
670 // remove primary path's flow rule intents
671 iterator.remove();
672 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && eraseBackup) {
673 //remove backup path's flow rule intents
674 iterator.remove();
675 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && erasePrimary) {
676 // promote backup path's flow rule intents to primary
677 iterator.set(new FlowRuleIntent(fIntent, PathIntent.ProtectionType.PRIMARY));
678 }
679 }
680 }
681 // remove buckets whose watchports are disabled if the failover group exists
Pier Ventreffe88d62016-10-13 14:34:40 -0700682 Group group = groupService.getGroup(pointIntent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700683 makeGroupKey(pointIntent.id()));
684 if (group != null) {
685 updateFailoverGroup(pointIntent);
686 }
687 }
688
689 // Removes buckets whose treatments rely on disabled ports from the
690 // failover group.
691 private void updateFailoverGroup(PointToPointIntent pointIntent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700692 DeviceId deviceId = pointIntent.filteredIngressPoint().connectPoint().deviceId();
helenyrwu2a674902016-07-20 09:48:04 -0700693 GroupKey groupKey = makeGroupKey(pointIntent.id());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700694 Group group = waitForGroup(deviceId, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700695 Iterator<GroupBucket> groupIterator = group.buckets().buckets().iterator();
696 while (groupIterator.hasNext()) {
697 GroupBucket bucket = groupIterator.next();
698 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
699 if (individualInstruction instanceof Instructions.OutputInstruction) {
700 Instructions.OutputInstruction outInstruction =
701 (Instructions.OutputInstruction) individualInstruction;
702 Port port = deviceService.getPort(deviceId, outInstruction.port());
703 if (port == null || !port.isEnabled()) {
704 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
705 groupService.removeBucketsFromGroup(deviceId, groupKey,
706 removeBuckets, groupKey,
707 pointIntent.appId());
708 }
709 }
710 }
711 }
712
713 // Adds failover group bucket with treatment outport determined by the
714 // ingress point of the links.
715 private void updateFailoverGroup(PointToPointIntent intent, List<Link> links) {
716 GroupKey groupKey = makeGroupKey(intent.id());
717
718 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
719 ConnectPoint src = links.get(0).src();
720 tBuilderIn.setOutput(src.port());
721 GroupBucket bucket = DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null);
722 GroupBuckets addBuckets = new GroupBuckets(Collections.singletonList(bucket));
723
724 groupService.addBucketsToGroup(src.deviceId(), groupKey, addBuckets, groupKey, intent.appId());
725 }
Ray Milkeya058c732014-10-08 13:52:34 -0700726}