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 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
342 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
344 String portInterfaceName = createOfPortInterface(routerPort, confTx);
345 createElanInterface(routerPort, portInterfaceName, confTx);
346 }), LOG, "Error creating ELAN interface for {}", routerPort);
348 LOG.error("Neutron network {} corresponding to router interface port {} for neutron router {}"
349 + " already associated to VPN {}", infNetworkId.getValue(), routerPort.getUuid().getValue(),
350 routerId.getValue(), existingVpnId.getValue());
355 private void handleRouterInterfaceRemoved(Port routerPort) {
356 if (routerPort.getDeviceId() != null) {
357 Uuid routerId = new Uuid(routerPort.getDeviceId());
358 Uuid infNetworkId = routerPort.getNetworkId();
359 elanService.removeKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
360 Uuid vpnId = ObjectUtils.defaultIfNull(neutronvpnUtils.getVpnForRouter(routerId, true), routerId);
361 List<FixedIps> portIps = routerPort.getFixedIps();
362 boolean vpnInstanceInternetIpVersionRemoved = false;
363 Uuid vpnInstanceInternetUuid = null;
364 for (FixedIps portIP : portIps) {
365 // Internet VPN : flush InternetVPN first
366 Uuid subnetId = portIP.getSubnetId();
367 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
368 if (sn != null && sn.getInternetVpnId() != null) {
369 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, sn.getInternetVpnId())) {
370 vpnInstanceInternetIpVersionRemoved = true;
371 vpnInstanceInternetUuid = sn.getInternetVpnId();
373 nvpnManager.updateVpnInternetForSubnet(sn, sn.getInternetVpnId(), false);
376 /* Remove ping responder for router interfaces
377 * A router interface reference in a VPN will have to be removed before the host interface references
378 * for that subnet in the VPN are removed. This is to ensure that the FIB Entry of the router interface
379 * is not the last entry to be removed for that subnet in the VPN.
380 * If router interface FIB entry is the last to be removed for a subnet in a VPN , then all the host
381 * interface references in the vpn will already have been cleared, which will cause failures in
382 * cleanup of router interface flows*/
383 nvpnManager.deleteVpnInterface(routerPort.getUuid().getValue(),
384 null /* vpn-id */, null /* wrtConfigTxn*/);
385 // update RouterInterfaces map
386 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
388 boolean vpnInstanceIpVersionRemoved = false;
389 IpVersionChoice vpnInstanceIpVersionToRemove = IpVersionChoice.UNDEFINED;
390 for (FixedIps portIP : portIps) {
391 Subnetmap sn = neutronvpnUtils.getSubnetmap(portIP.getSubnetId());
392 // router Port have either IPv4 or IPv6, never both
393 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, vpnId)) {
394 vpnInstanceIpVersionRemoved = true;
395 vpnInstanceIpVersionToRemove = NeutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
397 String ipValue = portIP.getIpAddress().stringValue();
398 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipValue, confTx);
399 // NOTE: Please donot change the order of calls to removeSubnetFromVpn and
400 // and updateSubnetNodeWithFixedIP
401 nvpnManager.removeSubnetFromVpn(vpnId, portIP.getSubnetId(),
402 sn != null ? sn.getInternetVpnId() : null);
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 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
410 if (vpnInstanceIpVersionRemoved) {
411 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), vpnInstanceIpVersionToRemove,
414 }), LOG, "Error handling interface removal");
415 if (vpnInstanceInternetIpVersionRemoved) {
416 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnInstanceInternetUuid.getValue(),
417 IpVersionChoice.IPV6, false);
418 neutronvpnUtils.updateVpnInstanceWithFallback(vpnInstanceInternetUuid.getValue(), false);
423 private void handleRouterGatewayUpdated(Port routerGwPort) {
424 Uuid routerId = new Uuid(routerGwPort.getDeviceId());
425 Uuid networkId = routerGwPort.getNetworkId();
426 Network network = neutronvpnUtils.getNeutronNetwork(networkId);
427 if (network == null) {
430 boolean isExternal = NeutronvpnUtils.getIsExternal(network);
432 Uuid vpnInternetId = neutronvpnUtils.getVpnForNetwork(networkId);
433 if (vpnInternetId != null) {
434 List<Subnetmap> snList = neutronvpnUtils.getNeutronRouterSubnetMaps(routerId);
435 for (Subnetmap sn : snList) {
436 if (sn.getNetworkId() == networkId) {
439 if (NeutronvpnUtils.getIpVersionFromString(sn.getSubnetIp()) != IpVersionChoice.IPV6) {
442 nvpnManager.addSubnetToVpn(null, sn.getId(), vpnInternetId);
446 elanService.addKnownL3DmacAddress(routerGwPort.getMacAddress().getValue(), networkId.getValue());
448 Router router = neutronvpnUtils.getNeutronRouter(routerId);
449 if (router == null) {
450 LOG.warn("No router found for router GW port {} for router {}", routerGwPort.getUuid().getValue(),
451 routerId.getValue());
453 eventCallbacks.onAddOrUpdate(LogicalDatastoreType.CONFIGURATION,
454 neutronvpnUtils.getNeutronRouterIid(routerId), (unused, newRouter) -> {
455 setupGwMac(newRouter, routerGwPort, routerId);
456 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
457 }, Duration.ofSeconds(3), iid -> {
458 LOG.error("GwPort {} added without Router", routerGwPort.getUuid().getValue());
462 setupGwMac(router, routerGwPort, routerId);
465 private void setupGwMac(Router router, Port routerGwPort, Uuid routerId) {
466 gwMacResolver.sendArpRequestsToExtGateways(router);
467 setExternalGwMac(routerGwPort, routerId);
471 private void setExternalGwMac(Port routerGwPort, Uuid routerId) {
472 // During full-sync networking-odl syncs routers before ports. As such,
473 // the MAC of the router's gw port is not available to be set when the
474 // router is written. We catch that here.
475 InstanceIdentifier<Routers> routersId = NeutronvpnUtils.buildExtRoutersIdentifier(routerId);
476 Optional<Routers> optionalRouter = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId);
477 if (!optionalRouter.isPresent()) {
481 Routers extRouters = optionalRouter.get();
482 if (extRouters.getExtGwMacAddress() != null) {
486 RoutersBuilder builder = new RoutersBuilder(extRouters);
487 builder.setExtGwMacAddress(routerGwPort.getMacAddress().getValue());
488 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId, builder.build());
491 private String getPortHostId(final Port port) {
493 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
494 if (portBinding != null) {
495 return portBinding.getHostId();
501 private Hostconfig getHostConfig(final Port port) {
502 String hostId = getPortHostId(port);
503 if (hostId == null) {
506 Optional<Hostconfig> hostConfig;
508 hostConfig = this.hostConfigCache.get(hostId);
509 } catch (ReadFailedException e) {
510 LOG.error("failed to read host config from host {}", hostId, e);
513 return hostConfig.isPresent() ? hostConfig.get() : null;
516 private boolean isPortBound(final Port port) {
517 String hostId = getPortHostId(port);
518 return hostId != null && !hostId.isEmpty();
521 private boolean isPortVnicTypeDirect(Port port) {
522 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
523 if (portBinding == null || portBinding.getVnicType() == null) {
524 // By default, VNIC_TYPE is NORMAL
527 String vnicType = portBinding.getVnicType().trim().toLowerCase(Locale.getDefault());
528 return vnicType.equals(NeutronConstants.VNIC_TYPE_DIRECT);
531 private boolean isSupportedVnicTypeByHost(final Port port, final String vnicType) {
532 Hostconfig hostConfig = getHostConfig(port);
533 String supportStr = String.format("\"vnic_type\": \"%s\"", vnicType);
534 if (hostConfig != null && hostConfig.getConfig().contains(supportStr)) {
540 private Map<String, JsonElement> unmarshal(final String profile) {
541 if (null == profile) {
544 Gson gson = new Gson();
545 JsonObject jsonObject = gson.fromJson(profile, JsonObject.class);
546 Map<String, JsonElement> map = new HashMap();
547 for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
548 map.put(entry.getKey(), entry.getValue());
553 private boolean isPortTypeSwitchdev(final Port port) {
554 if (!isPortVnicTypeDirect(port)) {
558 PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
559 String profile = portBinding.getProfile();
560 if (profile == null || profile.isEmpty()) {
561 LOG.debug("Port {} has no binding:profile values", port.getUuid());
565 Map<String, JsonElement> mapProfile = unmarshal(profile);
566 JsonElement capabilities = mapProfile.get(NeutronConstants.BINDING_PROFILE_CAPABILITIES);
567 LOG.debug("Port {} capabilities: {}", port.getUuid(), capabilities);
568 if (capabilities == null || !capabilities.isJsonArray()) {
569 LOG.debug("binding profile capabilities not in array format: {}", capabilities);
573 JsonArray capabilitiesArray = capabilities.getAsJsonArray();
574 Gson gson = new Gson();
575 JsonElement switchdevElement = gson.fromJson(NeutronConstants.SWITCHDEV, JsonElement.class);
576 return capabilitiesArray.contains(switchdevElement);
580 private void handleNeutronPortCreated(final Port port) {
581 final String portName = port.getUuid().getValue();
582 final Uuid portId = port.getUuid();
583 final List<FixedIps> portIpAddrsList = port.getFixedIps();
584 if (NeutronConstants.IS_ODL_DHCP_PORT.test(port)) {
587 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
588 // add direct port to subnetMaps config DS
589 if (!(NeutronUtils.isPortVnicTypeNormal(port)
590 || isPortTypeSwitchdev(port)
591 && isSupportedVnicTypeByHost(port, NeutronConstants.VNIC_TYPE_DIRECT))) {
592 for (FixedIps ip: portIpAddrsList) {
593 nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), null, portId);
595 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
596 + "OF Port interfaces are not created", portName);
597 return Collections.emptyList();
599 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
600 LOG.info("Of-port-interface creation for port {}", portName);
601 // Create of-port interface for this neutron port
602 String portInterfaceName = createOfPortInterface(port, tx);
603 LOG.debug("Creating ELAN Interface for port {}", portName);
604 createElanInterface(port, portInterfaceName, tx);
605 Set<Uuid> vpnIdList = new HashSet<>();
606 Set<Uuid> routerIds = new HashSet<>();
607 for (FixedIps ip: portIpAddrsList) {
608 Subnetmap subnetMap = nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), portId, null);
609 if (subnetMap != null && subnetMap.getInternetVpnId() != null) {
610 if (!vpnIdList.contains(subnetMap.getInternetVpnId())) {
611 vpnIdList.add(subnetMap.getInternetVpnId());
614 if (subnetMap != null && subnetMap.getVpnId() != null) {
615 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
616 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
617 Uuid vpnId = subnetMap.getVpnId();
619 vpnIdList.add(vpnId);
622 if (subnetMap != null && subnetMap.getRouterId() != null) {
623 routerIds.add(subnetMap.getRouterId());
626 if (!vpnIdList.isEmpty()) {
627 // create new vpn-interface for neutron port
628 LOG.debug("handleNeutronPortCreated: Adding VPN Interface for port {} from network {}", portName,
629 port.getNetworkId().toString());
630 nvpnManager.createVpnInterface(vpnIdList, port, tx);
631 if (!routerIds.isEmpty()) {
632 for (Uuid routerId : routerIds) {
633 nvpnManager.addToNeutronRouterInterfacesMap(routerId,port.getUuid().getValue());
641 private void handleNeutronPortDeleted(final Port port) {
642 final String portName = port.getUuid().getValue();
643 final Uuid portId = port.getUuid();
644 final List<FixedIps> portIpsList = port.getFixedIps();
645 jobCoordinator.enqueueJob("PORT- " + portName,
646 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
647 if (!(NeutronUtils.isPortVnicTypeNormal(port) || isPortTypeSwitchdev(port))) {
648 for (FixedIps ip : portIpsList) {
649 // remove direct port from subnetMaps config DS
650 nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), null, portId);
652 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
653 + "Skipping OF Port interfaces removal", portName);
657 Set<Uuid> routerIds = new HashSet<>();
658 Uuid internetVpnId = null;
659 for (FixedIps ip : portIpsList) {
660 Subnetmap subnetMap = nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), portId, null);
661 if (subnetMap == null) {
664 if (subnetMap.getVpnId() != null) {
665 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
666 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
667 vpnId = subnetMap.getVpnId();
669 if (subnetMap.getRouterId() != null) {
670 routerIds.add(subnetMap.getRouterId());
672 internetVpnId = subnetMap.getInternetVpnId();
674 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(port.getDeviceOwner())
675 || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(port.getDeviceOwner())) {
676 String ipAddress = ip.getIpAddress().stringValue();
678 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipAddress, confTx);
680 if (internetVpnId != null) {
681 neutronvpnUtils.removeVpnPortFixedIpToPort(internetVpnId.getValue(),
686 if (vpnId != null || internetVpnId != null) {
687 // remove vpn-interface for this neutron port
688 LOG.debug("removing VPN Interface for port {}", portName);
689 if (!routerIds.isEmpty()) {
690 for (Uuid routerId : routerIds) {
691 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portName);
694 nvpnManager.deleteVpnInterface(portName, null /* vpn-id */, confTx);
696 // Remove of-port interface for this neutron port
697 // ELAN interface is also implicitly deleted as part of this operation
698 LOG.debug("Of-port-interface removal for port {}", portName);
699 deleteOfPortInterface(port, confTx);
700 //dissociate fixedIP from floatingIP if associated
701 nvpnManager.dissociatefixedIPFromFloatingIP(port.getUuid().getValue());
706 private void handleNeutronPortUpdated(final Port portoriginal, final Port portupdate) {
707 final List<FixedIps> portoriginalIps = portoriginal.getFixedIps();
708 final List<FixedIps> portupdateIps = portupdate.getFixedIps();
709 if (portoriginalIps == null || portoriginalIps.isEmpty()) {
710 handleNeutronPortCreated(portupdate);
714 if (portupdateIps == null || portupdateIps.isEmpty()) {
715 LOG.info("Ignoring portUpdate (fixed_ip removal) for port {} as this case is handled "
716 + "during subnet deletion event.", portupdate.getUuid().getValue());
719 jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(),
720 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
721 final List<Uuid> originalSnMapsIds = portoriginalIps.stream().map(FixedIps::getSubnetId)
722 .collect(Collectors.toList());
723 final List<Uuid> updateSnMapsIds = portupdateIps.stream().map(FixedIps::getSubnetId)
724 .collect(Collectors.toList());
725 Set<Uuid> originalRouterIds = new HashSet<>();
726 Set<Uuid> oldVpnIds = new HashSet<>();
727 for (Uuid snId: originalSnMapsIds) {
728 if (!updateSnMapsIds.remove(snId)) {
729 // snId was present in originalSnMapsIds, but not in updateSnMapsIds
730 Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
732 if (subnetMapOld != null && subnetMapOld.getVpnId() != null) {
733 oldVpnIds.add(subnetMapOld.getVpnId());
735 if (subnetMapOld != null && subnetMapOld.getInternetVpnId() != null) {
736 oldVpnIds.add(subnetMapOld.getInternetVpnId());
738 if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
739 originalRouterIds.add(subnetMapOld.getRouterId());
743 Set<Uuid> newVpnIds = new HashSet<>();
744 Set<Uuid> newRouterIds = new HashSet<>();
745 for (Uuid snId: updateSnMapsIds) {
746 Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
747 if (subnetMapNew != null) {
748 if (subnetMapNew.getVpnId() != null) {
749 newVpnIds.add(subnetMapNew.getVpnId());
751 if (subnetMapNew.getInternetVpnId() != null) {
752 newVpnIds.add(subnetMapNew.getInternetVpnId());
754 if (subnetMapNew.getRouterId() != null) {
755 newRouterIds.add(subnetMapNew.getRouterId());
759 if (!oldVpnIds.isEmpty()) {
760 LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
761 if (!originalRouterIds.isEmpty()) {
762 for (Uuid routerId : originalRouterIds) {
763 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId,
764 portoriginal.getUuid().getValue());
767 nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(),
768 null /* vpn-id */, confTx);
770 if (!newVpnIds.isEmpty()) {
771 LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
772 nvpnManager.createVpnInterface(newVpnIds, portupdate, confTx);
773 if (!newRouterIds.isEmpty()) {
774 for (Uuid routerId : newRouterIds) {
775 nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
782 private InterfaceAclBuilder handlePortSecurityUpdated(Port portOriginal,
783 Port portUpdated, boolean origSecurityEnabled, boolean updatedSecurityEnabled,
784 InterfaceBuilder interfaceBuilder) {
785 InterfaceAclBuilder interfaceAclBuilder = null;
786 if (origSecurityEnabled != updatedSecurityEnabled) {
787 interfaceAclBuilder = new InterfaceAclBuilder();
788 interfaceAclBuilder.setPortSecurityEnabled(updatedSecurityEnabled);
789 if (updatedSecurityEnabled) {
790 // Handle security group enabled
791 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, portUpdated);
792 neutronvpnUtils.populateSubnetInfo(portUpdated);
794 // Handle security group disabled
795 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
796 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
799 if (updatedSecurityEnabled) {
800 // handle SG add/delete delta
801 InterfaceAcl interfaceAcl = interfaceBuilder.augmentation(InterfaceAcl.class);
802 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
803 interfaceAclBuilder.setSecurityGroups(
804 NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
805 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
806 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
807 interfaceAcl.getAllowedAddressPairs(), portOriginal.getAllowedAddressPairs(),
808 portUpdated.getAllowedAddressPairs());
809 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
810 updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
811 portUpdated.getFixedIps()));
813 if (portOriginal.getFixedIps() != null
814 && !portOriginal.getFixedIps().equals(portUpdated.getFixedIps())) {
815 neutronvpnUtils.populateSubnetInfo(portUpdated);
819 return interfaceAclBuilder;
822 private String createOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
823 Interface inf = createInterface(port);
824 String infName = inf.getName();
826 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
828 Optional<Interface> optionalInf =
829 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
830 interfaceIdentifier);
831 if (!optionalInf.isPresent()) {
832 wrtConfigTxn.put(interfaceIdentifier, inf);
834 LOG.warn("Interface {} is already present", infName);
836 } catch (ReadFailedException e) {
837 LOG.error("failed to create interface {}", infName, e);
842 private Interface createInterface(Port port) {
843 String interfaceName = port.getUuid().getValue();
844 IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
845 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
846 IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
848 Network network = neutronvpnUtils.getNeutronNetwork(port.getNetworkId());
849 Boolean isVlanTransparent = network.isVlanTransparent();
850 if (isVlanTransparent != null && isVlanTransparent) {
851 l2VlanMode = IfL2vlan.L2vlanMode.Transparent;
854 ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
856 interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
857 .addAugmentation(IfL2vlan.class, ifL2vlanBuilder.build());
859 if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
860 InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
861 interfaceAclBuilder.setPortSecurityEnabled(true);
862 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
863 interfaceBuilder.addAugmentation(InterfaceAcl.class, interfaceAclBuilder.build());
864 neutronvpnUtils.populateSubnetInfo(port);
866 return interfaceBuilder.build();
869 private void deleteOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
870 String name = port.getUuid().getValue();
871 LOG.debug("Removing OFPort Interface {}", name);
872 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
874 Optional<Interface> optionalInf =
875 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
876 interfaceIdentifier);
877 if (optionalInf.isPresent()) {
878 wrtConfigTxn.delete(interfaceIdentifier);
880 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
882 } catch (ReadFailedException e) {
883 LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
887 private void createElanInterface(Port port, String name,
888 TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
889 String elanInstanceName = port.getNetworkId().getValue();
890 List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
892 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
893 .class, new ElanInterfaceKey(name)).build();
894 ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
895 .setName(name).setStaticMacEntries(staticMacEntries).withKey(new ElanInterfaceKey(name)).build();
896 wrtConfigTxn.put(id, elanInterface);
897 LOG.debug("Creating new ELan Interface {}", elanInterface);
900 private void deleteElanInterface(String name, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
901 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
902 .class, new ElanInterfaceKey(name)).build();
903 wrtConfigTxn.delete(id);
906 // TODO Clean up the exception handling
907 @SuppressWarnings("checkstyle:IllegalCatch")
908 private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
909 floatingIpPortMacAddress) {
910 InstanceIdentifier id = NeutronvpnUtils.buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
912 FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
913 FloatingIpIdToPortMappingBuilder().withKey(new FloatingIpIdToPortMappingKey(floatingIpId))
914 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
915 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
916 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
917 LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
918 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
919 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
920 floatingipIdToPortMacMappingBuilder.build());
921 } catch (Exception e) {
922 LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
923 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
927 private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
928 return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();