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());
728 if (NeutronConstants.IS_ODL_DHCP_PORT.test(portupdate)) {
732 jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(),
733 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
734 final List<Uuid> originalSnMapsIds = portoriginalIps.stream().map(FixedIps::getSubnetId)
735 .collect(Collectors.toList());
736 final List<Uuid> updateSnMapsIds = portupdateIps.stream().map(FixedIps::getSubnetId)
737 .collect(Collectors.toList());
738 Set<Uuid> originalRouterIds = new HashSet<>();
739 Set<Uuid> oldVpnIds = new HashSet<>();
740 for (Uuid snId: originalSnMapsIds) {
741 if (!updateSnMapsIds.remove(snId)) {
742 // snId was present in originalSnMapsIds, but not in updateSnMapsIds
743 Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
745 if (subnetMapOld != null && subnetMapOld.getVpnId() != null) {
746 oldVpnIds.add(subnetMapOld.getVpnId());
748 if (subnetMapOld != null && subnetMapOld.getInternetVpnId() != null) {
749 oldVpnIds.add(subnetMapOld.getInternetVpnId());
751 if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
752 originalRouterIds.add(subnetMapOld.getRouterId());
756 Set<Uuid> newVpnIds = new HashSet<>();
757 Set<Uuid> newRouterIds = new HashSet<>();
758 for (Uuid snId: updateSnMapsIds) {
759 Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
760 if (subnetMapNew != null) {
761 if (subnetMapNew.getVpnId() != null) {
762 newVpnIds.add(subnetMapNew.getVpnId());
764 if (subnetMapNew.getInternetVpnId() != null) {
765 newVpnIds.add(subnetMapNew.getInternetVpnId());
767 if (subnetMapNew.getRouterId() != null) {
768 newRouterIds.add(subnetMapNew.getRouterId());
772 if (!oldVpnIds.isEmpty()) {
773 LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
774 if (!originalRouterIds.isEmpty()) {
775 for (Uuid routerId : originalRouterIds) {
776 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId,
777 portoriginal.getUuid().getValue());
780 nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(),
781 null /* vpn-id */, confTx);
783 if (!newVpnIds.isEmpty()) {
784 LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
785 nvpnManager.createVpnInterface(newVpnIds, portupdate, confTx);
786 if (!newRouterIds.isEmpty()) {
787 for (Uuid routerId : newRouterIds) {
788 nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
795 private InterfaceAclBuilder handlePortSecurityUpdated(Port portOriginal,
796 Port portUpdated, boolean origSecurityEnabled, boolean updatedSecurityEnabled,
797 InterfaceBuilder interfaceBuilder) {
798 InterfaceAclBuilder interfaceAclBuilder = null;
799 if (origSecurityEnabled != updatedSecurityEnabled) {
800 interfaceAclBuilder = new InterfaceAclBuilder();
801 interfaceAclBuilder.setPortSecurityEnabled(updatedSecurityEnabled);
802 if (updatedSecurityEnabled) {
803 // Handle security group enabled
804 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, portUpdated);
805 neutronvpnUtils.populateSubnetInfo(portUpdated);
807 // Handle security group disabled
808 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
809 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
812 if (updatedSecurityEnabled) {
813 // handle SG add/delete delta
814 InterfaceAcl interfaceAcl = interfaceBuilder.augmentation(InterfaceAcl.class);
815 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
816 interfaceAclBuilder.setSecurityGroups(
817 NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
818 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
819 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
820 interfaceAcl.getAllowedAddressPairs(), portOriginal.getAllowedAddressPairs(),
821 portUpdated.getAllowedAddressPairs());
822 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
823 updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
824 portUpdated.getFixedIps()));
826 if (portOriginal.getFixedIps() != null
827 && !portOriginal.getFixedIps().equals(portUpdated.getFixedIps())) {
828 neutronvpnUtils.populateSubnetInfo(portUpdated);
832 return interfaceAclBuilder;
835 private String createOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
836 Interface inf = createInterface(port);
837 String infName = inf.getName();
839 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
841 Optional<Interface> optionalInf =
842 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
843 interfaceIdentifier);
844 if (!optionalInf.isPresent()) {
845 wrtConfigTxn.put(interfaceIdentifier, inf);
847 LOG.warn("Interface {} is already present", infName);
849 } catch (ReadFailedException e) {
850 LOG.error("failed to create interface {}", infName, e);
855 private Interface createInterface(Port port) {
856 String interfaceName = port.getUuid().getValue();
857 IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
858 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
859 IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
861 Network network = neutronvpnUtils.getNeutronNetwork(port.getNetworkId());
862 Boolean isVlanTransparent = network.isVlanTransparent();
863 if (isVlanTransparent != null && isVlanTransparent) {
864 l2VlanMode = IfL2vlan.L2vlanMode.Transparent;
867 ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
869 interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
870 .addAugmentation(IfL2vlan.class, ifL2vlanBuilder.build());
872 if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
873 InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
874 interfaceAclBuilder.setPortSecurityEnabled(true);
875 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
876 interfaceBuilder.addAugmentation(InterfaceAcl.class, interfaceAclBuilder.build());
877 neutronvpnUtils.populateSubnetInfo(port);
879 return interfaceBuilder.build();
882 private void deleteOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
883 String name = port.getUuid().getValue();
884 LOG.debug("Removing OFPort Interface {}", name);
885 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
887 Optional<Interface> optionalInf =
888 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
889 interfaceIdentifier);
890 if (optionalInf.isPresent()) {
891 wrtConfigTxn.delete(interfaceIdentifier);
893 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
895 } catch (ReadFailedException e) {
896 LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
900 private void createElanInterface(Port port, String name,
901 TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
902 String elanInstanceName = port.getNetworkId().getValue();
903 List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
905 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
906 .class, new ElanInterfaceKey(name)).build();
907 ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
908 .setName(name).setStaticMacEntries(staticMacEntries).withKey(new ElanInterfaceKey(name)).build();
909 wrtConfigTxn.put(id, elanInterface);
910 LOG.debug("Creating new ELan Interface {}", elanInterface);
913 private void deleteElanInterface(String name, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
914 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
915 .class, new ElanInterfaceKey(name)).build();
916 wrtConfigTxn.delete(id);
919 // TODO Clean up the exception handling
920 @SuppressWarnings("checkstyle:IllegalCatch")
921 private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
922 floatingIpPortMacAddress) {
923 InstanceIdentifier id = NeutronvpnUtils.buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
925 FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
926 FloatingIpIdToPortMappingBuilder().withKey(new FloatingIpIdToPortMappingKey(floatingIpId))
927 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
928 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
929 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
930 LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
931 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
932 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
933 floatingipIdToPortMacMappingBuilder.build());
934 } catch (Exception e) {
935 LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
936 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
940 private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
941 return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();