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