dbbba527470357ef9ba6464108642995729814a9
[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.createPolicyEntry;
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 java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.function.Consumer;
30 import java.util.function.Supplier;
31 import com.google.common.annotations.VisibleForTesting;
32 import com.google.common.base.Optional;
33 import com.google.common.util.concurrent.CheckedFuture;
34 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
35 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
36 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
37 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
38 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
39 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyConfigurationContext;
40 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl;
41 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NetconfTransactionCreator;
42 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.PolicyWriter;
43 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
44 import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
45 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
46 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.RendererPathStates;
47 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathState;
48 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathStateKey;
49 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.ConfiguredRenderedPaths;
50 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPath;
51 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPathKey;
52 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RendererName;
53 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
54 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
55 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
56 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
57 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
58 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
59 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
60 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator;
61 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.sff.data.plane.locator.DataPlaneLocator;
62 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
63 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
64 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
65 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.LocatorType;
66 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.Ip;
67 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
68 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
69 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMap;
70 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain;
71 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChainBuilder;
72 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native._class.map.Match;
73 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.config.service.chain.grouping.IpBuilder;
74 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.Class;
75 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
76 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathBuilder;
77 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
78 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName;
79 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameBuilder;
80 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameKey;
81 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.ConfigServiceChainPathModeBuilder;
82 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.ServiceIndexBuilder;
83 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.Services;
84 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.ServicesBuilder;
85 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.ServiceTypeChoice;
86 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;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpoint;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.actions.Action;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
94 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
95 import org.slf4j.Logger;
96 import org.slf4j.LoggerFactory;
97
98 public class ServiceChainingUtil {
99
100     private static final Logger LOG = LoggerFactory.getLogger(ServiceChainingUtil.class);
101     private static final String RSP_SUFFIX = "-gbp-rsp";
102     private static final String RSP_REVERSED_SUFFIX = "-gbp-rsp-Reverse";
103     private static long timeout = 5000L;
104
105     /**
106      * According to input, creates class-maps ({@link ClassMap}) and entries into policy-map ({@link Class}). These
107      * components are created when particular RSP is build by SFC ios-xe renderer. If so, method continues by resolving
108      * first RSP hop in {@link this#resolveRemoteSfcComponents(RenderedServicePath, PolicyWriter)}.
109      *
110      * @param peerEndpoint   - peer endpoint, used to generate status and access to tenant ID
111      * @param sourceSgt      - security group tag of source endpoint
112      * @param destinationSgt - security group tag of destination endpoint
113      * @param actionMap      - contains all info to evaluate correct chain orientation according to endpoint participation
114      * @param context        - contains policy writer
115      * @param dataBroker     - to access odl datastore
116      */
117     static void resolveNewChainAction(final PeerEndpoint peerEndpoint, final Sgt sourceSgt,
118                                       final Sgt destinationSgt, final Map<PolicyManagerImpl.ActionCase, ActionInDirection> actionMap,
119                                       final PolicyConfigurationContext context, final DataBroker dataBroker) {
120         final ActionInDirection actionInDirection = actionMap.get(ActionCase.CHAIN);
121         if (actionInDirection == null) {
122             return;
123         }
124         // Rule action + orientation
125         final Action action = actionInDirection.getAction();
126         final EndpointPolicyParticipation participation = actionInDirection.getParticipation();
127         final Direction direction = actionInDirection.getDirection();
128         // Get service function path
129         final ServiceFunctionPath servicePath = ServiceChainingUtil.findServicePathFromParameterValues(action.getParameterValue());
130         if (servicePath == null || servicePath.getName() == null) {
131             final String info = String.format("service-path not found (sourceSgt=%s, destinationSgt=%s)",
132                     sourceSgt, destinationSgt);
133             context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context, peerEndpoint, info));
134             return;
135         }
136         final TenantId tenantId = PolicyManagerUtil.getTenantId(peerEndpoint);
137         if (tenantId == null) {
138             final String info = String.format("tenant-id not found (sourceSgt=%s, destinationSgt=%s)",
139                     sourceSgt, destinationSgt);
140             context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context, peerEndpoint, info));
141             return;
142         }
143         RenderedServicePath renderedServicePath;
144         boolean sfcPartSuccessful = true;
145         // Symmetric chain - create direct or reversed rendered service path in corresponding direction
146         if (servicePath.isSymmetric()) {
147             if ((participation.equals(PROVIDER) && direction.equals(Out)) ||
148                     (participation.equals(CONSUMER) && direction.equals(In))) {
149                 renderedServicePath = ServiceChainingUtil.createRenderedPath(servicePath, tenantId, dataBroker);
150                 // Rsp found, create class-map and policy-map entry
151                 final String classMapName = generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
152                 final Match match = createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue());
153                 final ClassMap classMap = createClassMap(classMapName, match);
154                 final Class policyMapEntry = createPolicyEntry(classMapName, renderedServicePath, ActionCase.CHAIN);
155                 context.getPolicyWriter().cache(classMap);
156                 context.getPolicyWriter().cache(policyMapEntry);
157                 sfcPartSuccessful = resolveRemoteSfcComponents(renderedServicePath, context.getPolicyWriter());
158             } else {
159                 // Direct path required if reversed has to be created
160                 final RenderedServicePath directPath = ServiceChainingUtil.createRenderedPath(servicePath, tenantId, dataBroker);
161                 // Create reversed path
162                 renderedServicePath =
163                         ServiceChainingUtil.createReversedRenderedPath(servicePath, directPath, tenantId, dataBroker);
164                 // Rsp found, create class-map and policy-map entry
165                 final String classMapName = generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
166                 final Match match = createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue());
167                 final ClassMap classMap = createClassMap(classMapName, match);
168                 final Class policyMapEntry = createPolicyEntry(classMapName, renderedServicePath, ActionCase.CHAIN);
169                 context.getPolicyWriter().cache(classMap);
170                 context.getPolicyWriter().cache(policyMapEntry);
171                 sfcPartSuccessful = resolveRemoteSfcComponents(renderedServicePath, context.getPolicyWriter());
172             }
173         }
174         // Asymmetric chain - create direct path if corresponding direction or skip
175         else if ((participation.equals(PROVIDER) && direction.equals(Out)) ||
176                 (participation.equals(CONSUMER) && direction.equals(In))) {
177             renderedServicePath = ServiceChainingUtil.createRenderedPath(servicePath, tenantId, dataBroker);
178             // Rsp found, create class-map and policy-map entry
179             final String classMapName = generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
180             final Match match = createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue());
181             final ClassMap classMap = createClassMap(classMapName, match);
182             final Class policyMapEntry = createPolicyEntry(classMapName, renderedServicePath, ActionCase.CHAIN);
183             context.getPolicyWriter().cache(classMap);
184             context.getPolicyWriter().cache(policyMapEntry);
185             sfcPartSuccessful = resolveRemoteSfcComponents(renderedServicePath, context.getPolicyWriter());
186         }
187         // Create appropriate service path && remote forwarder
188         if (!sfcPartSuccessful) {
189             //TODO: extract resolved-rule name
190             final String info = String.format("failed during sfc-part execution (sourceSgt=%s, destinationSgt=%s)",
191                     sourceSgt, destinationSgt);
192             //context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeerAndAction(context, peerEndpoint, info));
193         }
194     }
195
196     /**
197      * Removes class-map and policy-map entry in policy between endpoint pair. If necessary, method deletes remote SFC
198      * components.
199      *
200      * @param peerEndpoint    - contains info about tenant ID
201      * @param sourceSgt       - security group tag of source endpoint
202      * @param destinationSgt- security group tag of destination endpoint
203      * @param actionMap       - contains all info to evaluate correct chain orientation according to endpoint participation
204      * @param policyWriter    - used to access ios-xe capable device
205      */
206     static void resolveRemovedChainAction(final PeerEndpoint peerEndpoint, final Sgt sourceSgt, final Sgt destinationSgt,
207                                           final Map<ActionCase, ActionInDirection> actionMap, PolicyWriter policyWriter) {
208         final ActionInDirection actionInDirection = actionMap.get(ActionCase.CHAIN);
209         final Action action = actionInDirection.getAction();
210         final EndpointPolicyParticipation participation = actionInDirection.getParticipation();
211         final Direction direction = actionInDirection.getDirection();
212         final ServiceFunctionPath servicePath = ServiceChainingUtil.findServicePathFromParameterValues(action.getParameterValue());
213         if (servicePath == null || servicePath.getName() == null) {
214             return;
215         }
216         final TenantId tenantId = getTenantId(peerEndpoint);
217         if (tenantId == null) {
218             return;
219         }
220         //Symmetric chain
221         if (servicePath.isSymmetric()) {
222             if ((participation.equals(PROVIDER) && direction.equals(Out)) ||
223                     (participation.equals(CONSUMER) && direction.equals(In))) {
224                 // Cache class-maps, appropriate policy-map entries and service-chains
225                 final String classMapName = PolicyManagerUtil.generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
226                 final ClassMap classMap = PolicyManagerUtil.createClassMap(classMapName, null);
227                 final Class policyMapEntry = PolicyManagerUtil.createPolicyEntry(classMapName, null, PolicyManagerImpl.ActionCase.CHAIN);
228                 policyWriter.cache(classMap);
229                 policyWriter.cache(policyMapEntry);
230
231                 final RspName rspName = generateRspName(servicePath, tenantId);
232                 final RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
233                 final ServiceFunctionForwarder firstHopSff = getFirstHopSff(renderedServicePath);
234                 if (firstHopSff != null && firstHopSff.getIpMgmtAddress() != null &&
235                         firstHopSff.getIpMgmtAddress().getIpv4Address() != null) {
236                     final String sffMgmtIpValue = firstHopSff.getIpMgmtAddress().getIpv4Address().getValue();
237                     if (!sffMgmtIpValue.equals(policyWriter.getManagementIpAddress())) {
238                         // Remove service chain and remote forwarder
239                         final ServiceChain serviceChain = createServiceChain(renderedServicePath);
240                         final ServiceFfName remoteForwarder = createRemoteForwarder(firstHopSff);
241                         policyWriter.cache(serviceChain);
242                         policyWriter.cache(remoteForwarder);
243                     }
244                 }
245             } else {
246                 final String oppositeClassMapName = PolicyManagerUtil.generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
247                 final ClassMap oppositeClassMap = PolicyManagerUtil.createClassMap(oppositeClassMapName, null);
248                 final Class policyMapEntry = PolicyManagerUtil.createPolicyEntry(oppositeClassMapName, null, PolicyManagerImpl.ActionCase.CHAIN);
249                 policyWriter.cache(oppositeClassMap);
250                 policyWriter.cache(policyMapEntry);
251
252                 final RspName reversedRspName = generateReversedRspName(servicePath, tenantId);
253                 final RenderedServicePath reversedRenderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(reversedRspName);
254                 final ServiceFunctionForwarder reversedFirstHopSff = getFirstHopSff(reversedRenderedServicePath);
255                 if (reversedFirstHopSff != null && reversedFirstHopSff.getIpMgmtAddress() != null &&
256                         reversedFirstHopSff.getIpMgmtAddress().getIpv4Address() != null) {
257                     final String reversedSffMgmtIpValue = reversedFirstHopSff.getIpMgmtAddress().getIpv4Address().getValue();
258                     if (!reversedSffMgmtIpValue.equals(policyWriter.getManagementIpAddress())) {
259                         // Remove service chain and remote forwarder
260                         final ServiceChain serviceChain = createServiceChain(reversedRenderedServicePath);
261                         final ServiceFfName remoteForwarder = createRemoteForwarder(reversedFirstHopSff);
262                         policyWriter.cache(serviceChain);
263                         policyWriter.cache(remoteForwarder);
264                     }
265                 }
266             }
267         } else if ((participation.equals(PROVIDER) && direction.equals(Out)) ||
268                 (participation.equals(CONSUMER) && direction.equals(In))) {
269             // Asymmetric chain
270             final String classMapName = PolicyManagerUtil.generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
271             final ClassMap classMap = PolicyManagerUtil.createClassMap(classMapName, null);
272             final Class policyMapEntry = PolicyManagerUtil.createPolicyEntry(classMapName, null, PolicyManagerImpl.ActionCase.CHAIN);
273             policyWriter.cache(classMap);
274             policyWriter.cache(policyMapEntry);
275
276             final RspName rspName = generateRspName(servicePath, tenantId);
277             final RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
278             final ServiceFunctionForwarder firstHopSff = getFirstHopSff(renderedServicePath);
279             if (firstHopSff != null && firstHopSff.getIpMgmtAddress() != null &&
280                     firstHopSff.getIpMgmtAddress().getIpv4Address() != null) {
281                 final String sffMgmtIpValue = firstHopSff.getIpMgmtAddress().getIpv4Address().getValue();
282                 if (!sffMgmtIpValue.equals(policyWriter.getManagementIpAddress())) {
283                     // Remove service chain and remote forwarder
284                     final ServiceChain serviceChain = createServiceChain(renderedServicePath);
285                     final ServiceFfName remoteForwarder = createRemoteForwarder(firstHopSff);
286                     policyWriter.cache(serviceChain);
287                     policyWriter.cache(remoteForwarder);
288                 }
289             }
290         }
291     }
292
293     /**
294      * Service-path (netconf) is created on every netconf device, which contains service function belonging to specific
295      * chain. Classifier has to be able to reach first service function forwarder in order to send packet to chain. If
296      * first service function forwarder is present on the same node as classifier, service-path entry should be already
297      * present (created by IOS-XE renderer in SFC) also with appropriate remote SFF if necessary. If first SFF is on
298      * different node, classifier has to create it's own service-path entry with remote SFF.
299      *
300      * @param renderedServicePath classifier has to reach
301      * @param policyWriter        policy entries writer
302      * @return true if everything went good, false otherwise
303      */
304     static boolean resolveRemoteSfcComponents(final RenderedServicePath renderedServicePath, PolicyWriter policyWriter) {
305         final ServiceFunctionForwarder forwarder = getFirstHopSff(renderedServicePath);
306         if (forwarder == null) {
307             return false;
308         }
309         final SffName sffName = forwarder.getName();
310         if (forwarder.getSffDataPlaneLocator() == null || forwarder.getSffDataPlaneLocator().isEmpty()) {
311             LOG.warn("Service function forwarder {} does not contain data plane locator", sffName.getValue());
312             return false;
313         }
314         // TODO only first dpl resolved
315         final SffDataPlaneLocator sffDataPlaneLocator = forwarder.getSffDataPlaneLocator().get(0);
316         final DataPlaneLocator dataPlaneLocator = sffDataPlaneLocator.getDataPlaneLocator();
317         final LocatorType locatorType = dataPlaneLocator.getLocatorType();
318         if (locatorType != null && locatorType instanceof Ip) {
319             final IpAddress remoteForwarderIpAddress = (((Ip) locatorType).getIp());
320             if (remoteForwarderIpAddress == null || remoteForwarderIpAddress.getIpv4Address() == null) {
321                 LOG.warn("Service function forwarder {} data plane locator does not contain ip address", sffName.getValue());
322                 return false;
323             }
324             final String remoteForwarderStringIp = remoteForwarderIpAddress.getIpv4Address().getValue();
325             final java.util.Optional<IpAddress> optionalIpMgmtAddress = java.util.Optional.ofNullable(forwarder.getIpMgmtAddress());
326
327             return optionalIpMgmtAddress.map(IpAddress::getIpv4Address)
328                     .map(Ipv4Address::getValue)
329                     .map(addressValue -> {
330                         final ServiceTypeChoice serviceTypeChoice;
331                         if (!addressValue.equals(policyWriter.getManagementIpAddress())) {
332                             // Remote forwarder
333                             final ServiceFfNameBuilder remoteSffBuilder = new ServiceFfNameBuilder();
334                             remoteSffBuilder.setName(sffName.getValue())
335                                     .setKey(new ServiceFfNameKey(sffName.getValue()))
336                                     .setIp(new IpBuilder().setAddress(new Ipv4Address(remoteForwarderStringIp)).build());
337                             policyWriter.cache(remoteSffBuilder.build());
338                             serviceTypeChoice = createForwarderTypeChoice(sffName.getValue());
339                             // Service chain
340                             final List<Services> services = new ArrayList<>();
341                             final ServicesBuilder servicesBuilder = new ServicesBuilder();
342                             servicesBuilder.setServiceIndexId(renderedServicePath.getStartingIndex())
343                                     .setServiceTypeChoice(serviceTypeChoice);
344                             services.add(servicesBuilder.build());
345                             final List<ServicePath> servicePaths = new ArrayList<>();
346                             final ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
347                             servicePathBuilder.setKey(new ServicePathKey(renderedServicePath.getPathId()))
348                                     .setServicePathId(renderedServicePath.getPathId())
349                                     .setConfigServiceChainPathMode(new ConfigServiceChainPathModeBuilder()
350                                             .setServiceIndex(new ServiceIndexBuilder()
351                                                     .setServices(services).build()).build());
352                             servicePaths.add(servicePathBuilder.build());
353                             final ServiceChainBuilder chainBuilder = new ServiceChainBuilder();
354                             chainBuilder.setServicePath(servicePaths);
355                             final ServiceChain serviceChain = chainBuilder.build();
356                             policyWriter.cache(serviceChain);
357                         }
358                         return true;
359                     }).orElseGet(createNegativePathWithLogSupplier(sffName.getValue(),
360                             (value) -> LOG.error("Cannot create remote forwarder, SFF {} does not contain management ip address",
361                                     value))
362                     );
363         }
364         return false;
365     }
366
367     static ServiceFunctionPath findServicePathFromParameterValues(final List<ParameterValue> params) {
368         if (params == null || params.isEmpty()) {
369             LOG.error("Cannot found service path, parameter value is null");
370             return null;
371         }
372         final Map<String, Object> paramsMap = new HashMap<>();
373         for (ParameterValue value : params) {
374             if (value.getName() == null)
375                 continue;
376             if (value.getIntValue() != null) {
377                 paramsMap.put(value.getName().getValue(), value.getIntValue());
378             } else if (value.getStringValue() != null) {
379                 paramsMap.put(value.getName().getValue(), value.getStringValue());
380             }
381         }
382         String chainName = null;
383         for (String name : paramsMap.keySet()) {
384             if (name.equals(ChainActionDefinition.SFC_CHAIN_NAME)) {
385                 chainName = (String) paramsMap.get(name);
386             }
387         }
388         if (chainName == null) {
389             LOG.error("Cannot found service path, chain name is null");
390             return null;
391         }
392         final ServiceFunctionPath serviceFunctionPath = findServiceFunctionPathFromServiceChainName(new SfcName(chainName));
393         if (serviceFunctionPath == null) {
394             LOG.error("Service function path not found for name {}", chainName);
395             return null;
396         }
397         return serviceFunctionPath;
398     }
399
400     static ServiceFunctionPath findServiceFunctionPathFromServiceChainName(@Nonnull final SfcName chainName) {
401         final ServiceFunctionPaths allPaths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
402         if (allPaths == null || allPaths.getServiceFunctionPath() == null || allPaths.getServiceFunctionPath().isEmpty()) {
403             return null;
404         }
405         for (ServiceFunctionPath serviceFunctionPath : allPaths.getServiceFunctionPath()) {
406             if (chainName.equals(serviceFunctionPath.getServiceChainName())) {
407                 return serviceFunctionPath;
408             }
409         }
410         return null;
411     }
412
413     /**
414      * Creates {@link RenderedServicePath} if not exist. If created, ios-xe renderer in SFC is invoked, so this method
415      * has to wait till SFC part is done to prevent transaction collisions in {@link this#checkRspManagerStatus(RspName,
416      * DataBroker)}
417      *
418      * @param sfp        - path used to create RSP
419      * @param tenantId   - used to generate RSP name according to GBP standards
420      * @param dataBroker - data provider to access odl controller
421      * @return read/created RSP
422      */
423     static RenderedServicePath createRenderedPath(final ServiceFunctionPath sfp, final TenantId tenantId,
424                                                   final DataBroker dataBroker) {
425         RenderedServicePath renderedServicePath;
426         // Try to read existing RSP
427         final RspName rspName = generateRspName(sfp, tenantId);
428         renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
429         if (renderedServicePath != null) {
430             return renderedServicePath;
431         }
432         LOG.info("Rendered service path with name {} not found, creating a new one ..", rspName.getValue());
433         final CreateRenderedPathInput input = new CreateRenderedPathInputBuilder()
434                 .setParentServiceFunctionPath(sfp.getName().getValue())
435                 .setName(rspName.getValue())
436                 .setSymmetric(sfp.isSymmetric())
437                 .build();
438         renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, input);
439         LOG.info("Rendered service path {} created", rspName.getValue());
440         checkRspManagerStatus(rspName, dataBroker);
441         return renderedServicePath;
442     }
443
444     /**
445      * Creates reversed {@link RenderedServicePath} if not exist. To be successful, direct path has to exist.
446      * If created, ios-xe renderer in SFC is invoked, so this method has to wait till SFC part is done to prevent
447      * transaction collisions in {@link this#checkRspManagerStatus(RspName, DataBroker)}
448      *
449      * @param sfp        - path used to create RSP
450      * @param rsp        - appropriate direct RSP, used when the reversed path is created
451      * @param tenantId   - used to generate RSP name according to GBP standards
452      * @param dataBroker - data provider to access odl controller
453      * @return read/created RSP
454      */
455     static RenderedServicePath createReversedRenderedPath(final ServiceFunctionPath sfp, final RenderedServicePath rsp,
456                                                           final TenantId tenantId, final DataBroker dataBroker) {
457         RenderedServicePath reversedRenderedPath;
458         // Try to read existing RSP
459         final RspName rspName = generateReversedRspName(sfp, tenantId);
460         reversedRenderedPath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
461         if (reversedRenderedPath != null) {
462             return reversedRenderedPath;
463         }
464         LOG.info("Reversed rendered service path with name {} not found, creating a new one ..", rspName.getValue());
465         reversedRenderedPath = SfcProviderRenderedPathAPI.createReverseRenderedServicePathEntry(rsp);
466         LOG.info("Rendered service path {} created", rspName.getValue());
467         checkRspManagerStatus(rspName, dataBroker);
468         return reversedRenderedPath;
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      * @param value - set actual timeout value
588      */
589     @VisibleForTesting
590     public static void setTimeout(long value) {
591         timeout = value;
592     }
593 }