79767de0b5050a1270e083dae927988ffbc7cdd7
[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 edu.umd.cs.findbugs.annotations.CheckReturnValue;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.Objects;
17 import java.util.concurrent.TimeUnit;
18 import javax.annotation.PostConstruct;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
24 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
25 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
26 import org.opendaylight.genius.mdsalutil.MDSALUtil;
27 import org.opendaylight.infrautils.utils.concurrent.NamedLocks;
28 import org.opendaylight.infrautils.utils.concurrent.NamedSimpleReentrantLock.AcquireResult;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.FloatingIpInfo;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPortsBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPortsKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.PortsBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.PortsKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMapBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMapKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMapping;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.floatingips.attributes.Floatingips;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.floatingips.attributes.floatingips.Floatingip;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 @Singleton
51 public class NeutronFloatingToFixedIpMappingChangeListener extends AsyncDataTreeChangeListenerBase<Floatingip,
52         NeutronFloatingToFixedIpMappingChangeListener> {
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(Floatingip.class, NeutronFloatingToFixedIpMappingChangeListener.class);
62         this.dataBroker = dataBroker;
63     }
64
65     @Override
66     @PostConstruct
67     public void init() {
68         LOG.info("{} init", getClass().getSimpleName());
69         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
70     }
71
72     @Override
73     protected InstanceIdentifier<Floatingip> getWildCardPath() {
74         return InstanceIdentifier.create(Neutron.class).child(Floatingips.class).child(Floatingip.class);
75     }
76
77     @Override
78     protected NeutronFloatingToFixedIpMappingChangeListener getDataTreeChangeListener() {
79         return NeutronFloatingToFixedIpMappingChangeListener.this;
80     }
81
82     @Override
83     protected 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     protected 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     protected 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         IpAddress oldFixedIp = original.getFixedIpAddress();
115         IpAddress newFixedIp = update.getFixedIpAddress();
116         String floatingIp = update.getFloatingIpAddress().getIpv4Address().getValue();
117
118         if (oldFixedIp != null && !oldFixedIp.equals(newFixedIp)) {
119             clearFromFloatingIpInfo(original.getRouterId().getValue(), original.getPortId().getValue(), oldFixedIp
120                     .getIpv4Address().getValue());
121         }
122         if (newFixedIp != null && !newFixedIp.equals(oldFixedIp)) {
123             addToFloatingIpInfo(update.getRouterId().getValue(), update.getFloatingNetworkId(), update.getPortId()
124                     .getValue(), newFixedIp.getIpv4Address().getValue(), floatingIp, update.getUuid());
125         }
126     }
127
128     // TODO Clean up the exception handling
129     @SuppressWarnings("checkstyle:IllegalCatch")
130     private void addToFloatingIpInfo(String routerName, Uuid extNetworkId, String fixedNeutronPortName, String
131             fixedIpAddress, String floatingIpAddress, Uuid floatingIpId) {
132         RouterPortsBuilder routerPortsBuilder;
133         InstanceIdentifier<RouterPorts> routerPortsIdentifier = InstanceIdentifier.builder(FloatingIpInfo.class)
134                 .child(RouterPorts.class, new RouterPortsKey(routerName)).build();
135         try {
136             Optional<RouterPorts> optionalRouterPorts =
137                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
138                             routerPortsIdentifier);
139             if (optionalRouterPorts.isPresent()) {
140                 LOG.debug("Updating routerPorts node {} in floatingIpInfo DS for floating IP {} on fixed "
141                     + "neutron port {} : ", routerName, floatingIpAddress, fixedNeutronPortName);
142                 routerPortsBuilder = new RouterPortsBuilder(optionalRouterPorts.get());
143             } else {
144                 LOG.debug("Creating new routerPorts node {} in floatingIpInfo DS for floating IP {} on fixed "
145                     + "neutron port {} : ", routerName, floatingIpAddress, fixedNeutronPortName);
146                 routerPortsBuilder =
147                     new RouterPortsBuilder().withKey(new RouterPortsKey(routerName)).setRouterId(routerName);
148             }
149             if (extNetworkId != null) {
150                 routerPortsBuilder.setExternalNetworkId(extNetworkId);
151             }
152             if (fixedNeutronPortName != null) {
153                 List<Ports> portsList = routerPortsBuilder.getPorts() != null
154                         ? new ArrayList<>(routerPortsBuilder.getPorts()) : new ArrayList<>();
155                 PortsBuilder fixedNeutronPortBuilder = null;
156                 for (Ports neutronPort : portsList) {
157                     if (neutronPort.getPortName().equals(fixedNeutronPortName)) {
158                         fixedNeutronPortBuilder = new PortsBuilder(neutronPort);
159                         break;
160                     }
161                 }
162                 if (fixedNeutronPortBuilder == null) {
163                     fixedNeutronPortBuilder = new PortsBuilder().withKey(new PortsKey(fixedNeutronPortName))
164                             .setPortName(fixedNeutronPortName);
165                 }
166                 if (fixedIpAddress != null) {
167                     List<InternalToExternalPortMap> intExtPortMapList = fixedNeutronPortBuilder
168                             .getInternalToExternalPortMap();
169                     if (intExtPortMapList == null) {
170                         intExtPortMapList = new ArrayList<>();
171                     }
172                     InternalToExternalPortMap intExtPortMap = new InternalToExternalPortMapBuilder().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 (ReadFailedException | RuntimeException 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                 List<Ports> portsList = routerPorts.nonnullPorts();
211                 List<InternalToExternalPortMap> intExtPortMap = new ArrayList<>();
212                 for (Ports ports : portsList) {
213                     if (Objects.equals(ports.getPortName(), fixedNeutronPortName)) {
214                         intExtPortMap = ports.nonnullInternalToExternalPortMap();
215                         break;
216                     }
217                 }
218                 if (intExtPortMap.size() == 1) {
219                     removeRouterPortsOrPortsNode(routerName, routerPortsIdentifierBuilder, portsList,
220                             fixedNeutronPortName);
221                 } else {
222                     for (InternalToExternalPortMap intToExtMap : intExtPortMap) {
223                         if (Objects.equals(intToExtMap.getInternalIp(), fixedIpAddress)) {
224                             InstanceIdentifier<InternalToExternalPortMap> intExtPortMapIdentifier =
225                                     routerPortsIdentifierBuilder.child(Ports
226                                     .class, new PortsKey(fixedNeutronPortName)).child(InternalToExternalPortMap.class,
227                                             new InternalToExternalPortMapKey(fixedIpAddress)).build();
228                             try (AcquireResult lock = tryRouterLock(fixedIpAddress)) {
229                                 if (!lock.wasAcquired()) {
230                                     // FIXME: why do we even bother with locking if we do not honor it?!
231                                     logTryLockFailure(fixedIpAddress);
232                                 }
233
234                                 // remove particular internal-to-external-port-map
235                                 LOG.debug("removing particular internal-to-external-port-map {}", intExtPortMap);
236                                 try {
237                                     MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
238                                         intExtPortMapIdentifier);
239                                 } catch (Exception e) {
240                                     LOG.error("Failure in deletion of internal-to-external-port-map {}", intExtPortMap,
241                                         e);
242                                 }
243                             }
244                         }
245                     }
246                 }
247                 LOG.debug("Deletion from FloatingIpInfo DS successful for fixedIp {} ", fixedIpAddress);
248             } else {
249                 LOG.warn("routerPorts for router {} - fixedIp {} not found", routerName, fixedIpAddress);
250             }
251         } catch (RuntimeException | ReadFailedException e) {
252             LOG.error("Failed to delete internal-to-external-port-map from FloatingIpInfo DS for fixed Ip {}",
253                     fixedIpAddress, e);
254         }
255     }
256
257     protected void dissociatefixedIPFromFloatingIP(String fixedNeutronPortName) {
258         InstanceIdentifier.InstanceIdentifierBuilder<FloatingIpInfo> floatingIpInfoIdentifierBuilder =
259                 InstanceIdentifier.builder(FloatingIpInfo.class);
260         try {
261             Optional<FloatingIpInfo> optionalFloatingIPInfo =
262                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
263                             floatingIpInfoIdentifierBuilder.build());
264             if (optionalFloatingIPInfo.isPresent()) {
265                 List<RouterPorts> routerPortsList = optionalFloatingIPInfo.get().getRouterPorts();
266                 if (routerPortsList != null && !routerPortsList.isEmpty()) {
267                     for (RouterPorts routerPorts : routerPortsList) {
268                         List<Ports> portsList = routerPorts.getPorts();
269                         if (portsList != null && !portsList.isEmpty()) {
270                             for (Ports ports : portsList) {
271                                 if (Objects.equals(ports.getPortName(), fixedNeutronPortName)) {
272                                     String routerName = routerPorts.getRouterId();
273                                     InstanceIdentifier.InstanceIdentifierBuilder<RouterPorts>
274                                         routerPortsIdentifierBuilder = floatingIpInfoIdentifierBuilder
275                                         .child(RouterPorts.class, new RouterPortsKey(routerName));
276                                     removeRouterPortsOrPortsNode(routerName, routerPortsIdentifierBuilder, portsList,
277                                             fixedNeutronPortName);
278                                     LOG.debug("Deletion from FloatingIpInfo DS successful for fixedIP neutron port {} ",
279                                             fixedNeutronPortName);
280                                     break;
281                                 }
282                             }
283                         }
284                     }
285                 } else {
286                     LOG.debug("No router present containing fixed to floating IP association(s)");
287                 }
288             } else {
289                 LOG.debug("FloatingIPInfo DS empty. Hence, no router present containing fixed to floating IP "
290                     + "association(s)");
291             }
292         } catch (ReadFailedException e) {
293             LOG.error("Failed to dissociate fixedIP from FloatingIpInfo DS for neutron port {}",
294                     fixedNeutronPortName, e);
295         }
296     }
297
298     private void removeRouterPortsOrPortsNode(String routerName,
299             InstanceIdentifier.InstanceIdentifierBuilder<RouterPorts> routerPortsIdentifierBuilder,
300             List<Ports> portsList, String fixedNeutronPortName) {
301         if (portsList.size() == 1) {
302             // remove entire routerPorts node
303             try (AcquireResult lock = tryRouterLock(routerName)) {
304                 if (!lock.wasAcquired()) {
305                     // FIXME: why do we even bother with locking if we do not honor it?!
306                     logTryLockFailure(routerName);
307                 }
308
309                 LOG.debug("removing routerPorts node: {} ", routerName);
310                 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, routerPortsIdentifierBuilder
311                     .build());
312             }
313         } else {
314             // remove entire ports node under this routerPorts node
315             try (AcquireResult lock = tryRouterLock(fixedNeutronPortName)) {
316                 if (!lock.wasAcquired()) {
317                     // FIXME: why do we even bother with locking if we do not honor it?!
318                     logTryLockFailure(fixedNeutronPortName);
319                 }
320
321                 LOG.debug("removing ports node {} under routerPorts node {}", fixedNeutronPortName, routerName);
322                 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
323                     routerPortsIdentifierBuilder.child(Ports.class, new PortsKey(fixedNeutronPortName)).build());
324             }
325         }
326     }
327
328     // TODO Clean up the exception handling
329     @SuppressWarnings("checkstyle:IllegalCatch")
330     // updates FloatingIPPortInfo to have isFloatingIPDeleted set to true on a floating IP delete
331     private void updateFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId) {
332         InstanceIdentifier<FloatingIpIdToPortMapping>  id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
333         try {
334             FloatingIpIdToPortMappingBuilder floatingIpIdToPortMappingBuilder = new
335                     FloatingIpIdToPortMappingBuilder().setFloatingIpId(floatingIpId).setFloatingIpDeleted(true);
336             LOG.debug("Updating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
337                     + " Port Info Config DS to set isFloatingIpDeleted flag as true",
338                 floatingIpId.getValue(), floatingIpPortId.getValue());
339             MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
340                     floatingIpIdToPortMappingBuilder.build());
341         } catch (Exception e) {
342             LOG.error("Updating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
343                     + " Port Info Config DS to set isFloatingIpDeleted flag as true failed", floatingIpId.getValue(),
344                     floatingIpPortId.getValue(), e);
345         }
346     }
347
348     // TODO Clean up the exception handling
349     @SuppressWarnings("checkstyle:IllegalCatch")
350     private void removeFromFloatingIpPortInfo(Uuid floatingIpId) {
351         InstanceIdentifier<FloatingIpIdToPortMapping>  id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
352         try {
353             LOG.debug("Deleting floating IP UUID {} to Floating IP neutron port mapping from Floating "
354                 + "IP Port Info Config DS", floatingIpId.getValue());
355             MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
356         } catch (Exception e) {
357             LOG.error("Deleting floating IP UUID {} to Floating IP neutron port mapping from Floating "
358                 + "IP Port Info Config DS failed", floatingIpId.getValue(), e);
359         }
360     }
361
362     @CheckReturnValue
363     private AcquireResult tryRouterLock(final String lockName) {
364         return routerLock.tryAcquire(lockName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
365     }
366
367     private static void logTryLockFailure(String lockName) {
368         LOG.warn("Lock for {} was not acquired, continuing anyway", lockName, new Throwable());
369     }
370 }