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;
19 import java.time.Duration;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Locale;
28 import java.util.stream.Collectors;
29 import javax.annotation.PostConstruct;
30 import javax.inject.Singleton;
31 import org.apache.commons.lang3.ObjectUtils;
32 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
33 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
34 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
35 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
36 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
37 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
38 import org.opendaylight.genius.infra.Datastore;
39 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
40 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
41 import org.opendaylight.genius.infra.TypedWriteTransaction;
42 import org.opendaylight.genius.mdsalutil.MDSALUtil;
43 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
44 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
45 import org.opendaylight.netvirt.elanmanager.api.IElanService;
46 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
47 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
48 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlanBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAclBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.elan._interface.StaticMacEntries;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.RoutersBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingKey;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.hostconfig.rev150712.hostconfig.attributes.hostconfigs.Hostconfig;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
76 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
81 public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<Port, NeutronPortChangeListener> {
82 private static final Logger LOG = LoggerFactory.getLogger(NeutronPortChangeListener.class);
83 private final DataBroker dataBroker;
84 private final ManagedNewTransactionRunner txRunner;
85 private final NeutronvpnManager nvpnManager;
86 private final NeutronvpnNatManager nvpnNatManager;
87 private final NeutronSubnetGwMacResolver gwMacResolver;
88 private final IElanService elanService;
89 private final JobCoordinator jobCoordinator;
90 private final NeutronvpnUtils neutronvpnUtils;
91 private final HostConfigCache hostConfigCache;
92 private final DataTreeEventCallbackRegistrar eventCallbacks;
94 public NeutronPortChangeListener(final DataBroker dataBroker,
95 final NeutronvpnManager neutronvpnManager,
96 final NeutronvpnNatManager neutronvpnNatManager,
97 final NeutronSubnetGwMacResolver gwMacResolver,
98 final IElanService elanService,
99 final JobCoordinator jobCoordinator,
100 final NeutronvpnUtils neutronvpnUtils,
101 final HostConfigCache hostConfigCache,
102 final DataTreeEventCallbackRegistrar dataTreeEventCallbackRegistrar) {
103 super(Port.class, NeutronPortChangeListener.class);
104 this.dataBroker = dataBroker;
105 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
106 nvpnManager = neutronvpnManager;
107 nvpnNatManager = neutronvpnNatManager;
108 this.gwMacResolver = gwMacResolver;
109 this.elanService = elanService;
110 this.jobCoordinator = jobCoordinator;
111 this.neutronvpnUtils = neutronvpnUtils;
112 this.hostConfigCache = hostConfigCache;
113 this.eventCallbacks = dataTreeEventCallbackRegistrar;
119 LOG.info("{} init", getClass().getSimpleName());
120 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
124 protected InstanceIdentifier<Port> getWildCardPath() {
125 return InstanceIdentifier.create(Neutron.class).child(Ports.class).child(Port.class);
129 protected NeutronPortChangeListener getDataTreeChangeListener() {
130 return NeutronPortChangeListener.this;
135 protected void add(InstanceIdentifier<Port> identifier, Port input) {
136 String portName = input.getUuid().getValue();
137 LOG.trace("Adding Port : key: {}, value={}", identifier, input);
138 Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
139 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
140 LOG.warn("neutron vpn received a port add() for a network without a provider extension augmentation "
141 + "or with an unsupported network type for the port {} which is part of network {}",
146 neutronvpnUtils.addToPortCache(input);
147 String portStatus = NeutronUtils.PORT_STATUS_DOWN;
148 if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
149 if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(input.getDeviceOwner())) {
150 handleRouterInterfaceAdded(input);
151 NeutronUtils.createPortStatus(input.getUuid().getValue(), NeutronUtils.PORT_STATUS_ACTIVE, dataBroker);
154 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())) {
155 handleRouterGatewayUpdated(input);
156 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
157 } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
158 handleFloatingIpPortUpdated(null, input);
159 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
162 // Switchdev ports need to be bounded to a host before creation
163 // in order to validate the supported vnic types from the hostconfig
164 if (input.getFixedIps() != null
165 && !input.getFixedIps().isEmpty()
166 && !(isPortTypeSwitchdev(input) && !isPortBound(input))) {
167 handleNeutronPortCreated(input);
169 NeutronUtils.createPortStatus(input.getUuid().getValue(), portStatus, dataBroker);
173 protected void remove(InstanceIdentifier<Port> identifier, Port input) {
174 LOG.trace("Removing Port : key: {}, value={}", identifier, input);
175 Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
176 // need to proceed with deletion in case network is null for a case where v2 sync happens and a read for
177 // network from NN returns null, but the deletion process for port needs to continue
178 if (network != null && !NeutronvpnUtils.isNetworkTypeSupported(network)) {
179 String portName = input.getUuid().getValue();
180 LOG.warn("neutron vpn received a port remove() for a network without a provider extension augmentation "
181 + "or with an unsupported network type for the port {} which is part of network {}",
185 neutronvpnUtils.removeFromPortCache(input);
186 NeutronUtils.deletePortStatus(input.getUuid().getValue(), dataBroker);
188 if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
189 if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(input.getDeviceOwner())) {
190 handleRouterInterfaceRemoved(input);
191 /* nothing else to do here */
193 } else if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())
194 || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
195 elanService.removeKnownL3DmacAddress(input.getMacAddress().getValue(), input.getNetworkId().getValue());
198 if (input.getFixedIps() != null) {
199 handleNeutronPortDeleted(input);
204 protected void update(InstanceIdentifier<Port> identifier, Port original, Port update) {
205 // Switchdev ports need to be bounded to a host before creation
206 // in order to validate the supported vnic types from the hostconfig
207 if (isPortTypeSwitchdev(original)
208 && !isPortBound(original)
209 && isPortBound(update)) {
210 handleNeutronPortCreated(update);
212 final String portName = update.getUuid().getValue();
213 Network network = neutronvpnUtils.getNeutronNetwork(update.getNetworkId());
214 LOG.info("Update port {} from network {}", portName, update.getNetworkId().toString());
215 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
216 LOG.warn("neutron vpn received a port update() for a network without a provider extension augmentation "
217 + "or with an unsupported network type for the port {} which is part of network {}",
221 neutronvpnUtils.addToPortCache(update);
223 if ((Strings.isNullOrEmpty(original.getDeviceOwner()) || Strings.isNullOrEmpty(original.getDeviceId())
224 || NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING.equalsIgnoreCase(original.getDeviceId()))
225 && !Strings.isNullOrEmpty(update.getDeviceOwner()) && !Strings.isNullOrEmpty(update.getDeviceId())) {
226 if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(update.getDeviceOwner())) {
227 handleRouterInterfaceAdded(update);
230 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(update.getDeviceOwner())) {
231 handleRouterGatewayUpdated(update);
232 } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(update.getDeviceOwner())) {
233 handleFloatingIpPortUpdated(original, update);
236 Set<FixedIps> oldIPs = getFixedIpSet(original.getFixedIps());
237 Set<FixedIps> newIPs = getFixedIpSet(update.getFixedIps());
238 if (!oldIPs.equals(newIPs)) {
239 handleNeutronPortUpdated(original, update);
243 // check if port security enabled/disabled as part of port update
244 boolean origSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(original);
245 boolean updatedSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(update);
247 if (origSecurityEnabled || updatedSecurityEnabled) {
248 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(portName);
249 jobCoordinator.enqueueJob("PORT- " + portName,
250 () -> Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
252 Optional<Interface> optionalInf =
253 confTx.read(interfaceIdentifier).get();
254 if (optionalInf.isPresent()) {
255 InterfaceBuilder interfaceBuilder = new InterfaceBuilder(optionalInf.get());
256 InterfaceAcl infAcl = handlePortSecurityUpdated(original, update,
257 origSecurityEnabled, updatedSecurityEnabled, interfaceBuilder).build();
258 interfaceBuilder.addAugmentation(InterfaceAcl.class, infAcl);
259 LOG.info("update: Of-port-interface updation for port {}", portName);
260 // Update OFPort interface for this neutron port
261 confTx.put(interfaceIdentifier, interfaceBuilder.build());
263 LOG.warn("update: Interface {} is not present", portName);
269 private void handleFloatingIpPortUpdated(Port original, Port update) {
270 if ((original == null || original.getDeviceId().equals(NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING))
271 && !update.getDeviceId().equals(NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING)) {
272 // populate floating-ip uuid and floating-ip port attributes (uuid, mac and subnet id for the ONLY
273 // fixed IP) to be used by NAT, depopulated in NATService once mac is retrieved in the removal path
274 addToFloatingIpPortInfo(new Uuid(update.getDeviceId()), update.getUuid(), update.getFixedIps().get(0)
275 .getSubnetId(), update.getMacAddress().getValue());
276 elanService.addKnownL3DmacAddress(update.getMacAddress().getValue(), update.getNetworkId().getValue());
280 private void handleRouterInterfaceAdded(Port routerPort) {
281 if (routerPort.getDeviceId() != null) {
282 Uuid routerId = new Uuid(routerPort.getDeviceId());
283 Uuid infNetworkId = routerPort.getNetworkId();
284 Uuid existingVpnId = neutronvpnUtils.getVpnForNetwork(infNetworkId);
286 elanService.addKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
287 if (existingVpnId == null) {
288 Set<Uuid> listVpnIds = new HashSet<>();
289 Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true);
293 listVpnIds.add(vpnId);
294 Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId);
295 List<Subnetmap> subnetMapList = new ArrayList<>();
296 List<FixedIps> portIps = routerPort.getFixedIps();
297 boolean portIsIpv6 = false;
298 for (FixedIps portIP : portIps) {
299 // NOTE: Please donot change the order of calls to updateSubnetNodeWithFixedIP
300 // and addSubnetToVpn here
301 if (internetVpnId != null
302 && portIP.getIpAddress().getIpv6Address() != null) {
305 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
306 Uuid subnetId = portIP.getSubnetId();
307 nvpnManager.updateSubnetNodeWithFixedIp(subnetId, routerId,
308 routerPort.getUuid(), ipValue, routerPort.getMacAddress().getValue(), vpnId);
309 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
310 subnetMapList.add(sn);
313 listVpnIds.add(internetVpnId);
314 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChangeToAdd(
315 IpVersionChoice.IPV6, internetVpnId)) {
316 neutronvpnUtils.updateVpnInstanceWithIpFamily(internetVpnId.getValue(),
317 IpVersionChoice.IPV6, true);
318 neutronvpnUtils.updateVpnInstanceWithFallback(internetVpnId.getValue(), true);
321 if (! subnetMapList.isEmpty()) {
322 nvpnManager.createVpnInterface(listVpnIds, routerPort, null);
324 for (FixedIps portIP : routerPort.getFixedIps()) {
325 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
326 IpVersionChoice version = neutronvpnUtils.getIpVersionFromString(ipValue);
327 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChangeToAdd(version, vpnId)) {
328 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(),
331 if (version.isIpVersionChosen(IpVersionChoice.IPV4)) {
332 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(),
333 null /* internet-vpn-id */);
335 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(), internetVpnId);
337 LOG.trace("NeutronPortChangeListener Add Subnet Gateway IP {} MAC {} Interface {} VPN {}",
338 ipValue, routerPort.getMacAddress(),
339 routerPort.getUuid().getValue(), vpnId.getValue());
341 nvpnManager.addToNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
342 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
343 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
345 String portInterfaceName = createOfPortInterface(routerPort, confTx);
346 createElanInterface(routerPort, portInterfaceName, confTx);
347 }), LOG, "Error creating ELAN interface for {}", routerPort);
349 LOG.error("Neutron network {} corresponding to router interface port {} for neutron router {}"
350 + " already associated to VPN {}", infNetworkId.getValue(), routerPort.getUuid().getValue(),
351 routerId.getValue(), existingVpnId.getValue());
356 private void handleRouterInterfaceRemoved(Port routerPort) {
357 if (routerPort.getDeviceId() != null) {
358 Uuid routerId = new Uuid(routerPort.getDeviceId());
359 Uuid infNetworkId = routerPort.getNetworkId();
360 elanService.removeKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
361 Uuid vpnId = ObjectUtils.defaultIfNull(neutronvpnUtils.getVpnForRouter(routerId, true), routerId);
362 List<FixedIps> portIps = routerPort.getFixedIps();
363 boolean vpnInstanceInternetIpVersionRemoved = false;
364 Uuid vpnInstanceInternetUuid = null;
365 for (FixedIps portIP : portIps) {
366 // Internet VPN : flush InternetVPN first
367 Uuid subnetId = portIP.getSubnetId();
368 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
369 if (sn != null && sn.getInternetVpnId() != null) {
370 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, sn.getInternetVpnId())) {
371 vpnInstanceInternetIpVersionRemoved = true;
372 vpnInstanceInternetUuid = sn.getInternetVpnId();
374 nvpnManager.updateVpnInternetForSubnet(sn, sn.getInternetVpnId(), false);
377 /* Remove ping responder for router interfaces
378 * A router interface reference in a VPN will have to be removed before the host interface references
379 * for that subnet in the VPN are removed. This is to ensure that the FIB Entry of the router interface
380 * is not the last entry to be removed for that subnet in the VPN.
381 * If router interface FIB entry is the last to be removed for a subnet in a VPN , then all the host
382 * interface references in the vpn will already have been cleared, which will cause failures in
383 * cleanup of router interface flows*/
384 nvpnManager.deleteVpnInterface(routerPort.getUuid().getValue(),
385 null /* vpn-id */, null /* wrtConfigTxn*/);
386 // update RouterInterfaces map
387 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
389 boolean vpnInstanceIpVersionRemoved = false;
390 IpVersionChoice vpnInstanceIpVersionToRemove = IpVersionChoice.UNDEFINED;
391 for (FixedIps portIP : portIps) {
392 Subnetmap sn = neutronvpnUtils.getSubnetmap(portIP.getSubnetId());
393 // router Port have either IPv4 or IPv6, never both
394 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, vpnId)) {
395 vpnInstanceIpVersionRemoved = true;
396 vpnInstanceIpVersionToRemove = neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
398 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
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(),
403 sn != null ? sn.getInternetVpnId() : null);
404 nvpnManager.updateSubnetNodeWithFixedIp(portIP.getSubnetId(), null, null,
407 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
408 deleteElanInterface(routerPort.getUuid().getValue(), confTx);
409 deleteOfPortInterface(routerPort, confTx);
410 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
411 if (vpnInstanceIpVersionRemoved) {
412 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), vpnInstanceIpVersionToRemove,
415 }), LOG, "Error handling interface removal");
416 if (vpnInstanceInternetIpVersionRemoved) {
417 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnInstanceInternetUuid.getValue(),
418 IpVersionChoice.IPV6, false);
419 neutronvpnUtils.updateVpnInstanceWithFallback(vpnInstanceInternetUuid.getValue(), false);
424 private void handleRouterGatewayUpdated(Port routerGwPort) {
425 Uuid routerId = new Uuid(routerGwPort.getDeviceId());
426 Uuid networkId = routerGwPort.getNetworkId();
427 Network network = neutronvpnUtils.getNeutronNetwork(networkId);
428 if (network == null) {
431 boolean isExternal = neutronvpnUtils.getIsExternal(network);
433 Uuid vpnInternetId = neutronvpnUtils.getVpnForNetwork(networkId);
434 if (vpnInternetId != null) {
435 List<Subnetmap> snList = neutronvpnUtils.getNeutronRouterSubnetMaps(routerId);
436 for (Subnetmap sn : snList) {
437 if (sn.getNetworkId() == networkId) {
440 if (neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp()) != IpVersionChoice.IPV6) {
443 nvpnManager.addSubnetToVpn(null, sn.getId(), vpnInternetId);
447 elanService.addKnownL3DmacAddress(routerGwPort.getMacAddress().getValue(), networkId.getValue());
449 Router router = neutronvpnUtils.getNeutronRouter(routerId);
450 if (router == null) {
451 LOG.warn("No router found for router GW port {} for router {}", routerGwPort.getUuid().getValue(),
452 routerId.getValue());
454 eventCallbacks.onAddOrUpdate(LogicalDatastoreType.CONFIGURATION,
455 neutronvpnUtils.getNeutronRouterIid(routerId), (unused, newRouter) -> {
456 setupGwMac(newRouter, routerGwPort, routerId);
457 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
458 }, Duration.ofSeconds(3), iid -> {
459 LOG.error("GwPort {} added without Router", routerGwPort.getUuid().getValue());
463 setupGwMac(router, routerGwPort, routerId);
466 private void setupGwMac(Router router, Port routerGwPort, Uuid routerId) {
467 gwMacResolver.sendArpRequestsToExtGateways(router);
468 setExternalGwMac(routerGwPort, routerId);
472 private void setExternalGwMac(Port routerGwPort, Uuid routerId) {
473 // During full-sync networking-odl syncs routers before ports. As such,
474 // the MAC of the router's gw port is not available to be set when the
475 // router is written. We catch that here.
476 InstanceIdentifier<Routers> routersId = NeutronvpnUtils.buildExtRoutersIdentifier(routerId);
477 Optional<Routers> optionalRouter = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId);
478 if (!optionalRouter.isPresent()) {
482 Routers extRouters = optionalRouter.get();
483 if (extRouters.getExtGwMacAddress() != null) {
487 RoutersBuilder builder = new RoutersBuilder(extRouters);
488 builder.setExtGwMacAddress(routerGwPort.getMacAddress().getValue());
489 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId, builder.build());
492 private String getPortHostId(final Port port) {
494 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
495 if (portBinding != null) {
496 return portBinding.getHostId();
502 private Hostconfig getHostConfig(final Port port) {
503 String hostId = getPortHostId(port);
504 if (hostId == null) {
507 Optional<Hostconfig> hostConfig;
509 hostConfig = this.hostConfigCache.get(hostId);
510 } catch (ReadFailedException e) {
511 LOG.error("failed to read host config from host {}", hostId, e);
514 return hostConfig.isPresent() ? hostConfig.get() : null;
517 private boolean isPortBound(final Port port) {
518 String hostId = getPortHostId(port);
519 return hostId != null && !hostId.isEmpty();
522 private boolean isPortVnicTypeDirect(Port port) {
523 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
524 if (portBinding == null || portBinding.getVnicType() == null) {
525 // By default, VNIC_TYPE is NORMAL
528 String vnicType = portBinding.getVnicType().trim().toLowerCase(Locale.getDefault());
529 return vnicType.equals(NeutronConstants.VNIC_TYPE_DIRECT);
532 private boolean isSupportedVnicTypeByHost(final Port port, final String vnicType) {
533 Hostconfig hostConfig = getHostConfig(port);
534 String supportStr = String.format("\"vnic_type\": \"%s\"", vnicType);
535 if (hostConfig != null && hostConfig.getConfig().contains(supportStr)) {
541 private Map<String, JsonElement> unmarshal(final String profile) {
542 if (null == profile) {
545 Gson gson = new Gson();
546 JsonObject jsonObject = gson.fromJson(profile, JsonObject.class);
547 Map<String, JsonElement> map = new HashMap();
548 for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
549 map.put(entry.getKey(), entry.getValue());
554 private boolean isPortTypeSwitchdev(final Port port) {
555 if (!isPortVnicTypeDirect(port)) {
559 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
560 String profile = portBinding.getProfile();
561 if (profile == null || profile.isEmpty()) {
562 LOG.debug("Port {} has no binding:profile values", port.getUuid());
566 Map<String, JsonElement> mapProfile = unmarshal(profile);
567 JsonElement capabilities = mapProfile.get(NeutronConstants.BINDING_PROFILE_CAPABILITIES);
568 LOG.debug("Port {} capabilities: {}", port.getUuid(), capabilities);
569 if (capabilities == null || !capabilities.isJsonArray()) {
570 LOG.debug("binding profile capabilities not in array format: {}", capabilities);
574 JsonArray capabilitiesArray = capabilities.getAsJsonArray();
575 Gson gson = new Gson();
576 JsonElement switchdevElement = gson.fromJson(NeutronConstants.SWITCHDEV, JsonElement.class);
577 return capabilitiesArray.contains(switchdevElement);
581 private void handleNeutronPortCreated(final Port port) {
582 final String portName = port.getUuid().getValue();
583 final Uuid portId = port.getUuid();
584 final List<FixedIps> portIpAddrsList = port.getFixedIps();
585 if (NeutronConstants.IS_ODL_DHCP_PORT.test(port)) {
588 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
589 // add direct port to subnetMaps config DS
590 if (!(NeutronUtils.isPortVnicTypeNormal(port)
591 || (isPortTypeSwitchdev(port)
592 && isSupportedVnicTypeByHost(port, NeutronConstants.VNIC_TYPE_DIRECT)))) {
593 for (FixedIps ip: portIpAddrsList) {
594 nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), null, portId);
596 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
597 + "OF Port interfaces are not created", portName);
598 return Collections.emptyList();
600 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
601 LOG.info("Of-port-interface creation for port {}", portName);
602 // Create of-port interface for this neutron port
603 String portInterfaceName = createOfPortInterface(port, tx);
604 LOG.debug("Creating ELAN Interface for port {}", portName);
605 createElanInterface(port, portInterfaceName, tx);
606 Set<Uuid> vpnIdList = new HashSet<>();
607 Set<Uuid> routerIds = new HashSet<>();
608 for (FixedIps ip: portIpAddrsList) {
609 Subnetmap subnetMap = nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), portId, null);
610 if (subnetMap != null && subnetMap.getInternetVpnId() != null) {
611 if (!vpnIdList.contains(subnetMap.getInternetVpnId())) {
612 vpnIdList.add(subnetMap.getInternetVpnId());
615 if (subnetMap != null && subnetMap.getVpnId() != null) {
616 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
617 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
618 Uuid vpnId = subnetMap.getVpnId();
620 vpnIdList.add(vpnId);
623 if (subnetMap != null && subnetMap.getRouterId() != null) {
624 routerIds.add(subnetMap.getRouterId());
627 if (!vpnIdList.isEmpty()) {
628 // create new vpn-interface for neutron port
629 LOG.debug("handleNeutronPortCreated: Adding VPN Interface for port {} from network {}", portName,
630 port.getNetworkId().toString());
631 nvpnManager.createVpnInterface(vpnIdList, port, tx);
632 if (!routerIds.isEmpty()) {
633 for (Uuid routerId : routerIds) {
634 nvpnManager.addToNeutronRouterInterfacesMap(routerId,port.getUuid().getValue());
642 private void handleNeutronPortDeleted(final Port port) {
643 final String portName = port.getUuid().getValue();
644 final Uuid portId = port.getUuid();
645 final List<FixedIps> portIpsList = port.getFixedIps();
646 jobCoordinator.enqueueJob("PORT- " + portName,
647 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
648 if (!(NeutronUtils.isPortVnicTypeNormal(port) || isPortTypeSwitchdev(port))) {
649 for (FixedIps ip : portIpsList) {
650 // remove direct port from subnetMaps config DS
651 nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), null, portId);
653 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
654 + "Skipping OF Port interfaces removal", portName);
658 Set<Uuid> routerIds = new HashSet<>();
659 Uuid internetVpnId = null;
660 for (FixedIps ip : portIpsList) {
661 Subnetmap subnetMap = nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), portId, null);
662 if (subnetMap == null) {
665 if (subnetMap.getVpnId() != null) {
666 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
667 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
668 vpnId = subnetMap.getVpnId();
670 if (subnetMap.getRouterId() != null) {
671 routerIds.add(subnetMap.getRouterId());
673 internetVpnId = subnetMap.getInternetVpnId();
675 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(port.getDeviceOwner())
676 || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(port.getDeviceOwner())) {
677 String ipAddress = String.valueOf(ip.getIpAddress().getValue());
679 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipAddress, confTx);
681 if (internetVpnId != null) {
682 neutronvpnUtils.removeVpnPortFixedIpToPort(internetVpnId.getValue(),
687 if (vpnId != null || internetVpnId != null) {
688 // remove vpn-interface for this neutron port
689 LOG.debug("removing VPN Interface for port {}", portName);
690 if (!routerIds.isEmpty()) {
691 for (Uuid routerId : routerIds) {
692 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portName);
695 nvpnManager.deleteVpnInterface(portName, null /* vpn-id */, confTx);
697 // Remove of-port interface for this neutron port
698 // ELAN interface is also implicitly deleted as part of this operation
699 LOG.debug("Of-port-interface removal for port {}", portName);
700 deleteOfPortInterface(port, confTx);
701 //dissociate fixedIP from floatingIP if associated
702 nvpnManager.dissociatefixedIPFromFloatingIP(port.getUuid().getValue());
707 private void handleNeutronPortUpdated(final Port portoriginal, final Port portupdate) {
708 final List<FixedIps> portoriginalIps = portoriginal.getFixedIps();
709 final List<FixedIps> portupdateIps = portupdate.getFixedIps();
710 if (portoriginalIps == null || portoriginalIps.isEmpty()) {
711 handleNeutronPortCreated(portupdate);
715 if (portupdateIps == null || portupdateIps.isEmpty()) {
716 LOG.info("Ignoring portUpdate (fixed_ip removal) for port {} as this case is handled "
717 + "during subnet deletion event.", portupdate.getUuid().getValue());
720 jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(),
721 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
722 final List<Uuid> originalSnMapsIds = portoriginalIps.stream().map(FixedIps::getSubnetId)
723 .collect(Collectors.toList());
724 final List<Uuid> updateSnMapsIds = portupdateIps.stream().map(FixedIps::getSubnetId)
725 .collect(Collectors.toList());
726 Set<Uuid> originalRouterIds = new HashSet<>();
727 Set<Uuid> oldVpnIds = new HashSet<>();
728 for (Uuid snId: originalSnMapsIds) {
729 if (!updateSnMapsIds.remove(snId)) {
730 // snId was present in originalSnMapsIds, but not in updateSnMapsIds
731 Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
733 if (subnetMapOld != null && subnetMapOld.getVpnId() != null) {
734 oldVpnIds.add(subnetMapOld.getVpnId());
736 if (subnetMapOld != null && subnetMapOld.getInternetVpnId() != null) {
737 oldVpnIds.add(subnetMapOld.getInternetVpnId());
739 if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
740 originalRouterIds.add(subnetMapOld.getRouterId());
744 Set<Uuid> newVpnIds = new HashSet();
745 Set<Uuid> newRouterIds = new HashSet<>();
746 for (Uuid snId: updateSnMapsIds) {
747 Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
748 if (subnetMapNew != null) {
749 if (subnetMapNew.getVpnId() != null) {
750 newVpnIds.add(subnetMapNew.getVpnId());
752 if (subnetMapNew.getInternetVpnId() != null) {
753 newVpnIds.add(subnetMapNew.getInternetVpnId());
755 if (subnetMapNew.getRouterId() != null) {
756 newRouterIds.add(subnetMapNew.getRouterId());
760 if (!oldVpnIds.isEmpty()) {
761 LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
762 if (!originalRouterIds.isEmpty()) {
763 for (Uuid routerId : originalRouterIds) {
764 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId,
765 portoriginal.getUuid().getValue());
768 nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(),
769 null /* vpn-id */, confTx);
771 if (!newVpnIds.isEmpty()) {
772 LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
773 nvpnManager.createVpnInterface(newVpnIds, portupdate, confTx);
774 if (!newRouterIds.isEmpty()) {
775 for (Uuid routerId : newRouterIds) {
776 nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
783 private InterfaceAclBuilder handlePortSecurityUpdated(Port portOriginal,
784 Port portUpdated, boolean origSecurityEnabled, boolean updatedSecurityEnabled,
785 InterfaceBuilder interfaceBuilder) {
786 InterfaceAclBuilder interfaceAclBuilder = null;
787 if (origSecurityEnabled != updatedSecurityEnabled) {
788 interfaceAclBuilder = new InterfaceAclBuilder();
789 interfaceAclBuilder.setPortSecurityEnabled(updatedSecurityEnabled);
790 if (updatedSecurityEnabled) {
791 // Handle security group enabled
792 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, portUpdated);
793 neutronvpnUtils.populateSubnetInfo(portUpdated);
795 // Handle security group disabled
796 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
797 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
800 if (updatedSecurityEnabled) {
801 // handle SG add/delete delta
802 InterfaceAcl interfaceAcl = interfaceBuilder.augmentation(InterfaceAcl.class);
803 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
804 interfaceAclBuilder.setSecurityGroups(
805 NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
806 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
807 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
808 interfaceAcl.getAllowedAddressPairs(), portOriginal.getAllowedAddressPairs(),
809 portUpdated.getAllowedAddressPairs());
810 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
811 updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
812 portUpdated.getFixedIps()));
814 if (portOriginal.getFixedIps() != null
815 && !portOriginal.getFixedIps().equals(portUpdated.getFixedIps())) {
816 neutronvpnUtils.populateSubnetInfo(portUpdated);
820 return interfaceAclBuilder;
823 private String createOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
824 Interface inf = createInterface(port);
825 String infName = inf.getName();
827 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
829 Optional<Interface> optionalInf =
830 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
831 interfaceIdentifier);
832 if (!optionalInf.isPresent()) {
833 wrtConfigTxn.put(interfaceIdentifier, inf);
835 LOG.warn("Interface {} is already present", infName);
837 } catch (ReadFailedException e) {
838 LOG.error("failed to create interface {}", infName, e);
843 private Interface createInterface(Port port) {
844 String interfaceName = port.getUuid().getValue();
845 IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
846 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
847 IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
849 Network network = neutronvpnUtils.getNeutronNetwork(port.getNetworkId());
850 Boolean isVlanTransparent = network.isVlanTransparent();
851 if (isVlanTransparent != null && isVlanTransparent) {
852 l2VlanMode = IfL2vlan.L2vlanMode.Transparent;
855 ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
857 interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
858 .addAugmentation(IfL2vlan.class, ifL2vlanBuilder.build());
860 if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
861 InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
862 interfaceAclBuilder.setPortSecurityEnabled(true);
863 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
864 interfaceBuilder.addAugmentation(InterfaceAcl.class, interfaceAclBuilder.build());
865 neutronvpnUtils.populateSubnetInfo(port);
867 return interfaceBuilder.build();
870 private void deleteOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
871 String name = port.getUuid().getValue();
872 LOG.debug("Removing OFPort Interface {}", name);
873 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
875 Optional<Interface> optionalInf =
876 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
877 interfaceIdentifier);
878 if (optionalInf.isPresent()) {
879 wrtConfigTxn.delete(interfaceIdentifier);
881 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
883 } catch (ReadFailedException e) {
884 LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
888 private void createElanInterface(Port port, String name,
889 TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
890 String elanInstanceName = port.getNetworkId().getValue();
891 List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
893 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
894 .class, new ElanInterfaceKey(name)).build();
895 ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
896 .setName(name).setStaticMacEntries(staticMacEntries).withKey(new ElanInterfaceKey(name)).build();
897 wrtConfigTxn.put(id, elanInterface);
898 LOG.debug("Creating new ELan Interface {}", elanInterface);
901 private void deleteElanInterface(String name, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
902 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
903 .class, new ElanInterfaceKey(name)).build();
904 wrtConfigTxn.delete(id);
907 // TODO Clean up the exception handling
908 @SuppressWarnings("checkstyle:IllegalCatch")
909 private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
910 floatingIpPortMacAddress) {
911 InstanceIdentifier id = NeutronvpnUtils.buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
913 FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
914 FloatingIpIdToPortMappingBuilder().withKey(new FloatingIpIdToPortMappingKey(floatingIpId))
915 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
916 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
917 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
918 LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
919 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
920 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
921 floatingipIdToPortMacMappingBuilder.build());
922 } catch (Exception e) {
923 LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
924 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
928 private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
929 return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();