701628feb276c8dab338e963b379f00d3f695315
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronFloatingToFixedIpMappingChangeListener.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.netvirt.neutronvpn;
9
10 import static org.opendaylight.netvirt.neutronvpn.NeutronvpnUtils.buildfloatingIpIdToPortMappingIdentifier;
11
12 import com.google.common.base.Optional;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.concurrent.TimeUnit;
16 import javax.annotation.PostConstruct;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
22 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
23 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
24 import org.opendaylight.genius.mdsalutil.MDSALUtil;
25 import org.opendaylight.infrautils.utils.concurrent.KeyedLocks;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.FloatingIpInfo;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPortsBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPortsKey;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.PortsBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.PortsKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMapBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMapKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMapping;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.floatingips.attributes.Floatingips;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.floatingips.attributes.floatingips.Floatingip;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 @Singleton
48 public class NeutronFloatingToFixedIpMappingChangeListener extends AsyncDataTreeChangeListenerBase<Floatingip,
49         NeutronFloatingToFixedIpMappingChangeListener> {
50     private static final Logger LOG = LoggerFactory.getLogger(NeutronFloatingToFixedIpMappingChangeListener.class);
51     private static long LOCK_WAIT_TIME = 10L;
52
53     private final DataBroker dataBroker;
54     private final KeyedLocks<String> routerLock = new KeyedLocks<>();
55
56     @Inject
57     public NeutronFloatingToFixedIpMappingChangeListener(final DataBroker dataBroker) {
58         super(Floatingip.class, NeutronFloatingToFixedIpMappingChangeListener.class);
59         this.dataBroker = dataBroker;
60     }
61
62     @Override
63     @PostConstruct
64     public void init() {
65         LOG.info("{} init", getClass().getSimpleName());
66         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
67     }
68
69     @Override
70     protected InstanceIdentifier<Floatingip> getWildCardPath() {
71         return InstanceIdentifier.create(Neutron.class).child(Floatingips.class).child(Floatingip.class);
72     }
73
74     @Override
75     protected NeutronFloatingToFixedIpMappingChangeListener getDataTreeChangeListener() {
76         return NeutronFloatingToFixedIpMappingChangeListener.this;
77     }
78
79     @Override
80     protected void add(InstanceIdentifier<Floatingip> identifier, Floatingip input) {
81         LOG.trace("Neutron Floating IP created: key: {}, value={}", identifier, input);
82         IpAddress fixedIp = input.getFixedIpAddress();
83         String floatingIp = input.getFloatingIpAddress().getIpv4Address().getValue();
84         if (fixedIp != null) {
85             addToFloatingIpInfo(input.getRouterId().getValue(), input.getFloatingNetworkId(), input.getPortId()
86                     .getValue(), fixedIp.getIpv4Address().getValue(), floatingIp, input.getUuid());
87         }
88     }
89
90     @Override
91     protected void remove(InstanceIdentifier<Floatingip> identifier, Floatingip input) {
92         LOG.trace("Neutron Floating IP deleted : key: {}, value={}", identifier, input);
93         IpAddress fixedIp = input.getFixedIpAddress();
94         if (fixedIp != null) {
95             // update FloatingIpPortInfo to set isFloatingIpDeleted as true to enable deletion of FloatingIpPortInfo
96             // map once it is used for processing in the NAT removal path
97             updateFloatingIpPortInfo(input.getUuid(), input.getPortId());
98             clearFromFloatingIpInfo(input.getRouterId().getValue(), input.getPortId().getValue(), fixedIp
99                     .getIpv4Address().getValue());
100         } else {
101             // delete FloatingIpPortInfo mapping since floating IP is deleted and no fixed IP is associated to it
102             removeFromFloatingIpPortInfo(input.getUuid());
103         }
104     }
105
106     // populate the floating to fixed ip map upon association/dissociation from fixed ip
107     @Override
108     protected void update(InstanceIdentifier<Floatingip> identifier, Floatingip original, Floatingip update) {
109         LOG.trace("Handling FloatingIptoFixedIp mapping : key: {}, original value={}, update value={}", identifier,
110                 original, update);
111         IpAddress oldFixedIp = original.getFixedIpAddress();
112         IpAddress newFixedIp = update.getFixedIpAddress();
113         String floatingIp = update.getFloatingIpAddress().getIpv4Address().getValue();
114
115         if (oldFixedIp != null && !oldFixedIp.equals(newFixedIp)) {
116             clearFromFloatingIpInfo(original.getRouterId().getValue(), original.getPortId().getValue(), oldFixedIp
117                     .getIpv4Address().getValue());
118         }
119         if (newFixedIp != null && !newFixedIp.equals(oldFixedIp)) {
120             addToFloatingIpInfo(update.getRouterId().getValue(), update.getFloatingNetworkId(), update.getPortId()
121                     .getValue(), newFixedIp.getIpv4Address().getValue(), floatingIp, update.getUuid());
122         }
123     }
124
125     // TODO Clean up the exception handling
126     @SuppressWarnings("checkstyle:IllegalCatch")
127     private void addToFloatingIpInfo(String routerName, Uuid extNetworkId, String fixedNeutronPortName, String
128             fixedIpAddress, String floatingIpAddress, Uuid floatingIpId) {
129         RouterPortsBuilder routerPortsBuilder;
130         boolean isLockAcquired = false;
131         InstanceIdentifier<RouterPorts> routerPortsIdentifier = InstanceIdentifier.builder(FloatingIpInfo.class)
132                 .child(RouterPorts.class, new RouterPortsKey(routerName)).build();
133         try {
134             Optional<RouterPorts> optionalRouterPorts =
135                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
136                             routerPortsIdentifier);
137             if (optionalRouterPorts.isPresent()) {
138                 LOG.debug("Updating routerPorts node {} in floatingIpInfo DS for floating IP {} on fixed "
139                     + "neutron port {} : ", routerName, floatingIpAddress, fixedNeutronPortName);
140                 routerPortsBuilder = new RouterPortsBuilder(optionalRouterPorts.get());
141             } else {
142                 LOG.debug("Creating new routerPorts node {} in floatingIpInfo DS for floating IP {} on fixed "
143                     + "neutron port {} : ", routerName, floatingIpAddress, fixedNeutronPortName);
144                 routerPortsBuilder =
145                     new RouterPortsBuilder().setKey(new RouterPortsKey(routerName)).setRouterId(routerName);
146             }
147             if (extNetworkId != null) {
148                 routerPortsBuilder.setExternalNetworkId(extNetworkId);
149             }
150             if (fixedNeutronPortName != null) {
151                 List<Ports> portsList = routerPortsBuilder.getPorts();
152                 if (portsList == null) {
153                     portsList = new ArrayList<>();
154                 }
155                 PortsBuilder fixedNeutronPortBuilder = null;
156                 for (Ports neutronPort : portsList) {
157                     if (neutronPort.getPortName().equals(fixedNeutronPortName)) {
158                         fixedNeutronPortBuilder = new PortsBuilder(neutronPort);
159                         break;
160                     }
161                 }
162                 if (fixedNeutronPortBuilder == null) {
163                     fixedNeutronPortBuilder = new PortsBuilder().setKey(new PortsKey(fixedNeutronPortName))
164                             .setPortName(fixedNeutronPortName);
165                 }
166                 if (fixedIpAddress != null) {
167                     List<InternalToExternalPortMap> intExtPortMapList = fixedNeutronPortBuilder
168                             .getInternalToExternalPortMap();
169                     if (intExtPortMapList == null) {
170                         intExtPortMapList = new ArrayList<>();
171                     }
172                     InternalToExternalPortMap intExtPortMap = new InternalToExternalPortMapBuilder().setKey(new
173                             InternalToExternalPortMapKey(fixedIpAddress)).setInternalIp(fixedIpAddress)
174                             .setExternalIp(floatingIpAddress).setExternalId(floatingIpId).setLabel(null).build();
175                     intExtPortMapList.add(intExtPortMap);
176                     fixedNeutronPortBuilder.setInternalToExternalPortMap(intExtPortMapList);
177                 }
178                 portsList.add(fixedNeutronPortBuilder.build());
179                 routerPortsBuilder.setPorts(portsList);
180             }
181             isLockAcquired = routerLock.tryLock(routerName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
182             LOG.debug("Creating/Updating routerPorts node {} in floatingIpInfo DS for floating IP {} on fixed "
183                 + "neutron port {} : ", routerName, floatingIpAddress, fixedNeutronPortName);
184             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routerPortsIdentifier,
185                 routerPortsBuilder.build());
186             LOG.debug("FloatingIpInfo DS updated for floating IP {} ", floatingIpAddress);
187         } catch (ReadFailedException | RuntimeException e) {
188             LOG.error("addToFloatingIpInfo failed for floating IP: {} ", floatingIpAddress, e);
189         } finally {
190             if (isLockAcquired) {
191                 routerLock.unlock(routerName);
192             }
193         }
194     }
195
196     // TODO Clean up the exception handling
197     @SuppressWarnings("checkstyle:IllegalCatch")
198     private void clearFromFloatingIpInfo(String routerName, String fixedNeutronPortName, String fixedIpAddress) {
199         boolean isLockAcquired = false;
200         InstanceIdentifier.InstanceIdentifierBuilder<RouterPorts> routerPortsIdentifierBuilder = InstanceIdentifier
201                 .builder(FloatingIpInfo.class).child(RouterPorts.class, new RouterPortsKey(routerName));
202         try {
203             Optional<RouterPorts> optionalRouterPorts =
204                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
205                             routerPortsIdentifierBuilder.build());
206             if (optionalRouterPorts.isPresent()) {
207                 RouterPorts routerPorts = optionalRouterPorts.get();
208                 List<Ports> portsList = routerPorts.getPorts();
209                 List<InternalToExternalPortMap> intExtPortMap = new ArrayList<>();
210                 for (Ports ports : portsList) {
211                     if (ports.getPortName().equals(fixedNeutronPortName)) {
212                         intExtPortMap = ports.getInternalToExternalPortMap();
213                         break;
214                     }
215                 }
216                 if (intExtPortMap.size() == 1) {
217                     removeRouterPortsOrPortsNode(routerName, routerPortsIdentifierBuilder, portsList,
218                             fixedNeutronPortName, isLockAcquired);
219                 } else {
220                     for (InternalToExternalPortMap intToExtMap : intExtPortMap) {
221                         if (intToExtMap.getInternalIp().equals(fixedIpAddress)) {
222                             InstanceIdentifier<InternalToExternalPortMap> intExtPortMapIdentifier =
223                                     routerPortsIdentifierBuilder.child(Ports
224                                     .class, new PortsKey(fixedNeutronPortName)).child(InternalToExternalPortMap.class,
225                                             new InternalToExternalPortMapKey(fixedIpAddress)).build();
226                             try {
227                                 // remove particular internal-to-external-port-map
228                                 isLockAcquired = routerLock.tryLock(fixedIpAddress, LOCK_WAIT_TIME, TimeUnit.SECONDS);
229                                 LOG.debug("removing particular internal-to-external-port-map {}", intExtPortMap);
230                                 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
231                                         intExtPortMapIdentifier);
232                             } catch (Exception e) {
233                                 LOG.error("Failure in deletion of internal-to-external-port-map {}", intExtPortMap, e);
234                             } finally {
235                                 if (isLockAcquired) {
236                                     routerLock.unlock(fixedIpAddress);
237                                 }
238                             }
239                         }
240                     }
241                 }
242                 LOG.debug("Deletion from FloatingIpInfo DS successful for fixedIp {} ", fixedIpAddress);
243             } else {
244                 LOG.warn("routerPorts for router {} - fixedIp {} not found", routerName, fixedIpAddress);
245             }
246         } catch (RuntimeException | ReadFailedException e) {
247             LOG.error("Failed to delete internal-to-external-port-map from FloatingIpInfo DS for fixed Ip {}",
248                     fixedIpAddress, e);
249         }
250     }
251
252     protected void dissociatefixedIPFromFloatingIP(String fixedNeutronPortName) {
253         boolean isLockAcquired = false;
254         InstanceIdentifier.InstanceIdentifierBuilder<FloatingIpInfo> floatingIpInfoIdentifierBuilder =
255                 InstanceIdentifier.builder(FloatingIpInfo.class);
256         try {
257             Optional<FloatingIpInfo> optionalFloatingIPInfo =
258                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
259                             floatingIpInfoIdentifierBuilder.build());
260             if (optionalFloatingIPInfo.isPresent()) {
261                 List<RouterPorts> routerPortsList = optionalFloatingIPInfo.get().getRouterPorts();
262                 if (routerPortsList != null && !routerPortsList.isEmpty()) {
263                     for (RouterPorts routerPorts : routerPortsList) {
264                         List<Ports> portsList = routerPorts.getPorts();
265                         if (portsList != null && !portsList.isEmpty()) {
266                             for (Ports ports : portsList) {
267                                 if (ports.getPortName().equals(fixedNeutronPortName)) {
268                                     String routerName = routerPorts.getRouterId();
269                                     InstanceIdentifier.InstanceIdentifierBuilder<RouterPorts>
270                                         routerPortsIdentifierBuilder = floatingIpInfoIdentifierBuilder
271                                         .child(RouterPorts.class, new RouterPortsKey(routerName));
272                                     removeRouterPortsOrPortsNode(routerName, routerPortsIdentifierBuilder, portsList,
273                                             fixedNeutronPortName, isLockAcquired);
274                                     LOG.debug("Deletion from FloatingIpInfo DS successful for fixedIP neutron port {} ",
275                                             fixedNeutronPortName);
276                                     break;
277                                 }
278                             }
279                         }
280                     }
281                 } else {
282                     LOG.debug("No router present containing fixed to floating IP association(s)");
283                 }
284             } else {
285                 LOG.debug("FloatingIPInfo DS empty. Hence, no router present containing fixed to floating IP "
286                     + "association(s)");
287             }
288         } catch (ReadFailedException e) {
289             LOG.error("Failed to dissociate fixedIP from FloatingIpInfo DS for neutron port {}",
290                     fixedNeutronPortName, e);
291         }
292     }
293
294     // TODO Clean up the exception handling
295     @SuppressWarnings("checkstyle:IllegalCatch")
296     private void removeRouterPortsOrPortsNode(String routerName, InstanceIdentifier
297             .InstanceIdentifierBuilder<RouterPorts> routerPortsIdentifierBuilder, List<Ports> portsList,
298                                               String fixedNeutronPortName, boolean isLockAcquired) {
299         String lockName = null;
300         try {
301             if (portsList.size() == 1) {
302                 // remove entire routerPorts node
303                 lockName = routerName;
304                 isLockAcquired = routerLock.tryLock(lockName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
305                 LOG.debug("removing routerPorts node: {} ", routerName);
306                 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, routerPortsIdentifierBuilder
307                         .build());
308             } else {
309                 // remove entire ports node under this routerPorts node
310                 lockName = fixedNeutronPortName;
311                 isLockAcquired = routerLock.tryLock(lockName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
312                 LOG.debug("removing ports node {} under routerPorts node {}", fixedNeutronPortName, routerName);
313                 InstanceIdentifier.InstanceIdentifierBuilder<Ports> portsIdentifierBuilder =
314                     routerPortsIdentifierBuilder.child(Ports.class, new PortsKey(fixedNeutronPortName));
315                 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, portsIdentifierBuilder.build());
316             }
317         } catch (Exception e) {
318             LOG.error("Failure in deletion of routerPorts node {}", routerName, e);
319         } finally {
320             if (isLockAcquired) {
321                 routerLock.unlock(lockName);
322             }
323         }
324     }
325
326     // TODO Clean up the exception handling
327     @SuppressWarnings("checkstyle:IllegalCatch")
328     // updates FloatingIPPortInfo to have isFloatingIPDeleted set to true on a floating IP delete
329     private void updateFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId) {
330         InstanceIdentifier<FloatingIpIdToPortMapping>  id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
331         try {
332             FloatingIpIdToPortMappingBuilder floatingIpIdToPortMappingBuilder = new
333                     FloatingIpIdToPortMappingBuilder().setFloatingIpId(floatingIpId).setFloatingIpDeleted(true);
334             LOG.debug("Updating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
335                     + " Port Info Config DS to set isFloatingIpDeleted flag as true",
336                 floatingIpId.getValue(), floatingIpPortId.getValue());
337             MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
338                     floatingIpIdToPortMappingBuilder.build());
339         } catch (Exception e) {
340             LOG.error("Updating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
341                     + " Port Info Config DS to set isFloatingIpDeleted flag as true failed", floatingIpId.getValue(),
342                     floatingIpPortId.getValue(), e);
343         }
344     }
345
346     // TODO Clean up the exception handling
347     @SuppressWarnings("checkstyle:IllegalCatch")
348     private void removeFromFloatingIpPortInfo(Uuid floatingIpId) {
349         InstanceIdentifier<FloatingIpIdToPortMapping>  id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
350         try {
351             LOG.debug("Deleting floating IP UUID {} to Floating IP neutron port mapping from Floating "
352                 + "IP Port Info Config DS", floatingIpId.getValue());
353             MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
354         } catch (Exception e) {
355             LOG.error("Deleting floating IP UUID {} to Floating IP neutron port mapping from Floating "
356                 + "IP Port Info Config DS failed", floatingIpId.getValue(), e);
357         }
358     }
359 }