2 * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.neutronvpn;
10 import static org.opendaylight.netvirt.neutronvpn.NeutronvpnUtils.buildfloatingIpIdToPortMappingIdentifier;
12 import edu.umd.cs.findbugs.annotations.CheckReturnValue;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
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;
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;
58 private final DataBroker dataBroker;
59 private final NamedLocks<String> routerLock = new NamedLocks<>();
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;
70 LOG.info("{} init", getClass().getSimpleName());
77 Executors.shutdownAndAwaitTermination(getExecutorService());
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());
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());
102 // delete FloatingIpPortInfo mapping since floating IP is deleted and no fixed IP is associated to it
103 removeFromFloatingIpPortInfo(input.getUuid());
107 // populate the floating to fixed ip map upon association/dissociation from fixed ip
109 public void update(InstanceIdentifier<Floatingip> identifier, Floatingip original, Floatingip update) {
110 LOG.trace("Handling FloatingIptoFixedIp mapping : key: {}, original value={}, update value={}", identifier,
112 IpAddress oldFixedIp = original.getFixedIpAddress();
113 IpAddress newFixedIp = update.getFixedIpAddress();
114 String floatingIp = update.getFloatingIpAddress().getIpv4Address().getValue();
116 if (oldFixedIp != null && !oldFixedIp.equals(newFixedIp)) {
117 clearFromFloatingIpInfo(original.getRouterId().getValue(), original.getPortId().getValue(), oldFixedIp
118 .getIpv4Address().getValue());
120 if (newFixedIp != null && !newFixedIp.equals(oldFixedIp)) {
121 addToFloatingIpInfo(update.getRouterId().getValue(), update.getFloatingNetworkId(), update.getPortId()
122 .getValue(), newFixedIp.getIpv4Address().getValue(), floatingIp, update.getUuid());
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();
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());
142 LOG.debug("Creating new routerPorts node {} in floatingIpInfo DS for floating IP {} on fixed "
143 + "neutron port {} : ", routerName, floatingIpAddress, fixedNeutronPortName);
145 new RouterPortsBuilder().withKey(new RouterPortsKey(routerName)).setRouterId(routerName);
147 if (extNetworkId != null) {
148 routerPortsBuilder.setExternalNetworkId(extNetworkId);
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);
160 if (fixedNeutronPortBuilder == null) {
161 fixedNeutronPortBuilder = new PortsBuilder().withKey(new PortsKey(fixedNeutronPortName))
162 .setPortName(fixedNeutronPortName);
164 if (fixedIpAddress != null) {
165 List<InternalToExternalPortMap> intExtPortMapList
166 = new ArrayList<InternalToExternalPortMap>(fixedNeutronPortBuilder
167 .getInternalToExternalPortMap().values());
168 if (intExtPortMapList == null) {
169 intExtPortMapList = new ArrayList<>();
171 InternalToExternalPortMap intExtPortMap = new InternalToExternalPortMapBuilder().withKey(new
172 InternalToExternalPortMapKey(fixedIpAddress)).setInternalIp(fixedIpAddress)
173 .setExternalIp(floatingIpAddress).setExternalId(floatingIpId).build();
174 intExtPortMapList.add(intExtPortMap);
175 fixedNeutronPortBuilder.setInternalToExternalPortMap(intExtPortMapList);
177 portsList.add(fixedNeutronPortBuilder.build());
178 routerPortsBuilder.setPorts(portsList);
181 try (AcquireResult lock = tryRouterLock(routerName)) {
182 if (!lock.wasAcquired()) {
183 // FIXME: why do we even bother with locking if we do not honor it?!
184 logTryLockFailure(routerName);
187 LOG.debug("Creating/Updating routerPorts node {} in floatingIpInfo DS for floating IP {} on fixed "
188 + "neutron port {} : ", routerName, floatingIpAddress, fixedNeutronPortName);
189 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routerPortsIdentifier,
190 routerPortsBuilder.build());
191 LOG.debug("FloatingIpInfo DS updated for floating IP {} ", floatingIpAddress);
193 } catch (RuntimeException | ExecutionException | InterruptedException e) {
194 LOG.error("addToFloatingIpInfo failed for floating IP: {} ", floatingIpAddress, e);
198 // TODO Clean up the exception handling
199 @SuppressWarnings("checkstyle:IllegalCatch")
200 private void clearFromFloatingIpInfo(String routerName, String fixedNeutronPortName, String fixedIpAddress) {
201 InstanceIdentifier.InstanceIdentifierBuilder<RouterPorts> routerPortsIdentifierBuilder = InstanceIdentifier
202 .builder(FloatingIpInfo.class).child(RouterPorts.class, new RouterPortsKey(routerName));
204 Optional<RouterPorts> optionalRouterPorts =
205 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
206 routerPortsIdentifierBuilder.build());
207 if (optionalRouterPorts.isPresent()) {
208 RouterPorts routerPorts = optionalRouterPorts.get();
209 Map<PortsKey, Ports> keyPortsMap = routerPorts.nonnullPorts();
210 Map<InternalToExternalPortMapKey, InternalToExternalPortMap> keyInternalToExternalPortMapMap
211 = new HashMap<InternalToExternalPortMapKey, InternalToExternalPortMap>();
212 for (Ports ports : keyPortsMap.values()) {
213 if (Objects.equals(ports.getPortName(), fixedNeutronPortName)) {
214 keyInternalToExternalPortMapMap = ports.nonnullInternalToExternalPortMap();
218 if (keyInternalToExternalPortMapMap.size() == 1) {
219 removeRouterPortsOrPortsNode(routerName, routerPortsIdentifierBuilder,
220 new ArrayList<Ports>(keyPortsMap.values()), fixedNeutronPortName);
222 for (InternalToExternalPortMap intToExtMap : keyInternalToExternalPortMapMap.values()) {
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);
234 // remove particular internal-to-external-port-map
235 LOG.debug("removing particular internal-to-external-port-map {}",
236 keyInternalToExternalPortMapMap);
238 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
239 intExtPortMapIdentifier);
240 } catch (Exception e) {
241 LOG.error("Failure in deletion of internal-to-external-port-map {}",
242 keyInternalToExternalPortMapMap, e);
248 LOG.debug("Deletion from FloatingIpInfo DS successful for fixedIp {} ", fixedIpAddress);
250 LOG.warn("routerPorts for router {} - fixedIp {} not found", routerName, fixedIpAddress);
252 } catch (RuntimeException | ExecutionException | InterruptedException e) {
253 LOG.error("Failed to delete internal-to-external-port-map from FloatingIpInfo DS for fixed Ip {}",
258 protected void dissociatefixedIPFromFloatingIP(String fixedNeutronPortName) {
259 InstanceIdentifier.InstanceIdentifierBuilder<FloatingIpInfo> floatingIpInfoIdentifierBuilder =
260 InstanceIdentifier.builder(FloatingIpInfo.class);
262 Optional<FloatingIpInfo> optionalFloatingIPInfo =
263 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
264 floatingIpInfoIdentifierBuilder.build());
265 if (optionalFloatingIPInfo.isPresent()) {
266 Map<RouterPortsKey, RouterPorts> keyRouterPortsMap = optionalFloatingIPInfo.get().getRouterPorts();
267 if (keyRouterPortsMap != null && !keyRouterPortsMap.isEmpty()) {
268 for (RouterPorts routerPorts : keyRouterPortsMap.values()) {
269 Map<PortsKey, Ports> keyPortsMap = routerPorts.getPorts();
270 if (keyPortsMap != null && !keyPortsMap.isEmpty()) {
271 for (Ports ports : keyPortsMap.values()) {
272 if (Objects.equals(ports.getPortName(), fixedNeutronPortName)) {
273 String routerName = routerPorts.getRouterId();
274 InstanceIdentifier.InstanceIdentifierBuilder<RouterPorts>
275 routerPortsIdentifierBuilder = floatingIpInfoIdentifierBuilder
276 .child(RouterPorts.class, new RouterPortsKey(routerName));
277 removeRouterPortsOrPortsNode(routerName, routerPortsIdentifierBuilder,
278 new ArrayList<Ports>(keyPortsMap.values()), fixedNeutronPortName);
279 LOG.debug("Deletion from FloatingIpInfo DS successful for fixedIP neutron port {} ",
280 fixedNeutronPortName);
287 LOG.debug("No router present containing fixed to floating IP association(s)");
290 LOG.debug("FloatingIPInfo DS empty. Hence, no router present containing fixed to floating IP "
293 } catch (ExecutionException | InterruptedException e) {
294 LOG.error("Failed to dissociate fixedIP from FloatingIpInfo DS for neutron port {}",
295 fixedNeutronPortName, e);
299 private void removeRouterPortsOrPortsNode(String routerName,
300 InstanceIdentifier.InstanceIdentifierBuilder<RouterPorts> routerPortsIdentifierBuilder,
301 List<Ports> portsList, String fixedNeutronPortName) {
302 if (portsList.size() == 1) {
303 // remove entire routerPorts node
304 try (AcquireResult lock = tryRouterLock(routerName)) {
305 if (!lock.wasAcquired()) {
306 // FIXME: why do we even bother with locking if we do not honor it?!
307 logTryLockFailure(routerName);
310 LOG.debug("removing routerPorts node: {} ", routerName);
311 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, routerPortsIdentifierBuilder
315 // remove entire ports node under this routerPorts node
316 try (AcquireResult lock = tryRouterLock(fixedNeutronPortName)) {
317 if (!lock.wasAcquired()) {
318 // FIXME: why do we even bother with locking if we do not honor it?!
319 logTryLockFailure(fixedNeutronPortName);
322 LOG.debug("removing ports node {} under routerPorts node {}", fixedNeutronPortName, routerName);
323 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION,
324 routerPortsIdentifierBuilder.child(Ports.class, new PortsKey(fixedNeutronPortName)).build());
329 // TODO Clean up the exception handling
330 @SuppressWarnings("checkstyle:IllegalCatch")
331 // updates FloatingIPPortInfo to have isFloatingIPDeleted set to true on a floating IP delete
332 private void updateFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId) {
333 InstanceIdentifier<FloatingIpIdToPortMapping> id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
335 FloatingIpIdToPortMappingBuilder floatingIpIdToPortMappingBuilder = new
336 FloatingIpIdToPortMappingBuilder().setFloatingIpId(floatingIpId).setFloatingIpDeleted(true);
337 LOG.debug("Updating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
338 + " Port Info Config DS to set isFloatingIpDeleted flag as true",
339 floatingIpId.getValue(), floatingIpPortId.getValue());
340 MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
341 floatingIpIdToPortMappingBuilder.build());
342 } catch (Exception e) {
343 LOG.error("Updating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
344 + " Port Info Config DS to set isFloatingIpDeleted flag as true failed", floatingIpId.getValue(),
345 floatingIpPortId.getValue(), e);
349 // TODO Clean up the exception handling
350 @SuppressWarnings("checkstyle:IllegalCatch")
351 private void removeFromFloatingIpPortInfo(Uuid floatingIpId) {
352 InstanceIdentifier<FloatingIpIdToPortMapping> id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
354 LOG.debug("Deleting floating IP UUID {} to Floating IP neutron port mapping from Floating "
355 + "IP Port Info Config DS", floatingIpId.getValue());
356 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
357 } catch (Exception e) {
358 LOG.error("Deleting floating IP UUID {} to Floating IP neutron port mapping from Floating "
359 + "IP Port Info Config DS failed", floatingIpId.getValue(), e);
364 private AcquireResult tryRouterLock(final String lockName) {
365 return routerLock.tryAcquire(lockName, LOCK_WAIT_TIME, TimeUnit.SECONDS);
368 private static void logTryLockFailure(String lockName) {
369 LOG.warn("Lock for {} was not acquired, continuing anyway", lockName, new Throwable());