Fixup Augmentable and Identifiable methods changing
[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.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 @Singleton
70 public class GeniusProvider {
71     private static final Logger LOG = LoggerFactory.getLogger(GeniusProvider.class);
72     public static final String OPTION_KEY_EXTS = "exts";
73     public static final String OPTION_VALUE_EXTS_GPE = "gpe";
74
75     private final DataBroker dataBroker;
76     private final IInterfaceManager interfaceMgr;
77     private final OdlInterfaceRpcService interfaceManagerRpcService;
78
79     @Inject
80     public GeniusProvider(final DataBroker dataBroker, final RpcProviderRegistry rpcProviderRegistry,
81             final IInterfaceManager interfaceMgr) {
82         this.dataBroker = dataBroker;
83         this.interfaceMgr = interfaceMgr;
84         this.interfaceManagerRpcService = rpcProviderRegistry.getRpcService(OdlInterfaceRpcService.class);
85     }
86
87     // Package local constructor used by UT for simplification
88     GeniusProvider(final DataBroker dataBroker, final OdlInterfaceRpcService interfaceManagerRpcService,
89             final IInterfaceManager interfaceMgr) {
90         this.dataBroker = dataBroker;
91         this.interfaceMgr = interfaceMgr;
92         this.interfaceManagerRpcService = interfaceManagerRpcService;
93     }
94
95     public void bindPortOnIngressClassifier(String interfaceName) {
96         bindService(
97                 getBindServiceId(NwConstants.SFC_CLASSIFIER_INDEX, interfaceName, true),
98                 NwConstants.SFC_CLASSIFIER_INDEX,
99                 NwConstants.SFC_CLASSIFIER_SERVICE_NAME,
100                 NwConstants.SFC_SERVICE_INDEX,
101                 NwConstants.INGRESS_SFC_CLASSIFIER_FILTER_TABLE,
102                 OpenFlow13Provider.INGRESS_CLASSIFIER_FILTER_COOKIE);
103     }
104
105     public void bindPortOnEgressClassifier(String interfaceName, String destinationIp) {
106         bindService(
107                 getBindServiceId(NwConstants.EGRESS_SFC_CLASSIFIER_SERVICE_INDEX, interfaceName, false),
108                 NwConstants.EGRESS_SFC_CLASSIFIER_SERVICE_INDEX,
109                 NwConstants.EGRESS_SFC_CLASSIFIER_SERVICE_NAME,
110                 NwConstants.EGRESS_SFC_CLASSIFIER_SERVICE_INDEX,
111                 NwConstants.EGRESS_SFC_CLASSIFIER_FILTER_TABLE,
112                 OpenFlow13Provider.EGRESS_CLASSIFIER_FILTER_COOKIE,
113                 Collections.singletonList(
114                         createServiceBindingActionNxLoadReg0(
115                                 InetAddresses.coerceToInteger(
116                                             InetAddresses.forString(destinationIp)) & 0xffffffffL,
117                                 0)
118                 ));
119     }
120
121     public void unbindPortOnIngressClassifier(String interfaceName) {
122         unbindService(getBindServiceId(NwConstants.SFC_CLASSIFIER_INDEX, interfaceName, true));
123     }
124
125     public void unbindPortOnEgressClassifier(String interfaceName) {
126         unbindService(getBindServiceId(NwConstants.EGRESS_SFC_CLASSIFIER_SERVICE_INDEX, interfaceName, false));
127     }
128
129     public Optional<NodeId> getNodeIdFromLogicalInterface(String logicalInterface) {
130         Optional<DpnIdType> dpnId = getDpnIdFromInterfaceName(logicalInterface);
131
132         if (!dpnId.isPresent()) {
133             LOG.warn("getNodeIdFromLogicalInterface empty dpnId for logicalInterface [{}]", logicalInterface);
134             return Optional.empty();
135         }
136
137         return getNodeIdFromDpnId(dpnId.get());
138     }
139
140     public Optional<NodeId> getNodeIdFromDpnId(DpnIdType dpnId) {
141         if (dpnId == null) {
142             return Optional.empty();
143         }
144
145         if (dpnId.getValue() == null) {
146             return Optional.empty();
147         }
148
149         return Optional.of(new NodeId("openflow:" + dpnId.getValue()));
150     }
151
152     // TODO Should better use the Genius InterfaceManager to avoid duplicate code
153     //      https://bugs.opendaylight.org/show_bug.cgi?id=8127
154     public Optional<String> getIpFromDpnId(DpnIdType dpnid) {
155         GetEndpointIpForDpnInputBuilder builder = new GetEndpointIpForDpnInputBuilder();
156         builder.setDpid(dpnid.getValue());
157         GetEndpointIpForDpnInput input = builder.build();
158
159         if (interfaceManagerRpcService == null) {
160             LOG.error("getIpFromDpnId({}) failed (service couldn't be retrieved)", input);
161             return Optional.empty();
162         }
163
164         try {
165             LOG.debug("getIpFromDpnId: invoking rpc");
166             RpcResult<GetEndpointIpForDpnOutput> output = interfaceManagerRpcService.getEndpointIpForDpn(input).get();
167             if (!output.isSuccessful()) {
168                 LOG.error("getIpFromDpnId({}) failed: {}", input, output);
169                 return Optional.empty();
170             }
171             LOG.debug("getDpnIdFromInterfaceName({}) succeeded: {}", input, output);
172             List<IpAddress> localIps = output.getResult().getLocalIps();
173
174             // TODO need to figure out why it returns a list, using first entry for now
175             return Optional.ofNullable(localIps)
176                     .filter(ipAddresses -> !ipAddresses.isEmpty())
177                     .map(ipAddresses -> ipAddresses.get(0))
178                     .map(IpAddress::getIpv4Address)
179                     .map(Ipv4Address::getValue);
180         } catch (InterruptedException | ExecutionException e) {
181             LOG.error("getDpnIdFromInterfaceName failed to retrieve target interface name: ", e);
182         }
183
184         return Optional.empty();
185     }
186
187     public Optional<DpnIdType> getDpnIdFromInterfaceName(String interfaceName) {
188         LOG.debug("getDpnIdFromInterfaceName: starting (logical interface={})", interfaceName);
189         GetDpidFromInterfaceInputBuilder builder = new GetDpidFromInterfaceInputBuilder();
190         builder.setIntfName(interfaceName);
191         GetDpidFromInterfaceInput input = builder.build();
192
193         if (interfaceManagerRpcService == null) {
194             LOG.error("getDpnIdFromInterfaceName({}) failed (service couldn't be retrieved)", input);
195             return Optional.empty();
196         }
197
198         try {
199             LOG.debug("getDpnIdFromInterfaceName: invoking rpc");
200             RpcResult<GetDpidFromInterfaceOutput> output = interfaceManagerRpcService.getDpidFromInterface(input).get();
201             if (!output.isSuccessful()) {
202                 LOG.error("getDpnIdFromInterfaceName({}) failed: {}", input, output);
203                 return Optional.empty();
204             }
205
206             BigInteger dpnId = output.getResult().getDpid();
207             if (dpnId == null) {
208                 return Optional.empty();
209             }
210             LOG.debug("getDpnIdFromInterfaceName({}) succeeded: {}", input, output);
211
212             return Optional.of(new DpnIdType(dpnId));
213         } catch (InterruptedException | ExecutionException e) {
214             LOG.error("getDpnIdFromInterfaceName failed to retrieve target interface name: ", e);
215         }
216
217         return Optional.empty();
218     }
219
220     public Optional<String> getNodeConnectorIdFromInterfaceName(String interfaceName) {
221         LOG.debug("getDpnIdFromInterfaceName: starting (logical interface={})", interfaceName);
222         GetNodeconnectorIdFromInterfaceInputBuilder builder = new GetNodeconnectorIdFromInterfaceInputBuilder();
223         builder.setIntfName(interfaceName);
224         GetNodeconnectorIdFromInterfaceInput input = builder.build();
225
226         if (interfaceManagerRpcService == null) {
227             LOG.error("getNodeConnectorIdFromInterfaceName({}) failed (service couldn't be retrieved)", input);
228             return Optional.empty();
229         }
230
231         try {
232             LOG.debug("getNodeConnectorIdFromInterfaceName: invoking rpc");
233             RpcResult<GetNodeconnectorIdFromInterfaceOutput> output =
234                     interfaceManagerRpcService.getNodeconnectorIdFromInterface(input).get();
235             if (!output.isSuccessful()) {
236                 LOG.error("getNodeConnectorIdFromInterfaceName({}) failed: {}", input, output);
237                 return Optional.empty();
238             }
239             NodeConnectorId nodeConnId = output.getResult().getNodeconnectorId();
240             if (nodeConnId == null) {
241                 return Optional.empty();
242             }
243             LOG.debug("getNodeConnectorIdFromInterfaceName({}) succeeded: {}", input, output);
244
245             return Optional.ofNullable(nodeConnId.getValue());
246         } catch (InterruptedException | ExecutionException e) {
247             LOG.error("getNodeConnectorIdFromInterfaceName failed to retrieve target interface name: ", e);
248         }
249
250         return Optional.empty();
251     }
252
253     public Optional<Long> getEgressVxlanPortForNode(BigInteger dpnId) {
254         List<OvsdbTerminationPointAugmentation> tpList = interfaceMgr.getTunnelPortsOnBridge(dpnId);
255         if (tpList == null) {
256             // Most likely the bridge doesnt exist for this dpnId
257             LOG.warn("getEgressVxlanPortForNode Tunnel Port TerminationPoint list not available for dpnId [{}]",
258                     dpnId);
259             return Optional.empty();
260         }
261
262         for (OvsdbTerminationPointAugmentation tp : tpList) {
263             if (tp == null) {
264                 // Technically we should never have a list with NULL entries, but
265                 // in a preliminary version of interfaceMgr.getTunnelPortsOnBridge()
266                 // we were getting a list where all termination point entries were
267                 // null. Leaving this check for now for protection.
268                 LOG.error("getEgressVxlanPortForNode received a NULL termination point from tpList on dpnId [{}]",
269                         dpnId);
270                 continue;
271             }
272
273             Class<? extends InterfaceTypeBase> ifType = tp.getInterfaceType();
274             if (ifType.equals(InterfaceTypeVxlan.class)) {
275                 List<Options> tpOptions = tp.getOptions();
276                 for (Options tpOption : tpOptions) {
277                     // From the VXLAN Tunnels, we want the one with the GPE option set
278                     if (tpOption.key().getOption().equals(OPTION_KEY_EXTS)) {
279                         if (tpOption.getValue().equals(OPTION_VALUE_EXTS_GPE)) {
280                             return Optional.ofNullable(tp.getOfport());
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 }