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