X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=renderers%2Fios-xe%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fgroupbasedpolicy%2Frenderer%2Fios_xe_provider%2Fimpl%2Futil%2FServiceChainingUtil.java;h=c099b39638f58bf6fde062295123fd31d4d951b5;hb=1ebc9cd0e96570f4aed9d6489e1d02cc5f9a7239;hp=a83e5931b0edae87bac27de32adba3378da48cd1;hpb=4c675e9f323d115d0166e90c9cdf4d2844bd1567;p=groupbasedpolicy.git diff --git a/renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/util/ServiceChainingUtil.java b/renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/util/ServiceChainingUtil.java index a83e5931b..c099b3963 100644 --- a/renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/util/ServiceChainingUtil.java +++ b/renderers/ios-xe/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ios_xe_provider/impl/util/ServiceChainingUtil.java @@ -8,18 +8,50 @@ package org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util; +import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.ActionCase; +import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.ActionInDirection; +import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.createClassMap; +import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.createPolicyMapEntry; +import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.createSecurityGroupMatch; +import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.generateClassMapName; +import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.getTenantId; +import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction.In; +import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction.Out; +import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation.CONSUMER; +import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation.PROVIDER; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition; +import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyConfigurationContext; import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl; -import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.PolicyWriter; +import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NetconfTransactionCreator; +import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.PolicyWriterUtil; import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI; import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI; import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.RendererPathStates; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathState; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathStateKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.ConfiguredRenderedPaths; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPath; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPathKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RendererName; import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName; import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName; import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName; @@ -27,20 +59,25 @@ import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev1407 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder; import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath; import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.sff.data.plane.locator.DataPlaneLocator; import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder; import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths; import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.LocatorType; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.Ip; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; -import org.opendaylight.yang.gen.v1.urn.ios.rev160308.Native; +import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMap; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChainBuilder; +import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native._class.map.Match; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.config.service.chain.grouping.IpBuilder; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.Class; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathBuilder; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey; -import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.Local; -import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.LocalBuilder; +import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameBuilder; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameKey; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.ConfigServiceChainPathModeBuilder; @@ -48,27 +85,218 @@ import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.serv import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.Services; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.ServicesBuilder; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.ServiceTypeChoice; -import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.service.type.choice.ServiceFunctionBuilder; import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.service.type.choice.ServiceFunctionForwarderBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RuleName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue; -import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpointWithPolicy; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.has.unconfigured.rule.groups.unconfigured.rule.group.UnconfiguredResolvedRuleBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpoint; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.actions.Action; import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - public class ServiceChainingUtil { private static final Logger LOG = LoggerFactory.getLogger(ServiceChainingUtil.class); + private static final String RSP_SUFFIX = "-gbp-rsp"; + private static final String RSP_REVERSED_SUFFIX = "-gbp-rsp-Reverse"; + private static long timeout = 5000L; + + /** + * According to provided action, this method gets service function path and collects all info about participation + * and orientation of path. According to path symmetricity, participation and direction, one of these cases happens: + * 1. Path is asymmetric, and it starts in this classifier (specified by context) - direct chain is created + * 2. Path is asymmetric, and it starts in classifier on opposite side of the chain - skipped + * 3. Path is symmetric, and it starts in this classifier - direct chain is created + * 2. Path is symmetric, and it starts in classifier on opposite side of the chain - reversed path is created + *

+ * Behaviour is correct also in case when "this" and "opposite" classifier is the same + * + * @param peerEndpoint - peer endpoint, used to generate status and access to tenant ID + * @param sourceSgt - security group tag of source endpoint + * @param destinationSgt - security group tag of destination endpoint + * @param actionMap - contains all info to evaluate correct chain orientation according to endpoint participation + * @param context - contains policy-map location and status info + * @param dataBroker - to access odl datastore + */ + static void newChainAction(final PeerEndpoint peerEndpoint, final Sgt sourceSgt, + final Sgt destinationSgt, final Map actionMap, + final PolicyConfigurationContext context, final DataBroker dataBroker) { + final ActionInDirection actionInDirection = actionMap.get(ActionCase.CHAIN); + if (actionInDirection == null) { + return; + } + context.setCurrentUnconfiguredRule(new UnconfiguredResolvedRuleBuilder() + .setRuleName(new RuleName(actionInDirection.getRuleName())).build()); + // Rule action + orientation + final Action action = actionInDirection.getAction(); + final EndpointPolicyParticipation participation = actionInDirection.getParticipation(); + final Direction direction = actionInDirection.getDirection(); + // Get service function path + final ServiceFunctionPath servicePath = ServiceChainingUtil.findServicePathFromParameterValues(action.getParameterValue()); + if (servicePath == null || servicePath.getName() == null) { + final String info = String.format("service-path not found (sourceSgt=%s, destinationSgt=%s)", + sourceSgt, destinationSgt); + context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context, peerEndpoint, info)); + return; + } + final TenantId tenantId = PolicyManagerUtil.getTenantId(peerEndpoint); + if (tenantId == null) { + final String info = String.format("tenant-id not found (sourceSgt=%s, destinationSgt=%s)", + sourceSgt, destinationSgt); + context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context, peerEndpoint, info)); + return; + } + boolean sfcPartSuccessful = true; + // Creates direct path in corresponding direction + if ((participation.equals(PROVIDER) && direction.equals(Out)) || + (participation.equals(CONSUMER) && direction.equals(In))) { + final RenderedServicePath renderedServicePath = ServiceChainingUtil.resolveRenderedServicePath(servicePath, + tenantId, dataBroker, sourceSgt, destinationSgt, context); + sfcPartSuccessful = resolveRemoteSfcComponents(renderedServicePath, context); + // Creates reversed path if symmetric + } else if (servicePath.isSymmetric()) { + final RenderedServicePath renderedServicePath = + ServiceChainingUtil.resolveReversedRenderedServicePath(servicePath, tenantId, dataBroker, sourceSgt, + destinationSgt, context); + sfcPartSuccessful = resolveRemoteSfcComponents(renderedServicePath, context); + } + if (!sfcPartSuccessful) { + final String info = String.format("failed during sfc-part execution (sourceSgt=%s, destinationSgt=%s)", + sourceSgt, destinationSgt); + context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context, + peerEndpoint, info)); + } + } + + /** + * According to service function path and direction, creates appropriate rendered service path name {@link RspName} + * and starts appropriate method which removes policy for resolved endpoint pair + * + * @param peerEndpoint - contains info about tenant ID + * @param sourceSgt - security group tag of source endpoint + * @param destinationSgt - security group tag of destination endpoint + * @param actionMap - contains all info to evaluate correct chain orientation according to endpoint participation + * @param context - contains policy-map location and status info + */ + static void resolveRemovedChainAction(final PeerEndpoint peerEndpoint, final Sgt sourceSgt, final Sgt destinationSgt, + final Map actionMap, final PolicyConfigurationContext context) { + final ActionInDirection actionInDirection = actionMap.get(ActionCase.CHAIN); + final Action action = actionInDirection.getAction(); + final EndpointPolicyParticipation participation = actionInDirection.getParticipation(); + final Direction direction = actionInDirection.getDirection(); + final ServiceFunctionPath servicePath = ServiceChainingUtil.findServicePathFromParameterValues(action.getParameterValue()); + if (servicePath == null || servicePath.getName() == null) { + return; + } + final TenantId tenantId = getTenantId(peerEndpoint); + if (tenantId == null) { + return; + } + //Symmetric chain + if ((participation.equals(PROVIDER) && direction.equals(Out)) || + (participation.equals(CONSUMER) && direction.equals(In))) { + final RspName rspName = generateRspName(servicePath, tenantId); + resolveRemovedRenderedServicePath(rspName, sourceSgt, destinationSgt, context); + + } else if (servicePath.isSymmetric()) { + final RspName rspName = generateReversedRspName(servicePath, tenantId); + resolveRemovedRenderedServicePath(rspName, sourceSgt, destinationSgt, context); + } + } + + /** + * Service-path (netconf) is created on every netconf device, which contains service function belonging to specific + * chain. Classifier has to be able to reach first service function forwarder in order to send packet to chain. If + * first service function forwarder is present on the same node as classifier, service-path entry should be already + * present (created by IOS-XE renderer in SFC) also with appropriate remote SFF if necessary. If first SFF is on + * different node (remote classifier), classifier has to create it's own service-path entry with remote SFF. + * + * @param renderedServicePath - path classifier has to reach + * @param context - contains policy-map location and status info + * @return true if everything went good, false otherwise + */ + public static boolean resolveRemoteSfcComponents(final RenderedServicePath renderedServicePath, + final PolicyConfigurationContext context) { + final PolicyManagerImpl.PolicyMapLocation location = context.getPolicyMapLocation(); + final ServiceFunctionForwarder forwarder = getFirstHopSff(renderedServicePath); + if (forwarder == null) { + return false; + } + final SffName sffName = forwarder.getName(); + if (forwarder.getSffDataPlaneLocator() == null || forwarder.getSffDataPlaneLocator().isEmpty()) { + LOG.warn("Service function forwarder {} does not contain data plane locator", sffName.getValue()); + return false; + } + // TODO only first dpl resolved + final SffDataPlaneLocator sffDataPlaneLocator = forwarder.getSffDataPlaneLocator().get(0); + final DataPlaneLocator dataPlaneLocator = sffDataPlaneLocator.getDataPlaneLocator(); + final LocatorType locatorType = dataPlaneLocator.getLocatorType(); + if (locatorType != null && locatorType instanceof Ip) { + final IpAddress remoteForwarderIpAddress = (((Ip) locatorType).getIp()); + if (remoteForwarderIpAddress == null || remoteForwarderIpAddress.getIpv4Address() == null) { + LOG.warn("Service function forwarder {} data plane locator does not contain ip address", sffName.getValue()); + return false; + } + final String remoteForwarderStringIp = remoteForwarderIpAddress.getIpv4Address().getValue(); + final java.util.Optional optionalIpMgmtAddress = java.util.Optional.ofNullable(forwarder.getIpMgmtAddress()); - static ServiceFunctionPath getServicePath(final List params) { + return optionalIpMgmtAddress.map(IpAddress::getIpv4Address) + .map(Ipv4Address::getValue) + .map(addressValue -> { + final ServiceTypeChoice serviceTypeChoice; + if (!addressValue.equals(location.getManagementIpAddress())) { + // Remote forwarder + final ServiceFfNameBuilder remoteSffBuilder = new ServiceFfNameBuilder(); + remoteSffBuilder.setName(sffName.getValue()) + .setKey(new ServiceFfNameKey(sffName.getValue())) + .setIp(new IpBuilder().setAddress(new Ipv4Address(remoteForwarderStringIp)).build()); + boolean rsResult = PolicyWriterUtil.writeRemote(remoteSffBuilder.build(), location); + context.setFutureResult(Futures.immediateCheckedFuture(rsResult)); + serviceTypeChoice = createForwarderTypeChoice(sffName.getValue()); + // Service chain + final List services = new ArrayList<>(); + final ServicesBuilder servicesBuilder = new ServicesBuilder(); + servicesBuilder.setServiceIndexId(renderedServicePath.getStartingIndex()) + .setServiceTypeChoice(serviceTypeChoice); + services.add(servicesBuilder.build()); + final List servicePaths = new ArrayList<>(); + final ServicePathBuilder servicePathBuilder = new ServicePathBuilder(); + servicePathBuilder.setKey(new ServicePathKey(renderedServicePath.getPathId())) + .setServicePathId(renderedServicePath.getPathId()) + .setConfigServiceChainPathMode(new ConfigServiceChainPathModeBuilder() + .setServiceIndex(new ServiceIndexBuilder() + .setServices(services).build()).build()); + servicePaths.add(servicePathBuilder.build()); + final ServiceChainBuilder chainBuilder = new ServiceChainBuilder(); + chainBuilder.setServicePath(servicePaths); + final ServiceChain serviceChain = chainBuilder.build(); + boolean scResult = PolicyWriterUtil.writeServicePath(serviceChain, location); + context.setFutureResult(Futures.immediateCheckedFuture(scResult)); + } + return true; + }).orElseGet(createNegativePathWithLogSupplier(sffName.getValue(), + (value) -> LOG.error("Cannot create remote forwarder, SFF {} does not contain management ip address", + value)) + ); + } + return false; + } + + /** + * Investigates provided parameter values and derives service chain name. This name is used to find service function + * path + * + * @param params - list of parameters + * @return - service function path if found, null if provided parameters does not correspond with any chain or there + * is no service function path defined by that chain + */ + @Nullable + static ServiceFunctionPath findServicePathFromParameterValues(final List params) { if (params == null || params.isEmpty()) { LOG.error("Cannot found service path, parameter value is null"); return null; @@ -93,7 +321,7 @@ public class ServiceChainingUtil { LOG.error("Cannot found service path, chain name is null"); return null; } - final ServiceFunctionPath serviceFunctionPath = findServiceFunctionPath(new SfcName(chainName)); + final ServiceFunctionPath serviceFunctionPath = findServiceFunctionPathFromServiceChainName(new SfcName(chainName)); if (serviceFunctionPath == null) { LOG.error("Service function path not found for name {}", chainName); return null; @@ -101,213 +329,266 @@ public class ServiceChainingUtil { return serviceFunctionPath; } - static void resolveChainAction(final PeerEndpointWithPolicy peerEndpoint, final Sgt sourceSgt, - final Sgt destinationSgt, final Map actionMap, - final String classMapName, PolicyWriter policyWriter) { - final List entries = new ArrayList<>(); - final Action action = actionMap.get(PolicyManagerImpl.ActionCase.CHAIN); - final ServiceFunctionPath servicePath = ServiceChainingUtil.getServicePath(action.getParameterValue()); - if (servicePath == null) { - return; - } - final TenantId tenantId = PolicyManagerUtil.getTenantId(peerEndpoint); - if (tenantId == null) { - return; + static ServiceFunctionPath findServiceFunctionPathFromServiceChainName(@Nonnull final SfcName chainName) { + final ServiceFunctionPaths allPaths = SfcProviderServicePathAPI.readAllServiceFunctionPaths(); + if (allPaths == null || allPaths.getServiceFunctionPath() == null || allPaths.getServiceFunctionPath().isEmpty()) { + return null; } - final RenderedServicePath renderedPath = ServiceChainingUtil.createRenderedPath(servicePath, tenantId); - // Create appropriate service path && remote forwarder - setSfcPart(renderedPath, policyWriter); - - entries.add(PolicyManagerUtil.createPolicyEntry(classMapName, renderedPath, PolicyManagerImpl.ActionCase.CHAIN)); - if (servicePath.isSymmetric()) { - // symmetric path is in opposite direction. Roles of renderer and peer endpoint will invert - RenderedServicePath symmetricPath = ServiceChainingUtil - .createSymmetricRenderedPath(servicePath, renderedPath, tenantId); - final String oppositeClassMapName = PolicyManagerUtil.generateClassMapName(destinationSgt.getValue(), sourceSgt.getValue()); - entries.add(PolicyManagerUtil.createPolicyEntry(oppositeClassMapName, symmetricPath, PolicyManagerImpl.ActionCase.CHAIN)); + for (ServiceFunctionPath serviceFunctionPath : allPaths.getServiceFunctionPath()) { + if (chainName.equals(serviceFunctionPath.getServiceChainName())) { + return serviceFunctionPath; + } } - policyWriter.cache(entries); + return null; } - static RenderedServicePath createRenderedPath(final ServiceFunctionPath sfp, final TenantId tenantId) { - RenderedServicePath renderedServicePath; - // Try to read existing RSP - final RspName rspName = new RspName(sfp.getName().getValue() + tenantId.getValue() + "-gbp-rsp"); - renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName); - if (renderedServicePath != null) { - return renderedServicePath; + /** + * Creates {@link RenderedServicePath} if not exist. If created, ios-xe renderer in SFC is invoked, so this method + * has to wait till SFC part is done to prevent transaction collisions in {@link this#checkRspManagerStatus(RspName, + * DataBroker)}. If this operation is successful, class-map {@link ClassMap} and entry in policy-map {@link Class} + * is written + * + * @param sfp - path used to create RSP + * @param tenantId - used to generate RSP name according to GBP standards + * @param dataBroker - data provider to access odl controller + * @param sourceSgt - source security group tag + * @param destinationSgt - destination security group tag + * @param context - contains policy-map location and status info + * @return read/created RSP + */ + static RenderedServicePath resolveRenderedServicePath(final ServiceFunctionPath sfp, final TenantId tenantId, + final DataBroker dataBroker, final Sgt sourceSgt, final Sgt destinationSgt, + final PolicyConfigurationContext context) { + // Get rendered service path + final RspName rspName = generateRspName(sfp, tenantId); + RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName); + if (renderedServicePath == null) { + LOG.info("Rendered service path with name {} not found, creating a new one ..", rspName.getValue()); + final CreateRenderedPathInput input = new CreateRenderedPathInputBuilder() + .setParentServiceFunctionPath(sfp.getName().getValue()) + .setName(rspName.getValue()) + .setSymmetric(sfp.isSymmetric()) + .build(); + renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, input); + LOG.info("Rendered service path {} created", rspName.getValue()); + checkRspManagerStatus(rspName, dataBroker); } - LOG.info("Rendered service path with name {} not found, creating a new one ..", rspName.getValue()); - final CreateRenderedPathInput input = new CreateRenderedPathInputBuilder() - .setParentServiceFunctionPath(sfp.getName().getValue()) - .setName(rspName.getValue()) - .setSymmetric(sfp.isSymmetric()) - .build(); - renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, input); - LOG.info("Rendered service path {} created", rspName.getValue()); + // Create class-map and policy-map entry + final String classMapName = generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue()); + final Match match = createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue()); + final ClassMap classMap = createClassMap(classMapName, match); + final Class policyMapEntry = createPolicyMapEntry(classMapName, renderedServicePath, ActionCase.CHAIN); + boolean cmResult = PolicyWriterUtil.writeClassMap(classMap, context.getPolicyMapLocation()); + context.setFutureResult(Futures.immediateCheckedFuture(cmResult)); + boolean pmeResult = PolicyWriterUtil.writePolicyMapEntry(policyMapEntry, context.getPolicyMapLocation()); + context.setFutureResult(Futures.immediateCheckedFuture(pmeResult)); return renderedServicePath; } - static RenderedServicePath createSymmetricRenderedPath(final ServiceFunctionPath sfp, final RenderedServicePath rsp, - final TenantId tenantId) { - RenderedServicePath reversedRenderedPath; - // Try to read existing RSP - final RspName rspName = new RspName(sfp.getName().getValue() + tenantId.getValue() + "-gbp-rsp-Reverse"); - reversedRenderedPath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName); - if (reversedRenderedPath != null) { - return reversedRenderedPath; + /** + * Creates reversed {@link RenderedServicePath} if not exist. To be successful, direct path has to exist. + * If created, ios-xe renderer in SFC is invoked, so this method has to wait till SFC part is done to prevent + * transaction collisions. If this operation is successful, class-map {@link ClassMap} and entry in policy-map + * {@link Class} is written + * + * @param sfp - path used to create RSP + * @param tenantId - used to generate RSP name according to GBP standards + * @param dataBroker - data provider to access odl controller + * @param sourceSgt - source security group tag + * @param destinationSgt - destination security group tag + * @param context - contains policy-map location and status info + * @return read/created RSP + */ + public static RenderedServicePath resolveReversedRenderedServicePath(final ServiceFunctionPath sfp, final TenantId tenantId, + final DataBroker dataBroker, final Sgt sourceSgt, + final Sgt destinationSgt, final PolicyConfigurationContext context) { + // Get rendered service path + final RspName rspName = generateRspName(sfp, tenantId); + RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName); + if (renderedServicePath == null) { + LOG.info("Rendered service path with name {} not found, creating a new one ..", rspName.getValue()); + final CreateRenderedPathInput input = new CreateRenderedPathInputBuilder() + .setParentServiceFunctionPath(sfp.getName().getValue()) + .setName(rspName.getValue()) + .setSymmetric(sfp.isSymmetric()) + .build(); + renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, input); + LOG.info("Rendered service path {} created", rspName.getValue()); + checkRspManagerStatus(rspName, dataBroker); + } + // Get reversed rendered service path + final RspName reversedRspName = generateReversedRspName(sfp, tenantId); + RenderedServicePath reversedRenderedPath = SfcProviderRenderedPathAPI.readRenderedServicePath(reversedRspName); + if (reversedRenderedPath == null) { + LOG.info("Reversed rendered service path with name {} not found, creating a new one ..", reversedRspName.getValue()); + reversedRenderedPath = SfcProviderRenderedPathAPI.createReverseRenderedServicePathEntry(renderedServicePath); + LOG.info("Rendered service path {} created", reversedRspName.getValue()); + checkRspManagerStatus(reversedRspName, dataBroker); } - LOG.info("Reversed rendered service path with name {} not found, creating a new one ..", rspName.getValue()); - reversedRenderedPath = SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp); - LOG.info("Rendered service path {} created", rspName.getValue()); + // Create class-map and policy-map entry + final String classMapName = generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue()); + final Match match = createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue()); + final ClassMap classMap = createClassMap(classMapName, match); + final Class policyMapEntry = createPolicyMapEntry(classMapName, renderedServicePath, ActionCase.CHAIN); + boolean cmResult = PolicyWriterUtil.writeClassMap(classMap, context.getPolicyMapLocation()); + context.setFutureResult(Futures.immediateCheckedFuture(cmResult)); + boolean pmeResult = PolicyWriterUtil.writePolicyMapEntry(policyMapEntry, context.getPolicyMapLocation()); + context.setFutureResult(Futures.immediateCheckedFuture(pmeResult)); + resolveRemoteSfcComponents(renderedServicePath, context); return reversedRenderedPath; } /** - * Method checks up, whether a {@link Local} Service Function Forwarder is present on device or not. + * Removes all policy setup created according to rendered service path. * - * @param mountpoint used to access specific device - * @return true if Local Forwarder is present, false otherwise + * @param rspName - rendered service path name + * @param sourceSgt - source security group tag + * @param destinationSgt - destination security group tag + * @param context - context with policy-map location */ - private static boolean checkLocalForwarderPresence(DataBroker mountpoint) { - InstanceIdentifier localSffIid = InstanceIdentifier.builder(Native.class) - .child(ServiceChain.class) - .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder.class) - .child(Local.class).build(); - ReadWriteTransaction rwt = mountpoint.newReadWriteTransaction(); - CheckedFuture, ReadFailedException> submitFuture = rwt.read(LogicalDatastoreType.CONFIGURATION, - localSffIid); - try { - Optional optionalLocalSff = submitFuture.checkedGet(); - return optionalLocalSff.isPresent(); - } catch (ReadFailedException e) { - LOG.warn("Read transaction failed to {} ", e); - } catch (Exception e) { - LOG.error("Failed to .. {}", e.getMessage()); + private static void resolveRemovedRenderedServicePath(final RspName rspName, final Sgt sourceSgt, final Sgt destinationSgt, + final PolicyConfigurationContext context) { + final String classMapName = PolicyManagerUtil.generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue()); + final ClassMap classMap = PolicyManagerUtil.createClassMap(classMapName, null); + final Class policyMapEntry = PolicyManagerUtil.createPolicyMapEntry(classMapName, null, PolicyManagerImpl.ActionCase.CHAIN); + PolicyWriterUtil.removePolicyMapEntry(policyMapEntry, context.getPolicyMapLocation()); + PolicyWriterUtil.removeClassMap(classMap, context.getPolicyMapLocation()); + final RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName); + final ServiceFunctionForwarder firstHopSff = getFirstHopSff(renderedServicePath); + if (firstHopSff != null && firstHopSff.getIpMgmtAddress() != null && + firstHopSff.getIpMgmtAddress().getIpv4Address() != null) { + final String sffMgmtIpValue = firstHopSff.getIpMgmtAddress().getIpv4Address().getValue(); + if (!sffMgmtIpValue.equals(context.getPolicyMapLocation().getManagementIpAddress())) { + // Remove service chain and remote forwarder + final ServiceChain serviceChain = createServiceChain(renderedServicePath); + final ServiceFfName remoteForwarder = createRemoteForwarder(firstHopSff); + PolicyWriterUtil.removeServicePath(serviceChain, context.getPolicyMapLocation()); + PolicyWriterUtil.removeRemote(remoteForwarder, context.getPolicyMapLocation()); + } } - return false; + } + + static ServiceFfName createRemoteForwarder(ServiceFunctionForwarder firstHopSff) { + final ServiceFfNameBuilder serviceFfNameBuilder = new ServiceFfNameBuilder(); + serviceFfNameBuilder.setName(firstHopSff.getName().getValue()); + return serviceFfNameBuilder.build(); + } + + private static ServiceTypeChoice createForwarderTypeChoice(final String forwarderName) { + final ServiceFunctionForwarderBuilder sffBuilder = new ServiceFunctionForwarderBuilder(); + sffBuilder.setServiceFunctionForwarder(forwarderName); + return sffBuilder.build(); } /** - * Method checks up, if some {@link ServicePath} is present on device. + * Creates service-chain with name/key only, using rendered service path id. This object contains no data, it is used + * to create instance identifier when appropriate service-chain is removed from particular device * - * @param mountpoint used to access specific device - * @return true if service chain does not exist, is null or does not contain any service path. False otherwise + * @param renderedServicePath - it's path id is used as a identifier + * @return service-chain object with id */ - public static boolean checkServicePathPresence(DataBroker mountpoint) { - InstanceIdentifier serviceChainIid = InstanceIdentifier.builder(Native.class) - .child(ServiceChain.class).build(); - ReadWriteTransaction rwt = mountpoint.newReadWriteTransaction(); - CheckedFuture, ReadFailedException> submitFuture = rwt.read(LogicalDatastoreType.CONFIGURATION, - serviceChainIid); - try { - Optional optionalServiceChain = submitFuture.checkedGet(); - if (optionalServiceChain.isPresent()) { - ServiceChain chain = optionalServiceChain.get(); - return chain == null || chain.getServicePath() == null || chain.getServicePath().isEmpty(); - } else { - return true; - } - } catch (ReadFailedException e) { - LOG.warn("Read transaction failed to {} ", e); - } catch (Exception e) { - LOG.error("Failed to .. {}", e.getMessage()); - } - return false; + private static ServiceChain createServiceChain(final RenderedServicePath renderedServicePath) { + final Long pathId = renderedServicePath.getPathId(); + final ServicePathBuilder servicePathBuilder = new ServicePathBuilder(); + final ServiceChainBuilder serviceChainBuilder = new ServiceChainBuilder(); + servicePathBuilder.setServicePathId(pathId) + .setKey(new ServicePathKey(pathId)); + serviceChainBuilder.setServicePath(Collections.singletonList(servicePathBuilder.build())); + return serviceChainBuilder.build(); } - private static ServiceFunctionPath findServiceFunctionPath(final SfcName chainName) { - final ServiceFunctionPaths allPaths = SfcProviderServicePathAPI.readAllServiceFunctionPaths(); - for (ServiceFunctionPath serviceFunctionPath : allPaths.getServiceFunctionPath()) { - if (serviceFunctionPath.getServiceChainName().equals(chainName)) { - return serviceFunctionPath; - } + private static Supplier createNegativePathWithLogSupplier(final T value, final Consumer logCommand) { + return () -> { + // fireLog + logCommand.accept(value); + return false; + }; + } + + private static ServiceFunctionForwarder getFirstHopSff(RenderedServicePath renderedServicePath) { + if (renderedServicePath == null || renderedServicePath.getRenderedServicePathHop() == null || + renderedServicePath.getRenderedServicePathHop().isEmpty()) { + return null; } - return null; + final RenderedServicePathHop firstHop = renderedServicePath.getRenderedServicePathHop().get(0); + final SffName firstHopSff = firstHop.getServiceFunctionForwarder(); + if (firstHopSff == null) { + return null; + } + return SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(firstHopSff); } - private static void setSfcPart(final RenderedServicePath renderedServicePath, PolicyWriter policyWriter) { - if (renderedServicePath != null && renderedServicePath.getRenderedServicePathHop() != null && - !renderedServicePath.getRenderedServicePathHop().isEmpty()) { - final RenderedServicePathHop firstHop = renderedServicePath.getRenderedServicePathHop().get(0); - if (firstHop == null) { - LOG.error("Rendered service path {} does not contain any hop", renderedServicePath.getName().getValue()); - return; - } - final SffName sffName = firstHop.getServiceFunctionForwarder(); - final ServiceFunctionForwarder serviceFunctionForwarder = SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(sffName); - if (serviceFunctionForwarder == null) { - LOG.error("Sff with name {} does not exist", sffName.getValue()); - return; - } - // Forwarders - // - // If classifier node is also forwarder, first entry in service path has to point to first service function - // (Local case) - // - // If first hop Sff is on different node, first service path entry has to point to that specific service - // forwarder (Remote case) + private static RspName generateRspName(final ServiceFunctionPath serviceFunctionPath, final TenantId tenantId) { + return new RspName(serviceFunctionPath.getName().getValue() + "-" + tenantId.getValue() + RSP_SUFFIX); + } - // Local case (only when does not exist) + private static RspName generateReversedRspName(final ServiceFunctionPath serviceFunctionPath, final TenantId tenantId) { + return new RspName(serviceFunctionPath.getName().getValue() + "-" + tenantId.getValue() + RSP_REVERSED_SUFFIX); + } - if (!checkLocalForwarderPresence(policyWriter.getCurrentMountpoint())) { - final LocalBuilder localSffBuilder = new LocalBuilder(); - localSffBuilder.setIp(new IpBuilder().setAddress(new Ipv4Address(policyWriter.getManagementIpAddress())) - .build()); - policyWriter.cache(localSffBuilder.build()); - } else { - LOG.info("Local forwarder for node {} is already created", policyWriter.getCurrentNodeId()); + private static void checkRspManagerStatus(final RspName rspName, final DataBroker dataBroker) { + // TODO A better way to do this is to register listener and wait for notification than using hardcoded timeout + // with Thread.sleep(). Example in class BridgeDomainManagerImpl + ConfiguredRenderedPath renderedPath = null; + LOG.debug("Waiting for SFC to configure path {} ...", rspName.getValue()); + + byte attempt = 0; + do { + attempt++; + // Wait + try { + Thread.sleep(timeout); + } catch (InterruptedException e) { + LOG.error("Thread interrupted while waiting ... {} ", e); } - // Set up choice. If remote, this choice is overwritten - ServiceTypeChoice serviceTypeChoice = functionTypeChoice(firstHop.getServiceFunctionName().getValue()); - // Remote case - if (serviceFunctionForwarder.getIpMgmtAddress() == null - || serviceFunctionForwarder.getIpMgmtAddress().getIpv4Address() == null) { - LOG.error("Cannot create remote forwarder, SFF {} does not contain management ip address", - sffName.getValue()); + // Read actual status + final InstanceIdentifier statusIid = InstanceIdentifier.builder(RendererPathStates.class) + .child(RendererPathState.class, new RendererPathStateKey(new RendererName("ios-xe-renderer"))) + .child(ConfiguredRenderedPaths.class) + .child(ConfiguredRenderedPath.class, new ConfiguredRenderedPathKey(rspName)).build(); + final java.util.Optional optionalTransaction = + NetconfTransactionCreator.netconfReadWriteTransaction(dataBroker); + if (!optionalTransaction.isPresent()) { + LOG.warn("Failed to create transaction, mountpoint: {}", dataBroker); return; } - final String sffMgmtIpAddress = serviceFunctionForwarder.getIpMgmtAddress().getIpv4Address().getValue(); - // If local SFF has the same ip as first hop sff, it's the same SFF; no need to create a remote one - if (!sffMgmtIpAddress.equals(policyWriter.getManagementIpAddress())) { - final ServiceFfNameBuilder remoteSffBuilder = new ServiceFfNameBuilder(); - remoteSffBuilder.setName(sffName.getValue()) - .setKey(new ServiceFfNameKey(sffName.getValue())) - .setIp(new IpBuilder().setAddress(new Ipv4Address(sffMgmtIpAddress)).build()); - policyWriter.cache(remoteSffBuilder.build()); - serviceTypeChoice = forwarderTypeChoice(sffName.getValue()); + ReadWriteTransaction transaction = optionalTransaction.get(); + try { + final CheckedFuture, ReadFailedException> submitFuture = + transaction.read(LogicalDatastoreType.OPERATIONAL, statusIid); + final Optional optionalPath = submitFuture.checkedGet(); + if (optionalPath.isPresent()) { + renderedPath = optionalPath.get(); + } + } catch (ReadFailedException e) { + LOG.warn("Failed while read rendered path status ... {} ", e.getMessage()); + } + if (renderedPath == null || renderedPath.getPathStatus() == null || + renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.InProgress)) { + LOG.info("Still waiting for SFC ... "); + } else if (renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.Failure)) { + LOG.warn("SFC failed to configure rsp"); + } else if (renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.Success)) { + LOG.debug("RSP {} configured by SFC", rspName.getValue()); + try { + Thread.sleep(timeout); // Just for sure, maybe will be safe to remove this + } catch (InterruptedException e) { + LOG.error("Thread interrupted while waiting ... {} ", e); + } + return; } - - // Service chain - final List services = new ArrayList<>(); - final ServicesBuilder servicesBuilder = new ServicesBuilder(); - servicesBuilder.setServiceIndexId(renderedServicePath.getStartingIndex()) - .setServiceTypeChoice(serviceTypeChoice); - final List servicePaths = new ArrayList<>(); - final ServicePathBuilder servicePathBuilder = new ServicePathBuilder(); - servicePathBuilder.setKey(new ServicePathKey(renderedServicePath.getPathId())) - .setServicePathId(renderedServicePath.getPathId()) - .setConfigServiceChainPathMode(new ConfigServiceChainPathModeBuilder() - .setServiceIndex(new ServiceIndexBuilder() - .setServices(services).build()).build()); - servicePaths.add(servicePathBuilder.build()); - final ServiceChainBuilder chainBuilder = new ServiceChainBuilder(); - chainBuilder.setServicePath(servicePaths); - final ServiceChain serviceChain = chainBuilder.build(); - policyWriter.cache(serviceChain); } + while (attempt <= 6); + LOG.warn("Maximum number of attempts reached"); } - private static ServiceTypeChoice forwarderTypeChoice(final String forwarderName) { - final ServiceFunctionForwarderBuilder sffBuilder = new ServiceFunctionForwarderBuilder(); - sffBuilder.setServiceFunctionForwarder(forwarderName); - return sffBuilder.build(); - } - - private static ServiceTypeChoice functionTypeChoice(final String functionName) { - final ServiceFunctionBuilder sfBuilder = new ServiceFunctionBuilder(); - sfBuilder.setServiceFunction(functionName); - return sfBuilder.build(); + /** + * Only for test purposes + * + * @param value - set actual timeout value + */ + @VisibleForTesting + public static void setTimeout(long value) { + timeout = value; } - -} +} \ No newline at end of file