a85371c8caca486cece6418bfc4f92db943bc608
[unimgr.git] / netvirt / src / main / java / org / opendaylight / unimgr / mef / netvirt / GwMacListener.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.unimgr.mef.netvirt;
10
11 import java.util.Collections;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Set;
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.concurrent.ExecutorService;
17 import java.util.concurrent.Executors;
18 import java.util.concurrent.TimeUnit;
19
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
22 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.unimgr.api.UnimgrDataTreeChangeListener;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilService;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
28 import org.opendaylight.yangtools.concepts.ListenerRegistration;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 public class GwMacListener extends UnimgrDataTreeChangeListener<VpnPortipToPort> implements IGwMacListener {
33     private static final Logger Log = LoggerFactory.getLogger(GwMacListener.class);
34     private ListenerRegistration<GwMacListener> gwMacListenerRegistration;
35     private final OdlArputilService arpUtilService;
36     private final ExecutorService retriesHandler;
37     private final Short sleepInterval;
38
39     private final ConcurrentHashMap<GwMacKey, GwMacValue> gwMacResolver;
40
41     public GwMacListener(final DataBroker dataBroker, final OdlArputilService arputilService, Short sleepInterval) {
42         super(dataBroker);
43         this.arpUtilService = arputilService;
44         this.gwMacResolver = new ConcurrentHashMap<>();
45         this.sleepInterval = sleepInterval;
46         retriesHandler = Executors.newSingleThreadExecutor();
47         registerListener();
48     }
49
50     public void registerListener() {
51         try {
52             final DataTreeIdentifier<VpnPortipToPort> dataTreeIid = new DataTreeIdentifier<>(
53                     LogicalDatastoreType.OPERATIONAL, NetvirtVpnUtils.getVpnPortipToPortIdentifier());
54             gwMacListenerRegistration = dataBroker.registerDataTreeChangeListener(dataTreeIid, this);
55             startRetriesThread();
56             Log.info("GwMacListener created and registered");
57         } catch (final Exception e) {
58             Log.error("GwMacListener listener registration failed !", e);
59             throw new IllegalStateException("GwMacListener Listener failed.", e);
60         }
61     }
62
63     @Override
64     public void close() throws Exception {
65         gwMacListenerRegistration.close();
66         retriesHandler.shutdown();
67         if (!retriesHandler.awaitTermination(10, TimeUnit.SECONDS)) {
68             retriesHandler.shutdownNow();
69         }
70     }
71
72     @Override
73     public void add(DataTreeModification<VpnPortipToPort> newDataObject) {
74         if (newDataObject.getRootPath() != null && newDataObject.getRootNode() != null) {
75             VpnPortipToPort portIpToPort = newDataObject.getRootNode().getDataAfter();
76             updateMac(portIpToPort);
77         }
78     }
79
80     @Override
81     public void remove(DataTreeModification<VpnPortipToPort> removedDataObject) {
82     }
83
84     @Override
85     public void update(DataTreeModification<VpnPortipToPort> modifiedDataObject) {
86         if (modifiedDataObject.getRootPath() != null && modifiedDataObject.getRootNode() != null) {
87             VpnPortipToPort portIpToPort = modifiedDataObject.getRootNode().getDataAfter();
88             updateMac(portIpToPort);
89         }
90     }
91
92     private synchronized void updateMac(VpnPortipToPort portIpToPort) {
93         String portName = portIpToPort.getPortName();
94         String macAddress = portIpToPort.getMacAddress();
95         String vpnName = portIpToPort.getVpnName();
96         String ipAddress = portIpToPort.getPortFixedip();
97         GwMacKey gwMacKey = new GwMacKey(vpnName, portName, ipAddress);
98
99         if (!gwMacResolver.containsKey(gwMacKey)) {
100             Log.debug("Ignoring MAC update for vpn {} port {} ip {}", vpnName, portName, ipAddress);
101             return;
102         }
103
104         if (macAddress != null && vpnName != null) {
105             Log.trace("Updating vpn {} port {} with IP {} MAC {}", vpnName, portName, ipAddress, macAddress);
106         }
107
108         if (gwMacResolver.get(gwMacKey).getGwMac() == null
109                 || !gwMacResolver.get(gwMacKey).getGwMac().equals(macAddress)) {
110             String portIp = gwMacResolver.get(gwMacKey).getPortIp();
111             for (String subnet : gwMacResolver.get(gwMacKey).getSubnets()) {
112                 Log.info("Creating GW for vpn {} port {} ip {} subnet {} MAC {}", vpnName, portName, ipAddress, subnet,
113                         macAddress);
114
115                 NetvirtVpnUtils.createUpdateVpnInterface(dataBroker, vpnName, portName, subnet, macAddress, false,
116                         portIp, null);
117             }
118
119             gwMacResolver.get(gwMacKey).setGwMac(macAddress);
120         }
121     }
122
123     private void forceUpdateSubnet(GwMacKey gwMacKey, String subnet) {
124         if (!gwMacResolver.containsKey(gwMacKey)) {
125             return;
126         }
127         String portIp = gwMacResolver.get(gwMacKey).getPortIp();
128         String macAddress = gwMacResolver.get(gwMacKey).getGwMac();
129         if (macAddress == null) {
130             return;
131         }
132         Log.info("Creating GW for vpn {} port {} ip {} subnet {} MAC {}", gwMacKey.vpnId, gwMacKey.portId,
133                 gwMacKey.gwIp, subnet, macAddress);
134         NetvirtVpnUtils.createUpdateVpnInterface(dataBroker, gwMacKey.vpnId, gwMacKey.portId, subnet, macAddress, false,
135                 portIp, null);
136
137     }
138
139     @Override
140     public synchronized void resolveGwMac(String vpnName, String portName, IpAddress srcIpAddress,
141             IpAddress dstIpAddress, String subnet) {
142         String dstIpAddressStr = NetvirtVpnUtils.ipAddressToString(dstIpAddress);
143         GwMacKey gwMacKey = new GwMacKey(vpnName, portName, dstIpAddressStr);
144
145         if (!gwMacResolver.containsKey(gwMacKey)) {
146             gwMacResolver.putIfAbsent(gwMacKey,
147                     new GwMacValue(NetvirtVpnUtils.ipAddressToString(srcIpAddress), subnet));
148             Log.info("Starting GW mac resolution for vpn {} port {} GW ip {}", vpnName, portName, dstIpAddress);
149             NetvirtVpnUtils.sendArpRequest(arpUtilService, srcIpAddress, dstIpAddress, portName);
150         } else {
151             forceUpdateSubnet(gwMacKey, subnet);
152             gwMacResolver.get(gwMacKey).getSubnets().add(subnet);
153         }
154
155         VpnPortipToPort portIpToPort = NetvirtVpnUtils.getVpnPortFixedIp(dataBroker, vpnName, dstIpAddressStr);
156         if (portIpToPort != null) {
157             updateMac(portIpToPort);
158         }
159
160     }
161
162     @Override
163     public synchronized void unResolveGwMac(String vpnName, String portName, IpAddress srcIpAddress,
164             IpAddress dstIpAddress, String subnet) {
165         String dstIpAddressStr = NetvirtVpnUtils.ipAddressToString(dstIpAddress);
166         GwMacKey gwMacKey = new GwMacKey(vpnName, portName, dstIpAddressStr);
167         if (gwMacResolver.containsKey(gwMacKey)) {
168             Log.info("Stopping GW mac resolution for vpn {} port {} GW ip {} Subnet {}", vpnName, portName,
169                     dstIpAddress, subnet);
170             gwMacResolver.get(gwMacKey).getSubnets().remove(subnet);
171
172             if (gwMacResolver.get(gwMacKey).getSubnets().isEmpty()) {
173                 gwMacResolver.remove(gwMacKey);
174             }
175         }
176     }
177
178     private void resolveRetry() {
179         gwMacResolver.forEach((k, v) -> {
180             if (v.getGwMac() == null) {
181                 IpAddress dstIpAddress = new IpAddress(k.gwIp.toCharArray());
182                 IpAddress srcIpAddress = new IpAddress(v.portIp.toCharArray());
183                 Log.debug("Resending ARP for IP {} port {}", dstIpAddress, k.getGwIp());
184
185                 NetvirtVpnUtils.sendArpRequest(arpUtilService, srcIpAddress, dstIpAddress, k.portId);
186             }
187         });
188     }
189
190     void startRetriesThread() {
191         retriesHandler.submit(() -> {
192             Thread t = Thread.currentThread();
193             t.setName("ResolveSubnetGW");
194             Log.info("ResolveSubnetGW: started {}", t.getName());
195             while (true) {
196                 NetvirtUtils.safeSleep(sleepInterval);
197                 resolveRetry();
198             }
199         });
200         Log.debug("Subnet GW Arp Retries");
201     }
202
203     private static class GwMacKey {
204         private final String vpnId;
205         private final String portId;
206         private final String gwIp;
207
208         public GwMacKey(String vpId, String portId, String gwIp) {
209             this.vpnId = vpId;
210             this.portId = portId;
211             this.gwIp = gwIp;
212         }
213
214         public String getGwIp() {
215             return gwIp;
216         }
217
218         @Override
219         public int hashCode() {
220             final int prime = 31;
221             int result = 1;
222             result = prime * result + (gwIp == null ? 0 : gwIp.hashCode());
223             result = prime * result + (portId == null ? 0 : portId.hashCode());
224             result = prime * result + (vpnId == null ? 0 : vpnId.hashCode());
225             return result;
226         }
227
228         @Override
229         public boolean equals(Object obj) {
230             if (this == obj) {
231                 return true;
232             }
233             if (obj == null) {
234                 return false;
235             }
236             if (getClass() != obj.getClass()) {
237                 return false;
238             }
239             GwMacKey other = (GwMacKey) obj;
240             if (gwIp == null) {
241                 if (other.gwIp != null) {
242                     return false;
243                 }
244             } else if (!gwIp.equals(other.gwIp)) {
245                 return false;
246             }
247             if (portId == null) {
248                 if (other.portId != null) {
249                     return false;
250                 }
251             } else if (!portId.equals(other.portId)) {
252                 return false;
253             }
254             if (vpnId == null) {
255                 if (other.vpnId != null) {
256                     return false;
257                 }
258             } else if (!vpnId.equals(other.vpnId)) {
259                 return false;
260             }
261             return true;
262         }
263
264     }
265
266     private static class GwMacValue {
267         private String portIp;
268         private Set<String> subnets;
269         private String gwMac;
270
271         public GwMacValue(String portIp, String subnet) {
272             this.portIp = portIp;
273             this.subnets = new HashSet<String>();
274             this.subnets.add(subnet);
275         }
276
277         public String getGwMac() {
278             return gwMac;
279         }
280
281         public void setGwMac(String gwMac) {
282             this.gwMac = gwMac;
283         }
284
285         public String getPortIp() {
286             return portIp;
287         }
288
289         public Set<String> getSubnets() {
290             return subnets;
291         }
292
293     }
294
295 }