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