ios-xe renderer bugfix #2
[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 com.google.common.base.Optional;
12 import com.google.common.util.concurrent.CheckedFuture;
13 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
14 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
15 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
18 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
19 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyConfigurationContext;
20 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl;
21 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NetconfTransactionCreator;
22 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.PolicyWriter;
23 import org.opendaylight.groupbasedpolicy.util.IetfModelCodec;
24 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
25 import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
26 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
27 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.RendererPathStates;
28 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathState;
29 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathStateKey;
30 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.ConfiguredRenderedPaths;
31 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPath;
32 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPathKey;
33 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RendererName;
34 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
35 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
36 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
37 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
38 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
40 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
41 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator;
42 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.sff.data.plane.locator.DataPlaneLocator;
43 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
44 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
45 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
46 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.LocatorType;
47 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.Ip;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
50 import org.opendaylight.yang.gen.v1.urn.ios.rev160308.Native;
51 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMap;
52 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain;
53 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChainBuilder;
54 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native._class.map.Match;
55 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.config.service.chain.grouping.IpBuilder;
56 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.Class;
57 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
58 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathBuilder;
59 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
60 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName;
61 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameBuilder;
62 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameKey;
63 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.ConfigServiceChainPathModeBuilder;
64 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.ServiceIndexBuilder;
65 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.Services;
66 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.ServicesBuilder;
67 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.ServiceTypeChoice;
68 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;
69 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;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpoint;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.actions.Action;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
75 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
78
79 import java.util.ArrayList;
80 import java.util.Collections;
81 import java.util.HashMap;
82 import java.util.List;
83 import java.util.Map;
84 import java.util.function.Consumer;
85 import java.util.function.Supplier;
86
87 public class ServiceChainingUtil {
88
89     private static final Logger LOG = LoggerFactory.getLogger(ServiceChainingUtil.class);
90     private static final String RSP_SUFFIX = "-gbp-rsp";
91     private static final String RSP_REVERSED_SUFFIX = "-gbp-rsp-Reverse";
92
93     static ServiceFunctionPath getServicePath(final List<ParameterValue> params) {
94         if (params == null || params.isEmpty()) {
95             LOG.error("Cannot found service path, parameter value is null");
96             return null;
97         }
98         final Map<String, Object> paramsMap = new HashMap<>();
99         for (ParameterValue value : params) {
100             if (value.getName() == null)
101                 continue;
102             if (value.getIntValue() != null) {
103                 paramsMap.put(value.getName().getValue(), value.getIntValue());
104             } else if (value.getStringValue() != null) {
105                 paramsMap.put(value.getName().getValue(), value.getStringValue());
106             }
107         }
108         String chainName = null;
109         for (String name : paramsMap.keySet()) {
110             if (name.equals(ChainActionDefinition.SFC_CHAIN_NAME)) {
111                 chainName = (String) paramsMap.get(name);
112             }
113         }
114         if (chainName == null) {
115             LOG.error("Cannot found service path, chain name is null");
116             return null;
117         }
118         final ServiceFunctionPath serviceFunctionPath = findServiceFunctionPath(new SfcName(chainName));
119         if (serviceFunctionPath == null) {
120             LOG.error("Service function path not found for name {}", chainName);
121             return null;
122         }
123         return serviceFunctionPath;
124     }
125
126     static void resolveNewChainAction(final PeerEndpoint peerEndpoint, final Sgt sourceSgt,
127                                       final Sgt destinationSgt, final Map<PolicyManagerImpl.ActionCase, Action> actionMap,
128                                       final PolicyConfigurationContext context, final DataBroker dataBroker) {
129         final List<Class> policyMapEntries = new ArrayList<>();
130         final Action action = actionMap.get(PolicyManagerImpl.ActionCase.CHAIN);
131         final ServiceFunctionPath servicePath = ServiceChainingUtil.getServicePath(action.getParameterValue());
132         if (servicePath == null || servicePath.getName() == null) {
133             final String info = String.format("service-path not found (sourceSgt=%s, destinationSgt=%s)",
134                     sourceSgt, destinationSgt);
135             context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context, peerEndpoint, info));
136             return;
137         }
138         final TenantId tenantId = PolicyManagerUtil.getTenantId(peerEndpoint);
139         if (tenantId == null) {
140             final String info = String.format("tenant-id not found (sourceSgt=%s, destinationSgt=%s)",
141                     sourceSgt, destinationSgt);
142             context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context, peerEndpoint, info));
143             return;
144         }
145         final RenderedServicePath directPath = ServiceChainingUtil.createRenderedPath(servicePath, tenantId, dataBroker);
146         // Rsp found, create class-map and policy-map entry
147         final String classMapName = PolicyManagerUtil.generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
148         final Match match = PolicyManagerUtil.createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue());
149         final ClassMap classMap = PolicyManagerUtil.createClassMap(classMapName, match);
150         policyMapEntries.add(PolicyManagerUtil.createPolicyEntry(classMapName, directPath, PolicyManagerImpl.ActionCase.CHAIN));
151         RenderedServicePath reversedPath = null;
152         if (servicePath.isSymmetric()) {
153             // symmetric path is in opposite direction. Roles of renderer and peer endpoint will invert
154             reversedPath = ServiceChainingUtil.createSymmetricRenderedPath(servicePath, directPath, tenantId, dataBroker);
155             // Reversed Rsp found, create class-map and policy-map entry in opposite direction
156             final String oppositeClassMapName = PolicyManagerUtil.generateClassMapName(destinationSgt.getValue(), sourceSgt.getValue());
157             final Match oppositeMatch = PolicyManagerUtil.createSecurityGroupMatch(destinationSgt.getValue(), sourceSgt.getValue());
158             final ClassMap oppositeClassMap = PolicyManagerUtil.createClassMap(oppositeClassMapName, oppositeMatch);
159             policyMapEntries.add(PolicyManagerUtil.createPolicyEntry(oppositeClassMapName, reversedPath, PolicyManagerImpl.ActionCase.CHAIN));
160             context.getPolicyWriter().cache(oppositeClassMap);
161         }
162         // Create appropriate service path && remote forwarder
163         final boolean sfcPartSuccessful = setSfcPart(servicePath, directPath, reversedPath, context.getPolicyWriter());
164         if (!sfcPartSuccessful) {
165             //TODO: extract resolved-rule name
166             final String info = String.format("failed during sfc-part execution (sourceSgt=%s, destinationSgt=%s)",
167                     sourceSgt, destinationSgt);
168             //context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeerAndAction(context, peerEndpoint, info));
169             return;
170         }
171         context.getPolicyWriter().cache(classMap);
172         context.getPolicyWriter().cache(policyMapEntries);
173     }
174
175     static void removeChainAction(final PeerEndpoint peerEndpoint, final Sgt sourceSgt, final Sgt destinationSgt,
176                                   final Map<PolicyManagerImpl.ActionCase, Action> actionMap, PolicyWriter policyWriter) {
177         final Action action = actionMap.get(PolicyManagerImpl.ActionCase.CHAIN);
178         final ServiceFunctionPath servicePath = ServiceChainingUtil.getServicePath(action.getParameterValue());
179         if (servicePath == null || servicePath.getName() == null) {
180             return;
181         }
182         final TenantId tenantId = PolicyManagerUtil.getTenantId(peerEndpoint);
183         if (tenantId == null) {
184             return;
185         }
186         // Cache class-maps, appropriate policy-map entries and service-path (if there are some created by gbp)
187         final List<Class> policyMapEntries = new ArrayList<>();
188         final String classMapName = PolicyManagerUtil.generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
189         final ClassMap classMap = PolicyManagerUtil.createClassMap(classMapName, null);
190         policyWriter.cache(classMap);
191         policyMapEntries.add(PolicyManagerUtil.createPolicyEntry(classMapName, null, PolicyManagerImpl.ActionCase.CHAIN));
192
193         final RspName rspName = generateRspName(servicePath, tenantId);
194         final RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
195         final ServiceFunctionForwarder firstHopSff = getFirstHopSff(renderedServicePath);
196         if (firstHopSff != null && firstHopSff.getIpMgmtAddress() != null &&
197                 firstHopSff.getIpMgmtAddress().getIpv4Address() != null) {
198             final String sffMgmtIpValue = firstHopSff.getIpMgmtAddress().getIpv4Address().getValue();
199             if (sffMgmtIpValue.equals(policyWriter.getManagementIpAddress())) {
200                 // Remove service chain and remote forwarder
201                 final ServiceChain serviceChain = findServiceChainToRsp(renderedServicePath);
202                 final ServiceFfName remoteForwarder = findRemoteForwarder(firstHopSff);
203                 policyWriter.cache(serviceChain);
204                 policyWriter.cache(remoteForwarder);
205             }
206         }
207         if (servicePath.isSymmetric()) {
208             final String oppositeClassMapName = PolicyManagerUtil.generateClassMapName(destinationSgt.getValue(), sourceSgt.getValue());
209             final ClassMap oppositeClassMap = PolicyManagerUtil.createClassMap(oppositeClassMapName, null);
210             policyWriter.cache(oppositeClassMap);
211             policyMapEntries.add(PolicyManagerUtil.createPolicyEntry(oppositeClassMapName, null, PolicyManagerImpl.ActionCase.CHAIN));
212
213             final RspName reversedRspName = generateReversedRspName(servicePath, tenantId);
214             final RenderedServicePath reversedRenderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(reversedRspName);
215             final ServiceFunctionForwarder reversedFirstHopSff = getFirstHopSff(reversedRenderedServicePath);
216             if (reversedFirstHopSff != null && reversedFirstHopSff.getIpMgmtAddress() != null &&
217                     reversedFirstHopSff.getIpMgmtAddress().getIpv4Address() != null) {
218                 final String reversedSffMgmtIpValue = reversedFirstHopSff.getIpMgmtAddress().getIpv4Address().getValue();
219                 if (reversedSffMgmtIpValue.equals(policyWriter.getManagementIpAddress())) {
220                     // Remove service chain and remote forwarder
221                     final ServiceChain serviceChain = findServiceChainToRsp(renderedServicePath);
222                     final ServiceFfName remoteForwarder = findRemoteForwarder(reversedFirstHopSff);
223                     policyWriter.cache(serviceChain);
224                     policyWriter.cache(remoteForwarder);
225                 }
226             }
227         }
228         policyWriter.cache(policyMapEntries);
229     }
230
231     private static ServiceFfName findRemoteForwarder(ServiceFunctionForwarder firstHopSff) {
232         ServiceFfNameBuilder serviceFfNameBuilder = new ServiceFfNameBuilder();
233         serviceFfNameBuilder.setName(firstHopSff.getName().getValue())
234                 .setKey(new ServiceFfNameKey(firstHopSff.getName().getValue()));
235         return serviceFfNameBuilder.build();
236     }
237
238     private static ServiceFunctionForwarder getFirstHopSff(RenderedServicePath renderedServicePath) {
239         if (renderedServicePath == null || renderedServicePath.getRenderedServicePathHop() == null ||
240                 renderedServicePath.getRenderedServicePathHop().isEmpty()) {
241             return null;
242         }
243         final RenderedServicePathHop firstHop = renderedServicePath.getRenderedServicePathHop().get(0);
244         final SffName firstHopSff = firstHop.getServiceFunctionForwarder();
245         if (firstHopSff == null) {
246             return null;
247         }
248         return SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(firstHopSff);
249     }
250
251     private static ServiceChain findServiceChainToRsp(final RenderedServicePath renderedServicePath) {
252         // Construct service chain with key
253         final Long pathId = renderedServicePath.getPathId();
254         final ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
255         final ServiceChainBuilder serviceChainBuilder = new ServiceChainBuilder();
256         servicePathBuilder.setServicePathId(pathId)
257                 .setKey(new ServicePathKey(pathId));
258         serviceChainBuilder.setServicePath(Collections.singletonList(servicePathBuilder.build()));
259         return serviceChainBuilder.build();
260     }
261
262     static RenderedServicePath createRenderedPath(final ServiceFunctionPath sfp, final TenantId tenantId,
263                                                   final DataBroker dataBroker) {
264         RenderedServicePath renderedServicePath;
265         // Try to read existing RSP
266         final RspName rspName = generateRspName(sfp, tenantId);
267         renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
268         if (renderedServicePath != null) {
269             return renderedServicePath;
270         }
271         LOG.info("Rendered service path with name {} not found, creating a new one ..", rspName.getValue());
272         final CreateRenderedPathInput input = new CreateRenderedPathInputBuilder()
273                 .setParentServiceFunctionPath(sfp.getName().getValue())
274                 .setName(rspName.getValue())
275                 .setSymmetric(sfp.isSymmetric())
276                 .build();
277         renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, input);
278         LOG.info("Rendered service path {} created", rspName.getValue());
279         checkSfcRspStatus(rspName, dataBroker);
280         return renderedServicePath;
281     }
282
283     static RenderedServicePath createSymmetricRenderedPath(final ServiceFunctionPath sfp, final RenderedServicePath rsp,
284                                                            final TenantId tenantId, final DataBroker dataBroker) {
285         RenderedServicePath reversedRenderedPath;
286         // Try to read existing RSP
287         final RspName rspName = generateReversedRspName(sfp, tenantId);
288         reversedRenderedPath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
289         if (reversedRenderedPath != null) {
290             return reversedRenderedPath;
291         }
292         LOG.info("Reversed rendered service path with name {} not found, creating a new one ..", rspName.getValue());
293         reversedRenderedPath = SfcProviderRenderedPathAPI.createReverseRenderedServicePathEntry(rsp);
294         LOG.info("Rendered service path {} created", rspName.getValue());
295         checkSfcRspStatus(rspName, dataBroker);
296         return reversedRenderedPath;
297     }
298
299     static ServiceFunctionPath findServiceFunctionPath(final SfcName chainName) {
300         final ServiceFunctionPaths allPaths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
301         for (ServiceFunctionPath serviceFunctionPath : allPaths.getServiceFunctionPath()) {
302             if (serviceFunctionPath.getServiceChainName().equals(chainName)) {
303                 return serviceFunctionPath;
304             }
305         }
306         return null;
307     }
308
309     private static RspName generateRspName(final ServiceFunctionPath serviceFunctionPath, final TenantId tenantId) {
310         return new RspName(serviceFunctionPath.getName().getValue() + tenantId.getValue() + RSP_SUFFIX);
311     }
312
313     private static RspName generateReversedRspName(final ServiceFunctionPath serviceFunctionPath, final TenantId tenantId) {
314         return new RspName(serviceFunctionPath.getName().getValue() + tenantId.getValue() + RSP_REVERSED_SUFFIX);
315     }
316
317     private static <T> Supplier<Boolean> createNegativePathWithLogSupplier(final T value, final Consumer<T> logCommand) {
318         return () -> {
319             // fireLog
320             logCommand.accept(value);
321             return false;
322         };
323     }
324
325     static boolean setSfcPart(final ServiceFunctionPath serviceFunctionPath, final RenderedServicePath renderedServicePath,
326                               final RenderedServicePath reversedRenderedServicePath, PolicyWriter policyWriter) {
327         boolean outcome = true;
328         // Direct path
329         final java.util.Optional<RenderedServicePath> renderedServicePathSafe = java.util.Optional.ofNullable(renderedServicePath);
330         if (renderedServicePathSafe.isPresent()) {
331             if (renderedServicePath.getRenderedServicePathHop() != null
332                     && !renderedServicePath.getRenderedServicePathHop().isEmpty()) {
333                 if (!resolveRenderedServicePath(renderedServicePath, policyWriter)) {
334                     outcome = false;
335                 }
336             } else {
337                 LOG.warn("Rendered service path {} does not contain any hop",
338                         renderedServicePathSafe.map(RenderedServicePath::getName).map(RspName::getValue).orElse("n/a"));
339                 outcome = false;
340             }
341         } else {
342             LOG.warn("Rendered service path is null");
343             outcome = false;
344         }
345         if (serviceFunctionPath.isSymmetric()) {
346             // Reversed path
347             final java.util.Optional<RenderedServicePath> reversedRenderedServicePathSafe = java.util.Optional.ofNullable(reversedRenderedServicePath);
348             if (reversedRenderedServicePathSafe.isPresent()) {
349                 if (reversedRenderedServicePath.getRenderedServicePathHop() != null
350                         && !reversedRenderedServicePath.getRenderedServicePathHop().isEmpty()) {
351                     if (!resolveRenderedServicePath(reversedRenderedServicePath, policyWriter)) {
352                         outcome = false;
353                     }
354                 } else {
355                     LOG.warn("Rendered service path {} does not contain any hop",
356                             reversedRenderedServicePathSafe.map(RenderedServicePath::getName).map(RspName::getValue).orElse("n/a"));
357                     outcome = false;
358                 }
359             } else {
360                 LOG.warn("Reversed rendered service path is null");
361                 outcome = false;
362             }
363         }
364         return outcome;
365     }
366
367     private static boolean resolveRenderedServicePath(final RenderedServicePath renderedServicePath, PolicyWriter policyWriter) {
368         final RenderedServicePathHop firstHop = renderedServicePath.getRenderedServicePathHop().get(0);
369         if (firstHop == null) {
370             return false;
371         }
372         final SffName sffName = firstHop.getServiceFunctionForwarder();
373
374         // Create remote forwarder if necessary
375         final java.util.Optional<ServiceFunctionForwarder> serviceFunctionForwarder = java.util.Optional.ofNullable(
376                 SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(sffName));
377         if (!serviceFunctionForwarder.isPresent()) {
378             LOG.warn("Service function forwarder {} does not exist", sffName.getValue());
379             return false;
380         }
381         final ServiceFunctionForwarder forwarder = serviceFunctionForwarder.get();
382         if (forwarder.getSffDataPlaneLocator() == null || forwarder.getSffDataPlaneLocator().isEmpty()) {
383             LOG.warn("Service function forwarder {} does not contain data plane locator", sffName.getValue());
384             return false;
385         }
386         // TODO only first dpl resolved
387         final SffDataPlaneLocator sffDataPlaneLocator = forwarder.getSffDataPlaneLocator().get(0);
388         final DataPlaneLocator dataPlaneLocator = sffDataPlaneLocator.getDataPlaneLocator();
389         final LocatorType locatorType = dataPlaneLocator.getLocatorType();
390         if (locatorType != null && locatorType instanceof Ip) {
391             final IpAddress remoteForwarderIpAddress = IetfModelCodec.ipAddress2010(((Ip) locatorType).getIp());
392             if (remoteForwarderIpAddress == null || remoteForwarderIpAddress.getIpv4Address() == null) {
393                 LOG.warn("Service function forwarder {} data plane locator does not contain ip address", sffName.getValue());
394                 return false;
395             }
396             final String remoteForwarderStringIp = remoteForwarderIpAddress.getIpv4Address().getValue();
397             return serviceFunctionForwarder.map(sff -> java.util.Optional.ofNullable(sff.getIpMgmtAddress())
398                     .map(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress::getIpv4Address)
399                     .map(Ipv4Address::getValue)
400                     .map(addressValue -> {
401                         final ServiceTypeChoice serviceTypeChoice;
402                         if (!addressValue.equals(policyWriter.getManagementIpAddress())) {
403                             final ServiceFfNameBuilder remoteSffBuilder = new ServiceFfNameBuilder();
404                             remoteSffBuilder.setName(sffName.getValue())
405                                     .setKey(new ServiceFfNameKey(sffName.getValue()))
406                                     .setIp(new IpBuilder().setAddress(new Ipv4Address(remoteForwarderStringIp)).build());
407                             policyWriter.cache(remoteSffBuilder.build());
408                             serviceTypeChoice = forwarderTypeChoice(sffName.getValue());
409                             // Service chain
410                             final List<Services> services = new ArrayList<>();
411                             final ServicesBuilder servicesBuilder = new ServicesBuilder();
412                             servicesBuilder.setServiceIndexId(renderedServicePath.getStartingIndex())
413                                     .setServiceTypeChoice(serviceTypeChoice);
414                             services.add(servicesBuilder.build());
415                             final List<ServicePath> servicePaths = new ArrayList<>();
416                             final ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
417                             servicePathBuilder.setKey(new ServicePathKey(renderedServicePath.getPathId()))
418                                     .setServicePathId(renderedServicePath.getPathId())
419                                     .setConfigServiceChainPathMode(new ConfigServiceChainPathModeBuilder()
420                                             .setServiceIndex(new ServiceIndexBuilder()
421                                                     .setServices(services).build()).build());
422                             servicePaths.add(servicePathBuilder.build());
423                             final ServiceChainBuilder chainBuilder = new ServiceChainBuilder();
424                             chainBuilder.setServicePath(servicePaths);
425                             final ServiceChain serviceChain = chainBuilder.build();
426                             policyWriter.cache(serviceChain);
427                         }
428                         return true;
429                     }).orElseGet(createNegativePathWithLogSupplier(sffName.getValue(),
430                             (value) -> LOG.error("Cannot create remote forwarder, SFF {} does not contain management ip address",
431                                     value))
432                     )
433             ).orElseGet(createNegativePathWithLogSupplier(sffName.getValue(),
434                     (value) -> LOG.error("Sff with name {} does not exist", value))
435             );
436         }
437         return false;
438     }
439
440     static ServiceTypeChoice forwarderTypeChoice(final String forwarderName) {
441         final ServiceFunctionForwarderBuilder sffBuilder = new ServiceFunctionForwarderBuilder();
442         sffBuilder.setServiceFunctionForwarder(forwarderName);
443         return sffBuilder.build();
444     }
445
446     static ServiceTypeChoice functionTypeChoice(final String functionName) {
447         final ServiceFunctionBuilder sfBuilder = new ServiceFunctionBuilder();
448         sfBuilder.setServiceFunction(functionName);
449         return sfBuilder.build();
450     }
451
452     private static void checkSfcRspStatus(final RspName rspName, final DataBroker dataBroker) {
453         /** TODO A better way to do this is to register listener and wait for notification than using hardcoded timeout
454          *  with Thread.sleep(). Example in class BridgeDomainManagerImpl
455          */
456         ConfiguredRenderedPath renderedPath = null;
457         LOG.info("Waiting for SFC to configure path {} ...", rspName.getValue());
458
459         byte attempt = 0;
460         do {
461             attempt++;
462             // Wait
463             try {
464                 Thread.sleep(5000L);
465             } catch (InterruptedException e) {
466                 LOG.error("Thread interrupted while waiting ... {} ", e);
467             }
468             // Read actual status
469             final InstanceIdentifier<ConfiguredRenderedPath> statusIid = InstanceIdentifier.builder(RendererPathStates.class)
470                     .child(RendererPathState.class, new RendererPathStateKey(new RendererName("ios-xe-renderer")))
471                     .child(ConfiguredRenderedPaths.class)
472                     .child(ConfiguredRenderedPath.class, new ConfiguredRenderedPathKey(rspName)).build();
473             final java.util.Optional<ReadWriteTransaction> optionalTransaction =
474                     NetconfTransactionCreator.netconfReadWriteTransaction(dataBroker);
475             if (!optionalTransaction.isPresent()) {
476                 LOG.warn("Failed to create transaction, mountpoint: {}", dataBroker);
477                 return;
478             }
479             ReadWriteTransaction transaction = optionalTransaction.get();
480             try {
481                 final CheckedFuture<Optional<ConfiguredRenderedPath>, ReadFailedException> submitFuture =
482                         transaction.read(LogicalDatastoreType.OPERATIONAL, statusIid);
483                 final Optional<ConfiguredRenderedPath> optionalPath = submitFuture.checkedGet();
484                 if (optionalPath.isPresent()) {
485                     renderedPath = optionalPath.get();
486                 }
487             } catch (ReadFailedException e) {
488                 LOG.warn("Failed while read rendered path status ... {} ", e.getMessage());
489             }
490             if (renderedPath == null || renderedPath.getPathStatus() == null ||
491                     renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.InProgress)) {
492                 LOG.info("Still waiting for SFC ... ");
493             } else if (renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.Failure)) {
494                 LOG.warn("SFC failed to configure rsp");
495             } else if (renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.Success)) {
496                 LOG.info("RSP {} configured by SFC", rspName.getValue());
497                 try {
498                     Thread.sleep(5000); // Just for sure, maybe will be safe to remove this
499                 } catch (InterruptedException e) {
500                     LOG.error("Thread interrupted while waiting ... {} ", e);
501                 }
502                 return;
503             }
504         }
505         while (attempt <= 6);
506         LOG.warn("Maximum number of attempts reached");
507     }
508 }