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 static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Strings;
14 import com.google.gson.Gson;
15 import com.google.gson.JsonArray;
16 import com.google.gson.JsonElement;
17 import com.google.gson.JsonObject;
18 import java.time.Duration;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Locale;
27 import java.util.stream.Collectors;
28 import javax.annotation.PostConstruct;
29 import javax.inject.Singleton;
30 import org.apache.commons.lang3.ObjectUtils;
31 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
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.Datastore;
38 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
39 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
40 import org.opendaylight.genius.infra.TypedWriteTransaction;
41 import org.opendaylight.genius.mdsalutil.MDSALUtil;
42 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
43 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
44 import org.opendaylight.netvirt.elanmanager.api.IElanService;
45 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
46 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
47 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlanBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAclBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceKey;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.elan._interface.StaticMacEntries;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.RoutersBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingKey;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.hostconfig.rev150712.hostconfig.attributes.hostconfigs.Hostconfig;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
75 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
80 public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<Port, NeutronPortChangeListener> {
81 private static final Logger LOG = LoggerFactory.getLogger(NeutronPortChangeListener.class);
82 private final DataBroker dataBroker;
83 private final ManagedNewTransactionRunner txRunner;
84 private final NeutronvpnManager nvpnManager;
85 private final NeutronvpnNatManager nvpnNatManager;
86 private final NeutronSubnetGwMacResolver gwMacResolver;
87 private final IElanService elanService;
88 private final JobCoordinator jobCoordinator;
89 private final NeutronvpnUtils neutronvpnUtils;
90 private final HostConfigCache hostConfigCache;
91 private final DataTreeEventCallbackRegistrar eventCallbacks;
93 public NeutronPortChangeListener(final DataBroker dataBroker,
94 final NeutronvpnManager neutronvpnManager,
95 final NeutronvpnNatManager neutronvpnNatManager,
96 final NeutronSubnetGwMacResolver gwMacResolver,
97 final IElanService elanService,
98 final JobCoordinator jobCoordinator,
99 final NeutronvpnUtils neutronvpnUtils,
100 final HostConfigCache hostConfigCache,
101 final DataTreeEventCallbackRegistrar dataTreeEventCallbackRegistrar) {
102 super(Port.class, NeutronPortChangeListener.class);
103 this.dataBroker = dataBroker;
104 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
105 nvpnManager = neutronvpnManager;
106 nvpnNatManager = neutronvpnNatManager;
107 this.gwMacResolver = gwMacResolver;
108 this.elanService = elanService;
109 this.jobCoordinator = jobCoordinator;
110 this.neutronvpnUtils = neutronvpnUtils;
111 this.hostConfigCache = hostConfigCache;
112 this.eventCallbacks = dataTreeEventCallbackRegistrar;
118 LOG.info("{} init", getClass().getSimpleName());
119 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
123 protected InstanceIdentifier<Port> getWildCardPath() {
124 return InstanceIdentifier.create(Neutron.class).child(Ports.class).child(Port.class);
128 protected NeutronPortChangeListener getDataTreeChangeListener() {
129 return NeutronPortChangeListener.this;
134 protected void add(InstanceIdentifier<Port> identifier, Port input) {
135 String portName = input.getUuid().getValue();
136 LOG.trace("Adding Port : key: {}, value={}", identifier, input);
137 Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
138 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
139 LOG.warn("neutron vpn received a port add() for a network without a provider extension augmentation "
140 + "or with an unsupported network type for the port {} which is part of network {}",
145 neutronvpnUtils.addToPortCache(input);
146 String portStatus = NeutronUtils.PORT_STATUS_DOWN;
147 if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
148 if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(input.getDeviceOwner())) {
149 handleRouterInterfaceAdded(input);
150 NeutronUtils.createPortStatus(input.getUuid().getValue(), NeutronUtils.PORT_STATUS_ACTIVE, dataBroker);
153 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())) {
154 handleRouterGatewayUpdated(input);
155 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
156 } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
157 handleFloatingIpPortUpdated(null, input);
158 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
161 // Switchdev ports need to be bounded to a host before creation
162 // in order to validate the supported vnic types from the hostconfig
163 if (input.getFixedIps() != null
164 && !input.getFixedIps().isEmpty()
165 && !(isPortTypeSwitchdev(input) && !isPortBound(input))) {
166 handleNeutronPortCreated(input);
168 NeutronUtils.createPortStatus(input.getUuid().getValue(), portStatus, dataBroker);
172 protected void remove(InstanceIdentifier<Port> identifier, Port input) {
173 LOG.trace("Removing Port : key: {}, value={}", identifier, input);
174 Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
175 // need to proceed with deletion in case network is null for a case where v2 sync happens and a read for
176 // network from NN returns null, but the deletion process for port needs to continue
177 if (network != null && !NeutronvpnUtils.isNetworkTypeSupported(network)) {
178 String portName = input.getUuid().getValue();
179 LOG.warn("neutron vpn received a port remove() for a network without a provider extension augmentation "
180 + "or with an unsupported network type for the port {} which is part of network {}",
184 neutronvpnUtils.removeFromPortCache(input);
185 NeutronUtils.deletePortStatus(input.getUuid().getValue(), dataBroker);
187 if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
188 if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(input.getDeviceOwner())) {
189 handleRouterInterfaceRemoved(input);
190 /* nothing else to do here */
192 } else if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())
193 || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
194 elanService.removeKnownL3DmacAddress(input.getMacAddress().getValue(), input.getNetworkId().getValue());
197 if (input.getFixedIps() != null) {
198 handleNeutronPortDeleted(input);
203 protected void update(InstanceIdentifier<Port> identifier, Port original, Port update) {
204 // Switchdev ports need to be bounded to a host before creation
205 // in order to validate the supported vnic types from the hostconfig
206 if (isPortTypeSwitchdev(original)
207 && !isPortBound(original)
208 && isPortBound(update)) {
209 handleNeutronPortCreated(update);
211 final String portName = update.getUuid().getValue();
212 Network network = neutronvpnUtils.getNeutronNetwork(update.getNetworkId());
213 LOG.info("Update port {} from network {}", portName, update.getNetworkId().toString());
214 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
215 LOG.warn("neutron vpn received a port update() for a network without a provider extension augmentation "
216 + "or with an unsupported network type for the port {} which is part of network {}",
220 neutronvpnUtils.addToPortCache(update);
222 if ((Strings.isNullOrEmpty(original.getDeviceOwner()) || Strings.isNullOrEmpty(original.getDeviceId())
223 || NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING.equalsIgnoreCase(original.getDeviceId()))
224 && !Strings.isNullOrEmpty(update.getDeviceOwner()) && !Strings.isNullOrEmpty(update.getDeviceId())) {
225 if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(update.getDeviceOwner())) {
226 handleRouterInterfaceAdded(update);
229 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(update.getDeviceOwner())) {
230 handleRouterGatewayUpdated(update);
231 } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(update.getDeviceOwner())) {
232 handleFloatingIpPortUpdated(original, update);
235 Set<FixedIps> oldIPs = getFixedIpSet(original.getFixedIps());
236 Set<FixedIps> newIPs = getFixedIpSet(update.getFixedIps());
237 if (!oldIPs.equals(newIPs)) {
238 handleNeutronPortUpdated(original, update);
242 // check if port security enabled/disabled as part of port update
243 boolean origSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(original);
244 boolean updatedSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(update);
246 if (origSecurityEnabled || updatedSecurityEnabled) {
247 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(portName);
248 jobCoordinator.enqueueJob("PORT- " + portName,
249 () -> Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
251 Optional<Interface> optionalInf =
252 confTx.read(interfaceIdentifier).get();
253 if (optionalInf.isPresent()) {
254 InterfaceBuilder interfaceBuilder = new InterfaceBuilder(optionalInf.get());
255 InterfaceAcl infAcl = handlePortSecurityUpdated(original, update,
256 origSecurityEnabled, updatedSecurityEnabled, interfaceBuilder).build();
257 interfaceBuilder.addAugmentation(InterfaceAcl.class, infAcl);
258 LOG.info("update: Of-port-interface updation for port {}", portName);
259 // Update OFPort interface for this neutron port
260 confTx.put(interfaceIdentifier, interfaceBuilder.build());
262 LOG.warn("update: Interface {} is not present", portName);
268 private void handleFloatingIpPortUpdated(Port original, Port update) {
269 if ((original == null || original.getDeviceId().equals(NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING))
270 && !update.getDeviceId().equals(NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING)) {
271 // populate floating-ip uuid and floating-ip port attributes (uuid, mac and subnet id for the ONLY
272 // fixed IP) to be used by NAT, depopulated in NATService once mac is retrieved in the removal path
273 addToFloatingIpPortInfo(new Uuid(update.getDeviceId()), update.getUuid(), update.getFixedIps().get(0)
274 .getSubnetId(), update.getMacAddress().getValue());
275 elanService.addKnownL3DmacAddress(update.getMacAddress().getValue(), update.getNetworkId().getValue());
279 private void handleRouterInterfaceAdded(Port routerPort) {
280 if (routerPort.getDeviceId() != null) {
281 Uuid routerId = new Uuid(routerPort.getDeviceId());
282 Uuid infNetworkId = routerPort.getNetworkId();
283 Uuid existingVpnId = neutronvpnUtils.getVpnForNetwork(infNetworkId);
285 elanService.addKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
286 if (existingVpnId == null) {
287 Set<Uuid> listVpnIds = new HashSet<>();
288 Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true);
292 listVpnIds.add(vpnId);
293 Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId);
294 List<Subnetmap> subnetMapList = new ArrayList<>();
295 List<FixedIps> portIps = routerPort.getFixedIps();
296 boolean portIsIpv6 = false;
297 for (FixedIps portIP : portIps) {
298 // NOTE: Please donot change the order of calls to updateSubnetNodeWithFixedIP
299 // and addSubnetToVpn here
300 if (internetVpnId != null
301 && portIP.getIpAddress().getIpv6Address() != null) {
304 String ipValue = portIP.getIpAddress().stringValue();
305 Uuid subnetId = portIP.getSubnetId();
306 nvpnManager.updateSubnetNodeWithFixedIp(subnetId, routerId,
307 routerPort.getUuid(), ipValue, routerPort.getMacAddress().getValue(), vpnId);
308 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
309 subnetMapList.add(sn);
312 listVpnIds.add(internetVpnId);
313 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChangeToAdd(
314 IpVersionChoice.IPV6, internetVpnId)) {
315 neutronvpnUtils.updateVpnInstanceWithIpFamily(internetVpnId.getValue(),
316 IpVersionChoice.IPV6, true);
317 neutronvpnUtils.updateVpnInstanceWithFallback(internetVpnId.getValue(), true);
320 if (! subnetMapList.isEmpty()) {
321 nvpnManager.createVpnInterface(listVpnIds, routerPort, null);
323 for (FixedIps portIP : routerPort.getFixedIps()) {
324 String ipValue = portIP.getIpAddress().stringValue();
325 IpVersionChoice version = NeutronvpnUtils.getIpVersionFromString(ipValue);
326 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChangeToAdd(version, vpnId)) {
327 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(),
330 if (version.isIpVersionChosen(IpVersionChoice.IPV4)) {
331 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(),
332 null /* internet-vpn-id */);
334 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(), internetVpnId);
336 LOG.trace("NeutronPortChangeListener Add Subnet Gateway IP {} MAC {} Interface {} VPN {}",
337 ipValue, routerPort.getMacAddress(),
338 routerPort.getUuid().getValue(), vpnId.getValue());
340 nvpnManager.addToNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
341 jobCoordinator.enqueueJob(routerId.toString(), () -> {
342 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
343 return Collections.emptyList();
345 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
347 String portInterfaceName = createOfPortInterface(routerPort, confTx);
348 createElanInterface(routerPort, portInterfaceName, confTx);
349 }), LOG, "Error creating ELAN interface for {}", routerPort);
351 LOG.error("Neutron network {} corresponding to router interface port {} for neutron router {}"
352 + " already associated to VPN {}", infNetworkId.getValue(), routerPort.getUuid().getValue(),
353 routerId.getValue(), existingVpnId.getValue());
358 private void handleRouterInterfaceRemoved(Port routerPort) {
359 if (routerPort.getDeviceId() != null) {
360 Uuid routerId = new Uuid(routerPort.getDeviceId());
361 Uuid infNetworkId = routerPort.getNetworkId();
362 elanService.removeKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
363 Uuid vpnId = ObjectUtils.defaultIfNull(neutronvpnUtils.getVpnForRouter(routerId, true), routerId);
364 List<FixedIps> portIps = routerPort.getFixedIps();
365 boolean vpnInstanceInternetIpVersionRemoved = false;
366 Uuid vpnInstanceInternetUuid = null;
367 for (FixedIps portIP : portIps) {
368 // Internet VPN : flush InternetVPN first
369 Uuid subnetId = portIP.getSubnetId();
370 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
371 if (sn != null && sn.getInternetVpnId() != null) {
372 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, sn.getInternetVpnId())) {
373 vpnInstanceInternetIpVersionRemoved = true;
374 vpnInstanceInternetUuid = sn.getInternetVpnId();
376 nvpnManager.updateVpnInternetForSubnet(sn, sn.getInternetVpnId(), false);
379 /* Remove ping responder for router interfaces
380 * A router interface reference in a VPN will have to be removed before the host interface references
381 * for that subnet in the VPN are removed. This is to ensure that the FIB Entry of the router interface
382 * is not the last entry to be removed for that subnet in the VPN.
383 * If router interface FIB entry is the last to be removed for a subnet in a VPN , then all the host
384 * interface references in the vpn will already have been cleared, which will cause failures in
385 * cleanup of router interface flows*/
386 nvpnManager.deleteVpnInterface(routerPort.getUuid().getValue(),
387 null /* vpn-id */, null /* wrtConfigTxn*/);
388 // update RouterInterfaces map
389 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
391 boolean vpnInstanceIpVersionRemoved = false;
392 IpVersionChoice vpnInstanceIpVersionToRemove = IpVersionChoice.UNDEFINED;
393 for (FixedIps portIP : portIps) {
394 Subnetmap sn = neutronvpnUtils.getSubnetmap(portIP.getSubnetId());
395 // router Port have either IPv4 or IPv6, never both
396 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, vpnId)) {
397 vpnInstanceIpVersionRemoved = true;
398 vpnInstanceIpVersionToRemove = NeutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
400 String ipValue = portIP.getIpAddress().stringValue();
401 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipValue, confTx);
402 // NOTE: Please donot change the order of calls to removeSubnetFromVpn and
403 // and updateSubnetNodeWithFixedIP
404 nvpnManager.removeSubnetFromVpn(vpnId, portIP.getSubnetId(),
405 sn != null ? sn.getInternetVpnId() : null);
406 nvpnManager.updateSubnetNodeWithFixedIp(portIP.getSubnetId(), null, null,
409 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
410 deleteElanInterface(routerPort.getUuid().getValue(), confTx);
411 deleteOfPortInterface(routerPort, confTx);
412 jobCoordinator.enqueueJob(routerId.toString(), () -> {
413 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
414 return Collections.emptyList();
416 if (vpnInstanceIpVersionRemoved) {
417 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), vpnInstanceIpVersionToRemove,
420 }), LOG, "Error handling interface removal");
421 if (vpnInstanceInternetIpVersionRemoved) {
422 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnInstanceInternetUuid.getValue(),
423 IpVersionChoice.IPV6, false);
424 neutronvpnUtils.updateVpnInstanceWithFallback(vpnInstanceInternetUuid.getValue(), false);
429 private void handleRouterGatewayUpdated(Port routerGwPort) {
430 Uuid routerId = new Uuid(routerGwPort.getDeviceId());
431 Uuid networkId = routerGwPort.getNetworkId();
432 Network network = neutronvpnUtils.getNeutronNetwork(networkId);
433 if (network == null) {
436 boolean isExternal = NeutronvpnUtils.getIsExternal(network);
438 Uuid vpnInternetId = neutronvpnUtils.getVpnForNetwork(networkId);
439 if (vpnInternetId != null) {
440 List<Subnetmap> snList = neutronvpnUtils.getNeutronRouterSubnetMaps(routerId);
441 for (Subnetmap sn : snList) {
442 if (sn.getNetworkId() == networkId) {
445 if (NeutronvpnUtils.getIpVersionFromString(sn.getSubnetIp()) != IpVersionChoice.IPV6) {
448 nvpnManager.addSubnetToVpn(null, sn.getId(), vpnInternetId);
452 elanService.addKnownL3DmacAddress(routerGwPort.getMacAddress().getValue(), networkId.getValue());
454 Router router = neutronvpnUtils.getNeutronRouter(routerId);
455 if (router == null) {
456 LOG.warn("No router found for router GW port {} for router {}", routerGwPort.getUuid().getValue(),
457 routerId.getValue());
459 eventCallbacks.onAddOrUpdate(LogicalDatastoreType.CONFIGURATION,
460 neutronvpnUtils.getNeutronRouterIid(routerId), (unused, newRouter) -> {
461 setupGwMac(newRouter, routerGwPort, routerId);
462 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
463 }, Duration.ofSeconds(3), iid -> {
464 LOG.error("GwPort {} added without Router", routerGwPort.getUuid().getValue());
468 setupGwMac(router, routerGwPort, routerId);
471 private void setupGwMac(Router router, Port routerGwPort, Uuid routerId) {
472 gwMacResolver.sendArpRequestsToExtGateways(router);
473 jobCoordinator.enqueueJob(routerId.toString(), () -> {
474 setExternalGwMac(routerGwPort, routerId);
475 return Collections.emptyList();
479 private void setExternalGwMac(Port routerGwPort, Uuid routerId) {
480 // During full-sync networking-odl syncs routers before ports. As such,
481 // the MAC of the router's gw port is not available to be set when the
482 // router is written. We catch that here.
483 InstanceIdentifier<Routers> routersId = NeutronvpnUtils.buildExtRoutersIdentifier(routerId);
484 Optional<Routers> optionalRouter = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId);
485 if (!optionalRouter.isPresent()) {
489 Routers extRouters = optionalRouter.get();
490 if (extRouters.getExtGwMacAddress() != null) {
494 RoutersBuilder builder = new RoutersBuilder(extRouters);
495 builder.setExtGwMacAddress(routerGwPort.getMacAddress().getValue());
496 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId, builder.build());
499 private String getPortHostId(final Port port) {
501 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
502 if (portBinding != null) {
503 return portBinding.getHostId();
509 private Hostconfig getHostConfig(final Port port) {
510 String hostId = getPortHostId(port);
511 if (hostId == null) {
514 Optional<Hostconfig> hostConfig;
516 hostConfig = this.hostConfigCache.get(hostId);
517 } catch (ReadFailedException e) {
518 LOG.error("failed to read host config from host {}", hostId, e);
521 return hostConfig.isPresent() ? hostConfig.get() : null;
524 private boolean isPortBound(final Port port) {
525 String hostId = getPortHostId(port);
526 return hostId != null && !hostId.isEmpty();
529 private boolean isPortVnicTypeDirect(Port port) {
530 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
531 if (portBinding == null || portBinding.getVnicType() == null) {
532 // By default, VNIC_TYPE is NORMAL
535 String vnicType = portBinding.getVnicType().trim().toLowerCase(Locale.getDefault());
536 return vnicType.equals(NeutronConstants.VNIC_TYPE_DIRECT);
539 private boolean isSupportedVnicTypeByHost(final Port port, final String vnicType) {
540 Hostconfig hostConfig = getHostConfig(port);
541 String supportStr = String.format("\"vnic_type\": \"%s\"", vnicType);
542 if (hostConfig != null && hostConfig.getConfig().contains(supportStr)) {
548 private Map<String, JsonElement> unmarshal(final String profile) {
549 if (null == profile) {
552 Gson gson = new Gson();
553 JsonObject jsonObject = gson.fromJson(profile, JsonObject.class);
554 Map<String, JsonElement> map = new HashMap();
555 for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
556 map.put(entry.getKey(), entry.getValue());
561 private boolean isPortTypeSwitchdev(final Port port) {
562 if (!isPortVnicTypeDirect(port)) {
566 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
567 String profile = portBinding.getProfile();
568 if (profile == null || profile.isEmpty()) {
569 LOG.debug("Port {} has no binding:profile values", port.getUuid());
573 Map<String, JsonElement> mapProfile = unmarshal(profile);
574 JsonElement capabilities = mapProfile.get(NeutronConstants.BINDING_PROFILE_CAPABILITIES);
575 LOG.debug("Port {} capabilities: {}", port.getUuid(), capabilities);
576 if (capabilities == null || !capabilities.isJsonArray()) {
577 LOG.debug("binding profile capabilities not in array format: {}", capabilities);
581 JsonArray capabilitiesArray = capabilities.getAsJsonArray();
582 Gson gson = new Gson();
583 JsonElement switchdevElement = gson.fromJson(NeutronConstants.SWITCHDEV, JsonElement.class);
584 return capabilitiesArray.contains(switchdevElement);
588 private void handleNeutronPortCreated(final Port port) {
589 final String portName = port.getUuid().getValue();
590 final Uuid portId = port.getUuid();
591 final List<FixedIps> portIpAddrsList = port.getFixedIps();
592 if (NeutronConstants.IS_ODL_DHCP_PORT.test(port)) {
595 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
596 // add direct port to subnetMaps config DS
597 if (!(NeutronUtils.isPortVnicTypeNormal(port)
598 || isPortTypeSwitchdev(port)
599 && isSupportedVnicTypeByHost(port, NeutronConstants.VNIC_TYPE_DIRECT))) {
600 for (FixedIps ip: portIpAddrsList) {
601 nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), null, portId);
603 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
604 + "OF Port interfaces are not created", portName);
605 return Collections.emptyList();
607 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
608 LOG.info("Of-port-interface creation for port {}", portName);
609 // Create of-port interface for this neutron port
610 String portInterfaceName = createOfPortInterface(port, tx);
611 LOG.debug("Creating ELAN Interface for port {}", portName);
612 createElanInterface(port, portInterfaceName, tx);
613 Set<Uuid> vpnIdList = new HashSet<>();
614 Set<Uuid> routerIds = new HashSet<>();
615 for (FixedIps ip: portIpAddrsList) {
616 Subnetmap subnetMap = nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), portId, null);
617 if (subnetMap != null && subnetMap.getInternetVpnId() != null) {
618 if (!vpnIdList.contains(subnetMap.getInternetVpnId())) {
619 vpnIdList.add(subnetMap.getInternetVpnId());
622 if (subnetMap != null && subnetMap.getVpnId() != null) {
623 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
624 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
625 Uuid vpnId = subnetMap.getVpnId();
627 vpnIdList.add(vpnId);
630 if (subnetMap != null && subnetMap.getRouterId() != null) {
631 routerIds.add(subnetMap.getRouterId());
634 if (!vpnIdList.isEmpty()) {
635 // create new vpn-interface for neutron port
636 LOG.debug("handleNeutronPortCreated: Adding VPN Interface for port {} from network {}", portName,
637 port.getNetworkId().toString());
638 nvpnManager.createVpnInterface(vpnIdList, port, tx);
639 if (!routerIds.isEmpty()) {
640 for (Uuid routerId : routerIds) {
641 nvpnManager.addToNeutronRouterInterfacesMap(routerId,port.getUuid().getValue());
649 private void handleNeutronPortDeleted(final Port port) {
650 final String portName = port.getUuid().getValue();
651 final Uuid portId = port.getUuid();
652 final List<FixedIps> portIpsList = port.getFixedIps();
653 jobCoordinator.enqueueJob("PORT- " + portName,
654 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
655 if (!(NeutronUtils.isPortVnicTypeNormal(port) || isPortTypeSwitchdev(port))) {
656 for (FixedIps ip : portIpsList) {
657 // remove direct port from subnetMaps config DS
658 nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), null, portId);
660 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
661 + "Skipping OF Port interfaces removal", portName);
665 Set<Uuid> routerIds = new HashSet<>();
666 Uuid internetVpnId = null;
667 for (FixedIps ip : portIpsList) {
668 Subnetmap subnetMap = nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), portId, null);
669 if (subnetMap == null) {
672 if (subnetMap.getVpnId() != null) {
673 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
674 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
675 vpnId = subnetMap.getVpnId();
677 if (subnetMap.getRouterId() != null) {
678 routerIds.add(subnetMap.getRouterId());
680 internetVpnId = subnetMap.getInternetVpnId();
682 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(port.getDeviceOwner())
683 || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(port.getDeviceOwner())) {
684 String ipAddress = ip.getIpAddress().stringValue();
686 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipAddress, confTx);
688 if (internetVpnId != null) {
689 neutronvpnUtils.removeVpnPortFixedIpToPort(internetVpnId.getValue(),
694 if (vpnId != null || internetVpnId != null) {
695 // remove vpn-interface for this neutron port
696 LOG.debug("removing VPN Interface for port {}", portName);
697 if (!routerIds.isEmpty()) {
698 for (Uuid routerId : routerIds) {
699 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portName);
702 nvpnManager.deleteVpnInterface(portName, null /* vpn-id */, confTx);
704 // Remove of-port interface for this neutron port
705 // ELAN interface is also implicitly deleted as part of this operation
706 LOG.debug("Of-port-interface removal for port {}", portName);
707 deleteOfPortInterface(port, confTx);
708 //dissociate fixedIP from floatingIP if associated
709 nvpnManager.dissociatefixedIPFromFloatingIP(port.getUuid().getValue());
714 private void handleNeutronPortUpdated(final Port portoriginal, final Port portupdate) {
715 final List<FixedIps> portoriginalIps = portoriginal.getFixedIps();
716 final List<FixedIps> portupdateIps = portupdate.getFixedIps();
717 if (portoriginalIps == null || portoriginalIps.isEmpty()) {
718 handleNeutronPortCreated(portupdate);
722 if (portupdateIps == null || portupdateIps.isEmpty()) {
723 LOG.info("Ignoring portUpdate (fixed_ip removal) for port {} as this case is handled "
724 + "during subnet deletion event.", portupdate.getUuid().getValue());
727 jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(),
728 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
729 final List<Uuid> originalSnMapsIds = portoriginalIps.stream().map(FixedIps::getSubnetId)
730 .collect(Collectors.toList());
731 final List<Uuid> updateSnMapsIds = portupdateIps.stream().map(FixedIps::getSubnetId)
732 .collect(Collectors.toList());
733 Set<Uuid> originalRouterIds = new HashSet<>();
734 Set<Uuid> oldVpnIds = new HashSet<>();
735 for (Uuid snId: originalSnMapsIds) {
736 if (!updateSnMapsIds.remove(snId)) {
737 // snId was present in originalSnMapsIds, but not in updateSnMapsIds
738 Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
740 if (subnetMapOld != null && subnetMapOld.getVpnId() != null) {
741 oldVpnIds.add(subnetMapOld.getVpnId());
743 if (subnetMapOld != null && subnetMapOld.getInternetVpnId() != null) {
744 oldVpnIds.add(subnetMapOld.getInternetVpnId());
746 if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
747 originalRouterIds.add(subnetMapOld.getRouterId());
751 Set<Uuid> newVpnIds = new HashSet<>();
752 Set<Uuid> newRouterIds = new HashSet<>();
753 for (Uuid snId: updateSnMapsIds) {
754 Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
755 if (subnetMapNew != null) {
756 if (subnetMapNew.getVpnId() != null) {
757 newVpnIds.add(subnetMapNew.getVpnId());
759 if (subnetMapNew.getInternetVpnId() != null) {
760 newVpnIds.add(subnetMapNew.getInternetVpnId());
762 if (subnetMapNew.getRouterId() != null) {
763 newRouterIds.add(subnetMapNew.getRouterId());
767 if (!oldVpnIds.isEmpty()) {
768 LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
769 if (!originalRouterIds.isEmpty()) {
770 for (Uuid routerId : originalRouterIds) {
771 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId,
772 portoriginal.getUuid().getValue());
775 nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(),
776 null /* vpn-id */, confTx);
778 if (!newVpnIds.isEmpty()) {
779 LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
780 nvpnManager.createVpnInterface(newVpnIds, portupdate, confTx);
781 if (!newRouterIds.isEmpty()) {
782 for (Uuid routerId : newRouterIds) {
783 nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
790 private InterfaceAclBuilder handlePortSecurityUpdated(Port portOriginal,
791 Port portUpdated, boolean origSecurityEnabled, boolean updatedSecurityEnabled,
792 InterfaceBuilder interfaceBuilder) {
793 InterfaceAclBuilder interfaceAclBuilder = null;
794 if (origSecurityEnabled != updatedSecurityEnabled) {
795 interfaceAclBuilder = new InterfaceAclBuilder();
796 interfaceAclBuilder.setPortSecurityEnabled(updatedSecurityEnabled);
797 if (updatedSecurityEnabled) {
798 // Handle security group enabled
799 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, portUpdated);
800 neutronvpnUtils.populateSubnetInfo(portUpdated);
802 // Handle security group disabled
803 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
804 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
807 if (updatedSecurityEnabled) {
808 // handle SG add/delete delta
809 InterfaceAcl interfaceAcl = interfaceBuilder.augmentation(InterfaceAcl.class);
810 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
811 interfaceAclBuilder.setSecurityGroups(
812 NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
813 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
814 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
815 interfaceAcl.getAllowedAddressPairs(), portOriginal.getAllowedAddressPairs(),
816 portUpdated.getAllowedAddressPairs());
817 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
818 updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
819 portUpdated.getFixedIps()));
821 if (portOriginal.getFixedIps() != null
822 && !portOriginal.getFixedIps().equals(portUpdated.getFixedIps())) {
823 neutronvpnUtils.populateSubnetInfo(portUpdated);
827 return interfaceAclBuilder;
830 private String createOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
831 Interface inf = createInterface(port);
832 String infName = inf.getName();
834 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
836 Optional<Interface> optionalInf =
837 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
838 interfaceIdentifier);
839 if (!optionalInf.isPresent()) {
840 wrtConfigTxn.put(interfaceIdentifier, inf);
842 LOG.warn("Interface {} is already present", infName);
844 } catch (ReadFailedException e) {
845 LOG.error("failed to create interface {}", infName, e);
850 private Interface createInterface(Port port) {
851 String interfaceName = port.getUuid().getValue();
852 IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
853 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
854 IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
856 Network network = neutronvpnUtils.getNeutronNetwork(port.getNetworkId());
857 Boolean isVlanTransparent = network.isVlanTransparent();
858 if (isVlanTransparent != null && isVlanTransparent) {
859 l2VlanMode = IfL2vlan.L2vlanMode.Transparent;
862 ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
864 interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
865 .addAugmentation(IfL2vlan.class, ifL2vlanBuilder.build());
867 if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
868 InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
869 interfaceAclBuilder.setPortSecurityEnabled(true);
870 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
871 interfaceBuilder.addAugmentation(InterfaceAcl.class, interfaceAclBuilder.build());
872 neutronvpnUtils.populateSubnetInfo(port);
874 return interfaceBuilder.build();
877 private void deleteOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
878 String name = port.getUuid().getValue();
879 LOG.debug("Removing OFPort Interface {}", name);
880 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
882 Optional<Interface> optionalInf =
883 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
884 interfaceIdentifier);
885 if (optionalInf.isPresent()) {
886 wrtConfigTxn.delete(interfaceIdentifier);
888 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
890 } catch (ReadFailedException e) {
891 LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
895 private void createElanInterface(Port port, String name,
896 TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
897 String elanInstanceName = port.getNetworkId().getValue();
898 List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
900 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
901 .class, new ElanInterfaceKey(name)).build();
902 ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
903 .setName(name).setStaticMacEntries(staticMacEntries).withKey(new ElanInterfaceKey(name)).build();
904 wrtConfigTxn.put(id, elanInterface);
905 LOG.debug("Creating new ELan Interface {}", elanInterface);
908 private void deleteElanInterface(String name, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
909 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
910 .class, new ElanInterfaceKey(name)).build();
911 wrtConfigTxn.delete(id);
914 // TODO Clean up the exception handling
915 @SuppressWarnings("checkstyle:IllegalCatch")
916 private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
917 floatingIpPortMacAddress) {
918 InstanceIdentifier id = NeutronvpnUtils.buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
920 FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
921 FloatingIpIdToPortMappingBuilder().withKey(new FloatingIpIdToPortMappingKey(floatingIpId))
922 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
923 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
924 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
925 LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
926 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
927 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
928 floatingipIdToPortMacMappingBuilder.build());
929 } catch (Exception e) {
930 LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
931 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
935 private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
936 return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();