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