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