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