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