[ios-xe-renderer] Increases coverage for PolicyWriterUtil
[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 static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl.ActionCase;
12 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.ActionInDirection;
13 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.createClassMap;
14 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.createPolicyMapEntry;
15 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.createSecurityGroupMatch;
16 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.generateClassMapName;
17 import static org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.util.PolicyManagerUtil.getTenantId;
18 import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction.In;
19 import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction.Out;
20 import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation.CONSUMER;
21 import static org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation.PROVIDER;
22
23 import javax.annotation.Nonnull;
24 import javax.annotation.Nullable;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.function.Consumer;
31 import java.util.function.Supplier;
32 import com.google.common.annotations.VisibleForTesting;
33 import com.google.common.base.Optional;
34 import com.google.common.util.concurrent.CheckedFuture;
35 import com.google.common.util.concurrent.Futures;
36 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
37 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
38 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
39 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
40 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
41 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyConfigurationContext;
42 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl;
43 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.NetconfTransactionCreator;
44 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.PolicyWriterUtil;
45 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
46 import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
47 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
48 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.RendererPathStates;
49 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathState;
50 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.RendererPathStateKey;
51 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.ConfiguredRenderedPaths;
52 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPath;
53 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.rsp.manager.rev160421.renderer.path.states.renderer.path.state.configured.rendered.paths.ConfiguredRenderedPathKey;
54 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RendererName;
55 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
56 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
57 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
58 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
59 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
60 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
61 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
62 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator;
63 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.sff.data.plane.locator.DataPlaneLocator;
64 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
65 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
66 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
67 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.LocatorType;
68 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.Ip;
69 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
70 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
71 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ClassMap;
72 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain;
73 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChainBuilder;
74 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native._class.map.Match;
75 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.config.service.chain.grouping.IpBuilder;
76 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.Class;
77 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
78 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathBuilder;
79 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
80 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName;
81 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameBuilder;
82 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameKey;
83 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.ConfigServiceChainPathModeBuilder;
84 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.ServiceIndexBuilder;
85 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.Services;
86 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.ServicesBuilder;
87 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.ServiceTypeChoice;
88 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;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RuleName;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.has.unconfigured.rule.groups.unconfigured.rule.group.UnconfiguredResolvedRuleBuilder;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpoint;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.actions.Action;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
98 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
99 import org.slf4j.Logger;
100 import org.slf4j.LoggerFactory;
101
102 public class ServiceChainingUtil {
103
104     private static final Logger LOG = LoggerFactory.getLogger(ServiceChainingUtil.class);
105     private static final String RSP_SUFFIX = "-gbp-rsp";
106     private static final String RSP_REVERSED_SUFFIX = "-gbp-rsp-Reverse";
107     private static long timeout = 5000L;
108
109     private ServiceChainingUtil() {
110         throw new IllegalAccessError("instance of util class not supported");
111     }
112
113     /**
114      * According to provided action, this method gets service function path and collects all info about participation
115      * and orientation of path. According to path symmetricity, participation and direction, one of these cases happens:
116      * 1. Path is asymmetric, and it starts in  this classifier (specified by context) - direct chain is created
117      * 2. Path is asymmetric, and it starts in classifier on opposite side of the chain - skipped
118      * 3. Path is symmetric, and it starts in this classifier - direct chain is created
119      * 2. Path is symmetric, and it starts in classifier on opposite side of the chain - reversed path is created
120      * <p>
121      * Behaviour is correct also in case when "this" and "opposite" classifier is the same
122      *
123      * @param peerEndpoint   - peer endpoint, used to generate status and access to tenant ID
124      * @param sourceSgt      - security group tag of source endpoint
125      * @param destinationSgt - security group tag of destination endpoint
126      * @param actionMap      - contains all info to evaluate correct chain orientation according to endpoint participation
127      * @param context        - contains policy-map location and status info
128      * @param dataBroker     - to access odl datastore
129      */
130     static void newChainAction(final PeerEndpoint peerEndpoint, final Sgt sourceSgt,
131                                final Sgt destinationSgt, final Map<PolicyManagerImpl.ActionCase, ActionInDirection> actionMap,
132                                final PolicyConfigurationContext context, final DataBroker dataBroker) {
133         final ActionInDirection actionInDirection = actionMap.get(ActionCase.CHAIN);
134         if (actionInDirection == null) {
135             return;
136         }
137         context.setCurrentUnconfiguredRule(new UnconfiguredResolvedRuleBuilder()
138                 .setRuleName(new RuleName(actionInDirection.getRuleName())).build());
139         // Rule action + orientation
140         final Action action = actionInDirection.getAction();
141         final EndpointPolicyParticipation participation = actionInDirection.getParticipation();
142         final Direction direction = actionInDirection.getDirection();
143         // Get service function path
144         final ServiceFunctionPath servicePath = ServiceChainingUtil.findServicePathFromParameterValues(action.getParameterValue());
145         if (servicePath == null || servicePath.getName() == null) {
146             final String info = String.format("service-path not found (sourceSgt=%s, destinationSgt=%s)",
147                     sourceSgt, destinationSgt);
148             context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context, peerEndpoint, info));
149             return;
150         }
151         final TenantId tenantId = PolicyManagerUtil.getTenantId(peerEndpoint);
152         if (tenantId == null) {
153             final String info = String.format("tenant-id not found (sourceSgt=%s, destinationSgt=%s)",
154                     sourceSgt, destinationSgt);
155             context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context, peerEndpoint, info));
156             return;
157         }
158         boolean sfcPartSuccessful = true;
159         // Creates direct path in corresponding direction
160         if ((participation.equals(PROVIDER) && direction.equals(Out)) ||
161                 (participation.equals(CONSUMER) && direction.equals(In))) {
162             final RenderedServicePath renderedServicePath = ServiceChainingUtil.resolveRenderedServicePath(servicePath,
163                     tenantId, dataBroker, sourceSgt, destinationSgt, context);
164             sfcPartSuccessful = resolveRemoteSfcComponents(renderedServicePath, context);
165             // Creates reversed path if symmetric
166         } else if (servicePath.isSymmetric()) {
167             final RenderedServicePath renderedServicePath =
168                     ServiceChainingUtil.resolveReversedRenderedServicePath(servicePath, tenantId, dataBroker, sourceSgt,
169                             destinationSgt, context);
170             sfcPartSuccessful = resolveRemoteSfcComponents(renderedServicePath, context);
171         }
172         if (!sfcPartSuccessful) {
173             final String info = String.format("failed during sfc-part execution (sourceSgt=%s, destinationSgt=%s)",
174                     sourceSgt, destinationSgt);
175             context.appendUnconfiguredRendererEP(StatusUtil.assembleNotConfigurableRendererEPForPeer(context,
176                     peerEndpoint, info));
177         }
178     }
179
180     /**
181      * According to service function path and direction, creates appropriate rendered service path name {@link RspName}
182      * and starts appropriate method which removes policy for resolved endpoint pair
183      *
184      * @param peerEndpoint   - contains info about tenant ID
185      * @param sourceSgt      - security group tag of source endpoint
186      * @param destinationSgt - security group tag of destination endpoint
187      * @param actionMap      - contains all info to evaluate correct chain orientation according to endpoint participation
188      * @param context        - contains policy-map location and status info
189      */
190     static void resolveRemovedChainAction(final PeerEndpoint peerEndpoint, final Sgt sourceSgt, final Sgt destinationSgt,
191                                           final Map<ActionCase, ActionInDirection> actionMap, final PolicyConfigurationContext context) {
192         final ActionInDirection actionInDirection = actionMap.get(ActionCase.CHAIN);
193         final Action action = actionInDirection.getAction();
194         final EndpointPolicyParticipation participation = actionInDirection.getParticipation();
195         final Direction direction = actionInDirection.getDirection();
196         final ServiceFunctionPath servicePath = ServiceChainingUtil.findServicePathFromParameterValues(action.getParameterValue());
197         if (servicePath == null || servicePath.getName() == null) {
198             return;
199         }
200         final TenantId tenantId = getTenantId(peerEndpoint);
201         if (tenantId == null) {
202             return;
203         }
204         //Symmetric chain
205         if ((participation.equals(PROVIDER) && direction.equals(Out)) ||
206                 (participation.equals(CONSUMER) && direction.equals(In))) {
207             final RspName rspName = generateRspName(servicePath, tenantId);
208             resolveRemovedRenderedServicePath(rspName, sourceSgt, destinationSgt, context);
209
210         } else if (servicePath.isSymmetric()) {
211             final RspName rspName = generateReversedRspName(servicePath, tenantId);
212             resolveRemovedRenderedServicePath(rspName, sourceSgt, destinationSgt, context);
213         }
214     }
215
216     /**
217      * Service-path (netconf) is created on every netconf device, which contains service function belonging to specific
218      * chain. Classifier has to be able to reach first service function forwarder in order to send packet to chain. If
219      * first service function forwarder is present on the same node as classifier, service-path entry should be already
220      * present (created by IOS-XE renderer in SFC) also with appropriate remote SFF if necessary. If first SFF is on
221      * different node (remote classifier), classifier has to create it's own service-path entry with remote SFF.
222      *
223      * @param renderedServicePath - path classifier has to reach
224      * @param context             - contains policy-map location and status info
225      * @return true if everything went good, false otherwise
226      */
227     public static boolean resolveRemoteSfcComponents(final RenderedServicePath renderedServicePath,
228                                                       final PolicyConfigurationContext context) {
229         final PolicyManagerImpl.PolicyMapLocation location = context.getPolicyMapLocation();
230         final ServiceFunctionForwarder forwarder = getFirstHopSff(renderedServicePath);
231         if (forwarder == null) {
232             return false;
233         }
234         final SffName sffName = forwarder.getName();
235         if (forwarder.getSffDataPlaneLocator() == null || forwarder.getSffDataPlaneLocator().isEmpty()) {
236             LOG.warn("Service function forwarder {} does not contain data plane locator", sffName.getValue());
237             return false;
238         }
239         // TODO only first dpl resolved
240         final SffDataPlaneLocator sffDataPlaneLocator = forwarder.getSffDataPlaneLocator().get(0);
241         final DataPlaneLocator dataPlaneLocator = sffDataPlaneLocator.getDataPlaneLocator();
242         final LocatorType locatorType = dataPlaneLocator.getLocatorType();
243         if (locatorType != null && locatorType instanceof Ip) {
244             final IpAddress remoteForwarderIpAddress = (((Ip) locatorType).getIp());
245             if (remoteForwarderIpAddress == null || remoteForwarderIpAddress.getIpv4Address() == null) {
246                 LOG.warn("Service function forwarder {} data plane locator does not contain ip address", sffName.getValue());
247                 return false;
248             }
249             final String remoteForwarderStringIp = remoteForwarderIpAddress.getIpv4Address().getValue();
250             final java.util.Optional<IpAddress> optionalIpMgmtAddress = java.util.Optional.ofNullable(forwarder.getIpMgmtAddress());
251
252             return optionalIpMgmtAddress.map(IpAddress::getIpv4Address)
253                     .map(Ipv4Address::getValue)
254                     .map(addressValue -> {
255                         final ServiceTypeChoice serviceTypeChoice;
256                         if (!addressValue.equals(location.getManagementIpAddress())) {
257                             // Remote forwarder
258                             final ServiceFfNameBuilder remoteSffBuilder = new ServiceFfNameBuilder();
259                             remoteSffBuilder.setName(sffName.getValue())
260                                     .setKey(new ServiceFfNameKey(sffName.getValue()))
261                                     .setIp(new IpBuilder().setAddress(new Ipv4Address(remoteForwarderStringIp)).build());
262                             boolean rsResult = PolicyWriterUtil.writeRemote(remoteSffBuilder.build(), location);
263                             context.setFutureResult(Futures.immediateCheckedFuture(rsResult));
264                             serviceTypeChoice = createForwarderTypeChoice(sffName.getValue());
265                             // Service chain
266                             final List<Services> services = new ArrayList<>();
267                             final ServicesBuilder servicesBuilder = new ServicesBuilder();
268                             servicesBuilder.setServiceIndexId(renderedServicePath.getStartingIndex())
269                                     .setServiceTypeChoice(serviceTypeChoice);
270                             services.add(servicesBuilder.build());
271                             final List<ServicePath> servicePaths = new ArrayList<>();
272                             final ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
273                             servicePathBuilder.setKey(new ServicePathKey(renderedServicePath.getPathId()))
274                                     .setServicePathId(renderedServicePath.getPathId())
275                                     .setConfigServiceChainPathMode(new ConfigServiceChainPathModeBuilder()
276                                             .setServiceIndex(new ServiceIndexBuilder()
277                                                     .setServices(services).build()).build());
278                             servicePaths.add(servicePathBuilder.build());
279                             final ServiceChainBuilder chainBuilder = new ServiceChainBuilder();
280                             chainBuilder.setServicePath(servicePaths);
281                             final ServiceChain serviceChain = chainBuilder.build();
282                             boolean scResult = PolicyWriterUtil.writeServicePath(serviceChain, location);
283                             context.setFutureResult(Futures.immediateCheckedFuture(scResult));
284                         }
285                         return true;
286                     }).orElseGet(createNegativePathWithLogSupplier(sffName.getValue(),
287                             (value) -> LOG.error("Cannot create remote forwarder, SFF {} does not contain management ip address",
288                                     value))
289                     );
290         }
291         return false;
292     }
293
294     /**
295      * Investigates provided parameter values and derives service chain name. This name is used to find service function
296      * path
297      *
298      * @param params - list of parameters
299      * @return - service function path if found, null if provided parameters does not correspond with any chain or there
300      * is no service function path defined by that chain
301      */
302     @Nullable
303     static ServiceFunctionPath findServicePathFromParameterValues(final List<ParameterValue> params) {
304         if (params == null || params.isEmpty()) {
305             LOG.error("Cannot found service path, parameter value is null");
306             return null;
307         }
308         final Map<String, Object> paramsMap = new HashMap<>();
309         for (ParameterValue value : params) {
310             if (value.getName() == null)
311                 continue;
312             if (value.getIntValue() != null) {
313                 paramsMap.put(value.getName().getValue(), value.getIntValue());
314             } else if (value.getStringValue() != null) {
315                 paramsMap.put(value.getName().getValue(), value.getStringValue());
316             }
317         }
318         String chainName = null;
319         for (String name : paramsMap.keySet()) {
320             if (name.equals(ChainActionDefinition.SFC_CHAIN_NAME)) {
321                 chainName = (String) paramsMap.get(name);
322             }
323         }
324         if (chainName == null) {
325             LOG.error("Cannot found service path, chain name is null");
326             return null;
327         }
328         final ServiceFunctionPath serviceFunctionPath = findServiceFunctionPathFromServiceChainName(new SfcName(chainName));
329         if (serviceFunctionPath == null) {
330             LOG.error("Service function path not found for name {}", chainName);
331             return null;
332         }
333         return serviceFunctionPath;
334     }
335
336     static ServiceFunctionPath findServiceFunctionPathFromServiceChainName(@Nonnull final SfcName chainName) {
337         final ServiceFunctionPaths allPaths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
338         if (allPaths == null || allPaths.getServiceFunctionPath() == null || allPaths.getServiceFunctionPath().isEmpty()) {
339             return null;
340         }
341         for (ServiceFunctionPath serviceFunctionPath : allPaths.getServiceFunctionPath()) {
342             if (chainName.equals(serviceFunctionPath.getServiceChainName())) {
343                 return serviceFunctionPath;
344             }
345         }
346         return null;
347     }
348
349     /**
350      * Creates {@link RenderedServicePath} if not exist. If created, ios-xe renderer in SFC is invoked, so this method
351      * has to wait till SFC part is done to prevent transaction collisions in {@link this#checkRspManagerStatus(RspName,
352      * DataBroker)}. If this operation is successful, class-map {@link ClassMap} and entry in policy-map {@link Class}
353      * is written
354      *
355      * @param sfp            - path used to create RSP
356      * @param tenantId       - used to generate RSP name according to GBP standards
357      * @param dataBroker     - data provider to access odl controller
358      * @param sourceSgt      - source security group tag
359      * @param destinationSgt - destination security group tag
360      * @param context        - contains policy-map location and status info
361      * @return read/created RSP
362      */
363     static RenderedServicePath resolveRenderedServicePath(final ServiceFunctionPath sfp, final TenantId tenantId,
364                                                                   final DataBroker dataBroker, final Sgt sourceSgt, final Sgt destinationSgt,
365                                                                   final PolicyConfigurationContext context) {
366         // Get rendered service path
367         final RspName rspName = generateRspName(sfp, tenantId);
368         RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
369         if (renderedServicePath == null) {
370             LOG.info("Rendered service path with name {} not found, creating a new one ..", rspName.getValue());
371             final CreateRenderedPathInput input = new CreateRenderedPathInputBuilder()
372                     .setParentServiceFunctionPath(sfp.getName().getValue())
373                     .setName(rspName.getValue())
374                     .setSymmetric(sfp.isSymmetric())
375                     .build();
376             renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, input);
377             LOG.info("Rendered service path {} created", rspName.getValue());
378             checkRspManagerStatus(rspName, dataBroker);
379         }
380         // Create class-map and policy-map entry
381         final String classMapName = generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
382         final Match match = createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue());
383         final ClassMap classMap = createClassMap(classMapName, match);
384         final Class policyMapEntry = createPolicyMapEntry(classMapName, renderedServicePath, ActionCase.CHAIN);
385         boolean cmResult = PolicyWriterUtil.writeClassMap(classMap, context.getPolicyMapLocation());
386         context.setFutureResult(Futures.immediateCheckedFuture(cmResult));
387         boolean pmeResult = PolicyWriterUtil.writePolicyMapEntry(policyMapEntry, context.getPolicyMapLocation());
388         context.setFutureResult(Futures.immediateCheckedFuture(pmeResult));
389         return renderedServicePath;
390     }
391
392     /**
393      * Creates reversed {@link RenderedServicePath} if not exist. To be successful, direct path has to exist.
394      * If created, ios-xe renderer in SFC is invoked, so this method has to wait till SFC part is done to prevent
395      * transaction collisions. If this operation is successful, class-map {@link ClassMap} and entry in policy-map
396      * {@link Class} is written
397      *
398      * @param sfp            - path used to create RSP
399      * @param tenantId       - used to generate RSP name according to GBP standards
400      * @param dataBroker     - data provider to access odl controller
401      * @param sourceSgt      - source security group tag
402      * @param destinationSgt - destination security group tag
403      * @param context        - contains policy-map location and status info
404      * @return read/created RSP
405      */
406     public static RenderedServicePath resolveReversedRenderedServicePath(final ServiceFunctionPath sfp, final TenantId tenantId,
407                                                                           final DataBroker dataBroker, final Sgt sourceSgt,
408                                                                           final Sgt destinationSgt, final PolicyConfigurationContext context) {
409         // Get rendered service path
410         final RspName rspName = generateRspName(sfp, tenantId);
411         RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
412         if (renderedServicePath == null) {
413             LOG.info("Rendered service path with name {} not found, creating a new one ..", rspName.getValue());
414             final CreateRenderedPathInput input = new CreateRenderedPathInputBuilder()
415                     .setParentServiceFunctionPath(sfp.getName().getValue())
416                     .setName(rspName.getValue())
417                     .setSymmetric(sfp.isSymmetric())
418                     .build();
419             renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, input);
420             LOG.info("Rendered service path {} created", rspName.getValue());
421             checkRspManagerStatus(rspName, dataBroker);
422         }
423         // Get reversed rendered service path
424         final RspName reversedRspName = generateReversedRspName(sfp, tenantId);
425         RenderedServicePath reversedRenderedPath = SfcProviderRenderedPathAPI.readRenderedServicePath(reversedRspName);
426         if (reversedRenderedPath == null) {
427             LOG.info("Reversed rendered service path with name {} not found, creating a new one ..", reversedRspName.getValue());
428             reversedRenderedPath = SfcProviderRenderedPathAPI.createReverseRenderedServicePathEntry(renderedServicePath);
429             LOG.info("Rendered service path {} created", reversedRspName.getValue());
430             checkRspManagerStatus(reversedRspName, dataBroker);
431         }
432         // Create class-map and policy-map entry
433         final String classMapName = generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
434         final Match match = createSecurityGroupMatch(sourceSgt.getValue(), destinationSgt.getValue());
435         final ClassMap classMap = createClassMap(classMapName, match);
436         final Class policyMapEntry = createPolicyMapEntry(classMapName, renderedServicePath, ActionCase.CHAIN);
437         boolean cmResult = PolicyWriterUtil.writeClassMap(classMap, context.getPolicyMapLocation());
438         context.setFutureResult(Futures.immediateCheckedFuture(cmResult));
439         boolean pmeResult = PolicyWriterUtil.writePolicyMapEntry(policyMapEntry, context.getPolicyMapLocation());
440         context.setFutureResult(Futures.immediateCheckedFuture(pmeResult));
441         resolveRemoteSfcComponents(renderedServicePath, context);
442         return reversedRenderedPath;
443     }
444
445     /**
446      * Removes all policy setup created according to rendered service path.
447      *
448      * @param rspName        - rendered service path name
449      * @param sourceSgt      - source security group tag
450      * @param destinationSgt - destination security group tag
451      * @param context        - context with policy-map location
452      */
453     private static void resolveRemovedRenderedServicePath(final RspName rspName, final Sgt sourceSgt, final Sgt destinationSgt,
454                                                           final PolicyConfigurationContext context) {
455         final String classMapName = PolicyManagerUtil.generateClassMapName(sourceSgt.getValue(), destinationSgt.getValue());
456         final ClassMap classMap = PolicyManagerUtil.createClassMap(classMapName, null);
457         final Class policyMapEntry = PolicyManagerUtil.createPolicyMapEntry(classMapName, null, PolicyManagerImpl.ActionCase.CHAIN);
458         PolicyWriterUtil.removePolicyMapEntry(policyMapEntry, context.getPolicyMapLocation());
459         PolicyWriterUtil.removeClassMap(classMap, context.getPolicyMapLocation());
460         final RenderedServicePath renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
461         final ServiceFunctionForwarder firstHopSff = getFirstHopSff(renderedServicePath);
462         if (firstHopSff != null && firstHopSff.getIpMgmtAddress() != null &&
463                 firstHopSff.getIpMgmtAddress().getIpv4Address() != null) {
464             final String sffMgmtIpValue = firstHopSff.getIpMgmtAddress().getIpv4Address().getValue();
465             if (!sffMgmtIpValue.equals(context.getPolicyMapLocation().getManagementIpAddress())) {
466                 // Remove service chain and remote forwarder
467                 final ServiceChain serviceChain = createServiceChain(renderedServicePath);
468                 final ServiceFfName remoteForwarder = createRemoteForwarder(firstHopSff);
469                 PolicyWriterUtil.removeServicePath(serviceChain, context.getPolicyMapLocation());
470                 PolicyWriterUtil.removeRemote(remoteForwarder, context.getPolicyMapLocation());
471             }
472         }
473     }
474
475     static ServiceFfName createRemoteForwarder(ServiceFunctionForwarder firstHopSff) {
476         final ServiceFfNameBuilder serviceFfNameBuilder = new ServiceFfNameBuilder();
477         serviceFfNameBuilder.setName(firstHopSff.getName().getValue());
478         return serviceFfNameBuilder.build();
479     }
480
481     private static ServiceTypeChoice createForwarderTypeChoice(final String forwarderName) {
482         final ServiceFunctionForwarderBuilder sffBuilder = new ServiceFunctionForwarderBuilder();
483         sffBuilder.setServiceFunctionForwarder(forwarderName);
484         return sffBuilder.build();
485     }
486
487     /**
488      * Creates service-chain with name/key only, using rendered service path id. This object contains no data, it is used
489      * to create instance identifier when appropriate service-chain is removed from particular device
490      *
491      * @param renderedServicePath - it's path id is used as a identifier
492      * @return service-chain object with id
493      */
494     private static ServiceChain createServiceChain(final RenderedServicePath renderedServicePath) {
495         final Long pathId = renderedServicePath.getPathId();
496         final ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
497         final ServiceChainBuilder serviceChainBuilder = new ServiceChainBuilder();
498         servicePathBuilder.setServicePathId(pathId)
499                 .setKey(new ServicePathKey(pathId));
500         serviceChainBuilder.setServicePath(Collections.singletonList(servicePathBuilder.build()));
501         return serviceChainBuilder.build();
502     }
503
504     private static <T> Supplier<Boolean> createNegativePathWithLogSupplier(final T value, final Consumer<T> logCommand) {
505         return () -> {
506             // fireLog
507             logCommand.accept(value);
508             return false;
509         };
510     }
511
512     private static ServiceFunctionForwarder getFirstHopSff(RenderedServicePath renderedServicePath) {
513         if (renderedServicePath == null || renderedServicePath.getRenderedServicePathHop() == null ||
514                 renderedServicePath.getRenderedServicePathHop().isEmpty()) {
515             return null;
516         }
517         final RenderedServicePathHop firstHop = renderedServicePath.getRenderedServicePathHop().get(0);
518         final SffName firstHopSff = firstHop.getServiceFunctionForwarder();
519         if (firstHopSff == null) {
520             return null;
521         }
522         return SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(firstHopSff);
523     }
524
525     private static RspName generateRspName(final ServiceFunctionPath serviceFunctionPath, final TenantId tenantId) {
526         return new RspName(serviceFunctionPath.getName().getValue() + "-" + tenantId.getValue() + RSP_SUFFIX);
527     }
528
529     private static RspName generateReversedRspName(final ServiceFunctionPath serviceFunctionPath, final TenantId tenantId) {
530         return new RspName(serviceFunctionPath.getName().getValue() + "-" + tenantId.getValue() + RSP_REVERSED_SUFFIX);
531     }
532
533     private static void checkRspManagerStatus(final RspName rspName, final DataBroker dataBroker) {
534         // TODO A better way to do this is to register listener and wait for notification than using hardcoded timeout
535         // with Thread.sleep(). Example in class BridgeDomainManagerImpl
536         ConfiguredRenderedPath renderedPath = null;
537         LOG.debug("Waiting for SFC to configure path {} ...", rspName.getValue());
538
539         byte attempt = 0;
540         do {
541             attempt++;
542             // Wait
543             try {
544                 Thread.sleep(timeout);
545             } catch (InterruptedException e) {
546                 LOG.error("Thread interrupted while waiting ... {} ", e);
547             }
548             // Read actual status
549             final InstanceIdentifier<ConfiguredRenderedPath> statusIid = InstanceIdentifier.builder(RendererPathStates.class)
550                     .child(RendererPathState.class, new RendererPathStateKey(new RendererName("ios-xe-renderer")))
551                     .child(ConfiguredRenderedPaths.class)
552                     .child(ConfiguredRenderedPath.class, new ConfiguredRenderedPathKey(rspName)).build();
553             final java.util.Optional<ReadWriteTransaction> optionalTransaction =
554                     NetconfTransactionCreator.netconfReadWriteTransaction(dataBroker);
555             if (!optionalTransaction.isPresent()) {
556                 LOG.warn("Failed to create transaction, mountpoint: {}", dataBroker);
557                 return;
558             }
559             ReadWriteTransaction transaction = optionalTransaction.get();
560             try {
561                 final CheckedFuture<Optional<ConfiguredRenderedPath>, ReadFailedException> submitFuture =
562                         transaction.read(LogicalDatastoreType.OPERATIONAL, statusIid);
563                 final Optional<ConfiguredRenderedPath> optionalPath = submitFuture.checkedGet();
564                 if (optionalPath.isPresent()) {
565                     renderedPath = optionalPath.get();
566                 }
567             } catch (ReadFailedException e) {
568                 LOG.warn("Failed while read rendered path status ... {} ", e.getMessage());
569             }
570             if (renderedPath == null || renderedPath.getPathStatus() == null ||
571                     renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.InProgress)) {
572                 LOG.info("Still waiting for SFC ... ");
573             } else if (renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.Failure)) {
574                 LOG.warn("SFC failed to configure rsp");
575             } else if (renderedPath.getPathStatus().equals(ConfiguredRenderedPath.PathStatus.Success)) {
576                 LOG.debug("RSP {} configured by SFC", rspName.getValue());
577                 try {
578                     Thread.sleep(timeout); // Just for sure, maybe will be safe to remove this
579                 } catch (InterruptedException e) {
580                     LOG.error("Thread interrupted while waiting ... {} ", e);
581                 }
582                 return;
583             }
584         }
585         while (attempt <= 6);
586         LOG.warn("Maximum number of attempts reached");
587     }
588
589     /**
590      * Only for test purposes
591      *
592      * @param value - set actual timeout value
593      */
594     @VisibleForTesting
595     public static void setTimeout(long value) {
596         timeout = value;
597     }
598 }