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 || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(port.getDeviceOwner())) {
671 String ipAddress = String.valueOf(ip.getIpAddress().getValue());
673 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipAddress, confTx);
675 if (internetVpnId != null) {
676 neutronvpnUtils.removeVpnPortFixedIpToPort(internetVpnId.getValue(),
681 if (vpnId != null || internetVpnId != null) {
682 // remove vpn-interface for this neutron port
683 LOG.debug("removing VPN Interface for port {}", portName);
684 if (!routerIds.isEmpty()) {
685 for (Uuid routerId : routerIds) {
686 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portName);
689 nvpnManager.deleteVpnInterface(portName, null /* vpn-id */, confTx);
691 // Remove of-port interface for this neutron port
692 // ELAN interface is also implicitly deleted as part of this operation
693 LOG.debug("Of-port-interface removal for port {}", portName);
694 deleteOfPortInterface(port, confTx);
695 //dissociate fixedIP from floatingIP if associated
696 nvpnManager.dissociatefixedIPFromFloatingIP(port.getUuid().getValue());
701 private void handleNeutronPortUpdated(final Port portoriginal, final Port portupdate) {
702 final List<FixedIps> portoriginalIps = portoriginal.getFixedIps();
703 final List<FixedIps> portupdateIps = portupdate.getFixedIps();
704 if (portoriginalIps == null || portoriginalIps.isEmpty()) {
705 handleNeutronPortCreated(portupdate);
709 if (portupdateIps == null || portupdateIps.isEmpty()) {
710 LOG.info("Ignoring portUpdate (fixed_ip removal) for port {} as this case is handled "
711 + "during subnet deletion event.", portupdate.getUuid().getValue());
714 jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(),
715 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
716 final List<Uuid> originalSnMapsIds = portoriginalIps.stream().map(FixedIps::getSubnetId)
717 .collect(Collectors.toList());
718 final List<Uuid> updateSnMapsIds = portupdateIps.stream().map(FixedIps::getSubnetId)
719 .collect(Collectors.toList());
720 Set<Uuid> originalRouterIds = new HashSet<>();
721 Set<Uuid> oldVpnIds = new HashSet<>();
722 for (Uuid snId: originalSnMapsIds) {
723 if (!updateSnMapsIds.remove(snId)) {
724 // snId was present in originalSnMapsIds, but not in updateSnMapsIds
725 Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
727 if (subnetMapOld != null && subnetMapOld.getVpnId() != null) {
728 oldVpnIds.add(subnetMapOld.getVpnId());
730 if (subnetMapOld != null && subnetMapOld.getInternetVpnId() != null) {
731 oldVpnIds.add(subnetMapOld.getInternetVpnId());
733 if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
734 originalRouterIds.add(subnetMapOld.getRouterId());
738 Set<Uuid> newVpnIds = new HashSet();
739 Set<Uuid> newRouterIds = new HashSet<>();
740 for (Uuid snId: updateSnMapsIds) {
741 Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
742 if (subnetMapNew != null) {
743 if (subnetMapNew.getVpnId() != null) {
744 newVpnIds.add(subnetMapNew.getVpnId());
746 if (subnetMapNew.getInternetVpnId() != null) {
747 newVpnIds.add(subnetMapNew.getInternetVpnId());
749 if (subnetMapNew.getRouterId() != null) {
750 newRouterIds.add(subnetMapNew.getRouterId());
754 if (!oldVpnIds.isEmpty()) {
755 LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
756 if (!originalRouterIds.isEmpty()) {
757 for (Uuid routerId : originalRouterIds) {
758 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId,
759 portoriginal.getUuid().getValue());
762 nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(),
763 null /* vpn-id */, confTx);
765 if (!newVpnIds.isEmpty()) {
766 LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
767 nvpnManager.createVpnInterface(newVpnIds, portupdate, confTx);
768 if (!newRouterIds.isEmpty()) {
769 for (Uuid routerId : newRouterIds) {
770 nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
777 private InterfaceAclBuilder handlePortSecurityUpdated(Port portOriginal,
778 Port portUpdated, boolean origSecurityEnabled, boolean updatedSecurityEnabled,
779 InterfaceBuilder interfaceBuilder) {
780 InterfaceAclBuilder interfaceAclBuilder = null;
781 if (origSecurityEnabled != updatedSecurityEnabled) {
782 interfaceAclBuilder = new InterfaceAclBuilder();
783 interfaceAclBuilder.setPortSecurityEnabled(updatedSecurityEnabled);
784 if (updatedSecurityEnabled) {
785 // Handle security group enabled
786 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, portUpdated);
787 neutronvpnUtils.populateSubnetInfo(portUpdated);
789 // Handle security group disabled
790 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
791 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
794 if (updatedSecurityEnabled) {
795 // handle SG add/delete delta
796 InterfaceAcl interfaceAcl = interfaceBuilder.augmentation(InterfaceAcl.class);
797 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
798 interfaceAclBuilder.setSecurityGroups(
799 NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
800 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
801 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
802 interfaceAcl.getAllowedAddressPairs(), portOriginal.getAllowedAddressPairs(),
803 portUpdated.getAllowedAddressPairs());
804 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
805 updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
806 portUpdated.getFixedIps()));
808 if (portOriginal.getFixedIps() != null
809 && !portOriginal.getFixedIps().equals(portUpdated.getFixedIps())) {
810 neutronvpnUtils.populateSubnetInfo(portUpdated);
814 return interfaceAclBuilder;
817 private String createOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
818 Interface inf = createInterface(port);
819 String infName = inf.getName();
821 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
823 Optional<Interface> optionalInf =
824 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
825 interfaceIdentifier);
826 if (!optionalInf.isPresent()) {
827 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, inf);
829 LOG.warn("Interface {} is already present", infName);
831 } catch (ReadFailedException e) {
832 LOG.error("failed to create interface {}", infName, e);
837 private Interface createInterface(Port port) {
838 String interfaceName = port.getUuid().getValue();
839 IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
840 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
841 IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
843 Network network = neutronvpnUtils.getNeutronNetwork(port.getNetworkId());
844 Boolean isVlanTransparent = network.isVlanTransparent();
845 if (isVlanTransparent != null && isVlanTransparent) {
846 l2VlanMode = IfL2vlan.L2vlanMode.Transparent;
849 ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
851 interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
852 .addAugmentation(IfL2vlan.class, ifL2vlanBuilder.build());
854 if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
855 InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
856 interfaceAclBuilder.setPortSecurityEnabled(true);
857 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
858 interfaceBuilder.addAugmentation(InterfaceAcl.class, interfaceAclBuilder.build());
859 neutronvpnUtils.populateSubnetInfo(port);
861 return interfaceBuilder.build();
864 private void deleteOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
865 String name = port.getUuid().getValue();
866 LOG.debug("Removing OFPort Interface {}", name);
867 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
869 Optional<Interface> optionalInf =
870 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
871 interfaceIdentifier);
872 if (optionalInf.isPresent()) {
873 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier);
875 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
877 } catch (ReadFailedException e) {
878 LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
882 private void createElanInterface(Port port, String name, WriteTransaction wrtConfigTxn) {
883 String elanInstanceName = port.getNetworkId().getValue();
884 List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
886 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
887 .class, new ElanInterfaceKey(name)).build();
888 ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
889 .setName(name).setStaticMacEntries(staticMacEntries).withKey(new ElanInterfaceKey(name)).build();
890 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, id, elanInterface);
891 LOG.debug("Creating new ELan Interface {}", elanInterface);
894 private void deleteElanInterface(String name, WriteTransaction wrtConfigTxn) {
895 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
896 .class, new ElanInterfaceKey(name)).build();
897 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, id);
900 // TODO Clean up the exception handling
901 @SuppressWarnings("checkstyle:IllegalCatch")
902 private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
903 floatingIpPortMacAddress) {
904 InstanceIdentifier id = NeutronvpnUtils.buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
906 FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
907 FloatingIpIdToPortMappingBuilder().withKey(new FloatingIpIdToPortMappingKey(floatingIpId))
908 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
909 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
910 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
911 LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
912 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
913 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
914 floatingipIdToPortMacMappingBuilder.build());
915 } catch (Exception e) {
916 LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
917 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
921 private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
922 return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();