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