blob: acdaddd8f9cd37da5356eccbe937d23892731404 [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
Pier Ventreffe88d62016-10-13 14:34:40 -0700163 private List<Intent> createUnprotectedLinkCollectionIntent(PointToPointIntent intent) {
Luca Preted26ea652017-01-03 15:59:30 -0800164 Path path = getPathOrException(intent, intent.filteredIngressPoint().connectPoint().deviceId(),
165 intent.filteredEgressPoint().connectPoint().deviceId());
Pier Ventreffe88d62016-10-13 14:34:40 -0700166
Luca Pretede10c782017-01-05 17:23:08 -0800167 // Allocate bandwidth if a bandwidth constraint is set
168 ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
169 ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
170
171 List<ConnectPoint> pathCPs =
172 path.links().stream()
173 .flatMap(l -> Stream.of(l.src(), l.dst()))
174 .collect(Collectors.toList());
175
176 pathCPs.add(ingressCP);
177 pathCPs.add(egressCP);
178
179 allocateBandwidth(intent, pathCPs);
180
Pier Ventreffe88d62016-10-13 14:34:40 -0700181 return asList(createLinkCollectionIntent(ImmutableSet.copyOf(path.links()),
182 path.cost(),
183 intent));
184 }
185
helenyrwu2a674902016-07-20 09:48:04 -0700186 //FIXME: Compatibility with EncapsulationConstraint
187 private List<Intent> createProtectedIntent(ConnectPoint ingressPoint,
188 ConnectPoint egressPoint,
189 PointToPointIntent intent,
190 List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700191 log.trace("createProtectedIntent");
helenyrwu2a674902016-07-20 09:48:04 -0700192 DisjointPath path = getDisjointPath(intent, ingressPoint.deviceId(),
193 egressPoint.deviceId());
194
195 List<Intent> reusableIntents = null;
196 if (installable != null) {
197 reusableIntents = filterInvalidSubIntents(installable, intent);
198 if (reusableIntents.size() == installable.size()) {
199 // all old paths are still viable
200 return installable;
201 }
202 }
203
204 List<Intent> intentList = new ArrayList<>();
205
206 // primary path intent
207 List<Link> links = new ArrayList<>();
208 links.addAll(path.links());
209 links.add(createEdgeLink(egressPoint, false));
210
211 // backup path intent
212 List<Link> backupLinks = new ArrayList<>();
213 backupLinks.addAll(path.backup().links());
214 backupLinks.add(createEdgeLink(egressPoint, false));
215
216 /*
217 * One of the old paths is still entirely intact. This old path has
218 * already been made primary, so we must add a backup path intent
219 * and modify the failover group treatment accordingly.
220 */
221 if (reusableIntents != null && reusableIntents.size() > 1) {
222 /*
223 * Ensures that the egress port on source device is different than
224 * that of existing path so that failover group will be useful
225 * (would not be useful if both output ports in group bucket were
226 * the same). Does not necessarily ensure that the new backup path
227 * is entirely disjoint from the old path.
228 */
229 PortNumber primaryPort = getPrimaryPort(intent);
230 if (primaryPort != null && !links.get(0).src().port().equals(primaryPort)) {
231 reusableIntents.add(createPathIntent(new DefaultPath(PID, links,
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800232 path.weight(), path.annotations()),
helenyrwu2a674902016-07-20 09:48:04 -0700233 intent, PathIntent.ProtectionType.BACKUP));
234 updateFailoverGroup(intent, links);
235 return reusableIntents;
236
237 } else {
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800238 reusableIntents.add(createPathIntent(new DefaultPath(PID, backupLinks,
239 path.backup().weight(),
240 path.backup().annotations()), intent, PathIntent.ProtectionType.BACKUP));
helenyrwu2a674902016-07-20 09:48:04 -0700241 updateFailoverGroup(intent, backupLinks);
242 return reusableIntents;
243 }
244 }
245
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800246 intentList.add(createPathIntent(new DefaultPath(PID, links, path.weight(),
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700247 path.annotations()),
helenyrwu2a674902016-07-20 09:48:04 -0700248 intent, PathIntent.ProtectionType.PRIMARY));
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800249 intentList.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().weight(),
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700250 path.backup().annotations()),
251 intent, PathIntent.ProtectionType.BACKUP));
helenyrwu2a674902016-07-20 09:48:04 -0700252
253 // Create fast failover flow rule intent or, if it already exists,
254 // add contents appropriately.
255 if (groupService.getGroup(ingressPoint.deviceId(),
256 makeGroupKey(intent.id())) == null) {
257 // manufactured fast failover flow rule intent
258 createFailoverTreatmentGroup(path.links(), path.backup().links(), intent);
259
260 FlowRuleIntent frIntent = new FlowRuleIntent(intent.appId(),
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700261 intent.key(),
helenyrwu2a674902016-07-20 09:48:04 -0700262 createFailoverFlowRules(intent),
263 asList(ingressPoint.deviceId()),
Luca Prete670ac5d2017-02-03 15:55:43 -0800264 PathIntent.ProtectionType.FAILOVER,
265 intent.resourceGroup());
helenyrwu2a674902016-07-20 09:48:04 -0700266 intentList.add(frIntent);
267 } else {
268 updateFailoverGroup(intent, links);
269 updateFailoverGroup(intent, backupLinks);
270 }
271
272 return intentList;
273 }
274
275 private List<Intent> createSinglePathIntent(ConnectPoint ingressPoint,
276 ConnectPoint egressPoint,
277 PointToPointIntent intent,
278 List<Intent> installable) {
279 List<Link> links = new ArrayList<>();
Luca Preted26ea652017-01-03 15:59:30 -0800280 Path onlyPath = getPathOrException(intent, ingressPoint.deviceId(),
281 egressPoint.deviceId());
helenyrwu2a674902016-07-20 09:48:04 -0700282
283 List<Intent> reusableIntents = null;
284 if (installable != null) {
285 reusableIntents = filterInvalidSubIntents(installable, intent);
286 if (reusableIntents.size() == installable.size()) {
287 // all old paths are still viable
288 return installable;
289 }
290 }
291
292 // If there exists a full path from old installable intents,
293 // return the intents that comprise it.
294 if (reusableIntents != null && reusableIntents.size() > 1) {
295 return reusableIntents;
296 } else {
Luca Pretede10c782017-01-05 17:23:08 -0800297 // Allocate bandwidth if a bandwidth constraint is set
298 ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint();
299 ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint();
300
301 List<ConnectPoint> pathCPs =
302 onlyPath.links().stream()
303 .flatMap(l -> Stream.of(l.src(), l.dst()))
304 .collect(Collectors.toList());
305
306 pathCPs.add(ingressCP);
307 pathCPs.add(egressCP);
308
309 // Allocate bandwidth if a bandwidth constraint is set
310 allocateBandwidth(intent, pathCPs);
311
helenyrwu2a674902016-07-20 09:48:04 -0700312 links.add(createEdgeLink(ingressPoint, true));
313 links.addAll(onlyPath.links());
314 links.add(createEdgeLink(egressPoint, false));
Ray Milkeya7cf8c82018-02-08 15:07:06 -0800315 return asList(createPathIntent(new DefaultPath(PID, links, onlyPath.weight(),
helenyrwu2a674902016-07-20 09:48:04 -0700316 onlyPath.annotations()),
317 intent, PathIntent.ProtectionType.PRIMARY));
318 }
Ray Milkeya058c732014-10-08 13:52:34 -0700319 }
320
321 /**
322 * Creates a path intent from the specified path and original
323 * connectivity intent.
324 *
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700325 * @param path path to create an intent for
Ray Milkeya058c732014-10-08 13:52:34 -0700326 * @param intent original intent
helenyrwu2a674902016-07-20 09:48:04 -0700327 * @param type primary or backup
Ray Milkeya058c732014-10-08 13:52:34 -0700328 */
329 private Intent createPathIntent(Path path,
helenyrwu2a674902016-07-20 09:48:04 -0700330 PointToPointIntent intent,
331 PathIntent.ProtectionType type) {
Ray Milkeyebc5d222015-03-18 15:45:36 -0700332 return PathIntent.builder()
333 .appId(intent.appId())
Yuta HIGUCHI652f27f2016-10-31 16:54:30 -0700334 .key(intent.key())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700335 .selector(intent.selector())
336 .treatment(intent.treatment())
337 .path(path)
338 .constraints(intent.constraints())
339 .priority(intent.priority())
helenyrwu2a674902016-07-20 09:48:04 -0700340 .setType(type)
Luca Prete670ac5d2017-02-03 15:55:43 -0800341 .resourceGroup(intent.resourceGroup())
Ray Milkeyebc5d222015-03-18 15:45:36 -0700342 .build();
Ray Milkeya058c732014-10-08 13:52:34 -0700343 }
344
Pier Ventreffe88d62016-10-13 14:34:40 -0700345
346 /**
347 * Creates a link collection intent from the specified path and original
348 * point to point intent.
349 *
350 * @param links the links of the packets
351 * @param cost the cost associated to the links
352 * @param intent the point to point intent we are compiling
353 * @return the link collection intent
354 */
355 private Intent createLinkCollectionIntent(Set<Link> links,
356 double cost,
357 PointToPointIntent intent) {
358
359 return LinkCollectionIntent.builder()
360 .key(intent.key())
361 .appId(intent.appId())
362 .selector(intent.selector())
363 .treatment(intent.treatment())
364 .links(ImmutableSet.copyOf(links))
365 .filteredIngressPoints(ImmutableSet.of(
366 intent.filteredIngressPoint()
367 ))
368 .filteredEgressPoints(ImmutableSet.of(
369 intent.filteredEgressPoint()
370 ))
371 .applyTreatmentOnEgress(true)
372 .constraints(intent.constraints())
373 .priority(intent.priority())
374 .cost(cost)
Luca Prete670ac5d2017-02-03 15:55:43 -0800375 .resourceGroup(intent.resourceGroup())
Pier Ventreffe88d62016-10-13 14:34:40 -0700376 .build();
377 }
378
helenyrwu2a674902016-07-20 09:48:04 -0700379 /**
380 * Gets primary port number through failover group associated
381 * with this intent.
382 */
383 private PortNumber getPrimaryPort(PointToPointIntent intent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700384 Group group = groupService.getGroup(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700385 makeGroupKey(intent.id()));
386 PortNumber primaryPort = null;
387 if (group != null) {
388 List<GroupBucket> buckets = group.buckets().buckets();
389 Iterator<GroupBucket> iterator = buckets.iterator();
390 while (primaryPort == null && iterator.hasNext()) {
391 GroupBucket bucket = iterator.next();
392 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
393 if (individualInstruction instanceof Instructions.OutputInstruction) {
394 Instructions.OutputInstruction outInstruction =
395 (Instructions.OutputInstruction) individualInstruction;
396 PortNumber tempPortNum = outInstruction.port();
Pier Ventreffe88d62016-10-13 14:34:40 -0700397 Port port = deviceService.getPort(intent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700398 tempPortNum);
399 if (port != null && port.isEnabled()) {
400 primaryPort = tempPortNum;
401 }
402 }
403 }
404 }
405 return primaryPort;
406 }
407
408 /**
409 * Creates group key unique to each intent.
Ray Milkeyef794342016-11-09 16:20:29 -0800410 *
411 * @param intentId identifier of intent to get a key for
412 * @return unique group key for the intent identifier
helenyrwu2a674902016-07-20 09:48:04 -0700413 */
414 public static GroupKey makeGroupKey(IntentId intentId) {
415 ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
416 buffer.putLong(intentId.fingerprint());
417 return new DefaultGroupKey(buffer.array());
418 }
419
420 /**
421 * Creates a new failover group with the initial ports of the links
422 * from the primary and backup path.
423 *
424 * @param links links from the primary path
425 * @param backupLinks links from the backup path
426 * @param intent intent from which this call originates
427 */
428 private void createFailoverTreatmentGroup(List<Link> links,
429 List<Link> backupLinks,
430 PointToPointIntent intent) {
431
432 List<GroupBucket> buckets = new ArrayList<>();
433
434 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
435 ConnectPoint src = links.get(0).src();
436 tBuilderIn.setOutput(src.port());
437
438 TrafficTreatment.Builder tBuilderIn2 = DefaultTrafficTreatment.builder();
439 ConnectPoint src2 = backupLinks.get(0).src();
440 tBuilderIn2.setOutput(src2.port());
441
442 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null));
443 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn2.build(), src2.port(), null));
444
445 GroupBuckets groupBuckets = new GroupBuckets(buckets);
446
447 GroupDescription groupDesc = new DefaultGroupDescription(src.deviceId(), Group.Type.FAILOVER,
448 groupBuckets, makeGroupKey(intent.id()), null, intent.appId());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700449 log.trace("adding failover group {}", groupDesc);
helenyrwu2a674902016-07-20 09:48:04 -0700450 groupService.addGroup(groupDesc);
451 }
452
453 /**
454 * Manufactures flow rule with treatment that is defined by failover
455 * group and traffic selector determined by ingress port of the intent.
456 *
457 * @param intent intent which is being compiled (for appId)
458 * @return a list of a singular flow rule with fast failover
459 * outport traffic treatment
460 */
461 private List<FlowRule> createFailoverFlowRules(PointToPointIntent intent) {
462 List<FlowRule> flowRules = new ArrayList<>();
463
Ray Milkeya2cf3a12018-02-15 16:13:56 -0800464 ConnectPoint ingress = intent.filteredIngressPoint().connectPoint();
helenyrwu2a674902016-07-20 09:48:04 -0700465 DeviceId deviceId = ingress.deviceId();
466
467 // flow rule with failover traffic treatment
468 TrafficSelector trafficSelector = DefaultTrafficSelector.builder(intent.selector())
469 .matchInPort(ingress.port()).build();
470
471 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
472 flowRules.add(flowRuleBuilder.withSelector(trafficSelector)
473 .withTreatment(buildFailoverTreatment(deviceId, makeGroupKey(intent.id())))
474 .fromApp(intent.appId())
475 .makePermanent()
476 .forDevice(deviceId)
477 .withPriority(PRIORITY)
478 .build());
479
480 return flowRules;
481 }
482
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700483
484 /**
485 * Waits for specified group to appear maximum of {@value #GROUP_TIMEOUT} seconds.
486 *
487 * @param deviceId {@link DeviceId}
488 * @param groupKey {@link GroupKey} to wait for.
489 * @return {@link Group}
490 * @throws IntentCompilationException on any error.
491 */
492 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey) {
493 return waitForGroup(deviceId, groupKey, GROUP_TIMEOUT, TimeUnit.SECONDS);
494 }
495
496 /**
497 * Waits for specified group to appear until timeout.
498 *
499 * @param deviceId {@link DeviceId}
500 * @param groupKey {@link GroupKey} to wait for.
501 * @param timeout timeout
502 * @param unit unit of timeout
503 * @return {@link Group}
504 * @throws IntentCompilationException on any error.
505 */
506 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey, long timeout, TimeUnit unit) {
507 Group group = groupService.getGroup(deviceId, groupKey);
508 if (group != null) {
509 return group;
510 }
511
512 final CompletableFuture<Group> future = new CompletableFuture<>();
513 final GroupListener listener = event -> {
514 if (event.subject().deviceId() == deviceId &&
515 event.subject().appCookie().equals(groupKey)) {
516 future.complete(event.subject());
517 return;
518 }
519 };
520
521 groupService.addListener(listener);
522 try {
523 group = groupService.getGroup(deviceId, groupKey);
524 if (group != null) {
525 return group;
526 }
527 return future.get(timeout, unit);
528 } catch (InterruptedException e) {
529 log.debug("Interrupted", e);
530 Thread.currentThread().interrupt();
531 throw new IntentCompilationException("Interrupted", e);
532 } catch (ExecutionException e) {
533 log.debug("ExecutionException", e);
534 throw new IntentCompilationException("ExecutionException caught", e);
535 } catch (TimeoutException e) {
536 // one last try
537 group = groupService.getGroup(deviceId, groupKey);
538 if (group != null) {
539 return group;
540 } else {
541 log.debug("Timeout", e);
542 throw new IntentCompilationException("Timeout", e);
543 }
544 } finally {
545 groupService.removeListener(listener);
546 }
547 }
548
helenyrwu2a674902016-07-20 09:48:04 -0700549 private TrafficTreatment buildFailoverTreatment(DeviceId srcDevice,
550 GroupKey groupKey) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700551 Group group = waitForGroup(srcDevice, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700552 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
553 TrafficTreatment trafficTreatment = tBuilder.group(group.id()).build();
554 return trafficTreatment;
555 }
556
557 /**
558 * Deletes intents from the given list if the ports or links the intent
559 * relies on are no longer viable. The failover flow rule intent is never
560 * deleted -- only its contents are updated.
561 *
562 * @param oldInstallables list of intents to examine
563 * @return list of reusable installable intents
564 */
565 private List<Intent> filterInvalidSubIntents(List<Intent> oldInstallables,
566 PointToPointIntent pointIntent) {
567 List<Intent> intentList = new ArrayList<>();
568 intentList.addAll(oldInstallables);
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800569
570 Iterator<Intent> iterator = intentList.iterator();
571 while (iterator.hasNext()) {
572 Intent intent = iterator.next();
573 intent.resources().forEach(resource -> {
574 if (resource instanceof Link) {
575 Link link = (Link) resource;
576 if (link.state() == Link.State.INACTIVE) {
577 setPathsToRemove(intent);
578 } else if (link instanceof EdgeLink) {
579 ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId)
580 ? link.src() : link.dst();
581 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
582 if (port == null || !port.isEnabled()) {
helenyrwu2a674902016-07-20 09:48:04 -0700583 setPathsToRemove(intent);
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800584 }
585 } else {
586 Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port());
587 Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port());
588 if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) {
589 setPathsToRemove(intent);
helenyrwu2a674902016-07-20 09:48:04 -0700590 }
591 }
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800592 }
593 });
helenyrwu2a674902016-07-20 09:48:04 -0700594 }
Ray Milkeyfe0e0852018-01-18 11:14:05 -0800595 removeAndUpdateIntents(intentList, pointIntent);
596
helenyrwu2a674902016-07-20 09:48:04 -0700597 return intentList;
598 }
599
600 /**
601 * Sets instance variables erasePrimary and eraseBackup. If erasePrimary,
602 * the primary path is no longer viable and related intents will be deleted.
603 * If eraseBackup, the backup path is no longer viable and related intents
604 * will be deleted.
605 *
606 * @param intent intent whose resources are found to be disabled/inactive:
607 * if intent is part of primary path, primary path set for removal;
608 * if intent is part of backup path, backup path set for removal;
609 * if bad intent is of type failover, the ingress point is down,
610 * and both paths are rendered inactive.
611 * @return true if both primary and backup paths are to be removed
612 */
613 private boolean setPathsToRemove(Intent intent) {
614 if (intent instanceof FlowRuleIntent) {
615 FlowRuleIntent frIntent = (FlowRuleIntent) intent;
616 PathIntent.ProtectionType type = frIntent.type();
617 if (type == PathIntent.ProtectionType.PRIMARY || type == PathIntent.ProtectionType.FAILOVER) {
618 erasePrimary = true;
619 }
620 if (type == PathIntent.ProtectionType.BACKUP || type == PathIntent.ProtectionType.FAILOVER) {
621 eraseBackup = true;
622 }
623 }
624 return erasePrimary && eraseBackup;
625 }
626
627 /**
628 * Removes intents from installables list, depending on the values
629 * of instance variables erasePrimary and eraseBackup. Flow rule intents
630 * that contain the manufactured fast failover flow rules are never deleted.
631 * The contents are simply modified as necessary. If cleanUpIntents size
632 * is greater than 1 (failover intent), then one whole path from previous
633 * installables must be still viable.
634 *
635 * @param cleanUpIntents list of installable intents
636 */
637 private void removeAndUpdateIntents(List<Intent> cleanUpIntents,
638 PointToPointIntent pointIntent) {
639 ListIterator<Intent> iterator = cleanUpIntents.listIterator();
640 while (iterator.hasNext()) {
641 Intent cIntent = iterator.next();
642 if (cIntent instanceof FlowRuleIntent) {
643 FlowRuleIntent fIntent = (FlowRuleIntent) cIntent;
644 if (fIntent.type() == PathIntent.ProtectionType.PRIMARY && erasePrimary) {
645 // remove primary path's flow rule intents
646 iterator.remove();
647 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && eraseBackup) {
648 //remove backup path's flow rule intents
649 iterator.remove();
650 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && erasePrimary) {
651 // promote backup path's flow rule intents to primary
652 iterator.set(new FlowRuleIntent(fIntent, PathIntent.ProtectionType.PRIMARY));
653 }
654 }
655 }
656 // remove buckets whose watchports are disabled if the failover group exists
Pier Ventreffe88d62016-10-13 14:34:40 -0700657 Group group = groupService.getGroup(pointIntent.filteredIngressPoint().connectPoint().deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700658 makeGroupKey(pointIntent.id()));
659 if (group != null) {
660 updateFailoverGroup(pointIntent);
661 }
662 }
663
664 // Removes buckets whose treatments rely on disabled ports from the
665 // failover group.
666 private void updateFailoverGroup(PointToPointIntent pointIntent) {
Pier Ventreffe88d62016-10-13 14:34:40 -0700667 DeviceId deviceId = pointIntent.filteredIngressPoint().connectPoint().deviceId();
helenyrwu2a674902016-07-20 09:48:04 -0700668 GroupKey groupKey = makeGroupKey(pointIntent.id());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700669 Group group = waitForGroup(deviceId, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700670 Iterator<GroupBucket> groupIterator = group.buckets().buckets().iterator();
671 while (groupIterator.hasNext()) {
672 GroupBucket bucket = groupIterator.next();
673 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
674 if (individualInstruction instanceof Instructions.OutputInstruction) {
675 Instructions.OutputInstruction outInstruction =
676 (Instructions.OutputInstruction) individualInstruction;
677 Port port = deviceService.getPort(deviceId, outInstruction.port());
678 if (port == null || !port.isEnabled()) {
679 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
680 groupService.removeBucketsFromGroup(deviceId, groupKey,
681 removeBuckets, groupKey,
682 pointIntent.appId());
683 }
684 }
685 }
686 }
687
688 // Adds failover group bucket with treatment outport determined by the
689 // ingress point of the links.
690 private void updateFailoverGroup(PointToPointIntent intent, List<Link> links) {
691 GroupKey groupKey = makeGroupKey(intent.id());
692
693 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
694 ConnectPoint src = links.get(0).src();
695 tBuilderIn.setOutput(src.port());
696 GroupBucket bucket = DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null);
697 GroupBuckets addBuckets = new GroupBuckets(Collections.singletonList(bucket));
698
699 groupService.addBucketsToGroup(src.deviceId(), groupKey, addBuckets, groupKey, intent.appId());
700 }
Ray Milkeya058c732014-10-08 13:52:34 -0700701}