2 * Copyright (c) 2016 Hewlett Packard Enterprise, Co. and others. All rights reserved.
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
9 package org.opendaylight.unimgr.mef.netvirt;
11 import java.util.HashSet;
13 import java.util.concurrent.ConcurrentHashMap;
14 import java.util.concurrent.ExecutorService;
15 import java.util.concurrent.Executors;
16 import java.util.concurrent.TimeUnit;
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;
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;
37 private final ConcurrentHashMap<GwMacKey, GwMacValue> gwMacResolver;
39 public GwMacListener(final DataBroker dataBroker, final OdlArputilService arputilService, Short sleepInterval) {
41 this.arpUtilService = arputilService;
42 this.gwMacResolver = new ConcurrentHashMap<>();
43 this.sleepInterval = sleepInterval;
44 retriesHandler = Executors.newSingleThreadExecutor();
48 public void registerListener() {
50 final DataTreeIdentifier<LearntVpnVipToPort> dataTreeIid = new DataTreeIdentifier<>(
51 LogicalDatastoreType.OPERATIONAL, NetvirtVpnUtils.getLearntVpnVipToPortIdentifier());
52 gwMacListenerRegistration = dataBroker.registerDataTreeChangeListener(dataTreeIid, this);
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);
62 public void close() throws Exception {
63 gwMacListenerRegistration.close();
64 retriesHandler.shutdown();
65 if (!retriesHandler.awaitTermination(10, TimeUnit.SECONDS)) {
66 retriesHandler.shutdownNow();
71 public void add(DataTreeModification<LearntVpnVipToPort> newDataObject) {
72 if (newDataObject.getRootPath() != null && newDataObject.getRootNode() != null) {
73 LearntVpnVipToPort portIpToPort = newDataObject.getRootNode().getDataAfter();
74 updateMac(portIpToPort);
79 public void remove(DataTreeModification<LearntVpnVipToPort> removedDataObject) {
83 public void update(DataTreeModification<LearntVpnVipToPort> modifiedDataObject) {
84 if (modifiedDataObject.getRootPath() != null && modifiedDataObject.getRootNode() != null) {
85 LearntVpnVipToPort portIpToPort = modifiedDataObject.getRootNode().getDataAfter();
86 updateMac(portIpToPort);
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);
97 if (!gwMacResolver.containsKey(gwMacKey)) {
98 Log.debug("Ignoring MAC update for vpn {} port {} ip {}", vpnName, portName, ipAddress);
102 if (macAddress != null && vpnName != null) {
103 Log.trace("Updating vpn {} port {} with IP {} MAC {}", vpnName, portName, ipAddress, macAddress);
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,
113 NetvirtVpnUtils.createUpdateVpnInterface(dataBroker, vpnName, portName, subnet, macAddress, false,
117 gwMacResolver.get(gwMacKey).setGwMac(macAddress);
121 private void forceUpdateSubnet(GwMacKey gwMacKey, String subnet) {
122 if (!gwMacResolver.containsKey(gwMacKey)) {
125 String portIp = gwMacResolver.get(gwMacKey).getPortIp();
126 String macAddress = gwMacResolver.get(gwMacKey).getGwMac();
127 if (macAddress == null) {
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,
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);
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);
149 forceUpdateSubnet(gwMacKey, subnet);
150 gwMacResolver.get(gwMacKey).getSubnets().add(subnet);
153 LearntVpnVipToPort portIpToPort = NetvirtVpnUtils.getLearntVpnVipToPort(dataBroker, vpnName, dstIpAddressStr);
154 if (portIpToPort != null) {
155 updateMac(portIpToPort);
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);
170 if (gwMacResolver.get(gwMacKey).getSubnets().isEmpty()) {
171 gwMacResolver.remove(gwMacKey);
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());
183 NetvirtVpnUtils.sendArpRequest(arpUtilService, srcIpAddress, dstIpAddress, k.portId);
188 void startRetriesThread() {
189 retriesHandler.submit(() -> {
190 Thread t = Thread.currentThread();
191 t.setName("ResolveSubnetGW");
192 Log.info("ResolveSubnetGW: started {}", t.getName());
194 NetvirtUtils.safeSleep(sleepInterval);
198 Log.debug("Subnet GW Arp Retries");
201 private static class GwMacKey {
202 private final String vpnId;
203 private final String portId;
204 private final String gwIp;
206 public GwMacKey(String vpId, String portId, String gwIp) {
208 this.portId = portId;
212 public String getGwIp() {
217 public int hashCode() {
218 final int prime = 31;
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());
227 public boolean equals(Object obj) {
234 if (getClass() != obj.getClass()) {
237 GwMacKey other = (GwMacKey) obj;
239 if (other.gwIp != null) {
242 } else if (!gwIp.equals(other.gwIp)) {
245 if (portId == null) {
246 if (other.portId != null) {
249 } else if (!portId.equals(other.portId)) {
253 if (other.vpnId != null) {
256 } else if (!vpnId.equals(other.vpnId)) {
264 private static class GwMacValue {
265 private String portIp;
266 private Set<String> subnets;
267 private String gwMac;
269 public GwMacValue(String portIp, String subnet) {
270 this.portIp = portIp;
271 this.subnets = new HashSet<String>();
272 this.subnets.add(subnet);
275 public String getGwMac() {
279 public void setGwMac(String gwMac) {
283 public String getPortIp() {
287 public Set<String> getSubnets() {