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