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