/* * Copyright 2017-present Open Networking Laboratory * * 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.vpls.intent; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; import org.onosproject.core.ApplicationId; import org.onosproject.incubator.net.intf.Interface; import org.onosproject.net.ConnectPoint; import org.onosproject.net.EncapsulationType; import org.onosproject.net.FilteredConnectPoint; import org.onosproject.net.Host; import org.onosproject.net.ResourceGroup; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.intent.ConnectivityIntent; import org.onosproject.net.intent.Constraint; import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.Key; import org.onosproject.net.intent.MultiPointToSinglePointIntent; import org.onosproject.net.intent.SinglePointToMultiPointIntent; import org.onosproject.net.intent.constraint.EncapsulationConstraint; import org.onosproject.net.intent.constraint.PartialFailureConstraint; import org.onosproject.vpls.api.VplsData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import static java.util.Objects.*; import static org.onosproject.net.EncapsulationType.*; /** * Intent utilities for VPLS. */ public final class VplsIntentUtility { private static final String SP2MP = "Building sp2mp intent from {}"; private static final String MP2SP = "Building mp2sp intent to {}"; private static final Logger log = LoggerFactory.getLogger( VplsIntentUtility.class); private static final int PRIORITY_OFFSET = 1000; private static final int PRIORITY_UNI = 200; private static final int PRIORITY_BRC = 100; public static final String PREFIX_BROADCAST = "brc"; public static final String PREFIX_UNICAST = "uni"; private static final String SEPARATOR = "-"; public static final ImmutableList<Constraint> PARTIAL_FAILURE_CONSTRAINT = ImmutableList.of(new PartialFailureConstraint()); private VplsIntentUtility() { // Utility classes should not have a public or default constructor. } /** * Builds broadcast Intents for a VPLS. * * @param vplsData the VPLS * @param appId the application id for Intents * @return broadcast Intents for the VPLS */ public static Set<Intent> buildBrcIntents(VplsData vplsData, ApplicationId appId) { Set<Interface> interfaces = vplsData.interfaces(); // At least two or more network interfaces to build broadcast Intents if (interfaces.size() < 2) { return ImmutableSet.of(); } Set<Intent> brcIntents = Sets.newHashSet(); ResourceGroup resourceGroup = ResourceGroup.of(vplsData.name()); // Generates broadcast Intents from any network interface to other // network interface from the VPLS. interfaces.forEach(src -> { FilteredConnectPoint srcFcp = VplsIntentUtility.buildFilteredConnectedPoint(src); Set<FilteredConnectPoint> dstFcps = interfaces.stream() .filter(iface -> !iface.equals(src)) .map(VplsIntentUtility::buildFilteredConnectedPoint) .collect(Collectors.toSet()); Key key = VplsIntentUtility.buildKey(PREFIX_BROADCAST, srcFcp.connectPoint(), vplsData.name(), MacAddress.BROADCAST, appId); Intent brcIntent = buildBrcIntent(key, appId, srcFcp, dstFcps, vplsData.encapsulationType(), resourceGroup); brcIntents.add(brcIntent); }); return brcIntents; } /** * Builds a broadcast intent. * * @param key key to identify the intent * @param appId application ID for this Intent * @param src the source connect point * @param dsts the destination connect points * @param encap the encapsulation type * @param resourceGroup resource group for this Intent * @return the generated single-point to multi-point intent */ protected static SinglePointToMultiPointIntent buildBrcIntent(Key key, ApplicationId appId, FilteredConnectPoint src, Set<FilteredConnectPoint> dsts, EncapsulationType encap, ResourceGroup resourceGroup) { log.debug("Building broadcast intent {} for source {}", SP2MP, src); SinglePointToMultiPointIntent.Builder intentBuilder; TrafficSelector selector = DefaultTrafficSelector.builder() .matchEthDst(MacAddress.BROADCAST) .build(); intentBuilder = SinglePointToMultiPointIntent.builder() .appId(appId) .key(key) .selector(selector) .filteredIngressPoint(src) .filteredEgressPoints(dsts) .constraints(PARTIAL_FAILURE_CONSTRAINT) .priority(PRIORITY_OFFSET + PRIORITY_BRC) .resourceGroup(resourceGroup); setEncap(intentBuilder, PARTIAL_FAILURE_CONSTRAINT, encap); return intentBuilder.build(); } /** * Builds unicast Intents for a VPLS. * * @param vplsData the VPLS * @param hosts the hosts of the VPLS * @param appId application ID for Intents * @return unicast Intents for the VPLS */ public static Set<Intent> buildUniIntents(VplsData vplsData, Set<Host> hosts, ApplicationId appId) { Set<Interface> interfaces = vplsData.interfaces(); if (interfaces.size() < 2) { return ImmutableSet.of(); } Set<Intent> uniIntents = Sets.newHashSet(); ResourceGroup resourceGroup = ResourceGroup.of(vplsData.name()); hosts.forEach(host -> { FilteredConnectPoint hostFcp = buildFilteredConnectedPoint(host); Set<FilteredConnectPoint> srcFcps = interfaces.stream() .map(VplsIntentUtility::buildFilteredConnectedPoint) .filter(fcp -> !fcp.equals(hostFcp)) .collect(Collectors.toSet()); Key key = buildKey(PREFIX_UNICAST, hostFcp.connectPoint(), vplsData.name(), host.mac(), appId); Intent uniIntent = buildUniIntent(key, appId, srcFcps, hostFcp, host, vplsData.encapsulationType(), resourceGroup); uniIntents.add(uniIntent); }); return uniIntents; } /** * Builds a unicast intent. * * @param key key to identify the intent * @param appId application ID for this Intent * @param srcs the source Connect Points * @param dst the destination Connect Point * @param host destination Host * @param encap the encapsulation type * @param resourceGroup resource group for this Intent * @return the generated multi-point to single-point intent */ protected static MultiPointToSinglePointIntent buildUniIntent(Key key, ApplicationId appId, Set<FilteredConnectPoint> srcs, FilteredConnectPoint dst, Host host, EncapsulationType encap, ResourceGroup resourceGroup) { log.debug("Building unicast intent {} for destination {}", MP2SP, dst); MultiPointToSinglePointIntent.Builder intentBuilder; TrafficSelector selector = DefaultTrafficSelector.builder() .matchEthDst(host.mac()) .build(); intentBuilder = MultiPointToSinglePointIntent.builder() .appId(appId) .key(key) .selector(selector) .filteredIngressPoints(srcs) .filteredEgressPoint(dst) .constraints(PARTIAL_FAILURE_CONSTRAINT) .priority(PRIORITY_OFFSET + PRIORITY_UNI) .resourceGroup(resourceGroup); setEncap(intentBuilder, PARTIAL_FAILURE_CONSTRAINT, encap); return intentBuilder.build(); } /** * Builds an intent key either for single-point to multi-point or * multi-point to single-point intents, based on a prefix that defines * the type of intent, the single connect point representing the single * source or destination for that intent, the name of the VPLS the intent * belongs to, and the destination host MAC address the intent reaches. * * @param prefix the key prefix * @param cPoint the connect point identifying the source/destination * @param vplsName the name of the VPLS * @param hostMac the source/destination MAC address * @param appId application ID for the key * @return the key to identify the intent */ protected static Key buildKey(String prefix, ConnectPoint cPoint, String vplsName, MacAddress hostMac, ApplicationId appId) { String keyString = vplsName + SEPARATOR + prefix + SEPARATOR + cPoint.deviceId() + SEPARATOR + cPoint.port() + SEPARATOR + hostMac; return Key.of(keyString, appId); } /** * Sets one or more encapsulation constraints on the intent builder given. * * @param builder the intent builder * @param constraints the existing intent constraints * @param encap the encapsulation type to be set */ public static void setEncap(ConnectivityIntent.Builder builder, List<Constraint> constraints, EncapsulationType encap) { // Constraints might be an immutable list, so a new modifiable list // is created List<Constraint> newConstraints = new ArrayList<>(constraints); // Remove any encapsulation constraint if already in the list constraints.stream() .filter(c -> c instanceof EncapsulationConstraint) .forEach(newConstraints::remove); // if the new encapsulation is different from NONE, a new encapsulation // constraint should be added to the list if (!encap.equals(NONE)) { newConstraints.add(new EncapsulationConstraint(encap)); } // Submit new constraint list as immutable list builder.constraints(ImmutableList.copyOf(newConstraints)); } /** * Builds filtered connected point by a given network interface. * * @param iface the network interface * @return the filtered connected point of a given network interface */ protected static FilteredConnectPoint buildFilteredConnectedPoint(Interface iface) { Objects.requireNonNull(iface); TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder(); if (iface.vlan() != null && !iface.vlan().equals(VlanId.NONE)) { trafficSelector.matchVlanId(iface.vlan()); } return new FilteredConnectPoint(iface.connectPoint(), trafficSelector.build()); } /** * Builds filtered connected point by a given host. * * @param host the host * @return the filtered connected point of the given host */ protected static FilteredConnectPoint buildFilteredConnectedPoint(Host host) { requireNonNull(host); TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder(); if (host.vlan() != null && !host.vlan().equals(VlanId.NONE)) { trafficSelector.matchVlanId(host.vlan()); } return new FilteredConnectPoint(host.location(), trafficSelector.build()); } }