c099b39638f58bf6fde062295123fd31d4d951b5
[groupbasedpolicy.git] / renderers / ios-xe / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ios_xe_provider / impl / util / ServiceChainingUtil.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util;
10
11 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.ActionCase;
12 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.ActionInDirection;
13 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.createClassMap;
14 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.createPolicyMapEntry;
15 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.createSecurityGroupMatch;
16 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.generateClassMapName;
17 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.getTenantId;
18 import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction.In;
19 import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction.Out;
20 import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation.CONSUMER;
21 import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation.PROVIDER;
22
23 import javax.annotation.Nonnull;
24 import javax.annotation.Nullable;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.function.Consumer;
31 import java.util.function.Supplier;
32 import com.google.common.annotations.VisibleForTesting;
33 import com.google.common.base.Optional;
34 import com.google.common.util.concurrent.CheckedFuture;
35 import com.google.common.util.concurrent.Futures;
36 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
37 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
38 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
39 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
40 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
41 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyConfigurationContext;
42 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl;
43 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NetconfTransactionCreator;
44 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.PolicyWriterUtil;
45 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
46 import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
47 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
48 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.RendererPathStates;
49 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathState;
50 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathStateKey;
51 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.ConfiguredRenderedPaths;
52 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPath;
53 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPathKey;
54 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RendererName;
55 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
56 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
57 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
58 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
59 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
60 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
61 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
62 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator;
63 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.sff.data.plane.locator.DataPlaneLocator;
64 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
65 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
66 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
67 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.LocatorType;
68 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.Ip;
69 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
70 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
71 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMap;
72 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain;
73 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChainBuilder;
74 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native._class.map.Match;
75 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.config.service.chain.grouping.IpBuilder;
76 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.Class;
77 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
78 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathBuilder;
79 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
80 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName;
81 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameBuilder;
82 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameKey;
83 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.ConfigServiceChainPathModeBuilder;
84 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.ServiceIndexBuilder;
85 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.Services;
86 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.ServicesBuilder;
87 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.ServiceTypeChoice;
88 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;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RuleName;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.has.unconfigured.rule.groups.unconfigured.rule.group.UnconfiguredResolvedRuleBuilder;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpoint;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.actions.Action;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
98 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
99 import org.slf4j.Logger;
100 import org.slf4j.LoggerFactory;
101
102 public class ServiceChainingUtil {
103
104     private static final Logger LOG = LoggerFactory.getLogger(ServiceChainingUtil.class);
105     private static final String RSP_SUFFIX = "-gbp-rsp";
106     private static final String RSP_REVERSED_SUFFIX = "-gbp-rsp-Reverse";
107     private static long timeout = 5000L;
108
109     /**
110      * According to provided action, this method gets service function path and collects all info about participation
111      * and orientation of path. According to path symmetricity, participation and direction, one of these cases happens:
112      * 1. Path is asymmetric, and it starts in  this classifier (specified by context) - direct chain is created
113      * 2. Path is asymmetric, and it starts in classifier on opposite side of the chain - skipped
114      * 3. Path is symmetric, and it starts in this classifier - direct chain is created
115      * 2. Path is symmetric, and it starts in classifier on opposite side of the chain - reversed path is created
116      * <p>
117      * Behaviour is correct also in case when "this" and "opposite" classifier is the same
118      *
119      * @param peerEndpoint   - peer endpoint, used to generate status and access to tenant ID
120      * @param sourceSgt      - security group tag of source endpoint
121      * @param destinationSgt - security group tag of destination endpoint
122      * @param actionMap      - contains all info to evaluate correct chain orientation according to endpoint participation
123      * @param context        - contains policy-map location and status info
124      * @param dataBroker     - to access odl datastore
125      */
126     static void newChainAction(final PeerEndpoint peerEndpoint, final Sgt sourceSgt,
127                                final Sgt destinationSgt, final Map<PolicyManagerImpl.ActionCase, ActionInDirection> actionMap,
128                                final PolicyConfigurationContext context, final DataBroker dataBroker) {
129         final ActionInDirection actionInDirection = actionMap.get(ActionCase.CHAIN);
130         if (actionInDirection == null) {
131             return;
132         }
133         context.setCurrentUnconfiguredRule(new UnconfiguredResolvedRuleBuilder()
134                 .setRuleName(new RuleName(actionInDirection.getRuleName())).build());
135         // Rule action + orientation
136         final Action action = actionInDirection.getAction();
137         final EndpointPolicyParticipation participation = actionInDirection.getParticipation();
138         final Direction direction = actionInDirection.getDirection();
139         // Get service function path
140         final ServiceFunctionPath servicePath = ServiceChainingUtil.findServicePathFromParameterValues(action.getParameterValue());
141         if (servicePath == null || servicePath.getName() == null) {
142             final String info = String.format("service-path not found (sourceSgt=%s, destinationSgt=%s)",
143                     sourceSgt, destinationSgt);
144             context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context, peerEndpoint, info));
145             return;
146         }
147         final TenantId tenantId = PolicyManagerUtil.getTenantId(peerEndpoint);
148         if (tenantId == null) {
149             final String info = String.format("tenant-id not found (sourceSgt=%s, destinationSgt=%s)",
150                     sourceSgt, destinationSgt);
151             context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context, peerEndpoint, info));
152             return;
153         }
154         boolean sfcPartSuccessful = true;
155         // Creates direct path in corresponding direction
156         if ((participation.equals(PROVIDER) && direction.equals(Out)) ||
157                 (participation.equals(CONSUMER) && direction.equals(In))) {
158             final RenderedServicePath renderedServicePath = ServiceChainingUtil.resolveRenderedServicePath(servicePath,
159                     tenantId, dataBroker, sourceSgt, destinationSgt, context);
160             sfcPartSuccessful = resolveRemoteSfcComponents(renderedServicePath, context);
161             // Creates reversed path if symmetric
162         } else if (servicePath.isSymmetric()) {
163             final RenderedServicePath renderedServicePath =
164                     ServiceChainingUtil.resolveReversedRenderedServicePath(servicePath, tenantId, dataBroker, sourceSgt,
165                             destinationSgt, context);
166             sfcPartSuccessful = resolveRemoteSfcComponents(renderedServicePath, context);
167         }
168         if (!sfcPartSuccessful) {
169             final String info = String.format("failed during sfc-part execution (sourceSgt=%s, destinationSgt=%s)",
170                     sourceSgt, destinationSgt);
171             context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context,
172                     peerEndpoint, info));
173         }
174     }
175
176     /**
177      * According to service function path and direction, creates appropriate rendered service path name {@link RspName}
178      * and starts appropriate method which removes policy for resolved endpoint pair
179      *
180      * @param peerEndpoint   - contains info about tenant ID
181      * @param sourceSgt      - security group tag of source endpoint
182      * @param destinationSgt - security group tag of destination endpoint
183      * @param actionMap      - contains all info to evaluate correct chain orientation according to endpoint participation
184      * @param context        - contains policy-map location and status info
185      */
186     static void resolveRemovedChainAction(final PeerEndpoint peerEndpoint, final Sgt sourceSgt, final Sgt destinationSgt,
187                                           final Map<ActionCase, ActionInDirection> actionMap, final PolicyConfigurationContext context) {
188         final ActionInDirection actionInDirection = actionMap.get(ActionCase.CHAIN);
189         final Action action = actionInDirection.getAction();
190         final EndpointPolicyParticipation participation = actionInDirection.getParticipation();
191         final Direction direction = actionInDirection.getDirection();
192         final ServiceFunctionPath servicePath = ServiceChainingUtil.findServicePathFromParameterValues(action.getParameterValue());
193         if (servicePath == null || servicePath.getName() == null) {
194             return;
195         }
196         final TenantId tenantId = getTenantId(peerEndpoint);
197         if (tenantId == null) {
198             return;
199         }
200         //Symmetric chain
201         if ((participation.equals(PROVIDER) && direction.equals(Out)) ||
202                 (participation.equals(CONSUMER) && direction.equals(In))) {
203             final RspName rspName = generateRspName(servicePath, tenantId);
204             resolveRemovedRenderedServicePath(rspName, sourceSgt, destinationSgt, context);
205
206         } else if (servicePath.isSymmetric()) {
207             final RspName rspName = generateReversedRspName(servicePath, tenantId);
208             resolveRemovedRenderedServicePath(rspName, sourceSgt, destinationSgt, context);
209         }
210     }
211
212     /**
213      * Service-path (netconf) is created on every netconf device, which contains service function belonging to specific
214      * chain. Classifier has to be able to reach first service function forwarder in order to send packet to chain. If
215      * first service function forwarder is present on the same node as classifier, service-path entry should be already
216      * present (created by IOS-XE renderer in SFC) also with appropriate remote SFF if necessary. If first SFF is on
217      * different node (remote classifier), classifier has to create it's own service-path entry with remote SFF.
218      *
219      * @param renderedServicePath - path classifier has to reach
220      * @param context             - contains policy-map location and status info
221      * @return true if everything went good, false otherwise
222      */
223     public static boolean resolveRemoteSfcComponents(final RenderedServicePath renderedServicePath,
224                                                       final PolicyConfigurationContext context) {
225         final PolicyManagerImpl.PolicyMapLocation location = context.getPolicyMapLocation();
226         final ServiceFunctionForwarder forwarder = getFirstHopSff(renderedServicePath);
227         if (forwarder == null) {
228             return false;
229         }
230         final SffName sffName = forwarder.getName();
231         if (forwarder.getSffDataPlaneLocator() == null || forwarder.getSffDataPlaneLocator().isEmpty()) {
232             LOG.warn("Service function forwarder {} does not contain data plane locator", sffName.getValue());
233             return false;
234         }
235         // TODO only first dpl resolved
236         final SffDataPlaneLocator sffDataPlaneLocator = forwarder.getSffDataPlaneLocator().get(0);
237         final DataPlaneLocator dataPlaneLocator = sffDataPlaneLocator.getDataPlaneLocator();
238         final LocatorType locatorType = dataPlaneLocator.getLocatorType();
239         if (locatorType != null && locatorType instanceof Ip) {
240             final IpAddress remoteForwarderIpAddress = (((Ip) locatorType).getIp());
241             if (remoteForwarderIpAddress == null || remoteForwarderIpAddress.getIpv4Address() == null) {
242                 LOG.warn("Service function forwarder {} data plane locator does not contain ip address", sffName.getValue());
243                 return false;
244             }
245             final String remoteForwarderStringIp = remoteForwarderIpAddress.getIpv4Address().getValue();
246             final java.util.Optional<IpAddress> optionalIpMgmtAddress = java.util.Optional.ofNullable(forwarder.getIpMgmtAddress());
247
248             return optionalIpMgmtAddress.map(IpAddress::getIpv4Address)
249                     .map(Ipv4Address::getValue)
250                     .map(addressValue -> {
251                         final ServiceTypeChoice serviceTypeChoice;
252                         if (!addressValue.equals(location.getManagementIpAddress())) {
253                             // Remote forwarder
254                             final ServiceFfNameBuilder remoteSffBuilder = new ServiceFfNameBuilder();
255                             remoteSffBuilder.setName(sffName.getValue())
256                                     .setKey(new ServiceFfNameKey(sffName.getValue()))
257                                     .setIp(new IpBuilder().setAddress(new Ipv4Address(remoteForwarderStringIp)).build());
258                             boolean rsResult = PolicyWriterUtil.writeRemote(remoteSffBuilder.build(), location);
259                             context.setFutureResult(Futures.immediateCheckedFuture(rsResult));
260                             serviceTypeChoice = createForwarderTypeChoice(sffName.getValue());
261                             // Service chain
262                             final List<Services> services = new ArrayList<>();
263                             final ServicesBuilder servicesBuilder = new ServicesBuilder();
264                             servicesBuilder.setServiceIndexId(renderedServicePath.getStartingIndex())
265                                     .setServiceTypeChoice(serviceTypeChoice);
266                             services.add(servicesBuilder.build());
267                             final List<ServicePath> servicePaths = new ArrayList<>();
268                             final ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
269                             servicePathBuilder.setKey(new ServicePathKey(renderedServicePath.getPathId()))
270                                     .setServicePathId(renderedServicePath.getPathId())
271                                     .setConfigServiceChainPathMode(new ConfigServiceChainPathModeBuilder()
272                                             .setServiceIndex(new ServiceIndexBuilder()
273                                                     .setServices(services).build()).build());
274                             servicePaths.add(servicePathBuilder.build());
275                             final ServiceChainBuilder chainBuilder = new ServiceChainBuilder();
276                             chainBuilder.setServicePath(servicePaths);
277                             final ServiceChain serviceChain = chainBuilder.build();
278                             boolean scResult = PolicyWriterUtil.writeServicePath(serviceChain, location);
279                             context.setFutureResult(Futures.immediateCheckedFuture(scResult));
280                         }
281                         return true;
282                     }).orElseGet(createNegativePathWithLogSupplier(sffName.getValue(),
283                             (value) -> LOG.error("Cannot create remote forwarder, SFF {} does not contain management ip address",
284                                     value))
285                     );
286         }
287         return false;
288     }
289
290     /**
291      * Investigates provided parameter values and derives service chain name. This name is used to find service function
292      * path
293      *
294      * @param params - list of parameters
295      * @return - service function path if found, null if provided parameters does not correspond with any chain or there
296      * is no service function path defined by that chain
297      */
298     @Nullable
299     static ServiceFunctionPath findServicePathFromParameterValues(final List<ParameterValue> params) {
300         if (params == null || params.isEmpty()) {
301             LOG.error("Cannot found service path, parameter value is null");
302             return null;
303         }
304         final Map<String, Object> paramsMap = new HashMap<>();
305         for (ParameterValue value : params) {
306             if (value.getName() == null)
307                 continue;
308             if (value.getIntValue() != null) {
309                 paramsMap.put(value.getName().getValue(), value.getIntValue());
310             } else if (value.getStringValue() != null) {
311                 paramsMap.put(value.getName().getValue(), value.getStringValue());
312             }
313         }
314         String chainName = null;
315         for (String name : paramsMap.keySet()) {
316             if (name.equals(ChainActionDefinition.SFC_CHAIN_NAME)) {
317                 chainName = (String) paramsMap.get(name);
318             }
319         }
320         if (chainName == null) {
321             LOG.error("Cannot found service path, chain name is null");
322             return null;
323         }
324         final ServiceFunctionPath serviceFunctionPath = findServiceFunctionPathFromServiceChainName(new SfcName(chainName));
325         if (serviceFunctionPath == null) {
326             LOG.error("Service function path not found for name {}", chainName);
327             return null;
328         }
329         return serviceFunctionPath;
330     }
331
332     static ServiceFunctionPath findServiceFunctionPathFromServiceChainName(@Nonnull final SfcName chainName) {
333         final ServiceFunctionPaths allPaths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
334         if (allPaths == null || allPaths.getServiceFunctionPath() == null || allPaths.getServiceFunctionPath().isEmpty()) {
335             return null;
336         }
337         for (ServiceFunctionPath serviceFunctionPath : allPaths.getServiceFunctionPath()) {
338             if (chainName.equals(serviceFunctionPath.getServiceChainName())) {
339                 return serviceFunctionPath;
340             }
341         }
342         return null;
343     }
344
345     /**
346      * Creates {@link RenderedServicePath} if not exist. If created, ios-xe renderer in SFC is invoked, so this method
347      * has to wait till SFC part is done to prevent transaction collisions in {@link this#checkRspManagerStatus(RspName,
348      * DataBroker)}. If this operation is successful, class-map {@link ClassMap} and entry in policy-map {@link Class}
349      * is written
350      *
351      * @param sfp            - path used to create RSP
352      * @param tenantId       - used to generate RSP name according to GBP standards
353      * @param dataBroker     - data provider to access odl controller
354      * @param sourceSgt      - source security group tag
355      * @param destinationSgt - destination security group tag
356      * @param context        - contains policy-map location and status info
357      * @return read/created RSP
358      */
359     static RenderedServicePath resolveRenderedServicePath(final ServiceFunctionPath sfp, final TenantId tenantId,
360                                                                   final DataBroker dataBroker, final Sgt sourceSgt, final Sgt destinationSgt,
361                                                                   final PolicyConfigurationContext context) {
362         // Get rendered service path
363         final RspName rspName = generateRspName(sfp, tenantId);
364         RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
365         if (renderedServicePath == null) {
366             LOG.info("Rendered service path with name {} not found, creating a new one ..", rspName.getValue());
367             final CreateRenderedPathInput input = new CreateRenderedPathInputBuilder()
368                     .setParentServiceFunctionPath(sfp.getName().getValue())
369                     .setName(rspName.getValue())
370                     .setSymmetric(sfp.isSymmetric())
371                     .build();
372             renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, input);
373             LOG.info("Rendered service path {} created", rspName.getValue());
374             checkRspManagerStatus(rspName, dataBroker);
375         }
376         // Create class-map and policy-map entry
377         final String classMapName = generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
378         final Match match = createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue());
379         final ClassMap classMap = createClassMap(classMapName, match);
380         final Class policyMapEntry = createPolicyMapEntry(classMapName, renderedServicePath, ActionCase.CHAIN);
381         boolean cmResult = PolicyWriterUtil.writeClassMap(classMap, context.getPolicyMapLocation());
382         context.setFutureResult(Futures.immediateCheckedFuture(cmResult));
383         boolean pmeResult = PolicyWriterUtil.writePolicyMapEntry(policyMapEntry, context.getPolicyMapLocation());
384         context.setFutureResult(Futures.immediateCheckedFuture(pmeResult));
385         return renderedServicePath;
386     }
387
388     /**
389      * Creates reversed {@link RenderedServicePath} if not exist. To be successful, direct path has to exist.
390      * If created, ios-xe renderer in SFC is invoked, so this method has to wait till SFC part is done to prevent
391      * transaction collisions. If this operation is successful, class-map {@link ClassMap} and entry in policy-map
392      * {@link Class} is written
393      *
394      * @param sfp            - path used to create RSP
395      * @param tenantId       - used to generate RSP name according to GBP standards
396      * @param dataBroker     - data provider to access odl controller
397      * @param sourceSgt      - source security group tag
398      * @param destinationSgt - destination security group tag
399      * @param context        - contains policy-map location and status info
400      * @return read/created RSP
401      */
402     public static RenderedServicePath resolveReversedRenderedServicePath(final ServiceFunctionPath sfp, final TenantId tenantId,
403                                                                           final DataBroker dataBroker, final Sgt sourceSgt,
404                                                                           final Sgt destinationSgt, final PolicyConfigurationContext context) {
405         // Get rendered service path
406         final RspName rspName = generateRspName(sfp, tenantId);
407         RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
408         if (renderedServicePath == null) {
409             LOG.info("Rendered service path with name {} not found, creating a new one ..", rspName.getValue());
410             final CreateRenderedPathInput input = new CreateRenderedPathInputBuilder()
411                     .setParentServiceFunctionPath(sfp.getName().getValue())
412                     .setName(rspName.getValue())
413                     .setSymmetric(sfp.isSymmetric())
414                     .build();
415             renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, input);
416             LOG.info("Rendered service path {} created", rspName.getValue());
417             checkRspManagerStatus(rspName, dataBroker);
418         }
419         // Get reversed rendered service path
420         final RspName reversedRspName = generateReversedRspName(sfp, tenantId);
421         RenderedServicePath reversedRenderedPath = SfcProviderRenderedPathAPI.readRenderedServicePath(reversedRspName);
422         if (reversedRenderedPath == null) {
423             LOG.info("Reversed rendered service path with name {} not found, creating a new one ..", reversedRspName.getValue());
424             reversedRenderedPath = SfcProviderRenderedPathAPI.createReverseRenderedServicePathEntry(renderedServicePath);
425             LOG.info("Rendered service path {} created", reversedRspName.getValue());
426             checkRspManagerStatus(reversedRspName, dataBroker);
427         }
428         // Create class-map and policy-map entry
429         final String classMapName = generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
430         final Match match = createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue());
431         final ClassMap classMap = createClassMap(classMapName, match);
432         final Class policyMapEntry = createPolicyMapEntry(classMapName, renderedServicePath, ActionCase.CHAIN);
433         boolean cmResult = PolicyWriterUtil.writeClassMap(classMap, context.getPolicyMapLocation());
434         context.setFutureResult(Futures.immediateCheckedFuture(cmResult));
435         boolean pmeResult = PolicyWriterUtil.writePolicyMapEntry(policyMapEntry, context.getPolicyMapLocation());
436         context.setFutureResult(Futures.immediateCheckedFuture(pmeResult));
437         resolveRemoteSfcComponents(renderedServicePath, context);
438         return reversedRenderedPath;
439     }
440
441     /**
442      * Removes all policy setup created according to rendered service path.
443      *
444      * @param rspName        - rendered service path name
445      * @param sourceSgt      - source security group tag
446      * @param destinationSgt - destination security group tag
447      * @param context        - context with policy-map location
448      */
449     private static void resolveRemovedRenderedServicePath(final RspName rspName, final Sgt sourceSgt, final Sgt destinationSgt,
450                                                           final PolicyConfigurationContext context) {
451         final String classMapName = PolicyManagerUtil.generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
452         final ClassMap classMap = PolicyManagerUtil.createClassMap(classMapName, null);
453         final Class policyMapEntry = PolicyManagerUtil.createPolicyMapEntry(classMapName, null, PolicyManagerImpl.ActionCase.CHAIN);
454         PolicyWriterUtil.removePolicyMapEntry(policyMapEntry, context.getPolicyMapLocation());
455         PolicyWriterUtil.removeClassMap(classMap, context.getPolicyMapLocation());
456         final RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
457         final ServiceFunctionForwarder firstHopSff = getFirstHopSff(renderedServicePath);
458         if (firstHopSff != null && firstHopSff.getIpMgmtAddress() != null &&
459                 firstHopSff.getIpMgmtAddress().getIpv4Address() != null) {
460             final String sffMgmtIpValue = firstHopSff.getIpMgmtAddress().getIpv4Address().getValue();
461             if (!sffMgmtIpValue.equals(context.getPolicyMapLocation().getManagementIpAddress())) {
462                 // Remove service chain and remote forwarder
463                 final ServiceChain serviceChain = createServiceChain(renderedServicePath);
464                 final ServiceFfName remoteForwarder = createRemoteForwarder(firstHopSff);
465                 PolicyWriterUtil.removeServicePath(serviceChain, context.getPolicyMapLocation());
466                 PolicyWriterUtil.removeRemote(remoteForwarder, context.getPolicyMapLocation());
467             }
468         }
469     }
470
471     static ServiceFfName createRemoteForwarder(ServiceFunctionForwarder firstHopSff) {
472         final ServiceFfNameBuilder serviceFfNameBuilder = new ServiceFfNameBuilder();
473         serviceFfNameBuilder.setName(firstHopSff.getName().getValue());
474         return serviceFfNameBuilder.build();
475     }
476
477     private static ServiceTypeChoice createForwarderTypeChoice(final String forwarderName) {
478         final ServiceFunctionForwarderBuilder sffBuilder = new ServiceFunctionForwarderBuilder();
479         sffBuilder.setServiceFunctionForwarder(forwarderName);
480         return sffBuilder.build();
481     }
482
483     /**
484      * Creates service-chain with name/key only, using rendered service path id. This object contains no data, it is used
485      * to create instance identifier when appropriate service-chain is removed from particular device
486      *
487      * @param renderedServicePath - it's path id is used as a identifier
488      * @return service-chain object with id
489      */
490     private static ServiceChain createServiceChain(final RenderedServicePath renderedServicePath) {
491         final Long pathId = renderedServicePath.getPathId();
492         final ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
493         final ServiceChainBuilder serviceChainBuilder = new ServiceChainBuilder();
494         servicePathBuilder.setServicePathId(pathId)
495                 .setKey(new ServicePathKey(pathId));
496         serviceChainBuilder.setServicePath(Collections.singletonList(servicePathBuilder.build()));
497         return serviceChainBuilder.build();
498     }
499
500     private static <T> Supplier<Boolean> createNegativePathWithLogSupplier(final T value, final Consumer<T> logCommand) {
501         return () -> {
502             // fireLog
503             logCommand.accept(value);
504             return false;
505         };
506     }
507
508     private static ServiceFunctionForwarder getFirstHopSff(RenderedServicePath renderedServicePath) {
509         if (renderedServicePath == null || renderedServicePath.getRenderedServicePathHop() == null ||
510                 renderedServicePath.getRenderedServicePathHop().isEmpty()) {
511             return null;
512         }
513         final RenderedServicePathHop firstHop = renderedServicePath.getRenderedServicePathHop().get(0);
514         final SffName firstHopSff = firstHop.getServiceFunctionForwarder();
515         if (firstHopSff == null) {
516             return null;
517         }
518         return SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(firstHopSff);
519     }
520
521     private static RspName generateRspName(final ServiceFunctionPath serviceFunctionPath, final TenantId tenantId) {
522         return new RspName(serviceFunctionPath.getName().getValue() + "-" + tenantId.getValue() + RSP_SUFFIX);
523     }
524
525     private static RspName generateReversedRspName(final ServiceFunctionPath serviceFunctionPath, final TenantId tenantId) {
526         return new RspName(serviceFunctionPath.getName().getValue() + "-" + tenantId.getValue() + RSP_REVERSED_SUFFIX);
527     }
528
529     private static void checkRspManagerStatus(final RspName rspName, final DataBroker dataBroker) {
530         // TODO A better way to do this is to register listener and wait for notification than using hardcoded timeout
531         // with Thread.sleep(). Example in class BridgeDomainManagerImpl
532         ConfiguredRenderedPath renderedPath = null;
533         LOG.debug("Waiting for SFC to configure path {} ...", rspName.getValue());
534
535         byte attempt = 0;
536         do {
537             attempt++;
538             // Wait
539             try {
540                 Thread.sleep(timeout);
541             } catch (InterruptedException e) {
542                 LOG.error("Thread interrupted while waiting ... {} ", e);
543             }
544             // Read actual status
545             final InstanceIdentifier<ConfiguredRenderedPath> statusIid = InstanceIdentifier.builder(RendererPathStates.class)
546                     .child(RendererPathState.class, new RendererPathStateKey(new RendererName("ios-xe-renderer")))
547                     .child(ConfiguredRenderedPaths.class)
548                     .child(ConfiguredRenderedPath.class, new ConfiguredRenderedPathKey(rspName)).build();
549             final java.util.Optional<ReadWriteTransaction> optionalTransaction =
550                     NetconfTransactionCreator.netconfReadWriteTransaction(dataBroker);
551             if (!optionalTransaction.isPresent()) {
552                 LOG.warn("Failed to create transaction, mountpoint: {}", dataBroker);
553                 return;
554             }
555             ReadWriteTransaction transaction = optionalTransaction.get();
556             try {
557                 final CheckedFuture<Optional<ConfiguredRenderedPath>, ReadFailedException> submitFuture =
558                         transaction.read(LogicalDatastoreType.OPERATIONAL, statusIid);
559                 final Optional<ConfiguredRenderedPath> optionalPath = submitFuture.checkedGet();
560                 if (optionalPath.isPresent()) {
561                     renderedPath = optionalPath.get();
562                 }
563             } catch (ReadFailedException e) {
564                 LOG.warn("Failed while read rendered path status ... {} ", e.getMessage());
565             }
566             if (renderedPath == null || renderedPath.getPathStatus() == null ||
567                     renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.InProgress)) {
568                 LOG.info("Still waiting for SFC ... ");
569             } else if (renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.Failure)) {
570                 LOG.warn("SFC failed to configure rsp");
571             } else if (renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.Success)) {
572                 LOG.debug("RSP {} configured by SFC", rspName.getValue());
573                 try {
574                     Thread.sleep(timeout); // Just for sure, maybe will be safe to remove this
575                 } catch (InterruptedException e) {
576                     LOG.error("Thread interrupted while waiting ... {} ", e);
577                 }
578                 return;
579             }
580         }
581         while (attempt <= 6);
582         LOG.warn("Maximum number of attempts reached");
583     }
584
585     /**
586      * Only for test purposes
587      *
588      * @param value - set actual timeout value
589      */
590     @VisibleForTesting
591     public static void setTimeout(long value) {
592         timeout = value;
593     }
594 }