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