blob: ad65e46da9af8acef227dc26b5d876fabb5e8e02 [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 Milkeya7cf8c82018-02-08 15:07:06 -080019import org.onlab.graph.ScalarWeight;
Brian O'Connorabafb502014-12-02 22:26:20 -080020import org.onosproject.net.ConnectPoint;
21import org.onosproject.net.DefaultPath;
helenyrwu2a674902016-07-20 09:48:04 -070022import org.onosproject.net.DeviceId;
23import org.onosproject.net.DisjointPath;
24import org.onosproject.net.EdgeLink;
Brian O'Connorabafb502014-12-02 22:26:20 -080025import org.onosproject.net.Link;
26import org.onosproject.net.Path;
helenyrwu2a674902016-07-20 09:48:04 -070027import org.onosproject.net.Port;
28import org.onosproject.net.PortNumber;
helenyrwu2a674902016-07-20 09:48:04 -070029import org.onosproject.net.flow.DefaultFlowRule;
30import org.onosproject.net.flow.DefaultTrafficSelector;
31import org.onosproject.net.flow.DefaultTrafficTreatment;
32import org.onosproject.net.flow.FlowRule;
33import org.onosproject.net.flow.TrafficSelector;
34import org.onosproject.net.flow.TrafficTreatment;
35import org.onosproject.net.flow.instructions.Instruction;
36import org.onosproject.net.flow.instructions.Instructions;
37import org.onosproject.net.group.DefaultGroupBucket;
38import org.onosproject.net.group.DefaultGroupDescription;
39import org.onosproject.net.group.DefaultGroupKey;
40import org.onosproject.net.group.Group;
41import org.onosproject.net.group.GroupBucket;
42import org.onosproject.net.group.GroupBuckets;
43import org.onosproject.net.group.GroupDescription;
44import org.onosproject.net.group.GroupKey;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070045import org.onosproject.net.group.GroupListener;
helenyrwu2a674902016-07-20 09:48:04 -070046import org.onosproject.net.group.GroupService;
47import org.onosproject.net.intent.FlowRuleIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.intent.Intent;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070049import org.onosproject.net.intent.IntentCompilationException;
helenyrwu2a674902016-07-20 09:48:04 -070050import org.onosproject.net.intent.IntentId;
Pier Ventreffe88d62016-10-13 14:34:40 -070051import org.onosproject.net.intent.LinkCollectionIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.intent.PathIntent;
53import org.onosproject.net.intent.PointToPointIntent;
helenyrwu2a674902016-07-20 09:48:04 -070054import org.onosproject.net.intent.constraint.ProtectionConstraint;
55import org.onosproject.net.intent.impl.PathNotFoundException;
56import org.onosproject.net.link.LinkService;
Brian O'Connorabafb502014-12-02 22:26:20 -080057import org.onosproject.net.provider.ProviderId;
Ray Milkey86ad7bb2018-09-27 12:32:28 -070058import org.osgi.service.component.annotations.Activate;
59import org.osgi.service.component.annotations.Component;
60import org.osgi.service.component.annotations.Deactivate;
61import org.osgi.service.component.annotations.Reference;
62import org.osgi.service.component.annotations.ReferenceCardinality;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070063import org.slf4j.Logger;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080064
helenyrwu2a674902016-07-20 09:48:04 -070065import java.nio.ByteBuffer;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080066import java.util.ArrayList;
helenyrwu2a674902016-07-20 09:48:04 -070067import java.util.Collections;
68import java.util.Iterator;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080069import java.util.List;
helenyrwu2a674902016-07-20 09:48:04 -070070import java.util.ListIterator;
Pier Ventreffe88d62016-10-13 14:34:40 -070071import java.util.Set;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070072import java.util.concurrent.CompletableFuture;
73import java.util.concurrent.ExecutionException;
74import java.util.concurrent.TimeUnit;
75import java.util.concurrent.TimeoutException;
Luca Pretede10c782017-01-05 17:23:08 -080076import java.util.stream.Collectors;
77import java.util.stream.Stream;
Ray Milkeya058c732014-10-08 13:52:34 -070078
Thomas Vachuska425a2d72014-10-29 11:28:28 -070079import static java.util.Arrays.asList;
Brian O'Connorabafb502014-12-02 22:26:20 -080080import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070081import static org.slf4j.LoggerFactory.getLogger;
Thomas Vachuska425a2d72014-10-29 11:28:28 -070082
Ray Milkeya058c732014-10-08 13:52:34 -070083/**
Brian O'Connorabafb502014-12-02 22:26:20 -080084 * An intent compiler for {@link org.onosproject.net.intent.PointToPointIntent}.
Ray Milkeya058c732014-10-08 13:52:34 -070085 */
86@Component(immediate = true)
87public class PointToPointIntentCompiler
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080088 extends ConnectivityIntentCompiler<PointToPointIntent> {
Ray Milkeya058c732014-10-08 13:52:34 -070089
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080090 // TODO: use off-the-shell core provider ID
91 private static final ProviderId PID =
Brian O'Connorabafb502014-12-02 22:26:20 -080092 new ProviderId("core", "org.onosproject.core", true);
Sho SHIMIZU3908fde2014-11-19 16:30:22 -080093 // TODO: consider whether the default cost is appropriate or not
94 public static final int DEFAULT_COST = 1;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070095
helenyrwu2a674902016-07-20 09:48:04 -070096 protected static final int PRIORITY = Intent.DEFAULT_INTENT_PRIORITY;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070097
98 private static final int GROUP_TIMEOUT = 5;
99
100 private final Logger log = getLogger(getClass());
101
helenyrwu2a674902016-07-20 09:48:04 -0700102 protected boolean erasePrimary = false;
103 protected boolean eraseBackup = false;
104
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
helenyrwu2a674902016-07-20 09:48:04 -0700106 protected GroupService groupService;
107
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
helenyrwu2a674902016-07-20 09:48:04 -0700109 protected LinkService linkService;
110
Ray Milkeya058c732014-10-08 13:52:34 -0700111 @Activate
112 public void activate() {
Ray Milkeya058c732014-10-08 13:52:34 -0700113 intentManager.registerCompiler(PointToPointIntent.class, this);
114 }
115
116 @Deactivate
117 public void deactivate() {
118 intentManager.unregisterCompiler(PointToPointIntent.class);
119 }
120
121 @Override
Sho SHIMIZUec07ffd2016-02-22 20:45:21 -0800122 public List<Intent> compile(PointToPointIntent intent, List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700123 log.trace("compiling {} {}", intent, installable);
Luca Pretede10c782017-01-05 17:23:08 -0800124 ConnectPoint ingressPoint = intent.filteredIngressPoint().connectPoint();
125 ConnectPoint egressPoint = intent.filteredEgressPoint().connectPoint();
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800126
127 if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700128 return createZeroHopLinkCollectionIntent(intent);
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800129 }
Ray Milkeya058c732014-10-08 13:52:34 -0700130
helenyrwu2a674902016-07-20 09:48:04 -0700131 // proceed with no protected paths
132 if (!ProtectionConstraint.requireProtectedPath(intent)) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700133 return createUnprotectedLinkCollectionIntent(intent);
helenyrwu2a674902016-07-20 09:48:04 -0700134 }
135
136 try {
137 // attempt to compute and implement backup path
138 return createProtectedIntent(ingressPoint, egressPoint, intent, installable);
139 } catch (PathNotFoundException e) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700140 log.warn("Could not find disjoint Path for {}", intent);
helenyrwu2a674902016-07-20 09:48:04 -0700141 // no disjoint path extant -- maximum one path exists between devices
142 return createSinglePathIntent(ingressPoint, egressPoint, intent, installable);
143 }
144 }
145
146 private List<Intent> createZeroHopIntent(ConnectPoint ingressPoint,
147 ConnectPoint egressPoint,
148 PointToPointIntent intent) {
149 List<Link> links = asList(createEdgeLink(ingressPoint, true), createEdgeLink(egressPoint, false));
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800150 return asList(createPathIntent(new DefaultPath(PID, links, ScalarWeight.toWeight(DEFAULT_COST)),
helenyrwu2a674902016-07-20 09:48:04 -0700151 intent, PathIntent.ProtectionType.PRIMARY));
152 }
153
Pier Ventreffe88d62016-10-13 14:34:40 -0700154 private List<Intent> createZeroHopLinkCollectionIntent(PointToPointIntent intent) {
155 return asList(createLinkCollectionIntent(ImmutableSet.of(), DEFAULT_COST,
156 intent));
157 }
158
Pier Ventreffe88d62016-10-13 14:34:40 -0700159 private List<Intent> createUnprotectedLinkCollectionIntent(PointToPointIntent intent) {
Luca Preted26ea652017-01-03 15:59:30 -0800160 Path path = getPathOrException(intent, intent.filteredIngressPoint().connectPoint().deviceId(),
161 intent.filteredEgressPoint().connectPoint().deviceId());
Pier Ventreffe88d62016-10-13 14:34:40 -0700162
Luca Pretede10c782017-01-05 17:23:08 -0800163 // Allocate bandwidth if a bandwidth constraint is set
164 ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
165 ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
166
167 List<ConnectPoint> pathCPs =
168 path.links().stream()
169 .flatMap(l -> Stream.of(l.src(), l.dst()))
170 .collect(Collectors.toList());
171
172 pathCPs.add(ingressCP);
173 pathCPs.add(egressCP);
174
175 allocateBandwidth(intent, pathCPs);
176
Pier Ventreffe88d62016-10-13 14:34:40 -0700177 return asList(createLinkCollectionIntent(ImmutableSet.copyOf(path.links()),
178 path.cost(),
179 intent));
180 }
181
helenyrwu2a674902016-07-20 09:48:04 -0700182 //FIXME: Compatibility with EncapsulationConstraint
183 private List<Intent> createProtectedIntent(ConnectPoint ingressPoint,
184 ConnectPoint egressPoint,
185 PointToPointIntent intent,
186 List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700187 log.trace("createProtectedIntent");
helenyrwu2a674902016-07-20 09:48:04 -0700188 DisjointPath path = getDisjointPath(intent, ingressPoint.deviceId(),
189 egressPoint.deviceId());
190
191 List<Intent> reusableIntents = null;
192 if (installable != null) {
193 reusableIntents = filterInvalidSubIntents(installable, intent);
194 if (reusableIntents.size() == installable.size()) {
195 // all old paths are still viable
196 return installable;
197 }
198 }
199
200 List<Intent> intentList = new ArrayList<>();
201
202 // primary path intent
203 List<Link> links = new ArrayList<>();
204 links.addAll(path.links());
205 links.add(createEdgeLink(egressPoint, false));
206
207 // backup path intent
208 List<Link> backupLinks = new ArrayList<>();
209 backupLinks.addAll(path.backup().links());
210 backupLinks.add(createEdgeLink(egressPoint, false));
211
212 /*
213 * One of the old paths is still entirely intact. This old path has
214 * already been made primary, so we must add a backup path intent
215 * and modify the failover group treatment accordingly.
216 */
217 if (reusableIntents != null && reusableIntents.size() > 1) {
218 /*
219 * Ensures that the egress port on source device is different than
220 * that of existing path so that failover group will be useful
221 * (would not be useful if both output ports in group bucket were
222 * the same). Does not necessarily ensure that the new backup path
223 * is entirely disjoint from the old path.
224 */
225 PortNumber primaryPort = getPrimaryPort(intent);
226 if (primaryPort != null && !links.get(0).src().port().equals(primaryPort)) {
227 reusableIntents.add(createPathIntent(new DefaultPath(PID, links,
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800228 path.weight(), path.annotations()),
helenyrwu2a674902016-07-20 09:48:04 -0700229 intent, PathIntent.ProtectionType.BACKUP));
230 updateFailoverGroup(intent, links);
231 return reusableIntents;
232
233 } else {
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800234 reusableIntents.add(createPathIntent(new DefaultPath(PID, backupLinks,
235 path.backup().weight(),
236 path.backup().annotations()), intent, PathIntent.ProtectionType.BACKUP));
helenyrwu2a674902016-07-20 09:48:04 -0700237 updateFailoverGroup(intent, backupLinks);
238 return reusableIntents;
239 }
240 }
241
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800242 intentList.add(createPathIntent(new DefaultPath(PID, links, path.weight(),
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700243 path.annotations()),
helenyrwu2a674902016-07-20 09:48:04 -0700244 intent, PathIntent.ProtectionType.PRIMARY));
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800245 intentList.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().weight(),
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700246 path.backup().annotations()),
247 intent, PathIntent.ProtectionType.BACKUP));
helenyrwu2a674902016-07-20 09:48:04 -0700248
249 // Create fast failover flow rule intent or, if it already exists,
250 // add contents appropriately.
251 if (groupService.getGroup(ingressPoint.deviceId(),
252 makeGroupKey(intent.id())) == null) {
253 // manufactured fast failover flow rule intent
254 createFailoverTreatmentGroup(path.links(), path.backup().links(), intent);
255
256 FlowRuleIntent frIntent = new FlowRuleIntent(intent.appId(),
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700257 intent.key(),
helenyrwu2a674902016-07-20 09:48:04 -0700258 createFailoverFlowRules(intent),
259 asList(ingressPoint.deviceId()),
Luca Prete670ac5d2017-02-03 15:55:43 -0800260 PathIntent.ProtectionType.FAILOVER,
261 intent.resourceGroup());
helenyrwu2a674902016-07-20 09:48:04 -0700262 intentList.add(frIntent);
263 } else {
264 updateFailoverGroup(intent, links);
265 updateFailoverGroup(intent, backupLinks);
266 }
267
268 return intentList;
269 }
270
271 private List<Intent> createSinglePathIntent(ConnectPoint ingressPoint,
272 ConnectPoint egressPoint,
273 PointToPointIntent intent,
274 List<Intent> installable) {
275 List<Link> links = new ArrayList<>();
Luca Preted26ea652017-01-03 15:59:30 -0800276 Path onlyPath = getPathOrException(intent, ingressPoint.deviceId(),
277 egressPoint.deviceId());
helenyrwu2a674902016-07-20 09:48:04 -0700278
279 List<Intent> reusableIntents = null;
280 if (installable != null) {
281 reusableIntents = filterInvalidSubIntents(installable, intent);
282 if (reusableIntents.size() == installable.size()) {
283 // all old paths are still viable
284 return installable;
285 }
286 }
287
288 // If there exists a full path from old installable intents,
289 // return the intents that comprise it.
290 if (reusableIntents != null && reusableIntents.size() > 1) {
291 return reusableIntents;
292 } else {
Luca Pretede10c782017-01-05 17:23:08 -0800293 // Allocate bandwidth if a bandwidth constraint is set
294 ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
295 ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
296
297 List<ConnectPoint> pathCPs =
298 onlyPath.links().stream()
299 .flatMap(l -> Stream.of(l.src(), l.dst()))
300 .collect(Collectors.toList());
301
302 pathCPs.add(ingressCP);
303 pathCPs.add(egressCP);
304
305 // Allocate bandwidth if a bandwidth constraint is set
306 allocateBandwidth(intent, pathCPs);
307
helenyrwu2a674902016-07-20 09:48:04 -0700308 links.add(createEdgeLink(ingressPoint, true));
309 links.addAll(onlyPath.links());
310 links.add(createEdgeLink(egressPoint, false));
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800311 return asList(createPathIntent(new DefaultPath(PID, links, onlyPath.weight(),
helenyrwu2a674902016-07-20 09:48:04 -0700312 onlyPath.annotations()),
313 intent, PathIntent.ProtectionType.PRIMARY));
314 }
Ray Milkeya058c732014-10-08 13:52:34 -0700315 }
316
317 /**
318 * Creates a path intent from the specified path and original
319 * connectivity intent.
320 *
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700321 * @param path path to create an intent for
Ray Milkeya058c732014-10-08 13:52:34 -0700322 * @param intent original intent
helenyrwu2a674902016-07-20 09:48:04 -0700323 * @param type primary or backup
Ray Milkeya058c732014-10-08 13:52:34 -0700324 */
325 private Intent createPathIntent(Path path,
helenyrwu2a674902016-07-20 09:48:04 -0700326 PointToPointIntent intent,
327 PathIntent.ProtectionType type) {
Ray Milkeyebc5d222015-03-18 15:45:36 -0700328 return PathIntent.builder()
329 .appId(intent.appId())
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700330 .key(intent.key())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700331 .selector(intent.selector())
332 .treatment(intent.treatment())
333 .path(path)
334 .constraints(intent.constraints())
335 .priority(intent.priority())
helenyrwu2a674902016-07-20 09:48:04 -0700336 .setType(type)
Luca Prete670ac5d2017-02-03 15:55:43 -0800337 .resourceGroup(intent.resourceGroup())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700338 .build();
Ray Milkeya058c732014-10-08 13:52:34 -0700339 }
340
Pier Ventreffe88d62016-10-13 14:34:40 -0700341
342 /**
343 * Creates a link collection intent from the specified path and original
344 * point to point intent.
345 *
346 * @param links the links of the packets
347 * @param cost the cost associated to the links
348 * @param intent the point to point intent we are compiling
349 * @return the link collection intent
350 */
351 private Intent createLinkCollectionIntent(Set<Link> links,
352 double cost,
353 PointToPointIntent intent) {
354
355 return LinkCollectionIntent.builder()
356 .key(intent.key())
357 .appId(intent.appId())
358 .selector(intent.selector())
359 .treatment(intent.treatment())
360 .links(ImmutableSet.copyOf(links))
361 .filteredIngressPoints(ImmutableSet.of(
362 intent.filteredIngressPoint()
363 ))
364 .filteredEgressPoints(ImmutableSet.of(
365 intent.filteredEgressPoint()
366 ))
367 .applyTreatmentOnEgress(true)
368 .constraints(intent.constraints())
369 .priority(intent.priority())
370 .cost(cost)
Luca Prete670ac5d2017-02-03 15:55:43 -0800371 .resourceGroup(intent.resourceGroup())
Pier Ventreffe88d62016-10-13 14:34:40 -0700372 .build();
373 }
374
helenyrwu2a674902016-07-20 09:48:04 -0700375 /**
376 * Gets primary port number through failover group associated
377 * with this intent.
378 */
379 private PortNumber getPrimaryPort(PointToPointIntent intent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700380 Group group = groupService.getGroup(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700381 makeGroupKey(intent.id()));
382 PortNumber primaryPort = null;
383 if (group != null) {
384 List<GroupBucket> buckets = group.buckets().buckets();
385 Iterator<GroupBucket> iterator = buckets.iterator();
386 while (primaryPort == null && iterator.hasNext()) {
387 GroupBucket bucket = iterator.next();
388 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
389 if (individualInstruction instanceof Instructions.OutputInstruction) {
390 Instructions.OutputInstruction outInstruction =
391 (Instructions.OutputInstruction) individualInstruction;
392 PortNumber tempPortNum = outInstruction.port();
Pier Ventreffe88d62016-10-13 14:34:40 -0700393 Port port = deviceService.getPort(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700394 tempPortNum);
395 if (port != null && port.isEnabled()) {
396 primaryPort = tempPortNum;
397 }
398 }
399 }
400 }
401 return primaryPort;
402 }
403
404 /**
405 * Creates group key unique to each intent.
Ray Milkeyef794342016-11-09 16:20:29 -0800406 *
407 * @param intentId identifier of intent to get a key for
408 * @return unique group key for the intent identifier
helenyrwu2a674902016-07-20 09:48:04 -0700409 */
410 public static GroupKey makeGroupKey(IntentId intentId) {
411 ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
412 buffer.putLong(intentId.fingerprint());
413 return new DefaultGroupKey(buffer.array());
414 }
415
416 /**
417 * Creates a new failover group with the initial ports of the links
418 * from the primary and backup path.
419 *
420 * @param links links from the primary path
421 * @param backupLinks links from the backup path
422 * @param intent intent from which this call originates
423 */
424 private void createFailoverTreatmentGroup(List<Link> links,
425 List<Link> backupLinks,
426 PointToPointIntent intent) {
427
428 List<GroupBucket> buckets = new ArrayList<>();
429
430 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
431 ConnectPoint src = links.get(0).src();
432 tBuilderIn.setOutput(src.port());
433
434 TrafficTreatment.Builder tBuilderIn2 = DefaultTrafficTreatment.builder();
435 ConnectPoint src2 = backupLinks.get(0).src();
436 tBuilderIn2.setOutput(src2.port());
437
438 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null));
439 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn2.build(), src2.port(), null));
440
441 GroupBuckets groupBuckets = new GroupBuckets(buckets);
442
443 GroupDescription groupDesc = new DefaultGroupDescription(src.deviceId(), Group.Type.FAILOVER,
444 groupBuckets, makeGroupKey(intent.id()), null, intent.appId());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700445 log.trace("adding failover group {}", groupDesc);
helenyrwu2a674902016-07-20 09:48:04 -0700446 groupService.addGroup(groupDesc);
447 }
448
449 /**
450 * Manufactures flow rule with treatment that is defined by failover
451 * group and traffic selector determined by ingress port of the intent.
452 *
453 * @param intent intent which is being compiled (for appId)
454 * @return a list of a singular flow rule with fast failover
455 * outport traffic treatment
456 */
457 private List<FlowRule> createFailoverFlowRules(PointToPointIntent intent) {
458 List<FlowRule> flowRules = new ArrayList<>();
459
Ray Milkeya2cf3a12018-02-15 16:13:56 -0800460 ConnectPoint ingress = intent.filteredIngressPoint().connectPoint();
helenyrwu2a674902016-07-20 09:48:04 -0700461 DeviceId deviceId = ingress.deviceId();
462
463 // flow rule with failover traffic treatment
464 TrafficSelector trafficSelector = DefaultTrafficSelector.builder(intent.selector())
465 .matchInPort(ingress.port()).build();
466
467 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
468 flowRules.add(flowRuleBuilder.withSelector(trafficSelector)
469 .withTreatment(buildFailoverTreatment(deviceId, makeGroupKey(intent.id())))
470 .fromApp(intent.appId())
471 .makePermanent()
472 .forDevice(deviceId)
473 .withPriority(PRIORITY)
474 .build());
475
476 return flowRules;
477 }
478
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700479
480 /**
481 * Waits for specified group to appear maximum of {@value #GROUP_TIMEOUT} seconds.
482 *
483 * @param deviceId {@link DeviceId}
484 * @param groupKey {@link GroupKey} to wait for.
485 * @return {@link Group}
486 * @throws IntentCompilationException on any error.
487 */
488 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey) {
489 return waitForGroup(deviceId, groupKey, GROUP_TIMEOUT, TimeUnit.SECONDS);
490 }
491
492 /**
493 * Waits for specified group to appear until timeout.
494 *
495 * @param deviceId {@link DeviceId}
496 * @param groupKey {@link GroupKey} to wait for.
497 * @param timeout timeout
498 * @param unit unit of timeout
499 * @return {@link Group}
500 * @throws IntentCompilationException on any error.
501 */
502 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey, long timeout, TimeUnit unit) {
503 Group group = groupService.getGroup(deviceId, groupKey);
504 if (group != null) {
505 return group;
506 }
507
508 final CompletableFuture<Group> future = new CompletableFuture<>();
509 final GroupListener listener = event -> {
510 if (event.subject().deviceId() == deviceId &&
511 event.subject().appCookie().equals(groupKey)) {
512 future.complete(event.subject());
513 return;
514 }
515 };
516
517 groupService.addListener(listener);
518 try {
519 group = groupService.getGroup(deviceId, groupKey);
520 if (group != null) {
521 return group;
522 }
523 return future.get(timeout, unit);
524 } catch (InterruptedException e) {
525 log.debug("Interrupted", e);
526 Thread.currentThread().interrupt();
527 throw new IntentCompilationException("Interrupted", e);
528 } catch (ExecutionException e) {
529 log.debug("ExecutionException", e);
530 throw new IntentCompilationException("ExecutionException caught", e);
531 } catch (TimeoutException e) {
532 // one last try
533 group = groupService.getGroup(deviceId, groupKey);
534 if (group != null) {
535 return group;
536 } else {
537 log.debug("Timeout", e);
538 throw new IntentCompilationException("Timeout", e);
539 }
540 } finally {
541 groupService.removeListener(listener);
542 }
543 }
544
helenyrwu2a674902016-07-20 09:48:04 -0700545 private TrafficTreatment buildFailoverTreatment(DeviceId srcDevice,
546 GroupKey groupKey) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700547 Group group = waitForGroup(srcDevice, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700548 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
549 TrafficTreatment trafficTreatment = tBuilder.group(group.id()).build();
550 return trafficTreatment;
551 }
552
553 /**
554 * Deletes intents from the given list if the ports or links the intent
555 * relies on are no longer viable. The failover flow rule intent is never
556 * deleted -- only its contents are updated.
557 *
558 * @param oldInstallables list of intents to examine
559 * @return list of reusable installable intents
560 */
561 private List<Intent> filterInvalidSubIntents(List<Intent> oldInstallables,
562 PointToPointIntent pointIntent) {
563 List<Intent> intentList = new ArrayList<>();
564 intentList.addAll(oldInstallables);
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800565
566 Iterator<Intent> iterator = intentList.iterator();
567 while (iterator.hasNext()) {
568 Intent intent = iterator.next();
569 intent.resources().forEach(resource -> {
570 if (resource instanceof Link) {
571 Link link = (Link) resource;
572 if (link.state() == Link.State.INACTIVE) {
573 setPathsToRemove(intent);
574 } else if (link instanceof EdgeLink) {
575 ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId)
576 ? link.src() : link.dst();
577 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
578 if (port == null || !port.isEnabled()) {
helenyrwu2a674902016-07-20 09:48:04 -0700579 setPathsToRemove(intent);
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800580 }
581 } else {
582 Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port());
583 Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port());
584 if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) {
585 setPathsToRemove(intent);
helenyrwu2a674902016-07-20 09:48:04 -0700586 }
587 }
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800588 }
589 });
helenyrwu2a674902016-07-20 09:48:04 -0700590 }
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800591 removeAndUpdateIntents(intentList, pointIntent);
592
helenyrwu2a674902016-07-20 09:48:04 -0700593 return intentList;
594 }
595
596 /**
597 * Sets instance variables erasePrimary and eraseBackup. If erasePrimary,
598 * the primary path is no longer viable and related intents will be deleted.
599 * If eraseBackup, the backup path is no longer viable and related intents
600 * will be deleted.
601 *
602 * @param intent intent whose resources are found to be disabled/inactive:
603 * if intent is part of primary path, primary path set for removal;
604 * if intent is part of backup path, backup path set for removal;
605 * if bad intent is of type failover, the ingress point is down,
606 * and both paths are rendered inactive.
607 * @return true if both primary and backup paths are to be removed
608 */
609 private boolean setPathsToRemove(Intent intent) {
610 if (intent instanceof FlowRuleIntent) {
611 FlowRuleIntent frIntent = (FlowRuleIntent) intent;
612 PathIntent.ProtectionType type = frIntent.type();
613 if (type == PathIntent.ProtectionType.PRIMARY || type == PathIntent.ProtectionType.FAILOVER) {
614 erasePrimary = true;
615 }
616 if (type == PathIntent.ProtectionType.BACKUP || type == PathIntent.ProtectionType.FAILOVER) {
617 eraseBackup = true;
618 }
619 }
620 return erasePrimary && eraseBackup;
621 }
622
623 /**
624 * Removes intents from installables list, depending on the values
625 * of instance variables erasePrimary and eraseBackup. Flow rule intents
626 * that contain the manufactured fast failover flow rules are never deleted.
627 * The contents are simply modified as necessary. If cleanUpIntents size
628 * is greater than 1 (failover intent), then one whole path from previous
629 * installables must be still viable.
630 *
631 * @param cleanUpIntents list of installable intents
632 */
633 private void removeAndUpdateIntents(List<Intent> cleanUpIntents,
634 PointToPointIntent pointIntent) {
635 ListIterator<Intent> iterator = cleanUpIntents.listIterator();
636 while (iterator.hasNext()) {
637 Intent cIntent = iterator.next();
638 if (cIntent instanceof FlowRuleIntent) {
639 FlowRuleIntent fIntent = (FlowRuleIntent) cIntent;
640 if (fIntent.type() == PathIntent.ProtectionType.PRIMARY && erasePrimary) {
641 // remove primary path's flow rule intents
642 iterator.remove();
643 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && eraseBackup) {
644 //remove backup path's flow rule intents
645 iterator.remove();
646 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && erasePrimary) {
647 // promote backup path's flow rule intents to primary
648 iterator.set(new FlowRuleIntent(fIntent, PathIntent.ProtectionType.PRIMARY));
649 }
650 }
651 }
652 // remove buckets whose watchports are disabled if the failover group exists
Pier Ventreffe88d62016-10-13 14:34:40 -0700653 Group group = groupService.getGroup(pointIntent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700654 makeGroupKey(pointIntent.id()));
655 if (group != null) {
656 updateFailoverGroup(pointIntent);
657 }
658 }
659
660 // Removes buckets whose treatments rely on disabled ports from the
661 // failover group.
662 private void updateFailoverGroup(PointToPointIntent pointIntent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700663 DeviceId deviceId = pointIntent.filteredIngressPoint().connectPoint().deviceId();
helenyrwu2a674902016-07-20 09:48:04 -0700664 GroupKey groupKey = makeGroupKey(pointIntent.id());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700665 Group group = waitForGroup(deviceId, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700666 Iterator<GroupBucket> groupIterator = group.buckets().buckets().iterator();
667 while (groupIterator.hasNext()) {
668 GroupBucket bucket = groupIterator.next();
669 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
670 if (individualInstruction instanceof Instructions.OutputInstruction) {
671 Instructions.OutputInstruction outInstruction =
672 (Instructions.OutputInstruction) individualInstruction;
673 Port port = deviceService.getPort(deviceId, outInstruction.port());
674 if (port == null || !port.isEnabled()) {
675 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
676 groupService.removeBucketsFromGroup(deviceId, groupKey,
677 removeBuckets, groupKey,
678 pointIntent.appId());
679 }
680 }
681 }
682 }
683
684 // Adds failover group bucket with treatment outport determined by the
685 // ingress point of the links.
686 private void updateFailoverGroup(PointToPointIntent intent, List<Link> links) {
687 GroupKey groupKey = makeGroupKey(intent.id());
688
689 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
690 ConnectPoint src = links.get(0).src();
691 tBuilderIn.setOutput(src.port());
692 GroupBucket bucket = DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null);
693 GroupBuckets addBuckets = new GroupBuckets(Collections.singletonList(bucket));
694
695 groupService.addBucketsToGroup(src.deviceId(), groupKey, addBuckets, groupKey, intent.appId());
696 }
Ray Milkeya058c732014-10-08 13:52:34 -0700697}