2 * Copyright © 2017 Ericsson, Inc. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.sfc.classifier.providers;
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;
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";
76 private final DataBroker dataBroker;
77 private final IInterfaceManager interfaceMgr;
78 private final OdlInterfaceRpcService interfaceManagerRpcService;
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);
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;
96 public void bindPortOnIngressClassifier(String interfaceName) {
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);
106 public void bindPortOnEgressClassifier(String interfaceName, String destinationIp) {
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,
122 public void unbindPortOnIngressClassifier(String interfaceName) {
123 unbindService(getBindServiceId(NwConstants.SFC_CLASSIFIER_INDEX, interfaceName, true));
126 public void unbindPortOnEgressClassifier(String interfaceName) {
127 unbindService(getBindServiceId(NwConstants.EGRESS_SFC_CLASSIFIER_SERVICE_INDEX, interfaceName, false));
130 public Optional<NodeId> getNodeIdFromLogicalInterface(String logicalInterface) {
131 Optional<DpnIdType> dpnId = getDpnIdFromInterfaceName(logicalInterface);
133 if (!dpnId.isPresent()) {
134 LOG.warn("getNodeIdFromLogicalInterface empty dpnId for logicalInterface [{}]", logicalInterface);
135 return Optional.empty();
138 return getNodeIdFromDpnId(dpnId.get());
141 public Optional<NodeId> getNodeIdFromDpnId(DpnIdType dpnId) {
143 return Optional.empty();
146 if (dpnId.getValue() == null) {
147 return Optional.empty();
150 return Optional.of(new NodeId("openflow:" + dpnId.getValue()));
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();
160 if (interfaceManagerRpcService == null) {
161 LOG.error("getIpFromDpnId({}) failed (service couldn't be retrieved)", input);
162 return Optional.empty();
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();
172 LOG.debug("getDpnIdFromInterfaceName({}) succeeded: {}", input, output);
173 List<IpAddress> localIps = output.getResult().getLocalIps();
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);
185 return Optional.empty();
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();
194 if (interfaceManagerRpcService == null) {
195 LOG.error("getDpnIdFromInterfaceName({}) failed (service couldn't be retrieved)", input);
196 return Optional.empty();
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();
207 BigInteger dpnId = output.getResult().getDpid() != null ? output.getResult().getDpid().toJava() : null;
209 return Optional.empty();
211 LOG.debug("getDpnIdFromInterfaceName({}) succeeded: {}", input, output);
213 return Optional.of(new DpnIdType(dpnId));
214 } catch (InterruptedException | ExecutionException e) {
215 LOG.error("getDpnIdFromInterfaceName failed to retrieve target interface name: ", e);
218 return Optional.empty();
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();
227 if (interfaceManagerRpcService == null) {
228 LOG.error("getNodeConnectorIdFromInterfaceName({}) failed (service couldn't be retrieved)", input);
229 return Optional.empty();
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();
240 NodeConnectorId nodeConnId = output.getResult().getNodeconnectorId();
241 if (nodeConnId == null) {
242 return Optional.empty();
244 LOG.debug("getNodeConnectorIdFromInterfaceName({}) succeeded: {}", input, output);
246 return Optional.ofNullable(nodeConnId.getValue());
247 } catch (InterruptedException | ExecutionException e) {
248 LOG.error("getNodeConnectorIdFromInterfaceName failed to retrieve target interface name: ", e);
251 return Optional.empty();
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 [{}]",
260 return Optional.empty();
263 for (OvsdbTerminationPointAugmentation tp : tpList) {
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 [{}]",
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());
287 LOG.warn("getEgressVxlanPortForNode no Vxgpe tunnel ports available for dpnId [{}]", dpnId);
289 return Optional.empty();
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();
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();
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);
312 return Collections.emptyList();
315 public InstanceIdentifier<BoundServices> getBindServiceId(short serviceId, String interfaceName,
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();
327 private void bindService(InstanceIdentifier<BoundServices> id, short serviceId, String serviceName,
328 int servicePriority, short serviceDestTable, BigInteger serviceTableCookie) {
336 Collections.emptyList());
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())
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);
356 MDSALUtil.syncWrite(this.dataBroker, LogicalDatastoreType.CONFIGURATION, id, boundServices);
359 private void unbindService(InstanceIdentifier<BoundServices> id) {
360 MDSALUtil.syncDelete(this.dataBroker, LogicalDatastoreType.CONFIGURATION, id);
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);
371 public static Action createServiceBindingActionNxLoadReg0(long value, int order) {
372 return OpenFlow13Utils.createAction(
373 new ServiceBindingNxActionRegLoadApplyActionsCaseBuilder()
374 .setNxRegLoad(OpenFlow13Utils.createNxLoadReg0(value))