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