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.shouldVpnHandleIpVersionChoiceChange(
314 IpVersionChoice.IPV6, routerId, true)) {
315 neutronvpnUtils.updateVpnInstanceWithIpFamily(internetVpnId.getValue(), IpVersionChoice.IPV6,
317 neutronvpnUtils.updateVpnInstanceWithFallback(internetVpnId.getValue(), true);
320 if (! subnetMapList.isEmpty()) {
321 nvpnManager.createVpnInterface(listVpnIds, routerPort, null);
323 IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
324 for (FixedIps portIP : routerPort.getFixedIps()) {
325 String ipValue = portIP.getIpAddress().stringValue();
326 ipVersion = NeutronvpnUtils.getIpVersionFromString(ipValue);
327 if (ipVersion.isIpVersionChosen(IpVersionChoice.IPV4)) {
328 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(),
329 null /* internet-vpn-id */);
331 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(), internetVpnId);
333 LOG.trace("NeutronPortChangeListener Add Subnet Gateway IP {} MAC {} Interface {} VPN {}",
334 ipValue, routerPort.getMacAddress(),
335 routerPort.getUuid().getValue(), vpnId.getValue());
337 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChange(ipVersion, routerId, true)) {
338 LOG.debug("vpnInstanceOpDataEntry is getting update with ip address family {} for VPN {}",
339 ipVersion, vpnId.getValue());
340 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, true);
342 nvpnManager.addToNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
343 jobCoordinator.enqueueJob(routerId.toString(), () -> {
344 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
345 return Collections.emptyList();
347 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
349 String portInterfaceName = createOfPortInterface(routerPort, confTx);
350 createElanInterface(routerPort, portInterfaceName, confTx);
351 }), LOG, "Error creating ELAN interface for {}", routerPort);
353 LOG.error("Neutron network {} corresponding to router interface port {} for neutron router {}"
354 + " already associated to VPN {}", infNetworkId.getValue(), routerPort.getUuid().getValue(),
355 routerId.getValue(), existingVpnId.getValue());
360 private void handleRouterInterfaceRemoved(Port routerPort) {
361 if (routerPort.getDeviceId() != null) {
362 Uuid routerId = new Uuid(routerPort.getDeviceId());
363 Uuid infNetworkId = routerPort.getNetworkId();
364 elanService.removeKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
365 Uuid vpnId = ObjectUtils.defaultIfNull(neutronvpnUtils.getVpnForRouter(routerId, true), routerId);
366 List<FixedIps> portIps = routerPort.getFixedIps();
367 boolean vpnInstanceInternetIpVersionRemoved = false;
368 Uuid vpnInstanceInternetUuid = null;
369 for (FixedIps portIP : portIps) {
370 // Internet VPN : flush InternetVPN first
371 Uuid subnetId = portIP.getSubnetId();
372 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
373 if (sn != null && sn.getInternetVpnId() != null) {
374 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, sn.getInternetVpnId())) {
375 vpnInstanceInternetIpVersionRemoved = true;
376 vpnInstanceInternetUuid = sn.getInternetVpnId();
378 nvpnManager.updateVpnInternetForSubnet(sn, sn.getInternetVpnId(), false);
381 /* Remove ping responder for router interfaces
382 * A router interface reference in a VPN will have to be removed before the host interface references
383 * for that subnet in the VPN are removed. This is to ensure that the FIB Entry of the router interface
384 * is not the last entry to be removed for that subnet in the VPN.
385 * If router interface FIB entry is the last to be removed for a subnet in a VPN , then all the host
386 * interface references in the vpn will already have been cleared, which will cause failures in
387 * cleanup of router interface flows*/
388 nvpnManager.deleteVpnInterface(routerPort.getUuid().getValue(),
389 null /* vpn-id */, null /* wrtConfigTxn*/);
390 // update RouterInterfaces map
391 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
393 IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
394 for (FixedIps portIP : portIps) {
395 Subnetmap sn = neutronvpnUtils.getSubnetmap(portIP.getSubnetId());
396 // router Port have either IPv4 or IPv6, never both
397 ipVersion = neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
398 String ipValue = portIP.getIpAddress().stringValue();
399 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipValue, confTx);
400 // NOTE: Please donot change the order of calls to removeSubnetFromVpn and
401 // and updateSubnetNodeWithFixedIP
402 nvpnManager.removeSubnetFromVpn(vpnId, portIP.getSubnetId(), sn.getInternetVpnId());
403 nvpnManager.updateSubnetNodeWithFixedIp(portIP.getSubnetId(), null, null,
406 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
407 deleteElanInterface(routerPort.getUuid().getValue(), confTx);
408 deleteOfPortInterface(routerPort, confTx);
409 jobCoordinator.enqueueJob(routerId.toString(), () -> {
410 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
411 return Collections.emptyList();
413 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChange(ipVersion, routerId, false)) {
414 LOG.debug("vpnInstanceOpDataEntry is getting update with ip address family {} for VPN {}",
415 ipVersion, vpnId.getValue());
416 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, false);
418 }), LOG, "Error handling interface removal");
419 if (vpnInstanceInternetIpVersionRemoved) {
420 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnInstanceInternetUuid.getValue(), IpVersionChoice.IPV6,
422 neutronvpnUtils.updateVpnInstanceWithFallback(vpnInstanceInternetUuid.getValue(), false);
427 private void handleRouterGatewayUpdated(Port routerGwPort) {
428 Uuid routerId = new Uuid(routerGwPort.getDeviceId());
429 Uuid networkId = routerGwPort.getNetworkId();
430 Network network = neutronvpnUtils.getNeutronNetwork(networkId);
431 if (network == null) {
434 boolean isExternal = NeutronvpnUtils.getIsExternal(network);
436 Uuid vpnInternetId = neutronvpnUtils.getVpnForNetwork(networkId);
437 if (vpnInternetId != null) {
438 List<Subnetmap> snList = neutronvpnUtils.getNeutronRouterSubnetMaps(routerId);
439 for (Subnetmap sn : snList) {
440 if (sn.getNetworkId() == networkId) {
443 if (NeutronvpnUtils.getIpVersionFromString(sn.getSubnetIp()) != IpVersionChoice.IPV6) {
446 nvpnManager.addSubnetToVpn(null, sn.getId(), vpnInternetId);
450 elanService.addKnownL3DmacAddress(routerGwPort.getMacAddress().getValue(), networkId.getValue());
452 Router router = neutronvpnUtils.getNeutronRouter(routerId);
453 if (router == null) {
454 LOG.warn("No router found for router GW port {} for router {}", routerGwPort.getUuid().getValue(),
455 routerId.getValue());
457 eventCallbacks.onAddOrUpdate(LogicalDatastoreType.CONFIGURATION,
458 neutronvpnUtils.getNeutronRouterIid(routerId), (unused, newRouter) -> {
459 setupGwMac(newRouter, routerGwPort, routerId);
460 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
461 }, Duration.ofSeconds(3), iid -> {
462 LOG.error("GwPort {} added without Router", routerGwPort.getUuid().getValue());
466 setupGwMac(router, routerGwPort, routerId);
469 private void setupGwMac(Router router, Port routerGwPort, Uuid routerId) {
470 gwMacResolver.sendArpRequestsToExtGateways(router);
471 jobCoordinator.enqueueJob(routerId.toString(), () -> {
472 setExternalGwMac(routerGwPort, routerId);
473 return Collections.emptyList();
477 private void setExternalGwMac(Port routerGwPort, Uuid routerId) {
478 // During full-sync networking-odl syncs routers before ports. As such,
479 // the MAC of the router's gw port is not available to be set when the
480 // router is written. We catch that here.
481 InstanceIdentifier<Routers> routersId = NeutronvpnUtils.buildExtRoutersIdentifier(routerId);
482 Optional<Routers> optionalRouter = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId);
483 if (!optionalRouter.isPresent()) {
487 Routers extRouters = optionalRouter.get();
488 if (extRouters.getExtGwMacAddress() != null) {
492 RoutersBuilder builder = new RoutersBuilder(extRouters);
493 builder.setExtGwMacAddress(routerGwPort.getMacAddress().getValue());
494 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId, builder.build());
497 private String getPortHostId(final Port port) {
499 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
500 if (portBinding != null) {
501 return portBinding.getHostId();
507 private Hostconfig getHostConfig(final Port port) {
508 String hostId = getPortHostId(port);
509 if (hostId == null) {
512 Optional<Hostconfig> hostConfig;
514 hostConfig = this.hostConfigCache.get(hostId);
515 } catch (ReadFailedException e) {
516 LOG.error("failed to read host config from host {}", hostId, e);
519 return hostConfig.isPresent() ? hostConfig.get() : null;
522 private boolean isPortBound(final Port port) {
523 String hostId = getPortHostId(port);
524 return hostId != null && !hostId.isEmpty();
527 private boolean isPortVnicTypeDirect(Port port) {
528 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
529 if (portBinding == null || portBinding.getVnicType() == null) {
530 // By default, VNIC_TYPE is NORMAL
533 String vnicType = portBinding.getVnicType().trim().toLowerCase(Locale.getDefault());
534 return vnicType.equals(NeutronConstants.VNIC_TYPE_DIRECT);
537 private boolean isSupportedVnicTypeByHost(final Port port, final String vnicType) {
538 Hostconfig hostConfig = getHostConfig(port);
539 String supportStr = String.format("\"vnic_type\": \"%s\"", vnicType);
540 if (hostConfig != null && hostConfig.getConfig().contains(supportStr)) {
546 private Map<String, JsonElement> unmarshal(final String profile) {
547 if (null == profile) {
550 Gson gson = new Gson();
551 JsonObject jsonObject = gson.fromJson(profile, JsonObject.class);
552 Map<String, JsonElement> map = new HashMap();
553 for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
554 map.put(entry.getKey(), entry.getValue());
559 private boolean isPortTypeSwitchdev(final Port port) {
560 if (!isPortVnicTypeDirect(port)) {
564 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
565 String profile = portBinding.getProfile();
566 if (profile == null || profile.isEmpty()) {
567 LOG.debug("Port {} has no binding:profile values", port.getUuid());
571 Map<String, JsonElement> mapProfile = unmarshal(profile);
572 JsonElement capabilities = mapProfile.get(NeutronConstants.BINDING_PROFILE_CAPABILITIES);
573 LOG.debug("Port {} capabilities: {}", port.getUuid(), capabilities);
574 if (capabilities == null || !capabilities.isJsonArray()) {
575 LOG.debug("binding profile capabilities not in array format: {}", capabilities);
579 JsonArray capabilitiesArray = capabilities.getAsJsonArray();
580 Gson gson = new Gson();
581 JsonElement switchdevElement = gson.fromJson(NeutronConstants.SWITCHDEV, JsonElement.class);
582 return capabilitiesArray.contains(switchdevElement);
586 private void handleNeutronPortCreated(final Port port) {
587 final String portName = port.getUuid().getValue();
588 final Uuid portId = port.getUuid();
589 final List<FixedIps> portIpAddrsList = port.getFixedIps();
590 if (NeutronConstants.IS_ODL_DHCP_PORT.test(port)) {
593 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
594 // add direct port to subnetMaps config DS
595 if (!(NeutronUtils.isPortVnicTypeNormal(port)
596 || isPortTypeSwitchdev(port)
597 && isSupportedVnicTypeByHost(port, NeutronConstants.VNIC_TYPE_DIRECT))) {
598 for (FixedIps ip: portIpAddrsList) {
599 nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), null, portId);
601 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
602 + "OF Port interfaces are not created", portName);
603 return Collections.emptyList();
605 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
606 LOG.info("Of-port-interface creation for port {}", portName);
607 // Create of-port interface for this neutron port
608 String portInterfaceName = createOfPortInterface(port, tx);
609 LOG.debug("Creating ELAN Interface for port {}", portName);
610 createElanInterface(port, portInterfaceName, tx);
611 Set<Uuid> vpnIdList = new HashSet<>();
612 Set<Uuid> routerIds = new HashSet<>();
613 for (FixedIps ip: portIpAddrsList) {
614 Subnetmap subnetMap = nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), portId, null);
615 if (subnetMap != null && subnetMap.getInternetVpnId() != null) {
616 if (!vpnIdList.contains(subnetMap.getInternetVpnId())) {
617 vpnIdList.add(subnetMap.getInternetVpnId());
620 if (subnetMap != null && subnetMap.getVpnId() != null) {
621 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
622 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
623 Uuid vpnId = subnetMap.getVpnId();
625 vpnIdList.add(vpnId);
628 if (subnetMap != null && subnetMap.getRouterId() != null) {
629 routerIds.add(subnetMap.getRouterId());
632 if (!vpnIdList.isEmpty()) {
633 // create new vpn-interface for neutron port
634 LOG.debug("handleNeutronPortCreated: Adding VPN Interface for port {} from network {}", portName,
635 port.getNetworkId().toString());
636 nvpnManager.createVpnInterface(vpnIdList, port, tx);
637 if (!routerIds.isEmpty()) {
638 for (Uuid routerId : routerIds) {
639 nvpnManager.addToNeutronRouterInterfacesMap(routerId,port.getUuid().getValue());
647 private void handleNeutronPortDeleted(final Port port) {
648 final String portName = port.getUuid().getValue();
649 final Uuid portId = port.getUuid();
650 final List<FixedIps> portIpsList = port.getFixedIps();
651 jobCoordinator.enqueueJob("PORT- " + portName,
652 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
653 if (!(NeutronUtils.isPortVnicTypeNormal(port) || isPortTypeSwitchdev(port))) {
654 for (FixedIps ip : portIpsList) {
655 // remove direct port from subnetMaps config DS
656 nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), null, portId);
658 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
659 + "Skipping OF Port interfaces removal", portName);
663 Set<Uuid> routerIds = new HashSet<>();
664 Uuid internetVpnId = null;
665 for (FixedIps ip : portIpsList) {
666 Subnetmap subnetMap = nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), portId, null);
667 if (subnetMap == null) {
670 if (subnetMap.getVpnId() != null) {
671 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
672 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
673 vpnId = subnetMap.getVpnId();
675 if (subnetMap.getRouterId() != null) {
676 routerIds.add(subnetMap.getRouterId());
678 internetVpnId = subnetMap.getInternetVpnId();
680 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(port.getDeviceOwner())
681 || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(port.getDeviceOwner())) {
682 String ipAddress = ip.getIpAddress().stringValue();
684 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipAddress, confTx);
686 if (internetVpnId != null) {
687 neutronvpnUtils.removeVpnPortFixedIpToPort(internetVpnId.getValue(),
692 if (vpnId != null || internetVpnId != null) {
693 // remove vpn-interface for this neutron port
694 LOG.debug("removing VPN Interface for port {}", portName);
695 if (!routerIds.isEmpty()) {
696 for (Uuid routerId : routerIds) {
697 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portName);
700 nvpnManager.deleteVpnInterface(portName, null /* vpn-id */, confTx);
702 // Remove of-port interface for this neutron port
703 // ELAN interface is also implicitly deleted as part of this operation
704 LOG.debug("Of-port-interface removal for port {}", portName);
705 deleteOfPortInterface(port, confTx);
706 //dissociate fixedIP from floatingIP if associated
707 nvpnManager.dissociatefixedIPFromFloatingIP(port.getUuid().getValue());
712 private void handleNeutronPortUpdated(final Port portoriginal, final Port portupdate) {
713 final List<FixedIps> portoriginalIps = portoriginal.getFixedIps();
714 final List<FixedIps> portupdateIps = portupdate.getFixedIps();
715 if (portoriginalIps == null || portoriginalIps.isEmpty()) {
716 handleNeutronPortCreated(portupdate);
720 if (portupdateIps == null || portupdateIps.isEmpty()) {
721 LOG.info("Ignoring portUpdate (fixed_ip removal) for port {} as this case is handled "
722 + "during subnet deletion event.", portupdate.getUuid().getValue());
726 if (NeutronConstants.IS_ODL_DHCP_PORT.test(portupdate)) {
730 jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(),
731 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
732 final List<Uuid> originalSnMapsIds = portoriginalIps.stream().map(FixedIps::getSubnetId)
733 .collect(Collectors.toList());
734 final List<Uuid> updateSnMapsIds = portupdateIps.stream().map(FixedIps::getSubnetId)
735 .collect(Collectors.toList());
736 Set<Uuid> originalRouterIds = new HashSet<>();
737 Set<Uuid> oldVpnIds = new HashSet<>();
738 for (Uuid snId: originalSnMapsIds) {
739 if (!updateSnMapsIds.remove(snId)) {
740 // snId was present in originalSnMapsIds, but not in updateSnMapsIds
741 Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
743 if (subnetMapOld != null && subnetMapOld.getVpnId() != null) {
744 oldVpnIds.add(subnetMapOld.getVpnId());
746 if (subnetMapOld != null && subnetMapOld.getInternetVpnId() != null) {
747 oldVpnIds.add(subnetMapOld.getInternetVpnId());
749 if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
750 originalRouterIds.add(subnetMapOld.getRouterId());
754 Set<Uuid> newVpnIds = new HashSet<>();
755 Set<Uuid> newRouterIds = new HashSet<>();
756 for (Uuid snId: updateSnMapsIds) {
757 Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
758 if (subnetMapNew != null) {
759 if (subnetMapNew.getVpnId() != null) {
760 newVpnIds.add(subnetMapNew.getVpnId());
762 if (subnetMapNew.getInternetVpnId() != null) {
763 newVpnIds.add(subnetMapNew.getInternetVpnId());
765 if (subnetMapNew.getRouterId() != null) {
766 newRouterIds.add(subnetMapNew.getRouterId());
770 if (!oldVpnIds.isEmpty()) {
771 LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
772 if (!originalRouterIds.isEmpty()) {
773 for (Uuid routerId : originalRouterIds) {
774 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId,
775 portoriginal.getUuid().getValue());
778 nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(),
779 null /* vpn-id */, confTx);
781 if (!newVpnIds.isEmpty()) {
782 LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
783 nvpnManager.createVpnInterface(newVpnIds, portupdate, confTx);
784 if (!newRouterIds.isEmpty()) {
785 for (Uuid routerId : newRouterIds) {
786 nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
793 private InterfaceAclBuilder handlePortSecurityUpdated(Port portOriginal,
794 Port portUpdated, boolean origSecurityEnabled, boolean updatedSecurityEnabled,
795 InterfaceBuilder interfaceBuilder) {
796 InterfaceAclBuilder interfaceAclBuilder = null;
797 if (origSecurityEnabled != updatedSecurityEnabled) {
798 interfaceAclBuilder = new InterfaceAclBuilder();
799 interfaceAclBuilder.setPortSecurityEnabled(updatedSecurityEnabled);
800 if (updatedSecurityEnabled) {
801 // Handle security group enabled
802 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, portUpdated);
803 neutronvpnUtils.populateSubnetInfo(portUpdated);
805 // Handle security group disabled
806 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
807 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
810 if (updatedSecurityEnabled) {
811 // handle SG add/delete delta
812 InterfaceAcl interfaceAcl = interfaceBuilder.augmentation(InterfaceAcl.class);
813 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
814 interfaceAclBuilder.setSecurityGroups(
815 NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
816 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
817 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
818 interfaceAcl.getAllowedAddressPairs(), portOriginal.getAllowedAddressPairs(),
819 portUpdated.getAllowedAddressPairs());
820 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
821 updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
822 portUpdated.getFixedIps()));
824 if (portOriginal.getFixedIps() != null
825 && !portOriginal.getFixedIps().equals(portUpdated.getFixedIps())) {
826 neutronvpnUtils.populateSubnetInfo(portUpdated);
830 return interfaceAclBuilder;
833 private String createOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
834 Interface inf = createInterface(port);
835 String infName = inf.getName();
837 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
839 Optional<Interface> optionalInf =
840 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
841 interfaceIdentifier);
842 if (!optionalInf.isPresent()) {
843 wrtConfigTxn.put(interfaceIdentifier, inf);
845 LOG.warn("Interface {} is already present", infName);
847 } catch (ReadFailedException e) {
848 LOG.error("failed to create interface {}", infName, e);
853 private Interface createInterface(Port port) {
854 String interfaceName = port.getUuid().getValue();
855 IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
856 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
857 IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
859 Network network = neutronvpnUtils.getNeutronNetwork(port.getNetworkId());
860 Boolean isVlanTransparent = network.isVlanTransparent();
861 if (isVlanTransparent != null && isVlanTransparent) {
862 l2VlanMode = IfL2vlan.L2vlanMode.Transparent;
865 ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
867 interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
868 .addAugmentation(IfL2vlan.class, ifL2vlanBuilder.build());
870 if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
871 InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
872 interfaceAclBuilder.setPortSecurityEnabled(true);
873 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
874 interfaceBuilder.addAugmentation(InterfaceAcl.class, interfaceAclBuilder.build());
875 neutronvpnUtils.populateSubnetInfo(port);
877 return interfaceBuilder.build();
880 private void deleteOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
881 String name = port.getUuid().getValue();
882 LOG.debug("Removing OFPort Interface {}", name);
883 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
885 Optional<Interface> optionalInf =
886 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
887 interfaceIdentifier);
888 if (optionalInf.isPresent()) {
889 wrtConfigTxn.delete(interfaceIdentifier);
891 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
893 } catch (ReadFailedException e) {
894 LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
898 private void createElanInterface(Port port, String name,
899 TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
900 String elanInstanceName = port.getNetworkId().getValue();
901 List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
903 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
904 .class, new ElanInterfaceKey(name)).build();
905 ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
906 .setName(name).setStaticMacEntries(staticMacEntries).withKey(new ElanInterfaceKey(name)).build();
907 wrtConfigTxn.put(id, elanInterface);
908 LOG.debug("Creating new ELan Interface {}", elanInterface);
911 private void deleteElanInterface(String name, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
912 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
913 .class, new ElanInterfaceKey(name)).build();
914 wrtConfigTxn.delete(id);
917 // TODO Clean up the exception handling
918 @SuppressWarnings("checkstyle:IllegalCatch")
919 private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
920 floatingIpPortMacAddress) {
921 InstanceIdentifier id = NeutronvpnUtils.buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
923 FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
924 FloatingIpIdToPortMappingBuilder().withKey(new FloatingIpIdToPortMappingKey(floatingIpId))
925 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
926 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
927 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
928 LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
929 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
930 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
931 floatingipIdToPortMacMappingBuilder.build());
932 } catch (Exception e) {
933 LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
934 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
938 private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
939 return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();