blob: ee5f4e25197185b15e2fe5b324198796ef30d6dd [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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
Ray Milkeya058c732014-10-08 13:52:34 -070018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
helenyrwu2a674902016-07-20 09:48:04 -070021import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
Brian O'Connorabafb502014-12-02 22:26:20 -080023import org.onosproject.net.ConnectPoint;
24import org.onosproject.net.DefaultPath;
helenyrwu2a674902016-07-20 09:48:04 -070025import org.onosproject.net.DeviceId;
26import org.onosproject.net.DisjointPath;
27import org.onosproject.net.EdgeLink;
Brian O'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.net.Link;
29import org.onosproject.net.Path;
helenyrwu2a674902016-07-20 09:48:04 -070030import org.onosproject.net.Port;
31import org.onosproject.net.PortNumber;
32import org.onosproject.net.device.DeviceService;
33import org.onosproject.net.flow.DefaultFlowRule;
34import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.FlowRule;
37import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
39import org.onosproject.net.flow.instructions.Instruction;
40import org.onosproject.net.flow.instructions.Instructions;
41import org.onosproject.net.group.DefaultGroupBucket;
42import org.onosproject.net.group.DefaultGroupDescription;
43import org.onosproject.net.group.DefaultGroupKey;
44import org.onosproject.net.group.Group;
45import org.onosproject.net.group.GroupBucket;
46import org.onosproject.net.group.GroupBuckets;
47import org.onosproject.net.group.GroupDescription;
48import org.onosproject.net.group.GroupKey;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070049import org.onosproject.net.group.GroupListener;
helenyrwu2a674902016-07-20 09:48:04 -070050import org.onosproject.net.group.GroupService;
51import org.onosproject.net.intent.FlowRuleIntent;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.intent.Intent;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070053import org.onosproject.net.intent.IntentCompilationException;
helenyrwu2a674902016-07-20 09:48:04 -070054import org.onosproject.net.intent.IntentId;
Brian O'Connorabafb502014-12-02 22:26:20 -080055import org.onosproject.net.intent.PathIntent;
56import org.onosproject.net.intent.PointToPointIntent;
helenyrwu2a674902016-07-20 09:48:04 -070057import org.onosproject.net.intent.constraint.ProtectionConstraint;
58import org.onosproject.net.intent.impl.PathNotFoundException;
59import org.onosproject.net.link.LinkService;
Brian O'Connorabafb502014-12-02 22:26:20 -080060import org.onosproject.net.provider.ProviderId;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070061import org.slf4j.Logger;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080062
helenyrwu2a674902016-07-20 09:48:04 -070063import java.nio.ByteBuffer;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080064import java.util.ArrayList;
helenyrwu2a674902016-07-20 09:48:04 -070065import java.util.Collections;
66import java.util.Iterator;
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080067import java.util.List;
helenyrwu2a674902016-07-20 09:48:04 -070068import java.util.ListIterator;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070069import java.util.concurrent.CompletableFuture;
70import java.util.concurrent.ExecutionException;
71import java.util.concurrent.TimeUnit;
72import java.util.concurrent.TimeoutException;
Ray Milkeya058c732014-10-08 13:52:34 -070073
Thomas Vachuska425a2d72014-10-29 11:28:28 -070074import static java.util.Arrays.asList;
Brian O'Connorabafb502014-12-02 22:26:20 -080075import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070076import static org.slf4j.LoggerFactory.getLogger;
Thomas Vachuska425a2d72014-10-29 11:28:28 -070077
Ray Milkeya058c732014-10-08 13:52:34 -070078/**
Brian O'Connorabafb502014-12-02 22:26:20 -080079 * An intent compiler for {@link org.onosproject.net.intent.PointToPointIntent}.
Ray Milkeya058c732014-10-08 13:52:34 -070080 */
81@Component(immediate = true)
82public class PointToPointIntentCompiler
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080083 extends ConnectivityIntentCompiler<PointToPointIntent> {
Ray Milkeya058c732014-10-08 13:52:34 -070084
Thomas Vachuskaedc944c2014-11-04 15:42:25 -080085 // TODO: use off-the-shell core provider ID
86 private static final ProviderId PID =
Brian O'Connorabafb502014-12-02 22:26:20 -080087 new ProviderId("core", "org.onosproject.core", true);
Sho SHIMIZU3908fde2014-11-19 16:30:22 -080088 // TODO: consider whether the default cost is appropriate or not
89 public static final int DEFAULT_COST = 1;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070090
helenyrwu2a674902016-07-20 09:48:04 -070091 protected static final int PRIORITY = Intent.DEFAULT_INTENT_PRIORITY;
Yuta HIGUCHI625fb642016-09-01 16:10:24 -070092
93 private static final int GROUP_TIMEOUT = 5;
94
95 private final Logger log = getLogger(getClass());
96
helenyrwu2a674902016-07-20 09:48:04 -070097 protected boolean erasePrimary = false;
98 protected boolean eraseBackup = false;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected GroupService groupService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected LinkService linkService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected DeviceService deviceService;
weibit50eb95b2014-10-25 21:47:54 -0700108
Ray Milkeya058c732014-10-08 13:52:34 -0700109 @Activate
110 public void activate() {
Ray Milkeya058c732014-10-08 13:52:34 -0700111 intentManager.registerCompiler(PointToPointIntent.class, this);
112 }
113
114 @Deactivate
115 public void deactivate() {
116 intentManager.unregisterCompiler(PointToPointIntent.class);
117 }
118
119 @Override
Sho SHIMIZUec07ffd2016-02-22 20:45:21 -0800120 public List<Intent> compile(PointToPointIntent intent, List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700121 log.trace("compiling {} {}", intent, installable);
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800122 ConnectPoint ingressPoint = intent.ingressPoint();
123 ConnectPoint egressPoint = intent.egressPoint();
124
125 if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
helenyrwu2a674902016-07-20 09:48:04 -0700126 return createZeroHopIntent(ingressPoint, egressPoint, intent);
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800127 }
Ray Milkeya058c732014-10-08 13:52:34 -0700128
helenyrwu2a674902016-07-20 09:48:04 -0700129 // proceed with no protected paths
130 if (!ProtectionConstraint.requireProtectedPath(intent)) {
131 return createUnprotectedIntent(ingressPoint, egressPoint, intent);
132 }
133
134 try {
135 // attempt to compute and implement backup path
136 return createProtectedIntent(ingressPoint, egressPoint, intent, installable);
137 } catch (PathNotFoundException e) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700138 log.warn("Could not find disjoint Path for {}", intent);
helenyrwu2a674902016-07-20 09:48:04 -0700139 // no disjoint path extant -- maximum one path exists between devices
140 return createSinglePathIntent(ingressPoint, egressPoint, intent, installable);
141 }
142 }
143
144 private List<Intent> createZeroHopIntent(ConnectPoint ingressPoint,
145 ConnectPoint egressPoint,
146 PointToPointIntent intent) {
147 List<Link> links = asList(createEdgeLink(ingressPoint, true), createEdgeLink(egressPoint, false));
148 return asList(createPathIntent(new DefaultPath(PID, links, DEFAULT_COST),
149 intent, PathIntent.ProtectionType.PRIMARY));
150 }
151
152 private List<Intent> createUnprotectedIntent(ConnectPoint ingressPoint,
153 ConnectPoint egressPoint,
154 PointToPointIntent intent) {
Ray Milkeya058c732014-10-08 13:52:34 -0700155 List<Link> links = new ArrayList<>();
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800156 Path path = getPath(intent, ingressPoint.deviceId(),
helenyrwu2a674902016-07-20 09:48:04 -0700157 egressPoint.deviceId());
Sho SHIMIZUde8e6b52014-11-13 11:23:17 -0800158
Sho SHIMIZU3908fde2014-11-19 16:30:22 -0800159 links.add(createEdgeLink(ingressPoint, true));
Ray Milkeya058c732014-10-08 13:52:34 -0700160 links.addAll(path.links());
Sho SHIMIZU3908fde2014-11-19 16:30:22 -0800161 links.add(createEdgeLink(egressPoint, false));
Ray Milkeya058c732014-10-08 13:52:34 -0700162
Thomas Vachuskaedc944c2014-11-04 15:42:25 -0800163 return asList(createPathIntent(new DefaultPath(PID, links, path.cost(),
helenyrwu2a674902016-07-20 09:48:04 -0700164 path.annotations()), intent,
165 PathIntent.ProtectionType.PRIMARY));
166 }
167
168 //FIXME: Compatibility with EncapsulationConstraint
169 private List<Intent> createProtectedIntent(ConnectPoint ingressPoint,
170 ConnectPoint egressPoint,
171 PointToPointIntent intent,
172 List<Intent> installable) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700173 log.trace("createProtectedIntent");
helenyrwu2a674902016-07-20 09:48:04 -0700174 DisjointPath path = getDisjointPath(intent, ingressPoint.deviceId(),
175 egressPoint.deviceId());
176
177 List<Intent> reusableIntents = null;
178 if (installable != null) {
179 reusableIntents = filterInvalidSubIntents(installable, intent);
180 if (reusableIntents.size() == installable.size()) {
181 // all old paths are still viable
182 return installable;
183 }
184 }
185
186 List<Intent> intentList = new ArrayList<>();
187
188 // primary path intent
189 List<Link> links = new ArrayList<>();
190 links.addAll(path.links());
191 links.add(createEdgeLink(egressPoint, false));
192
193 // backup path intent
194 List<Link> backupLinks = new ArrayList<>();
195 backupLinks.addAll(path.backup().links());
196 backupLinks.add(createEdgeLink(egressPoint, false));
197
198 /*
199 * One of the old paths is still entirely intact. This old path has
200 * already been made primary, so we must add a backup path intent
201 * and modify the failover group treatment accordingly.
202 */
203 if (reusableIntents != null && reusableIntents.size() > 1) {
204 /*
205 * Ensures that the egress port on source device is different than
206 * that of existing path so that failover group will be useful
207 * (would not be useful if both output ports in group bucket were
208 * the same). Does not necessarily ensure that the new backup path
209 * is entirely disjoint from the old path.
210 */
211 PortNumber primaryPort = getPrimaryPort(intent);
212 if (primaryPort != null && !links.get(0).src().port().equals(primaryPort)) {
213 reusableIntents.add(createPathIntent(new DefaultPath(PID, links,
214 path.cost(), path.annotations()),
215 intent, PathIntent.ProtectionType.BACKUP));
216 updateFailoverGroup(intent, links);
217 return reusableIntents;
218
219 } else {
220 reusableIntents.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().cost(),
221 path.backup().annotations()), intent, PathIntent.ProtectionType.BACKUP));
222 updateFailoverGroup(intent, backupLinks);
223 return reusableIntents;
224 }
225 }
226
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700227 intentList.add(createPathIntent(new DefaultPath(PID, links, path.cost(),
228 path.annotations()),
helenyrwu2a674902016-07-20 09:48:04 -0700229 intent, PathIntent.ProtectionType.PRIMARY));
230 intentList.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().cost(),
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700231 path.backup().annotations()),
232 intent, PathIntent.ProtectionType.BACKUP));
helenyrwu2a674902016-07-20 09:48:04 -0700233
234 // Create fast failover flow rule intent or, if it already exists,
235 // add contents appropriately.
236 if (groupService.getGroup(ingressPoint.deviceId(),
237 makeGroupKey(intent.id())) == null) {
238 // manufactured fast failover flow rule intent
239 createFailoverTreatmentGroup(path.links(), path.backup().links(), intent);
240
241 FlowRuleIntent frIntent = new FlowRuleIntent(intent.appId(),
242 createFailoverFlowRules(intent),
243 asList(ingressPoint.deviceId()),
244 PathIntent.ProtectionType.FAILOVER);
245 intentList.add(frIntent);
246 } else {
247 updateFailoverGroup(intent, links);
248 updateFailoverGroup(intent, backupLinks);
249 }
250
251 return intentList;
252 }
253
254 private List<Intent> createSinglePathIntent(ConnectPoint ingressPoint,
255 ConnectPoint egressPoint,
256 PointToPointIntent intent,
257 List<Intent> installable) {
258 List<Link> links = new ArrayList<>();
259 Path onlyPath = getPath(intent, ingressPoint.deviceId(),
260 egressPoint.deviceId());
261
262 List<Intent> reusableIntents = null;
263 if (installable != null) {
264 reusableIntents = filterInvalidSubIntents(installable, intent);
265 if (reusableIntents.size() == installable.size()) {
266 // all old paths are still viable
267 return installable;
268 }
269 }
270
271 // If there exists a full path from old installable intents,
272 // return the intents that comprise it.
273 if (reusableIntents != null && reusableIntents.size() > 1) {
274 return reusableIntents;
275 } else {
276 links.add(createEdgeLink(ingressPoint, true));
277 links.addAll(onlyPath.links());
278 links.add(createEdgeLink(egressPoint, false));
279
280 return asList(createPathIntent(new DefaultPath(PID, links, onlyPath.cost(),
281 onlyPath.annotations()),
282 intent, PathIntent.ProtectionType.PRIMARY));
283 }
Ray Milkeya058c732014-10-08 13:52:34 -0700284 }
285
286 /**
287 * Creates a path intent from the specified path and original
288 * connectivity intent.
289 *
Thomas Vachuskab97cf282014-10-20 23:31:12 -0700290 * @param path path to create an intent for
Ray Milkeya058c732014-10-08 13:52:34 -0700291 * @param intent original intent
helenyrwu2a674902016-07-20 09:48:04 -0700292 * @param type primary or backup
Ray Milkeya058c732014-10-08 13:52:34 -0700293 */
294 private Intent createPathIntent(Path path,
helenyrwu2a674902016-07-20 09:48:04 -0700295 PointToPointIntent intent,
296 PathIntent.ProtectionType type) {
Ray Milkeyebc5d222015-03-18 15:45:36 -0700297 return PathIntent.builder()
298 .appId(intent.appId())
299 .selector(intent.selector())
300 .treatment(intent.treatment())
301 .path(path)
302 .constraints(intent.constraints())
303 .priority(intent.priority())
helenyrwu2a674902016-07-20 09:48:04 -0700304 .setType(type)
Ray Milkeyebc5d222015-03-18 15:45:36 -0700305 .build();
Ray Milkeya058c732014-10-08 13:52:34 -0700306 }
307
helenyrwu2a674902016-07-20 09:48:04 -0700308 /**
309 * Gets primary port number through failover group associated
310 * with this intent.
311 */
312 private PortNumber getPrimaryPort(PointToPointIntent intent) {
313 Group group = groupService.getGroup(intent.ingressPoint().deviceId(),
314 makeGroupKey(intent.id()));
315 PortNumber primaryPort = null;
316 if (group != null) {
317 List<GroupBucket> buckets = group.buckets().buckets();
318 Iterator<GroupBucket> iterator = buckets.iterator();
319 while (primaryPort == null && iterator.hasNext()) {
320 GroupBucket bucket = iterator.next();
321 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
322 if (individualInstruction instanceof Instructions.OutputInstruction) {
323 Instructions.OutputInstruction outInstruction =
324 (Instructions.OutputInstruction) individualInstruction;
325 PortNumber tempPortNum = outInstruction.port();
326 Port port = deviceService.getPort(intent.ingressPoint().deviceId(),
327 tempPortNum);
328 if (port != null && port.isEnabled()) {
329 primaryPort = tempPortNum;
330 }
331 }
332 }
333 }
334 return primaryPort;
335 }
336
337 /**
338 * Creates group key unique to each intent.
Ray Milkeyef794342016-11-09 16:20:29 -0800339 *
340 * @param intentId identifier of intent to get a key for
341 * @return unique group key for the intent identifier
helenyrwu2a674902016-07-20 09:48:04 -0700342 */
343 public static GroupKey makeGroupKey(IntentId intentId) {
344 ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
345 buffer.putLong(intentId.fingerprint());
346 return new DefaultGroupKey(buffer.array());
347 }
348
349 /**
350 * Creates a new failover group with the initial ports of the links
351 * from the primary and backup path.
352 *
353 * @param links links from the primary path
354 * @param backupLinks links from the backup path
355 * @param intent intent from which this call originates
356 */
357 private void createFailoverTreatmentGroup(List<Link> links,
358 List<Link> backupLinks,
359 PointToPointIntent intent) {
360
361 List<GroupBucket> buckets = new ArrayList<>();
362
363 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
364 ConnectPoint src = links.get(0).src();
365 tBuilderIn.setOutput(src.port());
366
367 TrafficTreatment.Builder tBuilderIn2 = DefaultTrafficTreatment.builder();
368 ConnectPoint src2 = backupLinks.get(0).src();
369 tBuilderIn2.setOutput(src2.port());
370
371 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null));
372 buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn2.build(), src2.port(), null));
373
374 GroupBuckets groupBuckets = new GroupBuckets(buckets);
375
376 GroupDescription groupDesc = new DefaultGroupDescription(src.deviceId(), Group.Type.FAILOVER,
377 groupBuckets, makeGroupKey(intent.id()), null, intent.appId());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700378 log.trace("adding failover group {}", groupDesc);
helenyrwu2a674902016-07-20 09:48:04 -0700379 groupService.addGroup(groupDesc);
380 }
381
382 /**
383 * Manufactures flow rule with treatment that is defined by failover
384 * group and traffic selector determined by ingress port of the intent.
385 *
386 * @param intent intent which is being compiled (for appId)
387 * @return a list of a singular flow rule with fast failover
388 * outport traffic treatment
389 */
390 private List<FlowRule> createFailoverFlowRules(PointToPointIntent intent) {
391 List<FlowRule> flowRules = new ArrayList<>();
392
393 ConnectPoint ingress = intent.ingressPoint();
394 DeviceId deviceId = ingress.deviceId();
395
396 // flow rule with failover traffic treatment
397 TrafficSelector trafficSelector = DefaultTrafficSelector.builder(intent.selector())
398 .matchInPort(ingress.port()).build();
399
400 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
401 flowRules.add(flowRuleBuilder.withSelector(trafficSelector)
402 .withTreatment(buildFailoverTreatment(deviceId, makeGroupKey(intent.id())))
403 .fromApp(intent.appId())
404 .makePermanent()
405 .forDevice(deviceId)
406 .withPriority(PRIORITY)
407 .build());
408
409 return flowRules;
410 }
411
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700412
413 /**
414 * Waits for specified group to appear maximum of {@value #GROUP_TIMEOUT} seconds.
415 *
416 * @param deviceId {@link DeviceId}
417 * @param groupKey {@link GroupKey} to wait for.
418 * @return {@link Group}
419 * @throws IntentCompilationException on any error.
420 */
421 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey) {
422 return waitForGroup(deviceId, groupKey, GROUP_TIMEOUT, TimeUnit.SECONDS);
423 }
424
425 /**
426 * Waits for specified group to appear until timeout.
427 *
428 * @param deviceId {@link DeviceId}
429 * @param groupKey {@link GroupKey} to wait for.
430 * @param timeout timeout
431 * @param unit unit of timeout
432 * @return {@link Group}
433 * @throws IntentCompilationException on any error.
434 */
435 private Group waitForGroup(DeviceId deviceId, GroupKey groupKey, long timeout, TimeUnit unit) {
436 Group group = groupService.getGroup(deviceId, groupKey);
437 if (group != null) {
438 return group;
439 }
440
441 final CompletableFuture<Group> future = new CompletableFuture<>();
442 final GroupListener listener = event -> {
443 if (event.subject().deviceId() == deviceId &&
444 event.subject().appCookie().equals(groupKey)) {
445 future.complete(event.subject());
446 return;
447 }
448 };
449
450 groupService.addListener(listener);
451 try {
452 group = groupService.getGroup(deviceId, groupKey);
453 if (group != null) {
454 return group;
455 }
456 return future.get(timeout, unit);
457 } catch (InterruptedException e) {
458 log.debug("Interrupted", e);
459 Thread.currentThread().interrupt();
460 throw new IntentCompilationException("Interrupted", e);
461 } catch (ExecutionException e) {
462 log.debug("ExecutionException", e);
463 throw new IntentCompilationException("ExecutionException caught", e);
464 } catch (TimeoutException e) {
465 // one last try
466 group = groupService.getGroup(deviceId, groupKey);
467 if (group != null) {
468 return group;
469 } else {
470 log.debug("Timeout", e);
471 throw new IntentCompilationException("Timeout", e);
472 }
473 } finally {
474 groupService.removeListener(listener);
475 }
476 }
477
helenyrwu2a674902016-07-20 09:48:04 -0700478 private TrafficTreatment buildFailoverTreatment(DeviceId srcDevice,
479 GroupKey groupKey) {
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700480 Group group = waitForGroup(srcDevice, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700481 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
482 TrafficTreatment trafficTreatment = tBuilder.group(group.id()).build();
483 return trafficTreatment;
484 }
485
486 /**
487 * Deletes intents from the given list if the ports or links the intent
488 * relies on are no longer viable. The failover flow rule intent is never
489 * deleted -- only its contents are updated.
490 *
491 * @param oldInstallables list of intents to examine
492 * @return list of reusable installable intents
493 */
494 private List<Intent> filterInvalidSubIntents(List<Intent> oldInstallables,
495 PointToPointIntent pointIntent) {
496 List<Intent> intentList = new ArrayList<>();
497 intentList.addAll(oldInstallables);
498 erasePrimary = false;
499 eraseBackup = false;
500 if (intentList != null) {
501 Iterator<Intent> iterator = intentList.iterator();
502 while (iterator.hasNext() && !(erasePrimary && eraseBackup)) {
503 Intent intent = iterator.next();
504 intent.resources().forEach(resource -> {
505 if (resource instanceof Link) {
506 Link link = (Link) resource;
507 if (link.state() == Link.State.INACTIVE) {
508 setPathsToRemove(intent);
509 } else if (link instanceof EdgeLink) {
510 ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId)
511 ? link.src() : link.dst();
512 Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
513 if (port == null || !port.isEnabled()) {
514 setPathsToRemove(intent);
515 }
516 } else {
517 Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port());
518 Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port());
519 if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) {
520 setPathsToRemove(intent);
521 }
522 }
523 }
524 });
525 }
526 removeAndUpdateIntents(intentList, pointIntent);
527 }
528 return intentList;
529 }
530
531 /**
532 * Sets instance variables erasePrimary and eraseBackup. If erasePrimary,
533 * the primary path is no longer viable and related intents will be deleted.
534 * If eraseBackup, the backup path is no longer viable and related intents
535 * will be deleted.
536 *
537 * @param intent intent whose resources are found to be disabled/inactive:
538 * if intent is part of primary path, primary path set for removal;
539 * if intent is part of backup path, backup path set for removal;
540 * if bad intent is of type failover, the ingress point is down,
541 * and both paths are rendered inactive.
542 * @return true if both primary and backup paths are to be removed
543 */
544 private boolean setPathsToRemove(Intent intent) {
545 if (intent instanceof FlowRuleIntent) {
546 FlowRuleIntent frIntent = (FlowRuleIntent) intent;
547 PathIntent.ProtectionType type = frIntent.type();
548 if (type == PathIntent.ProtectionType.PRIMARY || type == PathIntent.ProtectionType.FAILOVER) {
549 erasePrimary = true;
550 }
551 if (type == PathIntent.ProtectionType.BACKUP || type == PathIntent.ProtectionType.FAILOVER) {
552 eraseBackup = true;
553 }
554 }
555 return erasePrimary && eraseBackup;
556 }
557
558 /**
559 * Removes intents from installables list, depending on the values
560 * of instance variables erasePrimary and eraseBackup. Flow rule intents
561 * that contain the manufactured fast failover flow rules are never deleted.
562 * The contents are simply modified as necessary. If cleanUpIntents size
563 * is greater than 1 (failover intent), then one whole path from previous
564 * installables must be still viable.
565 *
566 * @param cleanUpIntents list of installable intents
567 */
568 private void removeAndUpdateIntents(List<Intent> cleanUpIntents,
569 PointToPointIntent pointIntent) {
570 ListIterator<Intent> iterator = cleanUpIntents.listIterator();
571 while (iterator.hasNext()) {
572 Intent cIntent = iterator.next();
573 if (cIntent instanceof FlowRuleIntent) {
574 FlowRuleIntent fIntent = (FlowRuleIntent) cIntent;
575 if (fIntent.type() == PathIntent.ProtectionType.PRIMARY && erasePrimary) {
576 // remove primary path's flow rule intents
577 iterator.remove();
578 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && eraseBackup) {
579 //remove backup path's flow rule intents
580 iterator.remove();
581 } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && erasePrimary) {
582 // promote backup path's flow rule intents to primary
583 iterator.set(new FlowRuleIntent(fIntent, PathIntent.ProtectionType.PRIMARY));
584 }
585 }
586 }
587 // remove buckets whose watchports are disabled if the failover group exists
588 Group group = groupService.getGroup(pointIntent.ingressPoint().deviceId(),
589 makeGroupKey(pointIntent.id()));
590 if (group != null) {
591 updateFailoverGroup(pointIntent);
592 }
593 }
594
595 // Removes buckets whose treatments rely on disabled ports from the
596 // failover group.
597 private void updateFailoverGroup(PointToPointIntent pointIntent) {
598 DeviceId deviceId = pointIntent.ingressPoint().deviceId();
599 GroupKey groupKey = makeGroupKey(pointIntent.id());
Yuta HIGUCHI625fb642016-09-01 16:10:24 -0700600 Group group = waitForGroup(deviceId, groupKey);
helenyrwu2a674902016-07-20 09:48:04 -0700601 Iterator<GroupBucket> groupIterator = group.buckets().buckets().iterator();
602 while (groupIterator.hasNext()) {
603 GroupBucket bucket = groupIterator.next();
604 Instruction individualInstruction = bucket.treatment().allInstructions().get(0);
605 if (individualInstruction instanceof Instructions.OutputInstruction) {
606 Instructions.OutputInstruction outInstruction =
607 (Instructions.OutputInstruction) individualInstruction;
608 Port port = deviceService.getPort(deviceId, outInstruction.port());
609 if (port == null || !port.isEnabled()) {
610 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
611 groupService.removeBucketsFromGroup(deviceId, groupKey,
612 removeBuckets, groupKey,
613 pointIntent.appId());
614 }
615 }
616 }
617 }
618
619 // Adds failover group bucket with treatment outport determined by the
620 // ingress point of the links.
621 private void updateFailoverGroup(PointToPointIntent intent, List<Link> links) {
622 GroupKey groupKey = makeGroupKey(intent.id());
623
624 TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder();
625 ConnectPoint src = links.get(0).src();
626 tBuilderIn.setOutput(src.port());
627 GroupBucket bucket = DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null);
628 GroupBuckets addBuckets = new GroupBuckets(Collections.singletonList(bucket));
629
630 groupService.addBucketsToGroup(src.deviceId(), groupKey, addBuckets, groupKey, intent.appId());
631 }
Ray Milkeya058c732014-10-08 13:52:34 -0700632}