2 * Copyright © 2015, 2017 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.netvirt.neutronvpn.NeutronvpnUtils.buildfloatingIpIdToPortMappingIdentifier;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Strings;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.gson.Gson;
16 import com.google.gson.JsonArray;
17 import com.google.gson.JsonElement;
18 import com.google.gson.JsonObject;
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.opendaylight.controller.md.sal.binding.api.DataBroker;
31 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
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.infra.ManagedNewTransactionRunner;
37 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
38 import org.opendaylight.genius.mdsalutil.MDSALUtil;
39 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
40 import org.opendaylight.netvirt.elanmanager.api.IElanService;
41 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
42 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
43 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlanBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAclBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceKey;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.elan._interface.StaticMacEntries;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.RoutersBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.hostconfig.rev150712.hostconfig.attributes.hostconfigs.Hostconfig;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
71 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
76 public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<Port, NeutronPortChangeListener> {
77 private static final Logger LOG = LoggerFactory.getLogger(NeutronPortChangeListener.class);
78 private final DataBroker dataBroker;
79 private final ManagedNewTransactionRunner txRunner;
80 private final NeutronvpnManager nvpnManager;
81 private final NeutronvpnNatManager nvpnNatManager;
82 private final NeutronSubnetGwMacResolver gwMacResolver;
83 private final IElanService elanService;
84 private final JobCoordinator jobCoordinator;
85 private final NeutronvpnUtils neutronvpnUtils;
86 private final HostConfigCache hostConfigCache;
88 public NeutronPortChangeListener(final DataBroker dataBroker,
89 final NeutronvpnManager neutronvpnManager,
90 final NeutronvpnNatManager neutronvpnNatManager,
91 final NeutronSubnetGwMacResolver gwMacResolver,
92 final IElanService elanService,
93 final JobCoordinator jobCoordinator,
94 final NeutronvpnUtils neutronvpnUtils,
95 final HostConfigCache hostConfigCache) {
96 super(Port.class, NeutronPortChangeListener.class);
97 this.dataBroker = dataBroker;
98 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
99 nvpnManager = neutronvpnManager;
100 nvpnNatManager = neutronvpnNatManager;
101 this.gwMacResolver = gwMacResolver;
102 this.elanService = elanService;
103 this.jobCoordinator = jobCoordinator;
104 this.neutronvpnUtils = neutronvpnUtils;
105 this.hostConfigCache = hostConfigCache;
111 LOG.info("{} init", getClass().getSimpleName());
112 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
116 protected InstanceIdentifier<Port> getWildCardPath() {
117 return InstanceIdentifier.create(Neutron.class).child(Ports.class).child(Port.class);
121 protected NeutronPortChangeListener getDataTreeChangeListener() {
122 return NeutronPortChangeListener.this;
127 protected void add(InstanceIdentifier<Port> identifier, Port input) {
128 String portName = input.getUuid().getValue();
129 LOG.trace("Adding Port : key: {}, value={}", identifier, input);
130 Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
131 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
132 LOG.warn("neutron vpn received a port add() for a network without a provider extension augmentation "
133 + "or with an unsupported network type for the port {} which is part of network {}",
137 neutronvpnUtils.addToPortCache(input);
138 String portStatus = NeutronUtils.PORT_STATUS_DOWN;
139 if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
140 if (input.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF)) {
141 handleRouterInterfaceAdded(input);
142 NeutronUtils.createPortStatus(input.getUuid().getValue(), NeutronUtils.PORT_STATUS_ACTIVE, dataBroker);
145 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())) {
146 handleRouterGatewayUpdated(input);
147 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
148 } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
149 handleFloatingIpPortUpdated(null, input);
150 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
153 // Switchdev ports need to be bounded to a host before creation
154 // in order to validate the supported vnic types from the hostconfig
155 if (input.getFixedIps() != null
156 && !input.getFixedIps().isEmpty()
157 && !(isPortTypeSwitchdev(input) && !isPortBound(input))) {
158 handleNeutronPortCreated(input);
160 NeutronUtils.createPortStatus(input.getUuid().getValue(), portStatus, dataBroker);
164 protected void remove(InstanceIdentifier<Port> identifier, Port input) {
165 LOG.trace("Removing Port : key: {}, value={}", identifier, input);
166 Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
167 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
168 String portName = input.getUuid().getValue();
169 LOG.warn("neutron vpn received a port remove() for a network without a provider extension augmentation "
170 + "or with an unsupported network type for the port {} which is part of network {}",
174 neutronvpnUtils.removeFromPortCache(input);
175 NeutronUtils.deletePortStatus(input.getUuid().getValue(), dataBroker);
177 if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
178 if (input.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF)) {
179 handleRouterInterfaceRemoved(input);
180 /* nothing else to do here */
182 } else if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())
183 || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
184 elanService.removeKnownL3DmacAddress(input.getMacAddress().getValue(), input.getNetworkId().getValue());
187 if (input.getFixedIps() != null) {
188 handleNeutronPortDeleted(input);
193 protected void update(InstanceIdentifier<Port> identifier, Port original, Port update) {
194 // Switchdev ports need to be bounded to a host before creation
195 // in order to validate the supported vnic types from the hostconfig
196 if (isPortTypeSwitchdev(original)
197 && !isPortBound(original)
198 && isPortBound(update)) {
199 handleNeutronPortCreated(update);
201 final String portName = update.getUuid().getValue();
202 Network network = neutronvpnUtils.getNeutronNetwork(update.getNetworkId());
203 LOG.info("Update port {} from network {}", portName, update.getNetworkId().toString());
204 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
205 LOG.error("neutron vpn received a port update() for a network without a provider extension augmentation "
206 + "or with an unsupported network type for the port {} which is part of network {}",
210 neutronvpnUtils.addToPortCache(update);
212 if ((Strings.isNullOrEmpty(original.getDeviceOwner()) || Strings.isNullOrEmpty(original.getDeviceId())
213 || NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING.equalsIgnoreCase(original.getDeviceId()))
214 && !Strings.isNullOrEmpty(update.getDeviceOwner()) && !Strings.isNullOrEmpty(update.getDeviceId())) {
215 if (update.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF)) {
216 handleRouterInterfaceAdded(update);
219 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(update.getDeviceOwner())) {
220 handleRouterGatewayUpdated(update);
221 } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(update.getDeviceOwner())) {
222 handleFloatingIpPortUpdated(original, update);
225 Set<FixedIps> oldIPs = getFixedIpSet(original.getFixedIps());
226 Set<FixedIps> newIPs = getFixedIpSet(update.getFixedIps());
227 if (!oldIPs.equals(newIPs)) {
228 handleNeutronPortUpdated(original, update);
232 // check if port security enabled/disabled as part of port update
233 boolean origSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(original);
234 boolean updatedSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(update);
236 if (origSecurityEnabled || updatedSecurityEnabled) {
237 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(portName);
238 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
239 WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
241 Optional<Interface> optionalInf =
242 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
243 interfaceIdentifier);
244 if (optionalInf.isPresent()) {
245 InterfaceBuilder interfaceBuilder = new InterfaceBuilder(optionalInf.get());
246 InterfaceAcl infAcl = handlePortSecurityUpdated(original, update,
247 origSecurityEnabled, updatedSecurityEnabled, interfaceBuilder).build();
248 interfaceBuilder.addAugmentation(InterfaceAcl.class, infAcl);
249 LOG.info("update: Of-port-interface updation for port {}", portName);
250 // Update OFPort interface for this neutron port
251 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier,
252 interfaceBuilder.build());
254 LOG.warn("update: Interface {} is not present", portName);
256 } catch (ReadFailedException e) {
257 LOG.error("update: Failed to update interface {}", portName, e);
259 List<ListenableFuture<Void>> futures = new ArrayList<>();
260 futures.add(wrtConfigTxn.submit());
266 private void handleFloatingIpPortUpdated(Port original, Port update) {
267 if ((original == null || original.getDeviceId().equals(NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING))
268 && !update.getDeviceId().equals(NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING)) {
269 // populate floating-ip uuid and floating-ip port attributes (uuid, mac and subnet id for the ONLY
270 // fixed IP) to be used by NAT, depopulated in NATService once mac is retrieved in the removal path
271 addToFloatingIpPortInfo(new Uuid(update.getDeviceId()), update.getUuid(), update.getFixedIps().get(0)
272 .getSubnetId(), update.getMacAddress().getValue());
273 elanService.addKnownL3DmacAddress(update.getMacAddress().getValue(), update.getNetworkId().getValue());
277 private void handleRouterInterfaceAdded(Port routerPort) {
278 if (routerPort.getDeviceId() != null) {
279 Uuid routerId = new Uuid(routerPort.getDeviceId());
280 Uuid infNetworkId = routerPort.getNetworkId();
281 Uuid existingVpnId = neutronvpnUtils.getVpnForNetwork(infNetworkId);
283 elanService.addKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
284 if (existingVpnId == null) {
285 Set<Uuid> listVpnIds = new HashSet<>();
286 Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true);
290 listVpnIds.add(vpnId);
291 Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId);
292 List<Subnetmap> subnetMapList = new ArrayList<>();
293 List<FixedIps> portIps = routerPort.getFixedIps();
294 boolean portIsIpv6 = false;
295 for (FixedIps portIP : portIps) {
296 // NOTE: Please donot change the order of calls to updateSubnetNodeWithFixedIP
297 // and addSubnetToVpn here
298 if (internetVpnId != null
299 && portIP.getIpAddress().getIpv6Address() != null) {
302 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
303 Uuid subnetId = portIP.getSubnetId();
304 nvpnManager.updateSubnetNodeWithFixedIp(subnetId, routerId,
305 routerPort.getUuid(), ipValue, routerPort.getMacAddress().getValue(), vpnId);
306 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
307 subnetMapList.add(sn);
310 listVpnIds.add(internetVpnId);
311 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChangeToAdd(
312 IpVersionChoice.IPV6, internetVpnId)) {
313 neutronvpnUtils.updateVpnInstanceWithIpFamily(internetVpnId.getValue(),
314 IpVersionChoice.IPV6, true);
315 neutronvpnUtils.updateVpnInstanceWithFallback(internetVpnId.getValue(), true);
318 if (! subnetMapList.isEmpty()) {
319 nvpnManager.createVpnInterface(listVpnIds, routerPort, null);
321 for (FixedIps portIP : routerPort.getFixedIps()) {
322 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
323 IpVersionChoice version = neutronvpnUtils.getIpVersionFromString(ipValue);
324 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChangeToAdd(version, vpnId)) {
325 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(),
328 if (version.isIpVersionChosen(IpVersionChoice.IPV4)) {
329 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(),
330 null /* internet-vpn-id */);
332 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(), internetVpnId);
334 LOG.trace("NeutronPortChangeListener Add Subnet Gateway IP {} MAC {} Interface {} VPN {}",
335 ipValue, routerPort.getMacAddress(),
336 routerPort.getUuid().getValue(), vpnId.getValue());
338 nvpnManager.addToNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
339 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
340 WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
341 String portInterfaceName = createOfPortInterface(routerPort, wrtConfigTxn);
342 createElanInterface(routerPort, portInterfaceName, wrtConfigTxn);
343 wrtConfigTxn.submit();
345 LOG.error("Neutron network {} corresponding to router interface port {} for neutron router {}"
346 + " already associated to VPN {}", infNetworkId.getValue(), routerPort.getUuid().getValue(),
347 routerId.getValue(), existingVpnId.getValue());
352 private void handleRouterInterfaceRemoved(Port routerPort) {
353 if (routerPort.getDeviceId() != null) {
354 Uuid routerId = new Uuid(routerPort.getDeviceId());
355 Uuid infNetworkId = routerPort.getNetworkId();
356 elanService.removeKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
357 Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true);
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 WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
387 boolean vpnInstanceIpVersionRemoved = false;
388 IpVersionChoice vpnInstanceIpVersionToRemove = IpVersionChoice.UNDEFINED;
389 for (FixedIps portIP : portIps) {
390 Subnetmap sn = neutronvpnUtils.getSubnetmap(portIP.getSubnetId());
391 // router Port have either IPv4 or IPv6, never both
392 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, vpnId)) {
393 vpnInstanceIpVersionRemoved = true;
394 vpnInstanceIpVersionToRemove = neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
396 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
397 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipValue, wrtConfigTxn);
398 // NOTE: Please donot change the order of calls to removeSubnetFromVpn and
399 // and updateSubnetNodeWithFixedIP
400 nvpnManager.removeSubnetFromVpn(vpnId, portIP.getSubnetId(), sn != null ? sn.getInternetVpnId() : null);
401 nvpnManager.updateSubnetNodeWithFixedIp(portIP.getSubnetId(), null, null, null, null, null);
403 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
404 deleteElanInterface(routerPort.getUuid().getValue(), wrtConfigTxn);
405 deleteOfPortInterface(routerPort, wrtConfigTxn);
406 wrtConfigTxn.submit();
407 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
408 if (vpnInstanceIpVersionRemoved) {
409 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), vpnInstanceIpVersionToRemove,
412 if (vpnInstanceInternetIpVersionRemoved) {
413 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnInstanceInternetUuid.getValue(),
414 IpVersionChoice.IPV6, false);
415 neutronvpnUtils.updateVpnInstanceWithFallback(vpnInstanceInternetUuid.getValue(), false);
420 private void handleRouterGatewayUpdated(Port routerGwPort) {
421 Uuid routerId = new Uuid(routerGwPort.getDeviceId());
422 Uuid networkId = routerGwPort.getNetworkId();
423 Network network = neutronvpnUtils.getNeutronNetwork(networkId);
424 if (network == null) {
427 boolean isExternal = neutronvpnUtils.getIsExternal(network);
429 Uuid vpnInternetId = neutronvpnUtils.getVpnForNetwork(networkId);
430 if (vpnInternetId != null) {
431 List<Subnetmap> snList = neutronvpnUtils.getNeutronRouterSubnetMaps(routerId);
432 for (Subnetmap sn : snList) {
433 if (sn.getNetworkId() == networkId) {
436 if (neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp()) != IpVersionChoice.IPV6) {
439 nvpnManager.addSubnetToVpn(null, sn.getId(), vpnInternetId);
443 elanService.addKnownL3DmacAddress(routerGwPort.getMacAddress().getValue(), networkId.getValue());
445 Router router = neutronvpnUtils.getNeutronRouter(routerId);
446 if (router == null) {
447 LOG.warn("No router found for router GW port {} for router {}", routerGwPort.getUuid().getValue(),
448 routerId.getValue());
451 gwMacResolver.sendArpRequestsToExtGateways(router);
453 setExternalGwMac(routerGwPort, routerId);
456 private void setExternalGwMac(Port routerGwPort, Uuid routerId) {
457 // During full-sync networking-odl syncs routers before ports. As such,
458 // the MAC of the router's gw port is not available to be set when the
459 // router is written. We catch that here.
460 InstanceIdentifier<Routers> routersId = NeutronvpnUtils.buildExtRoutersIdentifier(routerId);
461 Optional<Routers> optionalRouter = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId);
462 if (!optionalRouter.isPresent()) {
466 Routers extRouters = optionalRouter.get();
467 if (extRouters.getExtGwMacAddress() != null) {
471 RoutersBuilder builder = new RoutersBuilder(extRouters);
472 builder.setExtGwMacAddress(routerGwPort.getMacAddress().getValue());
473 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId, builder.build());
476 private String getPortHostId(final Port port) {
478 PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);
479 if (portBinding != null) {
480 return portBinding.getHostId();
486 private Hostconfig getHostConfig(final Port port) {
487 String hostId = getPortHostId(port);
488 if (hostId == null) {
491 Optional<Hostconfig> hostConfig;
493 hostConfig = this.hostConfigCache.get(hostId);
494 } catch (ReadFailedException e) {
495 LOG.error("failed to read host config from host {}", hostId, e);
498 return hostConfig.isPresent() ? hostConfig.get() : null;
501 private boolean isPortBound(final Port port) {
502 String hostId = getPortHostId(port);
503 return hostId != null && !hostId.isEmpty();
506 private boolean isPortVnicTypeDirect(Port port) {
507 PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);
508 if (portBinding == null || portBinding.getVnicType() == null) {
509 // By default, VNIC_TYPE is NORMAL
512 String vnicType = portBinding.getVnicType().trim().toLowerCase(Locale.getDefault());
513 return vnicType.equals(NeutronConstants.VNIC_TYPE_DIRECT);
516 private boolean isSupportedVnicTypeByHost(final Port port, final String vnicType) {
517 Hostconfig hostConfig = getHostConfig(port);
518 String supportStr = String.format("\"vnic_type\": \"%s\"", vnicType);
519 if (hostConfig != null && hostConfig.getConfig().contains(supportStr)) {
525 private Map<String, JsonElement> unmarshal(final String profile) {
526 if (null == profile) {
529 Gson gson = new Gson();
530 JsonObject jsonObject = gson.fromJson(profile, JsonObject.class);
531 Map<String, JsonElement> map = new HashMap();
532 for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
533 map.put(entry.getKey(), entry.getValue());
538 private boolean isPortTypeSwitchdev(final Port port) {
539 if (!isPortVnicTypeDirect(port)) {
543 PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);
544 String profile = portBinding.getProfile();
545 if (profile == null || profile.isEmpty()) {
546 LOG.debug("Port {} has no binding:profile values", port.getUuid());
550 Map<String, JsonElement> mapProfile = unmarshal(profile);
551 JsonElement capabilities = mapProfile.get(NeutronConstants.BINDING_PROFILE_CAPABILITIES);
552 LOG.debug("Port {} capabilities: {}", port.getUuid(), capabilities);
553 if (capabilities == null || !capabilities.isJsonArray()) {
554 LOG.debug("binding profile capabilities not in array format: {}", capabilities);
558 JsonArray capabilitiesArray = capabilities.getAsJsonArray();
559 Gson gson = new Gson();
560 JsonElement switchdevElement = gson.fromJson(NeutronConstants.SWITCHDEV, JsonElement.class);
561 return capabilitiesArray.contains(switchdevElement);
565 private void handleNeutronPortCreated(final Port port) {
566 final String portName = port.getUuid().getValue();
567 final Uuid portId = port.getUuid();
568 final List<FixedIps> portIpAddrsList = port.getFixedIps();
569 if (NeutronConstants.IS_ODL_DHCP_PORT.test(port)) {
572 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
573 // add direct port to subnetMaps config DS
574 if (!(NeutronUtils.isPortVnicTypeNormal(port)
575 || (isPortTypeSwitchdev(port)
576 && isSupportedVnicTypeByHost(port, NeutronConstants.VNIC_TYPE_DIRECT)))) {
577 for (FixedIps ip: portIpAddrsList) {
578 nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), null, portId);
580 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
581 + "OF Port interfaces are not created", portName);
582 return Collections.emptyList();
584 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
585 LOG.info("Of-port-interface creation for port {}", portName);
586 // Create of-port interface for this neutron port
587 String portInterfaceName = createOfPortInterface(port, tx);
588 LOG.debug("Creating ELAN Interface for port {}", portName);
589 createElanInterface(port, portInterfaceName, tx);
590 Set<Uuid> vpnIdList = new HashSet<>();
591 Set<Uuid> routerIds = new HashSet<>();
592 for (FixedIps ip: portIpAddrsList) {
593 Subnetmap subnetMap = nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), portId, null);
594 if (subnetMap != null && subnetMap.getInternetVpnId() != null) {
595 if (!vpnIdList.contains(subnetMap.getInternetVpnId())) {
596 vpnIdList.add(subnetMap.getInternetVpnId());
599 if (subnetMap != null && subnetMap.getVpnId() != null) {
600 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
601 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
602 Uuid vpnId = subnetMap.getVpnId();
604 vpnIdList.add(vpnId);
607 if (subnetMap != null && subnetMap.getRouterId() != null) {
608 routerIds.add(subnetMap.getRouterId());
611 if (!vpnIdList.isEmpty()) {
612 // create new vpn-interface for neutron port
613 LOG.debug("handleNeutronPortCreated: Adding VPN Interface for port {} from network {}", portName,
614 port.getNetworkId().toString());
615 nvpnManager.createVpnInterface(vpnIdList, port, tx);
616 if (!routerIds.isEmpty()) {
617 for (Uuid routerId : routerIds) {
618 nvpnManager.addToNeutronRouterInterfacesMap(routerId,port.getUuid().getValue());
626 private void handleNeutronPortDeleted(final Port port) {
627 final String portName = port.getUuid().getValue();
628 final Uuid portId = port.getUuid();
629 final List<FixedIps> portIpsList = port.getFixedIps();
630 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
631 WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
632 List<ListenableFuture<Void>> futures = new ArrayList<>();
633 if (!(NeutronUtils.isPortVnicTypeNormal(port) || isPortTypeSwitchdev(port))) {
634 for (FixedIps ip: portIpsList) {
635 // remove direct port from subnetMaps config DS
636 nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), null, portId);
638 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
639 + "Skipping OF Port interfaces removal", portName);
643 Set<Uuid> routerIds = new HashSet<>();
644 Uuid internetVpnId = null;
645 for (FixedIps ip: portIpsList) {
646 Subnetmap subnetMap = nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), portId, null);
647 if (subnetMap == null) {
650 if (subnetMap.getVpnId() != null) {
651 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
652 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
653 vpnId = subnetMap.getVpnId();
655 if (subnetMap.getRouterId() != null) {
656 routerIds.add(subnetMap.getRouterId());
658 internetVpnId = subnetMap.getInternetVpnId();
660 if (vpnId != null || internetVpnId != null) {
661 // remove vpn-interface for this neutron port
662 LOG.debug("removing VPN Interface for port {}", portName);
663 if (!routerIds.isEmpty()) {
664 for (Uuid routerId : routerIds) {
665 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portName);
668 nvpnManager.deleteVpnInterface(portName, null /* vpn-id */, wrtConfigTxn);
670 // Remove of-port interface for this neutron port
671 // ELAN interface is also implicitly deleted as part of this operation
672 LOG.debug("Of-port-interface removal for port {}", portName);
673 deleteOfPortInterface(port, wrtConfigTxn);
674 //dissociate fixedIP from floatingIP if associated
675 nvpnManager.dissociatefixedIPFromFloatingIP(port.getUuid().getValue());
676 futures.add(wrtConfigTxn.submit());
682 private void handleNeutronPortUpdated(final Port portoriginal, final Port portupdate) {
683 final List<FixedIps> portoriginalIps = portoriginal.getFixedIps();
684 final List<FixedIps> portupdateIps = portupdate.getFixedIps();
685 if (portoriginalIps == null || portoriginalIps.isEmpty()) {
686 handleNeutronPortCreated(portupdate);
690 if (portupdateIps == null || portupdateIps.isEmpty()) {
691 LOG.info("Ignoring portUpdate (fixed_ip removal) for port {} as this case is handled "
692 + "during subnet deletion event.", portupdate.getUuid().getValue());
695 jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(),
696 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
697 final List<Uuid> originalSnMapsIds = portoriginalIps.stream().map(FixedIps::getSubnetId)
698 .collect(Collectors.toList());
699 final List<Uuid> updateSnMapsIds = portupdateIps.stream().map(FixedIps::getSubnetId)
700 .collect(Collectors.toList());
701 Set<Uuid> originalRouterIds = new HashSet<>();
702 Set<Uuid> oldVpnIds = new HashSet<>();
703 Uuid oldRouterId = null;
704 for (Uuid snId: originalSnMapsIds) {
705 if (!updateSnMapsIds.remove(snId)) {
706 // snId was present in originalSnMapsIds, but not in updateSnMapsIds
707 Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
709 if (subnetMapOld != null && subnetMapOld.getVpnId() != null) {
710 oldVpnIds.add(subnetMapOld.getVpnId());
712 if (subnetMapOld != null && subnetMapOld.getInternetVpnId() != null) {
713 oldVpnIds.add(subnetMapOld.getInternetVpnId());
715 if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
716 originalRouterIds.add(subnetMapOld.getRouterId());
720 Set<Uuid> newVpnIds = new HashSet();
721 Set<Uuid> newRouterIds = new HashSet<>();
722 for (Uuid snId: updateSnMapsIds) {
723 Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
724 if (subnetMapNew != null) {
725 if (subnetMapNew.getVpnId() != null) {
726 newVpnIds.add(subnetMapNew.getVpnId());
728 if (subnetMapNew.getInternetVpnId() != null) {
729 newVpnIds.add(subnetMapNew.getInternetVpnId());
731 if (subnetMapNew.getRouterId() != null) {
732 newRouterIds.add(subnetMapNew.getRouterId());
736 WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
737 if (!oldVpnIds.isEmpty()) {
738 LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
739 if (!originalRouterIds.isEmpty()) {
740 for (Uuid routerId : originalRouterIds) {
741 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId,
742 portoriginal.getUuid().getValue());
745 nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(),
746 null /* vpn-id */, wrtConfigTxn);
748 if (!newVpnIds.isEmpty()) {
749 LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
750 nvpnManager.createVpnInterface(newVpnIds, portupdate, wrtConfigTxn);
751 if (!newRouterIds.isEmpty()) {
752 for (Uuid routerId : newRouterIds) {
753 nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
760 private static InterfaceAclBuilder handlePortSecurityUpdated(Port portOriginal,
761 Port portUpdated, boolean origSecurityEnabled, boolean updatedSecurityEnabled,
762 InterfaceBuilder interfaceBuilder) {
763 InterfaceAclBuilder interfaceAclBuilder = null;
764 if (origSecurityEnabled != updatedSecurityEnabled) {
765 interfaceAclBuilder = new InterfaceAclBuilder();
766 interfaceAclBuilder.setPortSecurityEnabled(updatedSecurityEnabled);
767 if (updatedSecurityEnabled) {
768 // Handle security group enabled
769 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, portUpdated);
771 // Handle security group disabled
772 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
773 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
776 if (updatedSecurityEnabled) {
777 // handle SG add/delete delta
778 InterfaceAcl interfaceAcl = interfaceBuilder.getAugmentation(InterfaceAcl.class);
779 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
780 interfaceAclBuilder.setSecurityGroups(
781 NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
782 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
783 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
784 interfaceAcl.getAllowedAddressPairs(), portOriginal.getAllowedAddressPairs(),
785 portUpdated.getAllowedAddressPairs());
786 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
787 updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
788 portUpdated.getFixedIps()));
791 return interfaceAclBuilder;
794 private String createOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
795 Interface inf = createInterface(port);
796 String infName = inf.getName();
798 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
800 Optional<Interface> optionalInf =
801 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
802 interfaceIdentifier);
803 if (!optionalInf.isPresent()) {
804 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, inf);
806 LOG.warn("Interface {} is already present", infName);
808 } catch (ReadFailedException e) {
809 LOG.error("failed to create interface {}", infName, e);
814 private Interface createInterface(Port port) {
815 String interfaceName = port.getUuid().getValue();
816 IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
817 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
818 IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
819 ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
821 interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
822 .addAugmentation(IfL2vlan.class, ifL2vlanBuilder.build());
824 if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
825 InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
826 interfaceAclBuilder.setPortSecurityEnabled(true);
827 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
828 interfaceBuilder.addAugmentation(InterfaceAcl.class, interfaceAclBuilder.build());
829 neutronvpnUtils.populateSubnetIpPrefixes(port);
831 return interfaceBuilder.build();
834 private void deleteOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
835 String name = port.getUuid().getValue();
836 LOG.debug("Removing OFPort Interface {}", name);
837 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
839 Optional<Interface> optionalInf =
840 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
841 interfaceIdentifier);
842 if (optionalInf.isPresent()) {
843 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier);
845 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
847 } catch (ReadFailedException e) {
848 LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
852 private void createElanInterface(Port port, String name, WriteTransaction wrtConfigTxn) {
853 String elanInstanceName = port.getNetworkId().getValue();
854 List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
856 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
857 .class, new ElanInterfaceKey(name)).build();
858 ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
859 .setName(name).setStaticMacEntries(staticMacEntries).setKey(new ElanInterfaceKey(name)).build();
860 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, id, elanInterface);
861 LOG.debug("Creating new ELan Interface {}", elanInterface);
864 private void deleteElanInterface(String name, WriteTransaction wrtConfigTxn) {
865 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
866 .class, new ElanInterfaceKey(name)).build();
867 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, id);
870 // TODO Clean up the exception handling
871 @SuppressWarnings("checkstyle:IllegalCatch")
872 private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
873 floatingIpPortMacAddress) {
874 InstanceIdentifier id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
876 FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
877 FloatingIpIdToPortMappingBuilder().setKey(new FloatingIpIdToPortMappingKey(floatingIpId))
878 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
879 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
880 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
881 LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
882 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
883 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
884 floatingipIdToPortMacMappingBuilder.build());
885 } catch (Exception e) {
886 LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
887 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
891 private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
892 return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();