ios-xe-renderer policy-manager update
[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.ReadWriteTransaction;
15 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
16 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
17 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
18 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.manager.PolicyManagerImpl;
19 import org.opendaylight.groupbasedpolicy.renderer.ios_xe_provider.impl.writer.PolicyWriter;
20 import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
21 import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
22 import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
23 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
24 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
25 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
26 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
27 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
28 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
29 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
30 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
31 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
32 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
34 import org.opendaylight.yang.gen.v1.urn.ios.rev160308.Native;
35 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain;
36 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChainBuilder;
37 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.config.service.chain.grouping.IpBuilder;
38 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.policy.map.Class;
39 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
40 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathBuilder;
41 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
42 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.Local;
43 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.LocalBuilder;
44 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameBuilder;
45 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameKey;
46 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.ConfigServiceChainPathModeBuilder;
47 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.ServiceIndexBuilder;
48 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.Services;
49 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.ServicesBuilder;
50 import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.ServiceTypeChoice;
51 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;
52 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;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpointWithPolicy;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.actions.Action;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
58 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 import java.util.ArrayList;
63 import java.util.HashMap;
64 import java.util.List;
65 import java.util.Map;
66
67 public class ServiceChainingUtil {
68
69     private static final Logger LOG = LoggerFactory.getLogger(ServiceChainingUtil.class);
70
71     static ServiceFunctionPath getServicePath(final List<ParameterValue> params) {
72         if (params == null || params.isEmpty()) {
73             LOG.error("Cannot found service path, parameter value is null");
74             return null;
75         }
76         final Map<String, Object> paramsMap = new HashMap<>();
77         for (ParameterValue value : params) {
78             if (value.getName() == null)
79                 continue;
80             if (value.getIntValue() != null) {
81                 paramsMap.put(value.getName().getValue(), value.getIntValue());
82             } else if (value.getStringValue() != null) {
83                 paramsMap.put(value.getName().getValue(), value.getStringValue());
84             }
85         }
86         String chainName = null;
87         for (String name : paramsMap.keySet()) {
88             if (name.equals(ChainActionDefinition.SFC_CHAIN_NAME)) {
89                 chainName = (String) paramsMap.get(name);
90             }
91         }
92         if (chainName == null) {
93             LOG.error("Cannot found service path, chain name is null");
94             return null;
95         }
96         final ServiceFunctionPath serviceFunctionPath = findServiceFunctionPath(new SfcName(chainName));
97         if (serviceFunctionPath == null) {
98             LOG.error("Service function path not found for name {}", chainName);
99             return null;
100         }
101         return serviceFunctionPath;
102     }
103
104     static void resolveChainAction(final PeerEndpointWithPolicy peerEndpoint, final Sgt sourceSgt,
105                                    final Sgt destinationSgt, final Map<PolicyManagerImpl.ActionCase, Action> actionMap,
106                                    final String classMapName, PolicyWriter policyWriter) {
107         final List<Class> entries = new ArrayList<>();
108         final Action action = actionMap.get(PolicyManagerImpl.ActionCase.CHAIN);
109         final ServiceFunctionPath servicePath = ServiceChainingUtil.getServicePath(action.getParameterValue());
110         if (servicePath == null) {
111             return;
112         }
113         final TenantId tenantId = PolicyManagerUtil.getTenantId(peerEndpoint);
114         if (tenantId == null) {
115             return;
116         }
117         final RenderedServicePath renderedPath = ServiceChainingUtil.createRenderedPath(servicePath, tenantId);
118         // Create appropriate service path && remote forwarder
119         setSfcPart(renderedPath, policyWriter);
120
121         entries.add(PolicyManagerUtil.createPolicyEntry(classMapName, renderedPath, PolicyManagerImpl.ActionCase.CHAIN));
122         if (servicePath.isSymmetric()) {
123             // symmetric path is in opposite direction. Roles of renderer and peer endpoint will invert
124             RenderedServicePath symmetricPath = ServiceChainingUtil
125                     .createSymmetricRenderedPath(servicePath, renderedPath, tenantId);
126             final String oppositeClassMapName = PolicyManagerUtil.generateClassMapName(destinationSgt.getValue(), sourceSgt.getValue());
127             entries.add(PolicyManagerUtil.createPolicyEntry(oppositeClassMapName, symmetricPath, PolicyManagerImpl.ActionCase.CHAIN));
128         }
129         policyWriter.cache(entries);
130     }
131
132     static RenderedServicePath createRenderedPath(final ServiceFunctionPath sfp, final TenantId tenantId) {
133         RenderedServicePath renderedServicePath;
134         // Try to read existing RSP
135         final RspName rspName = new RspName(sfp.getName().getValue() + tenantId.getValue() + "-gbp-rsp");
136         renderedServicePath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
137         if (renderedServicePath != null) {
138             return renderedServicePath;
139         }
140         LOG.info("Rendered service path with name {} not found, creating a new one ..", rspName.getValue());
141         final CreateRenderedPathInput input = new CreateRenderedPathInputBuilder()
142                 .setParentServiceFunctionPath(sfp.getName().getValue())
143                 .setName(rspName.getValue())
144                 .setSymmetric(sfp.isSymmetric())
145                 .build();
146         renderedServicePath = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, input);
147         LOG.info("Rendered service path {} created", rspName.getValue());
148         return renderedServicePath;
149     }
150
151     static RenderedServicePath createSymmetricRenderedPath(final ServiceFunctionPath sfp, final RenderedServicePath rsp,
152                                                            final TenantId tenantId) {
153         RenderedServicePath reversedRenderedPath;
154         // Try to read existing RSP
155         final RspName rspName = new RspName(sfp.getName().getValue() + tenantId.getValue() + "-gbp-rsp-Reverse");
156         reversedRenderedPath = SfcProviderRenderedPathAPI.readRenderedServicePath(rspName);
157         if (reversedRenderedPath != null) {
158             return reversedRenderedPath;
159         }
160         LOG.info("Reversed rendered service path with name {} not found, creating a new one ..", rspName.getValue());
161         reversedRenderedPath = SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp);
162         LOG.info("Rendered service path {} created", rspName.getValue());
163         return reversedRenderedPath;
164     }
165
166     /**
167      * Method checks up, whether a {@link Local} Service Function Forwarder is present on device or not.
168      *
169      * @param mountpoint used to access specific device
170      * @return true if Local Forwarder is present, false otherwise
171      */
172     private static boolean checkLocalForwarderPresence(DataBroker mountpoint) {
173         InstanceIdentifier<Local> localSffIid = InstanceIdentifier.builder(Native.class)
174                 .child(ServiceChain.class)
175                 .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder.class)
176                 .child(Local.class).build();
177         ReadWriteTransaction rwt = mountpoint.newReadWriteTransaction();
178         CheckedFuture<Optional<Local>, ReadFailedException> submitFuture = rwt.read(LogicalDatastoreType.CONFIGURATION,
179                 localSffIid);
180         try {
181             Optional<Local> optionalLocalSff = submitFuture.checkedGet();
182             return optionalLocalSff.isPresent();
183         } catch (ReadFailedException e) {
184             LOG.warn("Read transaction failed to {} ", e);
185         } catch (Exception e) {
186             LOG.error("Failed to .. {}", e.getMessage());
187         }
188         return false;
189     }
190
191     /**
192      * Method checks up, if some {@link ServicePath} is present on device.
193      *
194      * @param mountpoint used to access specific device
195      * @return true if service chain does not exist, is null or does not contain any service path. False otherwise
196      */
197     public static boolean checkServicePathPresence(DataBroker mountpoint) {
198         InstanceIdentifier<ServiceChain> serviceChainIid = InstanceIdentifier.builder(Native.class)
199                 .child(ServiceChain.class).build();
200         ReadWriteTransaction rwt = mountpoint.newReadWriteTransaction();
201         CheckedFuture<Optional<ServiceChain>, ReadFailedException> submitFuture = rwt.read(LogicalDatastoreType.CONFIGURATION,
202                 serviceChainIid);
203         try {
204             Optional<ServiceChain> optionalServiceChain = submitFuture.checkedGet();
205             if (optionalServiceChain.isPresent()) {
206                 ServiceChain chain = optionalServiceChain.get();
207                 return chain == null || chain.getServicePath() == null || chain.getServicePath().isEmpty();
208             } else {
209                 return true;
210             }
211         } catch (ReadFailedException e) {
212             LOG.warn("Read transaction failed to {} ", e);
213         } catch (Exception e) {
214             LOG.error("Failed to .. {}", e.getMessage());
215         }
216         return false;
217     }
218
219     private static ServiceFunctionPath findServiceFunctionPath(final SfcName chainName) {
220         final ServiceFunctionPaths allPaths = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
221         for (ServiceFunctionPath serviceFunctionPath : allPaths.getServiceFunctionPath()) {
222             if (serviceFunctionPath.getServiceChainName().equals(chainName)) {
223                 return serviceFunctionPath;
224             }
225         }
226         return null;
227     }
228
229     private static void setSfcPart(final RenderedServicePath renderedServicePath, PolicyWriter policyWriter) {
230         if (renderedServicePath != null && renderedServicePath.getRenderedServicePathHop() != null &&
231                 !renderedServicePath.getRenderedServicePathHop().isEmpty()) {
232             final RenderedServicePathHop firstHop = renderedServicePath.getRenderedServicePathHop().get(0);
233             if (firstHop == null) {
234                 LOG.error("Rendered service path {} does not contain any hop", renderedServicePath.getName().getValue());
235                 return;
236             }
237             final SffName sffName = firstHop.getServiceFunctionForwarder();
238             final ServiceFunctionForwarder serviceFunctionForwarder = SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(sffName);
239             if (serviceFunctionForwarder == null) {
240                 LOG.error("Sff with name {} does not exist", sffName.getValue());
241                 return;
242             }
243             // Forwarders
244             //
245             // If classifier node is also forwarder, first entry in service path has to point to first service function
246             // (Local case)
247             //
248             // If first hop Sff is on different node, first service path entry has to point to that specific service
249             // forwarder (Remote case)
250
251             // Local case (only when does not exist)
252
253             if (!checkLocalForwarderPresence(policyWriter.getCurrentMountpoint())) {
254                 final LocalBuilder localSffBuilder = new LocalBuilder();
255                 localSffBuilder.setIp(new IpBuilder().setAddress(new Ipv4Address(policyWriter.getManagementIpAddress()))
256                         .build());
257                 policyWriter.cache(localSffBuilder.build());
258             } else {
259                 LOG.info("Local forwarder for node {} is already created", policyWriter.getCurrentNodeId());
260             }
261             // Set up choice. If remote, this choice is overwritten
262             ServiceTypeChoice serviceTypeChoice = functionTypeChoice(firstHop.getServiceFunctionName().getValue());
263             // Remote case
264             if (serviceFunctionForwarder.getIpMgmtAddress() == null
265                     || serviceFunctionForwarder.getIpMgmtAddress().getIpv4Address() == null) {
266                 LOG.error("Cannot create remote forwarder, SFF {} does not contain management ip address",
267                         sffName.getValue());
268                 return;
269             }
270             final String sffMgmtIpAddress = serviceFunctionForwarder.getIpMgmtAddress().getIpv4Address().getValue();
271             // If local SFF has the same ip as first hop sff, it's the same SFF; no need to create a remote one
272             if (!sffMgmtIpAddress.equals(policyWriter.getManagementIpAddress())) {
273                 final ServiceFfNameBuilder remoteSffBuilder = new ServiceFfNameBuilder();
274                 remoteSffBuilder.setName(sffName.getValue())
275                         .setKey(new ServiceFfNameKey(sffName.getValue()))
276                         .setIp(new IpBuilder().setAddress(new Ipv4Address(sffMgmtIpAddress)).build());
277                 policyWriter.cache(remoteSffBuilder.build());
278                 serviceTypeChoice = forwarderTypeChoice(sffName.getValue());
279             }
280
281             // Service chain
282             final List<Services> services = new ArrayList<>();
283             final ServicesBuilder servicesBuilder = new ServicesBuilder();
284             servicesBuilder.setServiceIndexId(renderedServicePath.getStartingIndex())
285                     .setServiceTypeChoice(serviceTypeChoice);
286             final List<ServicePath> servicePaths = new ArrayList<>();
287             final ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
288             servicePathBuilder.setKey(new ServicePathKey(renderedServicePath.getPathId()))
289                     .setServicePathId(renderedServicePath.getPathId())
290                     .setConfigServiceChainPathMode(new ConfigServiceChainPathModeBuilder()
291                             .setServiceIndex(new ServiceIndexBuilder()
292                                     .setServices(services).build()).build());
293             servicePaths.add(servicePathBuilder.build());
294             final ServiceChainBuilder chainBuilder = new ServiceChainBuilder();
295             chainBuilder.setServicePath(servicePaths);
296             final ServiceChain serviceChain = chainBuilder.build();
297             policyWriter.cache(serviceChain);
298         }
299     }
300
301     private static ServiceTypeChoice forwarderTypeChoice(final String forwarderName) {
302         final ServiceFunctionForwarderBuilder sffBuilder = new ServiceFunctionForwarderBuilder();
303         sffBuilder.setServiceFunctionForwarder(forwarderName);
304         return sffBuilder.build();
305     }
306
307     private static ServiceTypeChoice functionTypeChoice(final String functionName) {
308         final ServiceFunctionBuilder sfBuilder = new ServiceFunctionBuilder();
309         sfBuilder.setServiceFunction(functionName);
310         return sfBuilder.build();
311     }
312
313 }