2 * Copyright © 2015, 2018 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 com.google.common.base.Optional;
11 import com.google.common.base.Strings;
12 import com.google.gson.Gson;
13 import com.google.gson.JsonArray;
14 import com.google.gson.JsonElement;
15 import com.google.gson.JsonObject;
17 import java.time.Duration;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Locale;
26 import java.util.stream.Collectors;
27 import javax.annotation.PostConstruct;
28 import javax.inject.Singleton;
29 import org.apache.commons.lang3.ObjectUtils;
30 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
31 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
32 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
33 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
34 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
35 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
36 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
37 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
38 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
39 import org.opendaylight.genius.mdsalutil.MDSALUtil;
40 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
41 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
42 import org.opendaylight.netvirt.elanmanager.api.IElanService;
43 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
44 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
45 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlanBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAclBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.elan._interface.StaticMacEntries;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.RoutersBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingKey;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.hostconfig.rev150712.hostconfig.attributes.hostconfigs.Hostconfig;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
73 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
78 public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<Port, NeutronPortChangeListener> {
79 private static final Logger LOG = LoggerFactory.getLogger(NeutronPortChangeListener.class);
80 private final DataBroker dataBroker;
81 private final ManagedNewTransactionRunner txRunner;
82 private final NeutronvpnManager nvpnManager;
83 private final NeutronvpnNatManager nvpnNatManager;
84 private final NeutronSubnetGwMacResolver gwMacResolver;
85 private final IElanService elanService;
86 private final JobCoordinator jobCoordinator;
87 private final NeutronvpnUtils neutronvpnUtils;
88 private final HostConfigCache hostConfigCache;
89 private final DataTreeEventCallbackRegistrar eventCallbacks;
91 public NeutronPortChangeListener(final DataBroker dataBroker,
92 final NeutronvpnManager neutronvpnManager,
93 final NeutronvpnNatManager neutronvpnNatManager,
94 final NeutronSubnetGwMacResolver gwMacResolver,
95 final IElanService elanService,
96 final JobCoordinator jobCoordinator,
97 final NeutronvpnUtils neutronvpnUtils,
98 final HostConfigCache hostConfigCache,
99 final DataTreeEventCallbackRegistrar dataTreeEventCallbackRegistrar) {
100 super(Port.class, NeutronPortChangeListener.class);
101 this.dataBroker = dataBroker;
102 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
103 nvpnManager = neutronvpnManager;
104 nvpnNatManager = neutronvpnNatManager;
105 this.gwMacResolver = gwMacResolver;
106 this.elanService = elanService;
107 this.jobCoordinator = jobCoordinator;
108 this.neutronvpnUtils = neutronvpnUtils;
109 this.hostConfigCache = hostConfigCache;
110 this.eventCallbacks = dataTreeEventCallbackRegistrar;
116 LOG.info("{} init", getClass().getSimpleName());
117 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
121 protected InstanceIdentifier<Port> getWildCardPath() {
122 return InstanceIdentifier.create(Neutron.class).child(Ports.class).child(Port.class);
126 protected NeutronPortChangeListener getDataTreeChangeListener() {
127 return NeutronPortChangeListener.this;
132 protected void add(InstanceIdentifier<Port> identifier, Port input) {
133 String portName = input.getUuid().getValue();
134 LOG.trace("Adding Port : key: {}, value={}", identifier, input);
135 Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
136 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
137 LOG.warn("neutron vpn received a port add() for a network without a provider extension augmentation "
138 + "or with an unsupported network type for the port {} which is part of network {}",
143 neutronvpnUtils.addToPortCache(input);
144 String portStatus = NeutronUtils.PORT_STATUS_DOWN;
145 if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
146 if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(input.getDeviceOwner())) {
147 handleRouterInterfaceAdded(input);
148 NeutronUtils.createPortStatus(input.getUuid().getValue(), NeutronUtils.PORT_STATUS_ACTIVE, dataBroker);
151 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())) {
152 handleRouterGatewayUpdated(input);
153 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
154 } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
155 handleFloatingIpPortUpdated(null, input);
156 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
159 // Switchdev ports need to be bounded to a host before creation
160 // in order to validate the supported vnic types from the hostconfig
161 if (input.getFixedIps() != null
162 && !input.getFixedIps().isEmpty()
163 && !(isPortTypeSwitchdev(input) && !isPortBound(input))) {
164 handleNeutronPortCreated(input);
166 NeutronUtils.createPortStatus(input.getUuid().getValue(), portStatus, dataBroker);
170 protected void remove(InstanceIdentifier<Port> identifier, Port input) {
171 LOG.trace("Removing Port : key: {}, value={}", identifier, input);
172 Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
173 // need to proceed with deletion in case network is null for a case where v2 sync happens and a read for
174 // network from NN returns null, but the deletion process for port needs to continue
175 if (network != null && !NeutronvpnUtils.isNetworkTypeSupported(network)) {
176 String portName = input.getUuid().getValue();
177 LOG.warn("neutron vpn received a port remove() for a network without a provider extension augmentation "
178 + "or with an unsupported network type for the port {} which is part of network {}",
182 neutronvpnUtils.removeFromPortCache(input);
183 NeutronUtils.deletePortStatus(input.getUuid().getValue(), dataBroker);
185 if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
186 if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(input.getDeviceOwner())) {
187 handleRouterInterfaceRemoved(input);
188 /* nothing else to do here */
190 } else if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())
191 || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
192 elanService.removeKnownL3DmacAddress(input.getMacAddress().getValue(), input.getNetworkId().getValue());
195 if (input.getFixedIps() != null) {
196 handleNeutronPortDeleted(input);
201 protected void update(InstanceIdentifier<Port> identifier, Port original, Port update) {
202 // Switchdev ports need to be bounded to a host before creation
203 // in order to validate the supported vnic types from the hostconfig
204 if (isPortTypeSwitchdev(original)
205 && !isPortBound(original)
206 && isPortBound(update)) {
207 handleNeutronPortCreated(update);
209 final String portName = update.getUuid().getValue();
210 Network network = neutronvpnUtils.getNeutronNetwork(update.getNetworkId());
211 LOG.info("Update port {} from network {}", portName, update.getNetworkId().toString());
212 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
213 LOG.warn("neutron vpn received a port update() for a network without a provider extension augmentation "
214 + "or with an unsupported network type for the port {} which is part of network {}",
218 neutronvpnUtils.addToPortCache(update);
220 if ((Strings.isNullOrEmpty(original.getDeviceOwner()) || Strings.isNullOrEmpty(original.getDeviceId())
221 || NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING.equalsIgnoreCase(original.getDeviceId()))
222 && !Strings.isNullOrEmpty(update.getDeviceOwner()) && !Strings.isNullOrEmpty(update.getDeviceId())) {
223 if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(update.getDeviceOwner())) {
224 handleRouterInterfaceAdded(update);
227 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(update.getDeviceOwner())) {
228 handleRouterGatewayUpdated(update);
229 } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(update.getDeviceOwner())) {
230 handleFloatingIpPortUpdated(original, update);
233 Set<FixedIps> oldIPs = getFixedIpSet(original.getFixedIps());
234 Set<FixedIps> newIPs = getFixedIpSet(update.getFixedIps());
235 if (!oldIPs.equals(newIPs)) {
236 handleNeutronPortUpdated(original, update);
240 // check if port security enabled/disabled as part of port update
241 boolean origSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(original);
242 boolean updatedSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(update);
244 if (origSecurityEnabled || updatedSecurityEnabled) {
245 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(portName);
246 jobCoordinator.enqueueJob("PORT- " + portName,
247 () -> Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(confTx -> {
248 Optional<Interface> optionalInf =
249 confTx.read(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier).checkedGet();
250 if (optionalInf.isPresent()) {
251 InterfaceBuilder interfaceBuilder = new InterfaceBuilder(optionalInf.get());
252 InterfaceAcl infAcl = handlePortSecurityUpdated(original, update,
253 origSecurityEnabled, updatedSecurityEnabled, interfaceBuilder).build();
254 interfaceBuilder.addAugmentation(InterfaceAcl.class, infAcl);
255 LOG.info("update: Of-port-interface updation for port {}", portName);
256 // Update OFPort interface for this neutron port
257 confTx.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier,
258 interfaceBuilder.build());
260 LOG.warn("update: Interface {} is not present", portName);
266 private void handleFloatingIpPortUpdated(Port original, Port update) {
267 if ((original == null || original.getDeviceId().equals(NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING))
268 && !update.getDeviceId().equals(NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING)) {
269 // populate floating-ip uuid and floating-ip port attributes (uuid, mac and subnet id for the ONLY
270 // fixed IP) to be used by NAT, depopulated in NATService once mac is retrieved in the removal path
271 addToFloatingIpPortInfo(new Uuid(update.getDeviceId()), update.getUuid(), update.getFixedIps().get(0)
272 .getSubnetId(), update.getMacAddress().getValue());
273 elanService.addKnownL3DmacAddress(update.getMacAddress().getValue(), update.getNetworkId().getValue());
277 private void handleRouterInterfaceAdded(Port routerPort) {
278 if (routerPort.getDeviceId() != null) {
279 Uuid routerId = new Uuid(routerPort.getDeviceId());
280 Uuid infNetworkId = routerPort.getNetworkId();
281 Uuid existingVpnId = neutronvpnUtils.getVpnForNetwork(infNetworkId);
283 elanService.addKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
284 if (existingVpnId == null) {
285 Set<Uuid> listVpnIds = new HashSet<>();
286 Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true);
290 listVpnIds.add(vpnId);
291 Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId);
292 List<Subnetmap> subnetMapList = new ArrayList<>();
293 List<FixedIps> portIps = routerPort.getFixedIps();
294 boolean portIsIpv6 = false;
295 for (FixedIps portIP : portIps) {
296 // NOTE: Please donot change the order of calls to updateSubnetNodeWithFixedIP
297 // and addSubnetToVpn here
298 if (internetVpnId != null
299 && portIP.getIpAddress().getIpv6Address() != null) {
302 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
303 Uuid subnetId = portIP.getSubnetId();
304 nvpnManager.updateSubnetNodeWithFixedIp(subnetId, routerId,
305 routerPort.getUuid(), ipValue, routerPort.getMacAddress().getValue(), vpnId);
306 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
307 subnetMapList.add(sn);
310 listVpnIds.add(internetVpnId);
311 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChangeToAdd(
312 IpVersionChoice.IPV6, internetVpnId)) {
313 neutronvpnUtils.updateVpnInstanceWithIpFamily(internetVpnId.getValue(),
314 IpVersionChoice.IPV6, true);
315 neutronvpnUtils.updateVpnInstanceWithFallback(internetVpnId.getValue(), true);
318 if (! subnetMapList.isEmpty()) {
319 nvpnManager.createVpnInterface(listVpnIds, routerPort, null);
321 for (FixedIps portIP : routerPort.getFixedIps()) {
322 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
323 IpVersionChoice version = neutronvpnUtils.getIpVersionFromString(ipValue);
324 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChangeToAdd(version, vpnId)) {
325 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(),
328 if (version.isIpVersionChosen(IpVersionChoice.IPV4)) {
329 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(),
330 null /* internet-vpn-id */);
332 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(), internetVpnId);
334 LOG.trace("NeutronPortChangeListener Add Subnet Gateway IP {} MAC {} Interface {} VPN {}",
335 ipValue, routerPort.getMacAddress(),
336 routerPort.getUuid().getValue(), vpnId.getValue());
338 nvpnManager.addToNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
339 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
340 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
341 String portInterfaceName = createOfPortInterface(routerPort, confTx);
342 createElanInterface(routerPort, portInterfaceName, confTx);
343 }), LOG, "Error creating ELAN interface for {}", routerPort);
345 LOG.error("Neutron network {} corresponding to router interface port {} for neutron router {}"
346 + " already associated to VPN {}", infNetworkId.getValue(), routerPort.getUuid().getValue(),
347 routerId.getValue(), existingVpnId.getValue());
352 private void handleRouterInterfaceRemoved(Port routerPort) {
353 if (routerPort.getDeviceId() != null) {
354 Uuid routerId = new Uuid(routerPort.getDeviceId());
355 Uuid infNetworkId = routerPort.getNetworkId();
356 elanService.removeKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
357 Uuid vpnId = ObjectUtils.defaultIfNull(neutronvpnUtils.getVpnForRouter(routerId, true), routerId);
358 List<FixedIps> portIps = routerPort.getFixedIps();
359 boolean vpnInstanceInternetIpVersionRemoved = false;
360 Uuid vpnInstanceInternetUuid = null;
361 for (FixedIps portIP : portIps) {
362 // Internet VPN : flush InternetVPN first
363 Uuid subnetId = portIP.getSubnetId();
364 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
365 if (sn != null && sn.getInternetVpnId() != null) {
366 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, sn.getInternetVpnId())) {
367 vpnInstanceInternetIpVersionRemoved = true;
368 vpnInstanceInternetUuid = sn.getInternetVpnId();
370 nvpnManager.updateVpnInternetForSubnet(sn, sn.getInternetVpnId(), false);
373 /* Remove ping responder for router interfaces
374 * A router interface reference in a VPN will have to be removed before the host interface references
375 * for that subnet in the VPN are removed. This is to ensure that the FIB Entry of the router interface
376 * is not the last entry to be removed for that subnet in the VPN.
377 * If router interface FIB entry is the last to be removed for a subnet in a VPN , then all the host
378 * interface references in the vpn will already have been cleared, which will cause failures in
379 * cleanup of router interface flows*/
380 nvpnManager.deleteVpnInterface(routerPort.getUuid().getValue(),
381 null /* vpn-id */, null /* wrtConfigTxn*/);
382 // update RouterInterfaces map
383 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
384 boolean vpnInstanceIpVersionRemoved = false;
385 IpVersionChoice vpnInstanceIpVersionToRemove = IpVersionChoice.UNDEFINED;
386 for (FixedIps portIP : portIps) {
387 Subnetmap sn = neutronvpnUtils.getSubnetmap(portIP.getSubnetId());
388 // router Port have either IPv4 or IPv6, never both
389 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, vpnId)) {
390 vpnInstanceIpVersionRemoved = true;
391 vpnInstanceIpVersionToRemove = neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
393 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
394 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipValue, confTx);
395 // NOTE: Please donot change the order of calls to removeSubnetFromVpn and
396 // and updateSubnetNodeWithFixedIP
397 nvpnManager.removeSubnetFromVpn(vpnId, portIP.getSubnetId(),
398 sn != null ? sn.getInternetVpnId() : null);
399 nvpnManager.updateSubnetNodeWithFixedIp(portIP.getSubnetId(), null, null, null, null, null);
401 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
402 deleteElanInterface(routerPort.getUuid().getValue(), confTx);
403 deleteOfPortInterface(routerPort, confTx);
404 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
405 if (vpnInstanceIpVersionRemoved) {
406 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), vpnInstanceIpVersionToRemove,
409 }), LOG, "Error handling interface removal");
410 if (vpnInstanceInternetIpVersionRemoved) {
411 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnInstanceInternetUuid.getValue(),
412 IpVersionChoice.IPV6, false);
413 neutronvpnUtils.updateVpnInstanceWithFallback(vpnInstanceInternetUuid.getValue(), false);
418 private void handleRouterGatewayUpdated(Port routerGwPort) {
419 Uuid routerId = new Uuid(routerGwPort.getDeviceId());
420 Uuid networkId = routerGwPort.getNetworkId();
421 Network network = neutronvpnUtils.getNeutronNetwork(networkId);
422 if (network == null) {
425 boolean isExternal = neutronvpnUtils.getIsExternal(network);
427 Uuid vpnInternetId = neutronvpnUtils.getVpnForNetwork(networkId);
428 if (vpnInternetId != null) {
429 List<Subnetmap> snList = neutronvpnUtils.getNeutronRouterSubnetMaps(routerId);
430 for (Subnetmap sn : snList) {
431 if (sn.getNetworkId() == networkId) {
434 if (neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp()) != IpVersionChoice.IPV6) {
437 nvpnManager.addSubnetToVpn(null, sn.getId(), vpnInternetId);
441 elanService.addKnownL3DmacAddress(routerGwPort.getMacAddress().getValue(), networkId.getValue());
443 Router router = neutronvpnUtils.getNeutronRouter(routerId);
444 if (router == null) {
445 LOG.warn("No router found for router GW port {} for router {}", routerGwPort.getUuid().getValue(),
446 routerId.getValue());
448 eventCallbacks.onAddOrUpdate(LogicalDatastoreType.CONFIGURATION,
449 neutronvpnUtils.getNeutronRouterIid(routerId), (unused, newRouter) -> {
450 setupGwMac(newRouter, routerGwPort, routerId);
451 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
452 }, Duration.ofSeconds(3), iid -> {
453 LOG.error("GwPort {} added without Router", routerGwPort.getUuid().getValue());
457 setupGwMac(router, routerGwPort, routerId);
460 private void setupGwMac(Router router, Port routerGwPort, Uuid routerId) {
461 gwMacResolver.sendArpRequestsToExtGateways(router);
462 setExternalGwMac(routerGwPort, routerId);
466 private void setExternalGwMac(Port routerGwPort, Uuid routerId) {
467 // During full-sync networking-odl syncs routers before ports. As such,
468 // the MAC of the router's gw port is not available to be set when the
469 // router is written. We catch that here.
470 InstanceIdentifier<Routers> routersId = NeutronvpnUtils.buildExtRoutersIdentifier(routerId);
471 Optional<Routers> optionalRouter = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId);
472 if (!optionalRouter.isPresent()) {
476 Routers extRouters = optionalRouter.get();
477 if (extRouters.getExtGwMacAddress() != null) {
481 RoutersBuilder builder = new RoutersBuilder(extRouters);
482 builder.setExtGwMacAddress(routerGwPort.getMacAddress().getValue());
483 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId, builder.build());
486 private String getPortHostId(final Port port) {
488 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
489 if (portBinding != null) {
490 return portBinding.getHostId();
496 private Hostconfig getHostConfig(final Port port) {
497 String hostId = getPortHostId(port);
498 if (hostId == null) {
501 Optional<Hostconfig> hostConfig;
503 hostConfig = this.hostConfigCache.get(hostId);
504 } catch (ReadFailedException e) {
505 LOG.error("failed to read host config from host {}", hostId, e);
508 return hostConfig.isPresent() ? hostConfig.get() : null;
511 private boolean isPortBound(final Port port) {
512 String hostId = getPortHostId(port);
513 return hostId != null && !hostId.isEmpty();
516 private boolean isPortVnicTypeDirect(Port port) {
517 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
518 if (portBinding == null || portBinding.getVnicType() == null) {
519 // By default, VNIC_TYPE is NORMAL
522 String vnicType = portBinding.getVnicType().trim().toLowerCase(Locale.getDefault());
523 return vnicType.equals(NeutronConstants.VNIC_TYPE_DIRECT);
526 private boolean isSupportedVnicTypeByHost(final Port port, final String vnicType) {
527 Hostconfig hostConfig = getHostConfig(port);
528 String supportStr = String.format("\"vnic_type\": \"%s\"", vnicType);
529 if (hostConfig != null && hostConfig.getConfig().contains(supportStr)) {
535 private Map<String, JsonElement> unmarshal(final String profile) {
536 if (null == profile) {
539 Gson gson = new Gson();
540 JsonObject jsonObject = gson.fromJson(profile, JsonObject.class);
541 Map<String, JsonElement> map = new HashMap();
542 for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
543 map.put(entry.getKey(), entry.getValue());
548 private boolean isPortTypeSwitchdev(final Port port) {
549 if (!isPortVnicTypeDirect(port)) {
553 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
554 String profile = portBinding.getProfile();
555 if (profile == null || profile.isEmpty()) {
556 LOG.debug("Port {} has no binding:profile values", port.getUuid());
560 Map<String, JsonElement> mapProfile = unmarshal(profile);
561 JsonElement capabilities = mapProfile.get(NeutronConstants.BINDING_PROFILE_CAPABILITIES);
562 LOG.debug("Port {} capabilities: {}", port.getUuid(), capabilities);
563 if (capabilities == null || !capabilities.isJsonArray()) {
564 LOG.debug("binding profile capabilities not in array format: {}", capabilities);
568 JsonArray capabilitiesArray = capabilities.getAsJsonArray();
569 Gson gson = new Gson();
570 JsonElement switchdevElement = gson.fromJson(NeutronConstants.SWITCHDEV, JsonElement.class);
571 return capabilitiesArray.contains(switchdevElement);
575 private void handleNeutronPortCreated(final Port port) {
576 final String portName = port.getUuid().getValue();
577 final Uuid portId = port.getUuid();
578 final List<FixedIps> portIpAddrsList = port.getFixedIps();
579 if (NeutronConstants.IS_ODL_DHCP_PORT.test(port)) {
582 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
583 // add direct port to subnetMaps config DS
584 if (!(NeutronUtils.isPortVnicTypeNormal(port)
585 || (isPortTypeSwitchdev(port)
586 && isSupportedVnicTypeByHost(port, NeutronConstants.VNIC_TYPE_DIRECT)))) {
587 for (FixedIps ip: portIpAddrsList) {
588 nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), null, portId);
590 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
591 + "OF Port interfaces are not created", portName);
592 return Collections.emptyList();
594 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
595 LOG.info("Of-port-interface creation for port {}", portName);
596 // Create of-port interface for this neutron port
597 String portInterfaceName = createOfPortInterface(port, tx);
598 LOG.debug("Creating ELAN Interface for port {}", portName);
599 createElanInterface(port, portInterfaceName, tx);
600 Set<Uuid> vpnIdList = new HashSet<>();
601 Set<Uuid> routerIds = new HashSet<>();
602 for (FixedIps ip: portIpAddrsList) {
603 Subnetmap subnetMap = nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), portId, null);
604 if (subnetMap != null && subnetMap.getInternetVpnId() != null) {
605 if (!vpnIdList.contains(subnetMap.getInternetVpnId())) {
606 vpnIdList.add(subnetMap.getInternetVpnId());
609 if (subnetMap != null && subnetMap.getVpnId() != null) {
610 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
611 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
612 Uuid vpnId = subnetMap.getVpnId();
614 vpnIdList.add(vpnId);
617 if (subnetMap != null && subnetMap.getRouterId() != null) {
618 routerIds.add(subnetMap.getRouterId());
621 if (!vpnIdList.isEmpty()) {
622 // create new vpn-interface for neutron port
623 LOG.debug("handleNeutronPortCreated: Adding VPN Interface for port {} from network {}", portName,
624 port.getNetworkId().toString());
625 nvpnManager.createVpnInterface(vpnIdList, port, tx);
626 if (!routerIds.isEmpty()) {
627 for (Uuid routerId : routerIds) {
628 nvpnManager.addToNeutronRouterInterfacesMap(routerId,port.getUuid().getValue());
636 private void handleNeutronPortDeleted(final Port port) {
637 final String portName = port.getUuid().getValue();
638 final Uuid portId = port.getUuid();
639 final List<FixedIps> portIpsList = port.getFixedIps();
640 jobCoordinator.enqueueJob("PORT- " + portName,
641 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
642 if (!(NeutronUtils.isPortVnicTypeNormal(port) || isPortTypeSwitchdev(port))) {
643 for (FixedIps ip : portIpsList) {
644 // remove direct port from subnetMaps config DS
645 nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), null, portId);
647 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
648 + "Skipping OF Port interfaces removal", portName);
652 Set<Uuid> routerIds = new HashSet<>();
653 Uuid internetVpnId = null;
654 for (FixedIps ip : portIpsList) {
655 Subnetmap subnetMap = nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), portId, null);
656 if (subnetMap == null) {
659 if (subnetMap.getVpnId() != null) {
660 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
661 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
662 vpnId = subnetMap.getVpnId();
664 if (subnetMap.getRouterId() != null) {
665 routerIds.add(subnetMap.getRouterId());
667 internetVpnId = subnetMap.getInternetVpnId();
669 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(port.getDeviceOwner())) {
670 String routerGwIpAddress = String.valueOf(ip.getIpAddress().getValue());
672 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), routerGwIpAddress, confTx);
674 if (internetVpnId != null) {
675 neutronvpnUtils.removeVpnPortFixedIpToPort(internetVpnId.getValue(),
676 routerGwIpAddress, confTx);
680 if (vpnId != null || internetVpnId != null) {
681 // remove vpn-interface for this neutron port
682 LOG.debug("removing VPN Interface for port {}", portName);
683 if (!routerIds.isEmpty()) {
684 for (Uuid routerId : routerIds) {
685 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portName);
688 nvpnManager.deleteVpnInterface(portName, null /* vpn-id */, confTx);
690 // Remove of-port interface for this neutron port
691 // ELAN interface is also implicitly deleted as part of this operation
692 LOG.debug("Of-port-interface removal for port {}", portName);
693 deleteOfPortInterface(port, confTx);
694 //dissociate fixedIP from floatingIP if associated
695 nvpnManager.dissociatefixedIPFromFloatingIP(port.getUuid().getValue());
700 private void handleNeutronPortUpdated(final Port portoriginal, final Port portupdate) {
701 final List<FixedIps> portoriginalIps = portoriginal.getFixedIps();
702 final List<FixedIps> portupdateIps = portupdate.getFixedIps();
703 if (portoriginalIps == null || portoriginalIps.isEmpty()) {
704 handleNeutronPortCreated(portupdate);
708 if (portupdateIps == null || portupdateIps.isEmpty()) {
709 LOG.info("Ignoring portUpdate (fixed_ip removal) for port {} as this case is handled "
710 + "during subnet deletion event.", portupdate.getUuid().getValue());
713 jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(),
714 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
715 final List<Uuid> originalSnMapsIds = portoriginalIps.stream().map(FixedIps::getSubnetId)
716 .collect(Collectors.toList());
717 final List<Uuid> updateSnMapsIds = portupdateIps.stream().map(FixedIps::getSubnetId)
718 .collect(Collectors.toList());
719 Set<Uuid> originalRouterIds = new HashSet<>();
720 Set<Uuid> oldVpnIds = new HashSet<>();
721 for (Uuid snId: originalSnMapsIds) {
722 if (!updateSnMapsIds.remove(snId)) {
723 // snId was present in originalSnMapsIds, but not in updateSnMapsIds
724 Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
726 if (subnetMapOld != null && subnetMapOld.getVpnId() != null) {
727 oldVpnIds.add(subnetMapOld.getVpnId());
729 if (subnetMapOld != null && subnetMapOld.getInternetVpnId() != null) {
730 oldVpnIds.add(subnetMapOld.getInternetVpnId());
732 if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
733 originalRouterIds.add(subnetMapOld.getRouterId());
737 Set<Uuid> newVpnIds = new HashSet();
738 Set<Uuid> newRouterIds = new HashSet<>();
739 for (Uuid snId: updateSnMapsIds) {
740 Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
741 if (subnetMapNew != null) {
742 if (subnetMapNew.getVpnId() != null) {
743 newVpnIds.add(subnetMapNew.getVpnId());
745 if (subnetMapNew.getInternetVpnId() != null) {
746 newVpnIds.add(subnetMapNew.getInternetVpnId());
748 if (subnetMapNew.getRouterId() != null) {
749 newRouterIds.add(subnetMapNew.getRouterId());
753 if (!oldVpnIds.isEmpty()) {
754 LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
755 if (!originalRouterIds.isEmpty()) {
756 for (Uuid routerId : originalRouterIds) {
757 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId,
758 portoriginal.getUuid().getValue());
761 nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(),
762 null /* vpn-id */, confTx);
764 if (!newVpnIds.isEmpty()) {
765 LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
766 nvpnManager.createVpnInterface(newVpnIds, portupdate, confTx);
767 if (!newRouterIds.isEmpty()) {
768 for (Uuid routerId : newRouterIds) {
769 nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
776 private InterfaceAclBuilder handlePortSecurityUpdated(Port portOriginal,
777 Port portUpdated, boolean origSecurityEnabled, boolean updatedSecurityEnabled,
778 InterfaceBuilder interfaceBuilder) {
779 InterfaceAclBuilder interfaceAclBuilder = null;
780 if (origSecurityEnabled != updatedSecurityEnabled) {
781 interfaceAclBuilder = new InterfaceAclBuilder();
782 interfaceAclBuilder.setPortSecurityEnabled(updatedSecurityEnabled);
783 if (updatedSecurityEnabled) {
784 // Handle security group enabled
785 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, portUpdated);
786 neutronvpnUtils.populateSubnetInfo(portUpdated);
788 // Handle security group disabled
789 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
790 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
793 if (updatedSecurityEnabled) {
794 // handle SG add/delete delta
795 InterfaceAcl interfaceAcl = interfaceBuilder.augmentation(InterfaceAcl.class);
796 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
797 interfaceAclBuilder.setSecurityGroups(
798 NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
799 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
800 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
801 interfaceAcl.getAllowedAddressPairs(), portOriginal.getAllowedAddressPairs(),
802 portUpdated.getAllowedAddressPairs());
803 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
804 updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
805 portUpdated.getFixedIps()));
807 if (portOriginal.getFixedIps() != null
808 && !portOriginal.getFixedIps().equals(portUpdated.getFixedIps())) {
809 neutronvpnUtils.populateSubnetInfo(portUpdated);
813 return interfaceAclBuilder;
816 private String createOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
817 Interface inf = createInterface(port);
818 String infName = inf.getName();
820 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
822 Optional<Interface> optionalInf =
823 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
824 interfaceIdentifier);
825 if (!optionalInf.isPresent()) {
826 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, inf);
828 LOG.warn("Interface {} is already present", infName);
830 } catch (ReadFailedException e) {
831 LOG.error("failed to create interface {}", infName, e);
836 private Interface createInterface(Port port) {
837 String interfaceName = port.getUuid().getValue();
838 IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
839 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
840 IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
842 Network network = neutronvpnUtils.getNeutronNetwork(port.getNetworkId());
843 Boolean isVlanTransparent = network.isVlanTransparent();
844 if (isVlanTransparent != null && isVlanTransparent) {
845 l2VlanMode = IfL2vlan.L2vlanMode.Transparent;
848 ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
850 interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
851 .addAugmentation(IfL2vlan.class, ifL2vlanBuilder.build());
853 if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
854 InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
855 interfaceAclBuilder.setPortSecurityEnabled(true);
856 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
857 interfaceBuilder.addAugmentation(InterfaceAcl.class, interfaceAclBuilder.build());
858 neutronvpnUtils.populateSubnetInfo(port);
860 return interfaceBuilder.build();
863 private void deleteOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
864 String name = port.getUuid().getValue();
865 LOG.debug("Removing OFPort Interface {}", name);
866 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
868 Optional<Interface> optionalInf =
869 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
870 interfaceIdentifier);
871 if (optionalInf.isPresent()) {
872 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier);
874 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
876 } catch (ReadFailedException e) {
877 LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
881 private void createElanInterface(Port port, String name, WriteTransaction wrtConfigTxn) {
882 String elanInstanceName = port.getNetworkId().getValue();
883 List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
885 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
886 .class, new ElanInterfaceKey(name)).build();
887 ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
888 .setName(name).setStaticMacEntries(staticMacEntries).withKey(new ElanInterfaceKey(name)).build();
889 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, id, elanInterface);
890 LOG.debug("Creating new ELan Interface {}", elanInterface);
893 private void deleteElanInterface(String name, WriteTransaction wrtConfigTxn) {
894 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
895 .class, new ElanInterfaceKey(name)).build();
896 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, id);
899 // TODO Clean up the exception handling
900 @SuppressWarnings("checkstyle:IllegalCatch")
901 private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
902 floatingIpPortMacAddress) {
903 InstanceIdentifier id = NeutronvpnUtils.buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
905 FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
906 FloatingIpIdToPortMappingBuilder().withKey(new FloatingIpIdToPortMappingKey(floatingIpId))
907 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
908 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
909 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
910 LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
911 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
912 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
913 floatingipIdToPortMacMappingBuilder.build());
914 } catch (Exception e) {
915 LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
916 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
920 private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
921 return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();