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