Migrate ListenableFutures.addErrorLogging() users
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronSubnetGwMacResolver.java
1 /*
2  * Copyright (c) 2016 Hewlett Packard Enterprise, Co. 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 package org.opendaylight.netvirt.neutronvpn;
9
10 import com.google.common.util.concurrent.JdkFutureAdapters;
11 import com.google.common.util.concurrent.ThreadFactoryBuilder;
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.Executors;
18 import java.util.concurrent.Future;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.TimeUnit;
21 import javax.annotation.PostConstruct;
22 import javax.annotation.PreDestroy;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.opendaylight.genius.arputil.api.ArpConstants;
27 import org.opendaylight.genius.mdsalutil.NWUtil;
28 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
29 import org.opendaylight.netvirt.elanmanager.api.IElanService;
30 import org.opendaylight.netvirt.vpnmanager.api.ICentralizedSwitchProvider;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilService;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddress;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddressBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.Ipv6NdUtilService;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationInput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationInputBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationOutput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.router.ExternalGatewayInfo;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIpsKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
51 import org.opendaylight.yangtools.yang.common.RpcResult;
52 import org.opendaylight.yangtools.yang.common.Uint64;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 @Singleton
57 public class NeutronSubnetGwMacResolver {
58     private static final Logger LOG = LoggerFactory.getLogger(NeutronSubnetGwMacResolver.class);
59     private static final long L3_INSTALL_DELAY_MILLIS = 5000;
60
61     private final OdlArputilService arpUtilService;
62     private final IElanService elanService;
63     private final ICentralizedSwitchProvider cswitchProvider;
64     private final NeutronvpnUtils neutronvpnUtils;
65     private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(
66             new ThreadFactoryBuilder().setNameFormat("Gw-Mac-Res").build());
67     private final Ipv6NdUtilService ipv6NdUtilService;
68
69     @Inject
70     public NeutronSubnetGwMacResolver(final OdlArputilService arputilService, final IElanService elanService,
71             final ICentralizedSwitchProvider cswitchProvider, final NeutronvpnUtils neutronvpnUtils,
72             final Ipv6NdUtilService ipv6NdUtilService) {
73         this.arpUtilService = arputilService;
74         this.elanService = elanService;
75         this.cswitchProvider = cswitchProvider;
76         this.neutronvpnUtils = neutronvpnUtils;
77         this.ipv6NdUtilService = ipv6NdUtilService;
78     }
79
80     // TODO Clean up the exception handling
81     @SuppressWarnings("checkstyle:IllegalCatch")
82     @PostConstruct
83     public void init() {
84         LOG.info("{} init", getClass().getSimpleName());
85
86         executorService.scheduleAtFixedRate(() -> {
87             try {
88                 sendArpRequestsToExtGateways();
89             } catch (Exception e) {
90                 LOG.warn("Failed to send ARP request to GW ips", e);
91             }
92         }, 0, ArpConstants.ARP_CACHE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
93
94     }
95
96     @PreDestroy
97     public void close() {
98         executorService.shutdownNow();
99     }
100
101     public void sendArpRequestsToExtGateways(Router router) {
102         // Let the FIB flows a chance to be installed
103         // otherwise the ARP response will be routed straight to L2
104         // and bypasses L3 arp cache
105         executorService.schedule(() -> sendArpRequestsToExtGatewayTask(router), L3_INSTALL_DELAY_MILLIS,
106                 TimeUnit.MILLISECONDS);
107     }
108
109     public void sendArpRequestsToExtGateways() {
110         LOG.trace("Sending ARP requests to external gateways");
111         for (Router router : neutronvpnUtils.getAllRouters()) {
112             sendArpRequestsToExtGateways(router);
113         }
114     }
115
116     private void sendArpRequestsToExtGatewayTask(Router router) {
117         LOG.trace("Send ARP requests to external GW for router {}", router);
118         Port extPort = getRouterExtGatewayPort(router);
119         if (extPort == null) {
120             LOG.trace("External GW port for router {} is missing", router.getUuid().getValue());
121             return;
122         }
123
124         String extInterface = getExternalInterface(router);
125         if (extInterface == null) {
126             LOG.trace("No external interface defined for router {}", router.getUuid().getValue());
127             return;
128         }
129
130         Map<FixedIpsKey, FixedIps> keyFixedIpsMap = extPort.getFixedIps();
131         if (keyFixedIpsMap == null || keyFixedIpsMap.isEmpty()) {
132             LOG.trace("External GW port {} for router {} has no fixed IPs", extPort.getUuid().getValue(),
133                     router.getUuid().getValue());
134             return;
135         }
136
137         MacAddress macAddress = extPort.getMacAddress();
138         if (macAddress == null) {
139             LOG.trace("External GW port {} for router {} has no mac address", extPort.getUuid().getValue(),
140                     router.getUuid().getValue());
141             return;
142         }
143
144         for (FixedIps fixIp : keyFixedIpsMap.values()) {
145             Uuid subnetId = fixIp.getSubnetId();
146             IpAddress srcIpAddress = fixIp.getIpAddress();
147             IpAddress dstIpAddress = getExternalGwIpAddress(subnetId);
148             String srcIpAddressString = srcIpAddress.stringValue();
149             String dstIpAddressString = dstIpAddress.stringValue();
150             if (NWUtil.isIpv4Address(srcIpAddressString)) {
151                 sendArpRequest(srcIpAddress, dstIpAddress, macAddress, extInterface);
152             } else {
153                 sendNeighborSolication(new Ipv6Address(srcIpAddressString),macAddress,
154                         new Ipv6Address(dstIpAddressString), extInterface);
155             }
156         }
157
158     }
159
160     // TODO Clean up the exception handling
161     @SuppressWarnings("checkstyle:IllegalCatch")
162     private void sendArpRequest(IpAddress srcIpAddress, IpAddress dstIpAddress, MacAddress srcMacAddress,
163             String interfaceName) {
164         if (srcIpAddress == null || dstIpAddress == null) {
165             LOG.trace("Skip sending ARP to external GW srcIp {} dstIp {}", srcIpAddress, dstIpAddress);
166             return;
167         }
168
169         PhysAddress srcMacPhysAddress = new PhysAddress(srcMacAddress.getValue());
170         try {
171             InterfaceAddress interfaceAddress = new InterfaceAddressBuilder().setInterface(interfaceName)
172                     .setIpAddress(srcIpAddress).setMacaddress(srcMacPhysAddress).build();
173
174             SendArpRequestInput sendArpRequestInput = new SendArpRequestInputBuilder().setIpaddress(dstIpAddress)
175                     .setInterfaceAddress(Collections.singletonList(interfaceAddress)).build();
176
177             LoggingFutures.addErrorLogging(JdkFutureAdapters.listenInPoolThread(
178                     arpUtilService.sendArpRequest(sendArpRequestInput)), LOG, "Send ARP request");
179         } catch (Exception e) {
180             LOG.error("Failed to send ARP request to external GW {} from interface {}",
181                     dstIpAddress.getIpv4Address().getValue(), interfaceName, e);
182         }
183     }
184
185     private void sendNeighborSolication(Ipv6Address srcIpv6Address,
186             MacAddress srcMac, Ipv6Address dstIpv6Address, String interfaceName) {
187         List<org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6
188             .nd.util.rev170210.interfaces.InterfaceAddress> interfaceAddresses = new ArrayList<>();
189         interfaceAddresses.add(new org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6
190                 .nd.util.rev170210.interfaces.InterfaceAddressBuilder()
191             .setInterface(interfaceName)
192             .setSrcIpAddress(srcIpv6Address)
193             .setSrcMacAddress(new PhysAddress(srcMac.getValue())).build());
194         SendNeighborSolicitationInput input = new SendNeighborSolicitationInputBuilder()
195                 .setInterfaceAddress(interfaceAddresses).setTargetIpAddress(dstIpv6Address)
196                 .build();
197         try {
198             Future<RpcResult<SendNeighborSolicitationOutput>> result = ipv6NdUtilService
199                     .sendNeighborSolicitation(input);
200             RpcResult<SendNeighborSolicitationOutput> rpcResult = result.get();
201             if (!rpcResult.isSuccessful()) {
202                 LOG.error("sendNeighborSolicitationToOfGroup: RPC Call failed for input={} and Errors={}", input,
203                         rpcResult.getErrors());
204             }
205         } catch (InterruptedException | ExecutionException e) {
206             LOG.error("Failed to send NS packet to ELAN group, input={}", input, e);
207         }
208     }
209
210     @Nullable
211     private Port getRouterExtGatewayPort(Router router) {
212         if (router == null) {
213             LOG.trace("Router is null");
214             return null;
215         }
216
217         Uuid extPortId = router.getGatewayPortId();
218         if (extPortId == null) {
219             LOG.trace("Router {} is not associated with any external GW port", router.getUuid().getValue());
220             return null;
221         }
222
223         return neutronvpnUtils.getNeutronPort(extPortId);
224     }
225
226     @Nullable
227     private String getExternalInterface(Router router) {
228         ExternalGatewayInfo extGatewayInfo = router.getExternalGatewayInfo();
229         String routerName = router.getUuid().getValue();
230         if (extGatewayInfo == null) {
231             LOG.warn("External GW info missing for router {}", routerName);
232             return null;
233         }
234
235         Uuid extNetworkId = extGatewayInfo.getExternalNetworkId();
236         if (extNetworkId == null) {
237             LOG.warn("External network id missing for router {}", routerName);
238             return null;
239         }
240
241         Uint64 primarySwitch = cswitchProvider.getPrimarySwitchForRouter(routerName);
242         if (primarySwitch == null || Uint64.ZERO.equals(primarySwitch)) {
243             LOG.warn("Primary switch has not been allocated for router {}", routerName);
244             return null;
245         }
246
247         return elanService.getExternalElanInterface(extNetworkId.getValue(), primarySwitch);
248     }
249
250     @Nullable
251     private IpAddress getExternalGwIpAddress(Uuid subnetId) {
252         if (subnetId == null) {
253             LOG.error("Subnet id is null");
254             return null;
255         }
256
257         Subnet subnet = neutronvpnUtils.getNeutronSubnet(subnetId);
258         return subnet != null ? subnet.getGatewayIp() : null;
259     }
260
261 }