| /* |
| * Copyright 2015-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.onosproject.net.intent.impl.compiler; |
| |
| import com.google.common.collect.ImmutableSet; |
| import org.apache.felix.scr.annotations.Activate; |
| import org.apache.felix.scr.annotations.Component; |
| import org.apache.felix.scr.annotations.Deactivate; |
| import org.apache.felix.scr.annotations.Reference; |
| import org.apache.felix.scr.annotations.ReferenceCardinality; |
| import org.onosproject.net.ConnectPoint; |
| import org.onosproject.net.DefaultPath; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.DisjointPath; |
| import org.onosproject.net.EdgeLink; |
| import org.onosproject.net.Link; |
| import org.onosproject.net.Path; |
| import org.onosproject.net.Port; |
| import org.onosproject.net.PortNumber; |
| import org.onosproject.net.device.DeviceService; |
| import org.onosproject.net.flow.DefaultFlowRule; |
| import org.onosproject.net.flow.DefaultTrafficSelector; |
| import org.onosproject.net.flow.DefaultTrafficTreatment; |
| import org.onosproject.net.flow.FlowRule; |
| import org.onosproject.net.flow.TrafficSelector; |
| import org.onosproject.net.flow.TrafficTreatment; |
| import org.onosproject.net.flow.instructions.Instruction; |
| import org.onosproject.net.flow.instructions.Instructions; |
| import org.onosproject.net.group.DefaultGroupBucket; |
| import org.onosproject.net.group.DefaultGroupDescription; |
| import org.onosproject.net.group.DefaultGroupKey; |
| import org.onosproject.net.group.Group; |
| import org.onosproject.net.group.GroupBucket; |
| import org.onosproject.net.group.GroupBuckets; |
| import org.onosproject.net.group.GroupDescription; |
| import org.onosproject.net.group.GroupKey; |
| import org.onosproject.net.group.GroupListener; |
| import org.onosproject.net.group.GroupService; |
| import org.onosproject.net.intent.FlowRuleIntent; |
| import org.onosproject.net.intent.Intent; |
| import org.onosproject.net.intent.IntentCompilationException; |
| import org.onosproject.net.intent.IntentId; |
| import org.onosproject.net.intent.LinkCollectionIntent; |
| import org.onosproject.net.intent.PathIntent; |
| import org.onosproject.net.intent.PointToPointIntent; |
| import org.onosproject.net.intent.constraint.ProtectionConstraint; |
| import org.onosproject.net.intent.impl.PathNotFoundException; |
| import org.onosproject.net.link.LinkService; |
| import org.onosproject.net.provider.ProviderId; |
| import org.slf4j.Logger; |
| |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Set; |
| import java.util.concurrent.CompletableFuture; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| |
| import static java.util.Arrays.asList; |
| import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; |
| import static org.slf4j.LoggerFactory.getLogger; |
| |
| /** |
| * An intent compiler for {@link org.onosproject.net.intent.PointToPointIntent}. |
| */ |
| @Component(immediate = true) |
| public class PointToPointIntentCompiler |
| extends ConnectivityIntentCompiler<PointToPointIntent> { |
| |
| // TODO: use off-the-shell core provider ID |
| private static final ProviderId PID = |
| new ProviderId("core", "org.onosproject.core", true); |
| // TODO: consider whether the default cost is appropriate or not |
| public static final int DEFAULT_COST = 1; |
| |
| protected static final int PRIORITY = Intent.DEFAULT_INTENT_PRIORITY; |
| |
| private static final int GROUP_TIMEOUT = 5; |
| |
| private final Logger log = getLogger(getClass()); |
| |
| protected boolean erasePrimary = false; |
| protected boolean eraseBackup = false; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected GroupService groupService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected LinkService linkService; |
| |
| @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| protected DeviceService deviceService; |
| |
| @Activate |
| public void activate() { |
| intentManager.registerCompiler(PointToPointIntent.class, this); |
| } |
| |
| @Deactivate |
| public void deactivate() { |
| intentManager.unregisterCompiler(PointToPointIntent.class); |
| } |
| |
| @Override |
| public List<Intent> compile(PointToPointIntent intent, List<Intent> installable) { |
| log.trace("compiling {} {}", intent, installable); |
| ConnectPoint ingressPoint = intent.filteredIngressPoint().connectPoint(); |
| ConnectPoint egressPoint = intent.filteredEgressPoint().connectPoint(); |
| |
| if (ingressPoint.deviceId().equals(egressPoint.deviceId())) { |
| return createZeroHopLinkCollectionIntent(intent); |
| } |
| |
| // proceed with no protected paths |
| if (!ProtectionConstraint.requireProtectedPath(intent)) { |
| return createUnprotectedLinkCollectionIntent(intent); |
| } |
| |
| try { |
| // attempt to compute and implement backup path |
| return createProtectedIntent(ingressPoint, egressPoint, intent, installable); |
| } catch (PathNotFoundException e) { |
| log.warn("Could not find disjoint Path for {}", intent); |
| // no disjoint path extant -- maximum one path exists between devices |
| return createSinglePathIntent(ingressPoint, egressPoint, intent, installable); |
| } |
| } |
| |
| private List<Intent> createZeroHopIntent(ConnectPoint ingressPoint, |
| ConnectPoint egressPoint, |
| PointToPointIntent intent) { |
| List<Link> links = asList(createEdgeLink(ingressPoint, true), createEdgeLink(egressPoint, false)); |
| return asList(createPathIntent(new DefaultPath(PID, links, DEFAULT_COST), |
| intent, PathIntent.ProtectionType.PRIMARY)); |
| } |
| |
| private List<Intent> createZeroHopLinkCollectionIntent(PointToPointIntent intent) { |
| return asList(createLinkCollectionIntent(ImmutableSet.of(), DEFAULT_COST, |
| intent)); |
| } |
| |
| /** |
| * Creates an unprotected intent. |
| * @param ingressPoint the ingress connect point |
| * @param egressPoint the egress connect point |
| * @param intent the original intent |
| * @return the compilation result |
| * @deprecated 1.10.0 |
| */ |
| @Deprecated |
| private List<Intent> createUnprotectedIntent(ConnectPoint ingressPoint, |
| ConnectPoint egressPoint, |
| PointToPointIntent intent) { |
| List<Link> links = new ArrayList<>(); |
| Path path = getPathOrException(intent, ingressPoint.deviceId(), |
| egressPoint.deviceId()); |
| |
| links.add(createEdgeLink(ingressPoint, true)); |
| links.addAll(path.links()); |
| links.add(createEdgeLink(egressPoint, false)); |
| |
| return asList(createPathIntent(new DefaultPath(PID, links, path.cost(), |
| path.annotations()), intent, |
| PathIntent.ProtectionType.PRIMARY)); |
| } |
| |
| private List<Intent> createUnprotectedLinkCollectionIntent(PointToPointIntent intent) { |
| Path path = getPathOrException(intent, intent.filteredIngressPoint().connectPoint().deviceId(), |
| intent.filteredEgressPoint().connectPoint().deviceId()); |
| |
| // Allocate bandwidth if a bandwidth constraint is set |
| ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint(); |
| ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint(); |
| |
| List<ConnectPoint> pathCPs = |
| path.links().stream() |
| .flatMap(l -> Stream.of(l.src(), l.dst())) |
| .collect(Collectors.toList()); |
| |
| pathCPs.add(ingressCP); |
| pathCPs.add(egressCP); |
| |
| allocateBandwidth(intent, pathCPs); |
| |
| return asList(createLinkCollectionIntent(ImmutableSet.copyOf(path.links()), |
| path.cost(), |
| intent)); |
| } |
| |
| //FIXME: Compatibility with EncapsulationConstraint |
| private List<Intent> createProtectedIntent(ConnectPoint ingressPoint, |
| ConnectPoint egressPoint, |
| PointToPointIntent intent, |
| List<Intent> installable) { |
| log.trace("createProtectedIntent"); |
| DisjointPath path = getDisjointPath(intent, ingressPoint.deviceId(), |
| egressPoint.deviceId()); |
| |
| List<Intent> reusableIntents = null; |
| if (installable != null) { |
| reusableIntents = filterInvalidSubIntents(installable, intent); |
| if (reusableIntents.size() == installable.size()) { |
| // all old paths are still viable |
| return installable; |
| } |
| } |
| |
| List<Intent> intentList = new ArrayList<>(); |
| |
| // primary path intent |
| List<Link> links = new ArrayList<>(); |
| links.addAll(path.links()); |
| links.add(createEdgeLink(egressPoint, false)); |
| |
| // backup path intent |
| List<Link> backupLinks = new ArrayList<>(); |
| backupLinks.addAll(path.backup().links()); |
| backupLinks.add(createEdgeLink(egressPoint, false)); |
| |
| /* |
| * One of the old paths is still entirely intact. This old path has |
| * already been made primary, so we must add a backup path intent |
| * and modify the failover group treatment accordingly. |
| */ |
| if (reusableIntents != null && reusableIntents.size() > 1) { |
| /* |
| * Ensures that the egress port on source device is different than |
| * that of existing path so that failover group will be useful |
| * (would not be useful if both output ports in group bucket were |
| * the same). Does not necessarily ensure that the new backup path |
| * is entirely disjoint from the old path. |
| */ |
| PortNumber primaryPort = getPrimaryPort(intent); |
| if (primaryPort != null && !links.get(0).src().port().equals(primaryPort)) { |
| reusableIntents.add(createPathIntent(new DefaultPath(PID, links, |
| path.cost(), path.annotations()), |
| intent, PathIntent.ProtectionType.BACKUP)); |
| updateFailoverGroup(intent, links); |
| return reusableIntents; |
| |
| } else { |
| reusableIntents.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().cost(), |
| path.backup().annotations()), intent, PathIntent.ProtectionType.BACKUP)); |
| updateFailoverGroup(intent, backupLinks); |
| return reusableIntents; |
| } |
| } |
| |
| intentList.add(createPathIntent(new DefaultPath(PID, links, path.cost(), |
| path.annotations()), |
| intent, PathIntent.ProtectionType.PRIMARY)); |
| intentList.add(createPathIntent(new DefaultPath(PID, backupLinks, path.backup().cost(), |
| path.backup().annotations()), |
| intent, PathIntent.ProtectionType.BACKUP)); |
| |
| // Create fast failover flow rule intent or, if it already exists, |
| // add contents appropriately. |
| if (groupService.getGroup(ingressPoint.deviceId(), |
| makeGroupKey(intent.id())) == null) { |
| // manufactured fast failover flow rule intent |
| createFailoverTreatmentGroup(path.links(), path.backup().links(), intent); |
| |
| FlowRuleIntent frIntent = new FlowRuleIntent(intent.appId(), |
| intent.key(), |
| createFailoverFlowRules(intent), |
| asList(ingressPoint.deviceId()), |
| PathIntent.ProtectionType.FAILOVER, |
| intent.resourceGroup()); |
| intentList.add(frIntent); |
| } else { |
| updateFailoverGroup(intent, links); |
| updateFailoverGroup(intent, backupLinks); |
| } |
| |
| return intentList; |
| } |
| |
| private List<Intent> createSinglePathIntent(ConnectPoint ingressPoint, |
| ConnectPoint egressPoint, |
| PointToPointIntent intent, |
| List<Intent> installable) { |
| List<Link> links = new ArrayList<>(); |
| Path onlyPath = getPathOrException(intent, ingressPoint.deviceId(), |
| egressPoint.deviceId()); |
| |
| List<Intent> reusableIntents = null; |
| if (installable != null) { |
| reusableIntents = filterInvalidSubIntents(installable, intent); |
| if (reusableIntents.size() == installable.size()) { |
| // all old paths are still viable |
| return installable; |
| } |
| } |
| |
| // If there exists a full path from old installable intents, |
| // return the intents that comprise it. |
| if (reusableIntents != null && reusableIntents.size() > 1) { |
| return reusableIntents; |
| } else { |
| // Allocate bandwidth if a bandwidth constraint is set |
| ConnectPoint ingressCP = intent.filteredIngressPoint().connectPoint(); |
| ConnectPoint egressCP = intent.filteredEgressPoint().connectPoint(); |
| |
| List<ConnectPoint> pathCPs = |
| onlyPath.links().stream() |
| .flatMap(l -> Stream.of(l.src(), l.dst())) |
| .collect(Collectors.toList()); |
| |
| pathCPs.add(ingressCP); |
| pathCPs.add(egressCP); |
| |
| // Allocate bandwidth if a bandwidth constraint is set |
| allocateBandwidth(intent, pathCPs); |
| |
| links.add(createEdgeLink(ingressPoint, true)); |
| links.addAll(onlyPath.links()); |
| links.add(createEdgeLink(egressPoint, false)); |
| return asList(createPathIntent(new DefaultPath(PID, links, onlyPath.cost(), |
| onlyPath.annotations()), |
| intent, PathIntent.ProtectionType.PRIMARY)); |
| } |
| } |
| |
| /** |
| * Creates a path intent from the specified path and original |
| * connectivity intent. |
| * |
| * @param path path to create an intent for |
| * @param intent original intent |
| * @param type primary or backup |
| */ |
| private Intent createPathIntent(Path path, |
| PointToPointIntent intent, |
| PathIntent.ProtectionType type) { |
| return PathIntent.builder() |
| .appId(intent.appId()) |
| .key(intent.key()) |
| .selector(intent.selector()) |
| .treatment(intent.treatment()) |
| .path(path) |
| .constraints(intent.constraints()) |
| .priority(intent.priority()) |
| .setType(type) |
| .resourceGroup(intent.resourceGroup()) |
| .build(); |
| } |
| |
| |
| /** |
| * Creates a link collection intent from the specified path and original |
| * point to point intent. |
| * |
| * @param links the links of the packets |
| * @param cost the cost associated to the links |
| * @param intent the point to point intent we are compiling |
| * @return the link collection intent |
| */ |
| private Intent createLinkCollectionIntent(Set<Link> links, |
| double cost, |
| PointToPointIntent intent) { |
| |
| return LinkCollectionIntent.builder() |
| .key(intent.key()) |
| .appId(intent.appId()) |
| .selector(intent.selector()) |
| .treatment(intent.treatment()) |
| .links(ImmutableSet.copyOf(links)) |
| .filteredIngressPoints(ImmutableSet.of( |
| intent.filteredIngressPoint() |
| )) |
| .filteredEgressPoints(ImmutableSet.of( |
| intent.filteredEgressPoint() |
| )) |
| .applyTreatmentOnEgress(true) |
| .constraints(intent.constraints()) |
| .priority(intent.priority()) |
| .cost(cost) |
| .resourceGroup(intent.resourceGroup()) |
| .build(); |
| } |
| |
| /** |
| * Gets primary port number through failover group associated |
| * with this intent. |
| */ |
| private PortNumber getPrimaryPort(PointToPointIntent intent) { |
| Group group = groupService.getGroup(intent.filteredIngressPoint().connectPoint().deviceId(), |
| makeGroupKey(intent.id())); |
| PortNumber primaryPort = null; |
| if (group != null) { |
| List<GroupBucket> buckets = group.buckets().buckets(); |
| Iterator<GroupBucket> iterator = buckets.iterator(); |
| while (primaryPort == null && iterator.hasNext()) { |
| GroupBucket bucket = iterator.next(); |
| Instruction individualInstruction = bucket.treatment().allInstructions().get(0); |
| if (individualInstruction instanceof Instructions.OutputInstruction) { |
| Instructions.OutputInstruction outInstruction = |
| (Instructions.OutputInstruction) individualInstruction; |
| PortNumber tempPortNum = outInstruction.port(); |
| Port port = deviceService.getPort(intent.filteredIngressPoint().connectPoint().deviceId(), |
| tempPortNum); |
| if (port != null && port.isEnabled()) { |
| primaryPort = tempPortNum; |
| } |
| } |
| } |
| } |
| return primaryPort; |
| } |
| |
| /** |
| * Creates group key unique to each intent. |
| * |
| * @param intentId identifier of intent to get a key for |
| * @return unique group key for the intent identifier |
| */ |
| public static GroupKey makeGroupKey(IntentId intentId) { |
| ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); |
| buffer.putLong(intentId.fingerprint()); |
| return new DefaultGroupKey(buffer.array()); |
| } |
| |
| /** |
| * Creates a new failover group with the initial ports of the links |
| * from the primary and backup path. |
| * |
| * @param links links from the primary path |
| * @param backupLinks links from the backup path |
| * @param intent intent from which this call originates |
| */ |
| private void createFailoverTreatmentGroup(List<Link> links, |
| List<Link> backupLinks, |
| PointToPointIntent intent) { |
| |
| List<GroupBucket> buckets = new ArrayList<>(); |
| |
| TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder(); |
| ConnectPoint src = links.get(0).src(); |
| tBuilderIn.setOutput(src.port()); |
| |
| TrafficTreatment.Builder tBuilderIn2 = DefaultTrafficTreatment.builder(); |
| ConnectPoint src2 = backupLinks.get(0).src(); |
| tBuilderIn2.setOutput(src2.port()); |
| |
| buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null)); |
| buckets.add(DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn2.build(), src2.port(), null)); |
| |
| GroupBuckets groupBuckets = new GroupBuckets(buckets); |
| |
| GroupDescription groupDesc = new DefaultGroupDescription(src.deviceId(), Group.Type.FAILOVER, |
| groupBuckets, makeGroupKey(intent.id()), null, intent.appId()); |
| log.trace("adding failover group {}", groupDesc); |
| groupService.addGroup(groupDesc); |
| } |
| |
| /** |
| * Manufactures flow rule with treatment that is defined by failover |
| * group and traffic selector determined by ingress port of the intent. |
| * |
| * @param intent intent which is being compiled (for appId) |
| * @return a list of a singular flow rule with fast failover |
| * outport traffic treatment |
| */ |
| private List<FlowRule> createFailoverFlowRules(PointToPointIntent intent) { |
| List<FlowRule> flowRules = new ArrayList<>(); |
| |
| ConnectPoint ingress = intent.ingressPoint(); |
| DeviceId deviceId = ingress.deviceId(); |
| |
| // flow rule with failover traffic treatment |
| TrafficSelector trafficSelector = DefaultTrafficSelector.builder(intent.selector()) |
| .matchInPort(ingress.port()).build(); |
| |
| FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder(); |
| flowRules.add(flowRuleBuilder.withSelector(trafficSelector) |
| .withTreatment(buildFailoverTreatment(deviceId, makeGroupKey(intent.id()))) |
| .fromApp(intent.appId()) |
| .makePermanent() |
| .forDevice(deviceId) |
| .withPriority(PRIORITY) |
| .build()); |
| |
| return flowRules; |
| } |
| |
| |
| /** |
| * Waits for specified group to appear maximum of {@value #GROUP_TIMEOUT} seconds. |
| * |
| * @param deviceId {@link DeviceId} |
| * @param groupKey {@link GroupKey} to wait for. |
| * @return {@link Group} |
| * @throws IntentCompilationException on any error. |
| */ |
| private Group waitForGroup(DeviceId deviceId, GroupKey groupKey) { |
| return waitForGroup(deviceId, groupKey, GROUP_TIMEOUT, TimeUnit.SECONDS); |
| } |
| |
| /** |
| * Waits for specified group to appear until timeout. |
| * |
| * @param deviceId {@link DeviceId} |
| * @param groupKey {@link GroupKey} to wait for. |
| * @param timeout timeout |
| * @param unit unit of timeout |
| * @return {@link Group} |
| * @throws IntentCompilationException on any error. |
| */ |
| private Group waitForGroup(DeviceId deviceId, GroupKey groupKey, long timeout, TimeUnit unit) { |
| Group group = groupService.getGroup(deviceId, groupKey); |
| if (group != null) { |
| return group; |
| } |
| |
| final CompletableFuture<Group> future = new CompletableFuture<>(); |
| final GroupListener listener = event -> { |
| if (event.subject().deviceId() == deviceId && |
| event.subject().appCookie().equals(groupKey)) { |
| future.complete(event.subject()); |
| return; |
| } |
| }; |
| |
| groupService.addListener(listener); |
| try { |
| group = groupService.getGroup(deviceId, groupKey); |
| if (group != null) { |
| return group; |
| } |
| return future.get(timeout, unit); |
| } catch (InterruptedException e) { |
| log.debug("Interrupted", e); |
| Thread.currentThread().interrupt(); |
| throw new IntentCompilationException("Interrupted", e); |
| } catch (ExecutionException e) { |
| log.debug("ExecutionException", e); |
| throw new IntentCompilationException("ExecutionException caught", e); |
| } catch (TimeoutException e) { |
| // one last try |
| group = groupService.getGroup(deviceId, groupKey); |
| if (group != null) { |
| return group; |
| } else { |
| log.debug("Timeout", e); |
| throw new IntentCompilationException("Timeout", e); |
| } |
| } finally { |
| groupService.removeListener(listener); |
| } |
| } |
| |
| private TrafficTreatment buildFailoverTreatment(DeviceId srcDevice, |
| GroupKey groupKey) { |
| Group group = waitForGroup(srcDevice, groupKey); |
| TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); |
| TrafficTreatment trafficTreatment = tBuilder.group(group.id()).build(); |
| return trafficTreatment; |
| } |
| |
| /** |
| * Deletes intents from the given list if the ports or links the intent |
| * relies on are no longer viable. The failover flow rule intent is never |
| * deleted -- only its contents are updated. |
| * |
| * @param oldInstallables list of intents to examine |
| * @return list of reusable installable intents |
| */ |
| private List<Intent> filterInvalidSubIntents(List<Intent> oldInstallables, |
| PointToPointIntent pointIntent) { |
| List<Intent> intentList = new ArrayList<>(); |
| intentList.addAll(oldInstallables); |
| erasePrimary = false; |
| eraseBackup = false; |
| if (intentList != null) { |
| Iterator<Intent> iterator = intentList.iterator(); |
| while (iterator.hasNext() && !(erasePrimary && eraseBackup)) { |
| Intent intent = iterator.next(); |
| intent.resources().forEach(resource -> { |
| if (resource instanceof Link) { |
| Link link = (Link) resource; |
| if (link.state() == Link.State.INACTIVE) { |
| setPathsToRemove(intent); |
| } else if (link instanceof EdgeLink) { |
| ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId) |
| ? link.src() : link.dst(); |
| Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port()); |
| if (port == null || !port.isEnabled()) { |
| setPathsToRemove(intent); |
| } |
| } else { |
| Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port()); |
| Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port()); |
| if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) { |
| setPathsToRemove(intent); |
| } |
| } |
| } |
| }); |
| } |
| removeAndUpdateIntents(intentList, pointIntent); |
| } |
| return intentList; |
| } |
| |
| /** |
| * Sets instance variables erasePrimary and eraseBackup. If erasePrimary, |
| * the primary path is no longer viable and related intents will be deleted. |
| * If eraseBackup, the backup path is no longer viable and related intents |
| * will be deleted. |
| * |
| * @param intent intent whose resources are found to be disabled/inactive: |
| * if intent is part of primary path, primary path set for removal; |
| * if intent is part of backup path, backup path set for removal; |
| * if bad intent is of type failover, the ingress point is down, |
| * and both paths are rendered inactive. |
| * @return true if both primary and backup paths are to be removed |
| */ |
| private boolean setPathsToRemove(Intent intent) { |
| if (intent instanceof FlowRuleIntent) { |
| FlowRuleIntent frIntent = (FlowRuleIntent) intent; |
| PathIntent.ProtectionType type = frIntent.type(); |
| if (type == PathIntent.ProtectionType.PRIMARY || type == PathIntent.ProtectionType.FAILOVER) { |
| erasePrimary = true; |
| } |
| if (type == PathIntent.ProtectionType.BACKUP || type == PathIntent.ProtectionType.FAILOVER) { |
| eraseBackup = true; |
| } |
| } |
| return erasePrimary && eraseBackup; |
| } |
| |
| /** |
| * Removes intents from installables list, depending on the values |
| * of instance variables erasePrimary and eraseBackup. Flow rule intents |
| * that contain the manufactured fast failover flow rules are never deleted. |
| * The contents are simply modified as necessary. If cleanUpIntents size |
| * is greater than 1 (failover intent), then one whole path from previous |
| * installables must be still viable. |
| * |
| * @param cleanUpIntents list of installable intents |
| */ |
| private void removeAndUpdateIntents(List<Intent> cleanUpIntents, |
| PointToPointIntent pointIntent) { |
| ListIterator<Intent> iterator = cleanUpIntents.listIterator(); |
| while (iterator.hasNext()) { |
| Intent cIntent = iterator.next(); |
| if (cIntent instanceof FlowRuleIntent) { |
| FlowRuleIntent fIntent = (FlowRuleIntent) cIntent; |
| if (fIntent.type() == PathIntent.ProtectionType.PRIMARY && erasePrimary) { |
| // remove primary path's flow rule intents |
| iterator.remove(); |
| } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && eraseBackup) { |
| //remove backup path's flow rule intents |
| iterator.remove(); |
| } else if (fIntent.type() == PathIntent.ProtectionType.BACKUP && erasePrimary) { |
| // promote backup path's flow rule intents to primary |
| iterator.set(new FlowRuleIntent(fIntent, PathIntent.ProtectionType.PRIMARY)); |
| } |
| } |
| } |
| // remove buckets whose watchports are disabled if the failover group exists |
| Group group = groupService.getGroup(pointIntent.filteredIngressPoint().connectPoint().deviceId(), |
| makeGroupKey(pointIntent.id())); |
| if (group != null) { |
| updateFailoverGroup(pointIntent); |
| } |
| } |
| |
| // Removes buckets whose treatments rely on disabled ports from the |
| // failover group. |
| private void updateFailoverGroup(PointToPointIntent pointIntent) { |
| DeviceId deviceId = pointIntent.filteredIngressPoint().connectPoint().deviceId(); |
| GroupKey groupKey = makeGroupKey(pointIntent.id()); |
| Group group = waitForGroup(deviceId, groupKey); |
| Iterator<GroupBucket> groupIterator = group.buckets().buckets().iterator(); |
| while (groupIterator.hasNext()) { |
| GroupBucket bucket = groupIterator.next(); |
| Instruction individualInstruction = bucket.treatment().allInstructions().get(0); |
| if (individualInstruction instanceof Instructions.OutputInstruction) { |
| Instructions.OutputInstruction outInstruction = |
| (Instructions.OutputInstruction) individualInstruction; |
| Port port = deviceService.getPort(deviceId, outInstruction.port()); |
| if (port == null || !port.isEnabled()) { |
| GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket)); |
| groupService.removeBucketsFromGroup(deviceId, groupKey, |
| removeBuckets, groupKey, |
| pointIntent.appId()); |
| } |
| } |
| } |
| } |
| |
| // Adds failover group bucket with treatment outport determined by the |
| // ingress point of the links. |
| private void updateFailoverGroup(PointToPointIntent intent, List<Link> links) { |
| GroupKey groupKey = makeGroupKey(intent.id()); |
| |
| TrafficTreatment.Builder tBuilderIn = DefaultTrafficTreatment.builder(); |
| ConnectPoint src = links.get(0).src(); |
| tBuilderIn.setOutput(src.port()); |
| GroupBucket bucket = DefaultGroupBucket.createFailoverGroupBucket(tBuilderIn.build(), src.port(), null); |
| GroupBuckets addBuckets = new GroupBuckets(Collections.singletonList(bucket)); |
| |
| groupService.addBucketsToGroup(src.deviceId(), groupKey, addBuckets, groupKey, intent.appId()); |
| } |
| } |