blob: bc16ddcd06274cfa6ea232cca441e4f2d9d443f5 [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 Milkeyd84f89b2018-08-17 14:54:17 -070019import org.osgi.service.component.annotations.Activate;
20import org.osgi.service.component.annotations.Component;
21import org.osgi.service.component.annotations.Deactivate;
22import org.osgi.service.component.annotations.Reference;
23import org.osgi.service.component.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
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
helenyrwu2a674902016-07-20 09:48:04 -0700107 protected GroupService groupService;
108
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
helenyrwu2a674902016-07-20 09:48:04 -0700110 protected LinkService linkService;
111
Ray Milkeya058c732014-10-08 13:52:34 -0700112 @Activate
113 public void activate() {
Ray Milkeya058c732014-10-08 13:52:34 -0700114 intentManager.registerCompiler(PointToPointIntent.class, this);
115 }
116
117 @Deactivate
118 public void deactivate() {
119 intentManager.unregisterCompiler(PointToPointIntent.class);
120 }
121
122 @Override
Sho SHIMIZUec07ffd2016-02-22 20:45:21 -0800123 public List<Intent> compile(PointToPointIntent intent, List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700124 log.trace("compiling {} {}", intent, installable);
Luca Pretede10c782017-01-05 17:23:08 -0800125 ConnectPoint ingressPoint = intent.filteredIngressPoint().connectPoint();
126 ConnectPoint egressPoint = intent.filteredEgressPoint().connectPoint();
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800127
128 if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700129 return createZeroHopLinkCollectionIntent(intent);
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800130 }
Ray Milkeya058c732014-10-08 13:52:34 -0700131
helenyrwu2a674902016-07-20 09:48:04 -0700132 // proceed with no protected paths
133 if (!ProtectionConstraint.requireProtectedPath(intent)) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700134 return createUnprotectedLinkCollectionIntent(intent);
helenyrwu2a674902016-07-20 09:48:04 -0700135 }
136
137 try {
138 // attempt to compute and implement backup path
139 return createProtectedIntent(ingressPoint, egressPoint, intent, installable);
140 } catch (PathNotFoundException e) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700141 log.warn("Could not find disjoint Path for {}", intent);
helenyrwu2a674902016-07-20 09:48:04 -0700142 // no disjoint path extant -- maximum one path exists between devices
143 return createSinglePathIntent(ingressPoint, egressPoint, intent, installable);
144 }
145 }
146
147 private List<Intent> createZeroHopIntent(ConnectPoint ingressPoint,
148 ConnectPoint egressPoint,
149 PointToPointIntent intent) {
150 List<Link> links = asList(createEdgeLink(ingressPoint, true), createEdgeLink(egressPoint, false));
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800151 return asList(createPathIntent(new DefaultPath(PID, links, ScalarWeight.toWeight(DEFAULT_COST)),
helenyrwu2a674902016-07-20 09:48:04 -0700152 intent, PathIntent.ProtectionType.PRIMARY));
153 }
154
Pier Ventreffe88d62016-10-13 14:34:40 -0700155 private List<Intent> createZeroHopLinkCollectionIntent(PointToPointIntent intent) {
156 return asList(createLinkCollectionIntent(ImmutableSet.of(), DEFAULT_COST,
157 intent));
158 }
159
Pier Ventreffe88d62016-10-13 14:34:40 -0700160 private List<Intent> createUnprotectedLinkCollectionIntent(PointToPointIntent intent) {
Luca Preted26ea652017-01-03 15:59:30 -0800161 Path path = getPathOrException(intent, intent.filteredIngressPoint().connectPoint().deviceId(),
162 intent.filteredEgressPoint().connectPoint().deviceId());
Pier Ventreffe88d62016-10-13 14:34:40 -0700163
Luca Pretede10c782017-01-05 17:23:08 -0800164 // Allocate bandwidth if a bandwidth constraint is set
165 ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
166 ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
167
168 List<ConnectPoint> pathCPs =
169 path.links().stream()
170 .flatMap(l -> Stream.of(l.src(), l.dst()))
171 .collect(Collectors.toList());
172
173 pathCPs.add(ingressCP);
174 pathCPs.add(egressCP);
175
176 allocateBandwidth(intent, pathCPs);
177
Pier Ventreffe88d62016-10-13 14:34:40 -0700178 return asList(createLinkCollectionIntent(ImmutableSet.copyOf(path.links()),
179 path.cost(),
180 intent));
181 }
182
helenyrwu2a674902016-07-20 09:48:04 -0700183 //FIXME: Compatibility with EncapsulationConstraint
184 private List<Intent> createProtectedIntent(ConnectPoint ingressPoint,
185 ConnectPoint egressPoint,
186 PointToPointIntent intent,
187 List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700188 log.trace("createProtectedIntent");
helenyrwu2a674902016-07-20 09:48:04 -0700189 DisjointPath path = getDisjointPath(intent, ingressPoint.deviceId(),
190 egressPoint.deviceId());
191
192 List<Intent> reusableIntents = null;
193 if (installable != null) {
194 reusableIntents = filterInvalidSubIntents(installable, intent);
195 if (reusableIntents.size() == installable.size()) {
196 // all old paths are still viable
197 return installable;
198 }
199 }
200
201 List<Intent> intentList = new ArrayList<>();
202
203 // primary path intent
204 List<Link> links = new ArrayList<>();
205 links.addAll(path.links());
206 links.add(createEdgeLink(egressPoint, false));
207
208 // backup path intent
209 List<Link> backupLinks = new ArrayList<>();
210 backupLinks.addAll(path.backup().links());
211 backupLinks.add(createEdgeLink(egressPoint, false));
212
213 /*
214 * One of the old paths is still entirely intact. This old path has
215 * already been made primary, so we must add a backup path intent
216 * and modify the failover group treatment accordingly.
217 */
218 if (reusableIntents != null && reusableIntents.size() > 1) {
219 /*
220 * Ensures that the egress port on source device is different than
221 * that of existing path so that failover group will be useful
222 * (would not be useful if both output ports in group bucket were
223 * the same). Does not necessarily ensure that the new backup path
224 * is entirely disjoint from the old path.
225 */
226 PortNumber primaryPort = getPrimaryPort(intent);
227 if (primaryPort != null && !links.get(0).src().port().equals(primaryPort)) {
228 reusableIntents.add(createPathIntent(new DefaultPath(PID, links,
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800229 path.weight(), path.annotations()),
helenyrwu2a674902016-07-20 09:48:04 -0700230 intent, PathIntent.ProtectionType.BACKUP));
231 updateFailoverGroup(intent, links);
232 return reusableIntents;
233
234 } else {
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800235 reusableIntents.add(createPathIntent(new DefaultPath(PID, backupLinks,
236 path.backup().weight(),
237 path.backup().annotations()), intent, PathIntent.ProtectionType.BACKUP));
helenyrwu2a674902016-07-20 09:48:04 -0700238 updateFailoverGroup(intent, backupLinks);
239 return reusableIntents;
240 }
241 }
242
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800243 intentList.add(createPathIntent(new DefaultPath(PID, links, path.weight(),
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700244 path.annotations()),
helenyrwu2a674902016-07-20 09:48:04 -0700245 intent, PathIntent.ProtectionType.PRIMARY));
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800246 intentList.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().weight(),
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700247 path.backup().annotations()),
248 intent, PathIntent.ProtectionType.BACKUP));
helenyrwu2a674902016-07-20 09:48:04 -0700249
250 // Create fast failover flow rule intent or, if it already exists,
251 // add contents appropriately.
252 if (groupService.getGroup(ingressPoint.deviceId(),
253 makeGroupKey(intent.id())) == null) {
254 // manufactured fast failover flow rule intent
255 createFailoverTreatmentGroup(path.links(), path.backup().links(), intent);
256
257 FlowRuleIntent frIntent = new FlowRuleIntent(intent.appId(),
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700258 intent.key(),
helenyrwu2a674902016-07-20 09:48:04 -0700259 createFailoverFlowRules(intent),
260 asList(ingressPoint.deviceId()),
Luca Prete670ac5d2017-02-03 15:55:43 -0800261 PathIntent.ProtectionType.FAILOVER,
262 intent.resourceGroup());
helenyrwu2a674902016-07-20 09:48:04 -0700263 intentList.add(frIntent);
264 } else {
265 updateFailoverGroup(intent, links);
266 updateFailoverGroup(intent, backupLinks);
267 }
268
269 return intentList;
270 }
271
272 private List<Intent> createSinglePathIntent(ConnectPoint ingressPoint,
273 ConnectPoint egressPoint,
274 PointToPointIntent intent,
275 List<Intent> installable) {
276 List<Link> links = new ArrayList<>();
Luca Preted26ea652017-01-03 15:59:30 -0800277 Path onlyPath = getPathOrException(intent, ingressPoint.deviceId(),
278 egressPoint.deviceId());
helenyrwu2a674902016-07-20 09:48:04 -0700279
280 List<Intent> reusableIntents = null;
281 if (installable != null) {
282 reusableIntents = filterInvalidSubIntents(installable, intent);
283 if (reusableIntents.size() == installable.size()) {
284 // all old paths are still viable
285 return installable;
286 }
287 }
288
289 // If there exists a full path from old installable intents,
290 // return the intents that comprise it.
291 if (reusableIntents != null && reusableIntents.size() > 1) {
292 return reusableIntents;
293 } else {
Luca Pretede10c782017-01-05 17:23:08 -0800294 // Allocate bandwidth if a bandwidth constraint is set
295 ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
296 ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
297
298 List<ConnectPoint> pathCPs =
299 onlyPath.links().stream()
300 .flatMap(l -> Stream.of(l.src(), l.dst()))
301 .collect(Collectors.toList());
302
303 pathCPs.add(ingressCP);
304 pathCPs.add(egressCP);
305
306 // Allocate bandwidth if a bandwidth constraint is set
307 allocateBandwidth(intent, pathCPs);
308
helenyrwu2a674902016-07-20 09:48:04 -0700309 links.add(createEdgeLink(ingressPoint, true));
310 links.addAll(onlyPath.links());
311 links.add(createEdgeLink(egressPoint, false));
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800312 return asList(createPathIntent(new DefaultPath(PID, links, onlyPath.weight(),
helenyrwu2a674902016-07-20 09:48:04 -0700313 onlyPath.annotations()),
314 intent, PathIntent.ProtectionType.PRIMARY));
315 }
Ray Milkeya058c732014-10-08 13:52:34 -0700316 }
317
318 /**
319 * Creates a path intent from the specified path and original
320 * connectivity intent.
321 *
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700322 * @param path path to create an intent for
Ray Milkeya058c732014-10-08 13:52:34 -0700323 * @param intent original intent
helenyrwu2a674902016-07-20 09:48:04 -0700324 * @param type primary or backup
Ray Milkeya058c732014-10-08 13:52:34 -0700325 */
326 private Intent createPathIntent(Path path,
helenyrwu2a674902016-07-20 09:48:04 -0700327 PointToPointIntent intent,
328 PathIntent.ProtectionType type) {
Ray Milkeyebc5d222015-03-18 15:45:36 -0700329 return PathIntent.builder()
330 .appId(intent.appId())
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700331 .key(intent.key())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700332 .selector(intent.selector())
333 .treatment(intent.treatment())
334 .path(path)
335 .constraints(intent.constraints())
336 .priority(intent.priority())
helenyrwu2a674902016-07-20 09:48:04 -0700337 .setType(type)
Luca Prete670ac5d2017-02-03 15:55:43 -0800338 .resourceGroup(intent.resourceGroup())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700339 .build();
Ray Milkeya058c732014-10-08 13:52:34 -0700340 }
341
Pier Ventreffe88d62016-10-13 14:34:40 -0700342
343 /**
344 * Creates a link collection intent from the specified path and original
345 * point to point intent.
346 *
347 * @param links the links of the packets
348 * @param cost the cost associated to the links
349 * @param intent the point to point intent we are compiling
350 * @return the link collection intent
351 */
352 private Intent createLinkCollectionIntent(Set<Link> links,
353 double cost,
354 PointToPointIntent intent) {
355
356 return LinkCollectionIntent.builder()
357 .key(intent.key())
358 .appId(intent.appId())
359 .selector(intent.selector())
360 .treatment(intent.treatment())
361 .links(ImmutableSet.copyOf(links))
362 .filteredIngressPoints(ImmutableSet.of(
363 intent.filteredIngressPoint()
364 ))
365 .filteredEgressPoints(ImmutableSet.of(
366 intent.filteredEgressPoint()
367 ))
368 .applyTreatmentOnEgress(true)
369 .constraints(intent.constraints())
370 .priority(intent.priority())
371 .cost(cost)
Luca Prete670ac5d2017-02-03 15:55:43 -0800372 .resourceGroup(intent.resourceGroup())
Pier Ventreffe88d62016-10-13 14:34:40 -0700373 .build();
374 }
375
helenyrwu2a674902016-07-20 09:48:04 -0700376 /**
377 * Gets primary port number through failover group associated
378 * with this intent.
379 */
380 private PortNumber getPrimaryPort(PointToPointIntent intent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700381 Group group = groupService.getGroup(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700382 makeGroupKey(intent.id()));
383 PortNumber primaryPort = null;
384 if (group != null) {
385 List<GroupBucket> buckets = group.buckets().buckets();
386 Iterator<GroupBucket> iterator = buckets.iterator();
387 while (primaryPort == null && iterator.hasNext()) {
388 GroupBucket bucket = iterator.next();
389 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
390 if (individualInstruction instanceof Instructions.OutputInstruction) {
391 Instructions.OutputInstruction outInstruction =
392 (Instructions.OutputInstruction) individualInstruction;
393 PortNumber tempPortNum = outInstruction.port();
Pier Ventreffe88d62016-10-13 14:34:40 -0700394 Port port = deviceService.getPort(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700395 tempPortNum);
396 if (port != null && port.isEnabled()) {
397 primaryPort = tempPortNum;
398 }
399 }
400 }
401 }
402 return primaryPort;
403 }
404
405 /**
406 * Creates group key unique to each intent.
Ray Milkeyef794342016-11-09 16:20:29 -0800407 *
408 * @param intentId identifier of intent to get a key for
409 * @return unique group key for the intent identifier
helenyrwu2a674902016-07-20 09:48:04 -0700410 */
411 public static GroupKey makeGroupKey(IntentId intentId) {
412 ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
413 buffer.putLong(intentId.fingerprint());
414 return new DefaultGroupKey(buffer.array());
415 }
416
417 /**
418 * Creates a new failover group with the initial ports of the links
419 * from the primary and backup path.
420 *
421 * @param links links from the primary path
422 * @param backupLinks links from the backup path
423 * @param intent intent from which this call originates
424 */
425 private void createFailoverTreatmentGroup(List<Link> links,
426 List<Link> backupLinks,
427 PointToPointIntent intent) {
428
429 List<GroupBucket> buckets = new ArrayList<>();
430
431 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
432 ConnectPoint src = links.get(0).src();
433 tBuilderIn.setOutput(src.port());
434
435 TrafficTreatment.Builder tBuilderIn2 = DefaultTrafficTreatment.builder();
436 ConnectPoint src2 = backupLinks.get(0).src();
437 tBuilderIn2.setOutput(src2.port());
438
439 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null));
440 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn2.build(), src2.port(), null));
441
442 GroupBuckets groupBuckets = new GroupBuckets(buckets);
443
444 GroupDescription groupDesc = new DefaultGroupDescription(src.deviceId(), Group.Type.FAILOVER,
445 groupBuckets, makeGroupKey(intent.id()), null, intent.appId());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700446 log.trace("adding failover group {}", groupDesc);
helenyrwu2a674902016-07-20 09:48:04 -0700447 groupService.addGroup(groupDesc);
448 }
449
450 /**
451 * Manufactures flow rule with treatment that is defined by failover
452 * group and traffic selector determined by ingress port of the intent.
453 *
454 * @param intent intent which is being compiled (for appId)
455 * @return a list of a singular flow rule with fast failover
456 * outport traffic treatment
457 */
458 private List<FlowRule> createFailoverFlowRules(PointToPointIntent intent) {
459 List<FlowRule> flowRules = new ArrayList<>();
460
Ray Milkeya2cf3a12018-02-15 16:13:56 -0800461 ConnectPoint ingress = intent.filteredIngressPoint().connectPoint();
helenyrwu2a674902016-07-20 09:48:04 -0700462 DeviceId deviceId = ingress.deviceId();
463
464 // flow rule with failover traffic treatment
465 TrafficSelector trafficSelector = DefaultTrafficSelector.builder(intent.selector())
466 .matchInPort(ingress.port()).build();
467
468 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
469 flowRules.add(flowRuleBuilder.withSelector(trafficSelector)
470 .withTreatment(buildFailoverTreatment(deviceId, makeGroupKey(intent.id())))
471 .fromApp(intent.appId())
472 .makePermanent()
473 .forDevice(deviceId)
474 .withPriority(PRIORITY)
475 .build());
476
477 return flowRules;
478 }
479
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700480
481 /**
482 * Waits for specified group to appear maximum of {@value #GROUP_TIMEOUT} seconds.
483 *
484 * @param deviceId {@link DeviceId}
485 * @param groupKey {@link GroupKey} to wait for.
486 * @return {@link Group}
487 * @throws IntentCompilationException on any error.
488 */
489 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey) {
490 return waitForGroup(deviceId, groupKey, GROUP_TIMEOUT, TimeUnit.SECONDS);
491 }
492
493 /**
494 * Waits for specified group to appear until timeout.
495 *
496 * @param deviceId {@link DeviceId}
497 * @param groupKey {@link GroupKey} to wait for.
498 * @param timeout timeout
499 * @param unit unit of timeout
500 * @return {@link Group}
501 * @throws IntentCompilationException on any error.
502 */
503 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey, long timeout, TimeUnit unit) {
504 Group group = groupService.getGroup(deviceId, groupKey);
505 if (group != null) {
506 return group;
507 }
508
509 final CompletableFuture<Group> future = new CompletableFuture<>();
510 final GroupListener listener = event -> {
511 if (event.subject().deviceId() == deviceId &&
512 event.subject().appCookie().equals(groupKey)) {
513 future.complete(event.subject());
514 return;
515 }
516 };
517
518 groupService.addListener(listener);
519 try {
520 group = groupService.getGroup(deviceId, groupKey);
521 if (group != null) {
522 return group;
523 }
524 return future.get(timeout, unit);
525 } catch (InterruptedException e) {
526 log.debug("Interrupted", e);
527 Thread.currentThread().interrupt();
528 throw new IntentCompilationException("Interrupted", e);
529 } catch (ExecutionException e) {
530 log.debug("ExecutionException", e);
531 throw new IntentCompilationException("ExecutionException caught", e);
532 } catch (TimeoutException e) {
533 // one last try
534 group = groupService.getGroup(deviceId, groupKey);
535 if (group != null) {
536 return group;
537 } else {
538 log.debug("Timeout", e);
539 throw new IntentCompilationException("Timeout", e);
540 }
541 } finally {
542 groupService.removeListener(listener);
543 }
544 }
545
helenyrwu2a674902016-07-20 09:48:04 -0700546 private TrafficTreatment buildFailoverTreatment(DeviceId srcDevice,
547 GroupKey groupKey) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700548 Group group = waitForGroup(srcDevice, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700549 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
550 TrafficTreatment trafficTreatment = tBuilder.group(group.id()).build();
551 return trafficTreatment;
552 }
553
554 /**
555 * Deletes intents from the given list if the ports or links the intent
556 * relies on are no longer viable. The failover flow rule intent is never
557 * deleted -- only its contents are updated.
558 *
559 * @param oldInstallables list of intents to examine
560 * @return list of reusable installable intents
561 */
562 private List<Intent> filterInvalidSubIntents(List<Intent> oldInstallables,
563 PointToPointIntent pointIntent) {
564 List<Intent> intentList = new ArrayList<>();
565 intentList.addAll(oldInstallables);
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800566
567 Iterator<Intent> iterator = intentList.iterator();
568 while (iterator.hasNext()) {
569 Intent intent = iterator.next();
570 intent.resources().forEach(resource -> {
571 if (resource instanceof Link) {
572 Link link = (Link) resource;
573 if (link.state() == Link.State.INACTIVE) {
574 setPathsToRemove(intent);
575 } else if (link instanceof EdgeLink) {
576 ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId)
577 ? link.src() : link.dst();
578 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
579 if (port == null || !port.isEnabled()) {
helenyrwu2a674902016-07-20 09:48:04 -0700580 setPathsToRemove(intent);
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800581 }
582 } else {
583 Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port());
584 Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port());
585 if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) {
586 setPathsToRemove(intent);
helenyrwu2a674902016-07-20 09:48:04 -0700587 }
588 }
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800589 }
590 });
helenyrwu2a674902016-07-20 09:48:04 -0700591 }
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800592 removeAndUpdateIntents(intentList, pointIntent);
593
helenyrwu2a674902016-07-20 09:48:04 -0700594 return intentList;
595 }
596
597 /**
598 * Sets instance variables erasePrimary and eraseBackup. If erasePrimary,
599 * the primary path is no longer viable and related intents will be deleted.
600 * If eraseBackup, the backup path is no longer viable and related intents
601 * will be deleted.
602 *
603 * @param intent intent whose resources are found to be disabled/inactive:
604 * if intent is part of primary path, primary path set for removal;
605 * if intent is part of backup path, backup path set for removal;
606 * if bad intent is of type failover, the ingress point is down,
607 * and both paths are rendered inactive.
608 * @return true if both primary and backup paths are to be removed
609 */
610 private boolean setPathsToRemove(Intent intent) {
611 if (intent instanceof FlowRuleIntent) {
612 FlowRuleIntent frIntent = (FlowRuleIntent) intent;
613 PathIntent.ProtectionType type = frIntent.type();
614 if (type == PathIntent.ProtectionType.PRIMARY || type == PathIntent.ProtectionType.FAILOVER) {
615 erasePrimary = true;
616 }
617 if (type == PathIntent.ProtectionType.BACKUP || type == PathIntent.ProtectionType.FAILOVER) {
618 eraseBackup = true;
619 }
620 }
621 return erasePrimary && eraseBackup;
622 }
623
624 /**
625 * Removes intents from installables list, depending on the values
626 * of instance variables erasePrimary and eraseBackup. Flow rule intents
627 * that contain the manufactured fast failover flow rules are never deleted.
628 * The contents are simply modified as necessary. If cleanUpIntents size
629 * is greater than 1 (failover intent), then one whole path from previous
630 * installables must be still viable.
631 *
632 * @param cleanUpIntents list of installable intents
633 */
634 private void removeAndUpdateIntents(List<Intent> cleanUpIntents,
635 PointToPointIntent pointIntent) {
636 ListIterator<Intent> iterator = cleanUpIntents.listIterator();
637 while (iterator.hasNext()) {
638 Intent cIntent = iterator.next();
639 if (cIntent instanceof FlowRuleIntent) {
640 FlowRuleIntent fIntent = (FlowRuleIntent) cIntent;
641 if (fIntent.type() == PathIntent.ProtectionType.PRIMARY && erasePrimary) {
642 // remove primary path's flow rule intents
643 iterator.remove();
644 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && eraseBackup) {
645 //remove backup path's flow rule intents
646 iterator.remove();
647 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && erasePrimary) {
648 // promote backup path's flow rule intents to primary
649 iterator.set(new FlowRuleIntent(fIntent, PathIntent.ProtectionType.PRIMARY));
650 }
651 }
652 }
653 // remove buckets whose watchports are disabled if the failover group exists
Pier Ventreffe88d62016-10-13 14:34:40 -0700654 Group group = groupService.getGroup(pointIntent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700655 makeGroupKey(pointIntent.id()));
656 if (group != null) {
657 updateFailoverGroup(pointIntent);
658 }
659 }
660
661 // Removes buckets whose treatments rely on disabled ports from the
662 // failover group.
663 private void updateFailoverGroup(PointToPointIntent pointIntent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700664 DeviceId deviceId = pointIntent.filteredIngressPoint().connectPoint().deviceId();
helenyrwu2a674902016-07-20 09:48:04 -0700665 GroupKey groupKey = makeGroupKey(pointIntent.id());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700666 Group group = waitForGroup(deviceId, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700667 Iterator<GroupBucket> groupIterator = group.buckets().buckets().iterator();
668 while (groupIterator.hasNext()) {
669 GroupBucket bucket = groupIterator.next();
670 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
671 if (individualInstruction instanceof Instructions.OutputInstruction) {
672 Instructions.OutputInstruction outInstruction =
673 (Instructions.OutputInstruction) individualInstruction;
674 Port port = deviceService.getPort(deviceId, outInstruction.port());
675 if (port == null || !port.isEnabled()) {
676 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
677 groupService.removeBucketsFromGroup(deviceId, groupKey,
678 removeBuckets, groupKey,
679 pointIntent.appId());
680 }
681 }
682 }
683 }
684
685 // Adds failover group bucket with treatment outport determined by the
686 // ingress point of the links.
687 private void updateFailoverGroup(PointToPointIntent intent, List<Link> links) {
688 GroupKey groupKey = makeGroupKey(intent.id());
689
690 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
691 ConnectPoint src = links.get(0).src();
692 tBuilderIn.setOutput(src.port());
693 GroupBucket bucket = DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null);
694 GroupBuckets addBuckets = new GroupBuckets(Collections.singletonList(bucket));
695
696 groupService.addBucketsToGroup(src.deviceId(), groupKey, addBuckets, groupKey, intent.appId());
697 }
Ray Milkeya058c732014-10-08 13:52:34 -0700698}