542e04815446450c53b6453d3981cba59e3f39fa
[netvirt.git] / vpnservice / sfc / classifier / impl / src / main / java / org / opendaylight / netvirt / sfc / classifier / providers / GeniusProvider.java
1 /*
2  * Copyright © 2017 Ericsson, 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.netvirt.sfc.classifier.providers;
10
11 import com.google.common.net.InetAddresses;
12 import java.math.BigInteger;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.Optional;
16 import java.util.concurrent.ExecutionException;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
22 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
23 import org.opendaylight.genius.mdsalutil.MDSALUtil;
24 import org.opendaylight.genius.mdsalutil.NwConstants;
25 import org.opendaylight.netvirt.sfc.classifier.utils.OpenFlow13Utils;
26 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.sff.logical.rev160620.DpnIdType;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfTunnel;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInput;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInputBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceOutput;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpnInterfaceListInput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpnInterfaceListInputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpnInterfaceListOutput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEndpointIpForDpnInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEndpointIpForDpnInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEndpointIpForDpnOutput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetNodeconnectorIdFromInterfaceInput;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetNodeconnectorIdFromInterfaceInputBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetNodeconnectorIdFromInterfaceOutput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeEgress;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflow;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.bound.services.instruction.instruction.apply.actions._case.apply.actions.action.action.ServiceBindingNxActionRegLoadApplyActionsCaseBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.InterfaceTypeBase;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.InterfaceTypeVxlan;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.Options;
63 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
64 import org.opendaylight.yangtools.yang.common.RpcResult;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68 @Singleton
69 public class GeniusProvider {
70
71     private final DataBroker dataBroker;
72     private final IInterfaceManager interfaceMgr;
73     private final OdlInterfaceRpcService interfaceManagerRpcService;
74     private static final Logger LOG = LoggerFactory.getLogger(GeniusProvider.class);
75     public static final String OPTION_KEY_EXTS = "exts";
76     public static final String OPTION_VALUE_EXTS_GPE = "gpe";
77
78     @Inject
79     public GeniusProvider(final DataBroker dataBroker, final RpcProviderRegistry rpcProviderRegistry,
80             final IInterfaceManager interfaceMgr) {
81         this.dataBroker = dataBroker;
82         this.interfaceMgr = interfaceMgr;
83         this.interfaceManagerRpcService = rpcProviderRegistry.getRpcService(OdlInterfaceRpcService.class);
84     }
85
86     // Package local constructor used by UT for simplification
87     GeniusProvider(final DataBroker dataBroker, final OdlInterfaceRpcService interfaceManagerRpcService,
88             final IInterfaceManager interfaceMgr) {
89         this.dataBroker = dataBroker;
90         this.interfaceMgr = interfaceMgr;
91         this.interfaceManagerRpcService = interfaceManagerRpcService;
92     }
93
94     public void bindPortOnIngressClassifier(String interfaceName) {
95         bindService(
96                 getBindServiceId(NwConstants.SFC_CLASSIFIER_INDEX, interfaceName, true),
97                 NwConstants.SFC_CLASSIFIER_INDEX,
98                 NwConstants.SFC_CLASSIFIER_SERVICE_NAME,
99                 NwConstants.SFC_SERVICE_INDEX,
100                 NwConstants.INGRESS_SFC_CLASSIFIER_FILTER_TABLE,
101                 OpenFlow13Provider.INGRESS_CLASSIFIER_FILTER_COOKIE);
102     }
103
104     public void bindPortOnEgressClassifier(String interfaceName, String destinationIp) {
105         bindService(
106                 getBindServiceId(NwConstants.EGRESS_SFC_CLASSIFIER_SERVICE_INDEX, interfaceName, false),
107                 NwConstants.EGRESS_SFC_CLASSIFIER_SERVICE_INDEX,
108                 NwConstants.EGRESS_SFC_CLASSIFIER_SERVICE_NAME,
109                 NwConstants.EGRESS_SFC_CLASSIFIER_SERVICE_INDEX,
110                 NwConstants.EGRESS_SFC_CLASSIFIER_FILTER_TABLE,
111                 OpenFlow13Provider.EGRESS_CLASSIFIER_FILTER_COOKIE,
112                 Collections.singletonList(
113                         createServiceBindingActionNxLoadReg0(
114                                 InetAddresses.coerceToInteger(
115                                             InetAddresses.forString(destinationIp)) & 0xffffffffL,
116                                 0)
117                 ));
118     }
119
120     public void unbindPortOnIngressClassifier(String interfaceName) {
121         unbindService(getBindServiceId(NwConstants.SFC_CLASSIFIER_INDEX, interfaceName, true));
122     }
123
124     public void unbindPortOnEgressClassifier(String interfaceName) {
125         unbindService(getBindServiceId(NwConstants.EGRESS_SFC_CLASSIFIER_SERVICE_INDEX, interfaceName, false));
126     }
127
128     public Optional<NodeId> getNodeIdFromLogicalInterface(String logicalInterface) {
129         Optional<DpnIdType> dpnId = getDpnIdFromInterfaceName(logicalInterface);
130
131         if (!dpnId.isPresent()) {
132             LOG.warn("getNodeIdFromLogicalInterface empty dpnId for logicalInterface [{}]", logicalInterface);
133             return Optional.empty();
134         }
135
136         return getNodeIdFromDpnId(dpnId.get());
137     }
138
139     public Optional<NodeId> getNodeIdFromDpnId(DpnIdType dpnId) {
140         if (dpnId == null) {
141             return Optional.empty();
142         }
143
144         if (dpnId.getValue() == null) {
145             return Optional.empty();
146         }
147
148         return Optional.of(new NodeId("openflow:" + dpnId.getValue()));
149     }
150
151     public Optional<String> getIpFromInterfaceName(String interfaceName) {
152         Optional<DpnIdType> dpnId = getDpnIdFromInterfaceName(interfaceName);
153         if (!dpnId.isPresent()) {
154             LOG.warn("getIpFromInterfaceName empty dpnId for interfaceName [{}]", interfaceName);
155             return Optional.empty();
156         }
157
158         List<IpAddress> ipList = getIpFromDpnId(dpnId.get());
159         if (ipList.isEmpty()) {
160             LOG.warn("getIpFromInterfaceName empty ipList for interfaceName [{}]", interfaceName);
161             return Optional.empty();
162         }
163
164         // TODO need to figure out why it returns a list, using first entry for now
165         return Optional.ofNullable(ipList.get(0).getIpv4Address().getValue());
166     }
167
168     // TODO Should better use the Genius InterfaceManager to avoid duplicate code
169     //      https://bugs.opendaylight.org/show_bug.cgi?id=8127
170     public List<IpAddress> getIpFromDpnId(DpnIdType dpnid) {
171         GetEndpointIpForDpnInputBuilder builder = new GetEndpointIpForDpnInputBuilder();
172         builder.setDpid(dpnid.getValue());
173         GetEndpointIpForDpnInput input = builder.build();
174
175         if (interfaceManagerRpcService == null) {
176             LOG.error("getIpFromDpnId({}) failed (service couldn't be retrieved)", input);
177             return Collections.emptyList();
178         }
179
180         try {
181             LOG.debug("getIpFromDpnId: invoking rpc");
182             RpcResult<GetEndpointIpForDpnOutput> output = interfaceManagerRpcService.getEndpointIpForDpn(input).get();
183             if (!output.isSuccessful()) {
184                 LOG.error("getIpFromDpnId({}) failed: {}", input, output);
185                 return Collections.emptyList();
186             }
187             List<IpAddress> localIps = output.getResult().getLocalIps();
188             LOG.debug("getDpnIdFromInterfaceName({}) succeeded: {}", input, output);
189             if (localIps != null) {
190                 return localIps;
191             }
192         } catch (InterruptedException | ExecutionException e) {
193             LOG.error("getDpnIdFromInterfaceName failed to retrieve target interface name: ", e);
194         }
195
196         return Collections.emptyList();
197     }
198
199     public Optional<DpnIdType> getDpnIdFromInterfaceName(String interfaceName) {
200         LOG.debug("getDpnIdFromInterfaceName: starting (logical interface={})", interfaceName);
201         GetDpidFromInterfaceInputBuilder builder = new GetDpidFromInterfaceInputBuilder();
202         builder.setIntfName(interfaceName);
203         GetDpidFromInterfaceInput input = builder.build();
204
205         if (interfaceManagerRpcService == null) {
206             LOG.error("getDpnIdFromInterfaceName({}) failed (service couldn't be retrieved)", input);
207             return Optional.empty();
208         }
209
210         try {
211             LOG.debug("getDpnIdFromInterfaceName: invoking rpc");
212             RpcResult<GetDpidFromInterfaceOutput> output = interfaceManagerRpcService.getDpidFromInterface(input).get();
213             if (!output.isSuccessful()) {
214                 LOG.error("getDpnIdFromInterfaceName({}) failed: {}", input, output);
215                 return Optional.empty();
216             }
217
218             BigInteger dpnId = output.getResult().getDpid();
219             if (dpnId == null) {
220                 return Optional.empty();
221             }
222             LOG.debug("getDpnIdFromInterfaceName({}) succeeded: {}", input, output);
223
224             return Optional.of(new DpnIdType(dpnId));
225         } catch (InterruptedException | ExecutionException e) {
226             LOG.error("getDpnIdFromInterfaceName failed to retrieve target interface name: ", e);
227         }
228
229         return Optional.empty();
230     }
231
232     public Optional<String> getNodeConnectorIdFromInterfaceName(String interfaceName) {
233         LOG.debug("getDpnIdFromInterfaceName: starting (logical interface={})", interfaceName);
234         GetNodeconnectorIdFromInterfaceInputBuilder builder = new GetNodeconnectorIdFromInterfaceInputBuilder();
235         builder.setIntfName(interfaceName);
236         GetNodeconnectorIdFromInterfaceInput input = builder.build();
237
238         if (interfaceManagerRpcService == null) {
239             LOG.error("getNodeConnectorIdFromInterfaceName({}) failed (service couldn't be retrieved)", input);
240             return Optional.empty();
241         }
242
243         try {
244             LOG.debug("getNodeConnectorIdFromInterfaceName: invoking rpc");
245             RpcResult<GetNodeconnectorIdFromInterfaceOutput> output =
246                     interfaceManagerRpcService.getNodeconnectorIdFromInterface(input).get();
247             if (!output.isSuccessful()) {
248                 LOG.error("getNodeConnectorIdFromInterfaceName({}) failed: {}", input, output);
249                 return Optional.empty();
250             }
251             NodeConnectorId nodeConnId = output.getResult().getNodeconnectorId();
252             if (nodeConnId == null) {
253                 return Optional.empty();
254             }
255             LOG.debug("getNodeConnectorIdFromInterfaceName({}) succeeded: {}", input, output);
256
257             return Optional.ofNullable(nodeConnId.getValue());
258         } catch (InterruptedException | ExecutionException e) {
259             LOG.error("getNodeConnectorIdFromInterfaceName failed to retrieve target interface name: ", e);
260         }
261
262         return Optional.empty();
263     }
264
265     public Optional<Long> getEgressVxlanPortForNode(BigInteger dpnId) {
266         List<OvsdbTerminationPointAugmentation> tpList = interfaceMgr.getTunnelPortsOnBridge(dpnId);
267         if (tpList == null) {
268             // Most likely the bridge doesnt exist for this dpnId
269             LOG.warn("getEgressVxlanPortForNode Tunnel Port TerminationPoint list not available for dpnId [{}]",
270                     dpnId);
271             return Optional.empty();
272         }
273
274         for (OvsdbTerminationPointAugmentation tp : tpList) {
275             if (tp == null) {
276                 // Technically we should never have a list with NULL entries, but
277                 // in a preliminary version of interfaceMgr.getTunnelPortsOnBridge()
278                 // we were getting a list where all termination point entries were
279                 // null. Leaving this check for now for protection.
280                 LOG.error("getEgressVxlanPortForNode received a NULL termination point from tpList on dpnId [{}]",
281                         dpnId);
282                 continue;
283             }
284
285             Class<? extends InterfaceTypeBase> ifType = tp.getInterfaceType();
286             if (ifType.equals(InterfaceTypeVxlan.class)) {
287                 List<Options> tpOptions = tp.getOptions();
288                 for (Options tpOption : tpOptions) {
289                     // From the VXLAN Tunnels, we want the one with the GPE option set
290                     if (tpOption.getKey().getOption().equals(OPTION_KEY_EXTS)) {
291                         if (tpOption.getValue().equals(OPTION_VALUE_EXTS_GPE)) {
292                             return Optional.ofNullable(tp.getOfport());
293                         }
294                     }
295                 }
296             }
297         }
298
299         LOG.warn("getEgressVxlanPortForNode no Vxgpe tunnel ports available for dpnId [{}]", dpnId);
300
301         return Optional.empty();
302     }
303
304     public List<String> getInterfacesFromNode(NodeId nodeId) {
305         // getPortsOnBridge() only returns Tunnel ports, so instead using getDpnInterfaceList.
306         GetDpnInterfaceListInputBuilder inputBuilder = new GetDpnInterfaceListInputBuilder();
307         inputBuilder.setDpid(BigInteger.valueOf(Long.valueOf(nodeId.getValue().split(":")[1])));
308         GetDpnInterfaceListInput input = inputBuilder.build();
309
310         try {
311             LOG.debug("getInterfacesFromNode: invoking rpc");
312             RpcResult<GetDpnInterfaceListOutput> output =
313                     interfaceManagerRpcService.getDpnInterfaceList(input).get();
314             if (!output.isSuccessful()) {
315                 LOG.error("getInterfacesFromNode({}) failed: {}", input, output);
316                 return Collections.emptyList();
317             }
318             LOG.debug("getInterfacesFromNode({}) succeeded: {}", input, output);
319             return output.getResult().getInterfacesList();
320         } catch (InterruptedException | ExecutionException e) {
321             LOG.error("getInterfacesFromNode failed to retrieve target interface name: ", e);
322         }
323
324         return Collections.emptyList();
325     }
326
327     public InstanceIdentifier<BoundServices> getBindServiceId(short serviceId, String interfaceName,
328             boolean isIngress) {
329         ServicesInfoKey servicesInfoKey = isIngress
330                 ? new ServicesInfoKey(interfaceName, ServiceModeIngress.class) :
331                   new ServicesInfoKey(interfaceName, ServiceModeEgress.class);
332         InstanceIdentifier<BoundServices> id = InstanceIdentifier.builder(ServiceBindings.class)
333                 .child(ServicesInfo.class, servicesInfoKey)
334                 .child(BoundServices.class, new BoundServicesKey(serviceId)).build();
335
336         return id;
337     }
338
339     private void bindService(InstanceIdentifier<BoundServices> id, short serviceId, String serviceName,
340                              int servicePriority, short serviceDestTable, BigInteger serviceTableCookie) {
341         bindService(
342                 id,
343                 serviceId,
344                 serviceName,
345                 servicePriority,
346                 serviceDestTable,
347                 serviceTableCookie,
348                 Collections.emptyList());
349     }
350
351     private void bindService(InstanceIdentifier<BoundServices> id, short serviceId, String serviceName,
352             int servicePriority, short serviceDestTable, BigInteger serviceTableCookie, List<Action> extraActions) {
353         InstructionsBuilder isb = extraActions.isEmpty()
354                 ? new InstructionsBuilder()
355                 : OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(extraActions);
356         isb = OpenFlow13Utils.appendGotoTableInstruction(isb, serviceDestTable);
357         StypeOpenflow stypeOpenflow = new StypeOpenflowBuilder()
358                 .setFlowCookie(serviceTableCookie)
359                 .setFlowPriority(servicePriority)
360                 .setInstruction(isb.build().getInstruction())
361                 .build();
362         BoundServices boundServices = new BoundServicesBuilder().setServiceName(serviceName)
363                 .setServicePriority(serviceId).setServiceType(ServiceTypeFlowBased.class)
364                 .addAugmentation(StypeOpenflow.class, stypeOpenflow).build();
365         LOG.info("Binding Service ID [{}] name [{}] priority [{}] table [{}] cookie [{}] extraActions [{}]",
366                 serviceId, serviceName, servicePriority, serviceDestTable, serviceTableCookie, extraActions);
367
368         MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION, id, boundServices);
369     }
370
371     private void unbindService(InstanceIdentifier<BoundServices> id) {
372         MDSALUtil.syncDelete(this.dataBroker, LogicalDatastoreType.CONFIGURATION, id);
373     }
374
375     public Optional<String> getRemoteIpAddress(String interfaceName) {
376         return Optional.ofNullable(interfaceMgr.getInterfaceInfoFromConfigDataStore(interfaceName))
377                 .map(anInterface -> anInterface.getAugmentation(IfTunnel.class))
378                 .map(IfTunnel::getTunnelDestination)
379                 .map(IpAddress::getIpv4Address)
380                 .map(Ipv4Address::getValue);
381     }
382
383     public static Action createServiceBindingActionNxLoadReg0(long value, int order) {
384         return OpenFlow13Utils.createAction(
385                 new ServiceBindingNxActionRegLoadApplyActionsCaseBuilder()
386                         .setNxRegLoad(OpenFlow13Utils.createNxLoadReg0(value))
387                         .build(),
388                 order);
389     }
390 }