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.gson.Gson;
15 import com.google.gson.JsonArray;
16 import com.google.gson.JsonElement;
17 import com.google.gson.JsonObject;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Locale;
26 import java.util.stream.Collectors;
27 import javax.annotation.PostConstruct;
28 import javax.inject.Singleton;
29 import org.apache.commons.lang3.ObjectUtils;
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.infrautils.utils.concurrent.ListenableFutures;
41 import org.opendaylight.netvirt.elanmanager.api.IElanService;
42 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
43 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
44 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlanBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAclBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceKey;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.elan._interface.StaticMacEntries;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.RoutersBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.hostconfig.rev150712.hostconfig.attributes.hostconfigs.Hostconfig;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
72 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
77 public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<Port, NeutronPortChangeListener> {
78 private static final Logger LOG = LoggerFactory.getLogger(NeutronPortChangeListener.class);
79 private final DataBroker dataBroker;
80 private final ManagedNewTransactionRunner txRunner;
81 private final NeutronvpnManager nvpnManager;
82 private final NeutronvpnNatManager nvpnNatManager;
83 private final NeutronSubnetGwMacResolver gwMacResolver;
84 private final IElanService elanService;
85 private final JobCoordinator jobCoordinator;
86 private final NeutronvpnUtils neutronvpnUtils;
87 private final HostConfigCache hostConfigCache;
89 public NeutronPortChangeListener(final DataBroker dataBroker,
90 final NeutronvpnManager neutronvpnManager,
91 final NeutronvpnNatManager neutronvpnNatManager,
92 final NeutronSubnetGwMacResolver gwMacResolver,
93 final IElanService elanService,
94 final JobCoordinator jobCoordinator,
95 final NeutronvpnUtils neutronvpnUtils,
96 final HostConfigCache hostConfigCache) {
97 super(Port.class, NeutronPortChangeListener.class);
98 this.dataBroker = dataBroker;
99 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
100 nvpnManager = neutronvpnManager;
101 nvpnNatManager = neutronvpnNatManager;
102 this.gwMacResolver = gwMacResolver;
103 this.elanService = elanService;
104 this.jobCoordinator = jobCoordinator;
105 this.neutronvpnUtils = neutronvpnUtils;
106 this.hostConfigCache = hostConfigCache;
112 LOG.info("{} init", getClass().getSimpleName());
113 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
117 protected InstanceIdentifier<Port> getWildCardPath() {
118 return InstanceIdentifier.create(Neutron.class).child(Ports.class).child(Port.class);
122 protected NeutronPortChangeListener getDataTreeChangeListener() {
123 return NeutronPortChangeListener.this;
128 protected void add(InstanceIdentifier<Port> identifier, Port input) {
129 String portName = input.getUuid().getValue();
130 LOG.trace("Adding Port : key: {}, value={}", identifier, input);
131 Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
132 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
133 LOG.warn("neutron vpn received a port add() for a network without a provider extension augmentation "
134 + "or with an unsupported network type for the port {} which is part of network {}",
138 neutronvpnUtils.addToPortCache(input);
139 String portStatus = NeutronUtils.PORT_STATUS_DOWN;
140 if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
141 if (input.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF)) {
142 handleRouterInterfaceAdded(input);
143 NeutronUtils.createPortStatus(input.getUuid().getValue(), NeutronUtils.PORT_STATUS_ACTIVE, dataBroker);
146 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())) {
147 handleRouterGatewayUpdated(input);
148 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
149 } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
150 handleFloatingIpPortUpdated(null, input);
151 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
154 // Switchdev ports need to be bounded to a host before creation
155 // in order to validate the supported vnic types from the hostconfig
156 if (input.getFixedIps() != null
157 && !input.getFixedIps().isEmpty()
158 && !(isPortTypeSwitchdev(input) && !isPortBound(input))) {
159 handleNeutronPortCreated(input);
161 NeutronUtils.createPortStatus(input.getUuid().getValue(), portStatus, dataBroker);
165 protected void remove(InstanceIdentifier<Port> identifier, Port input) {
166 LOG.trace("Removing Port : key: {}, value={}", identifier, input);
167 Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
168 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
169 String portName = input.getUuid().getValue();
170 LOG.warn("neutron vpn received a port remove() for a network without a provider extension augmentation "
171 + "or with an unsupported network type for the port {} which is part of network {}",
175 neutronvpnUtils.removeFromPortCache(input);
176 NeutronUtils.deletePortStatus(input.getUuid().getValue(), dataBroker);
178 if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
179 if (input.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF)) {
180 handleRouterInterfaceRemoved(input);
181 /* nothing else to do here */
183 } else if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())
184 || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
185 elanService.removeKnownL3DmacAddress(input.getMacAddress().getValue(), input.getNetworkId().getValue());
188 if (input.getFixedIps() != null) {
189 handleNeutronPortDeleted(input);
194 protected void update(InstanceIdentifier<Port> identifier, Port original, Port update) {
195 // Switchdev ports need to be bounded to a host before creation
196 // in order to validate the supported vnic types from the hostconfig
197 if (isPortTypeSwitchdev(original)
198 && !isPortBound(original)
199 && isPortBound(update)) {
200 handleNeutronPortCreated(update);
202 final String portName = update.getUuid().getValue();
203 Network network = neutronvpnUtils.getNeutronNetwork(update.getNetworkId());
204 LOG.info("Update port {} from network {}", portName, update.getNetworkId().toString());
205 if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
206 LOG.error("neutron vpn received a port update() for a network without a provider extension augmentation "
207 + "or with an unsupported network type for the port {} which is part of network {}",
211 neutronvpnUtils.addToPortCache(update);
213 if ((Strings.isNullOrEmpty(original.getDeviceOwner()) || Strings.isNullOrEmpty(original.getDeviceId())
214 || NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING.equalsIgnoreCase(original.getDeviceId()))
215 && !Strings.isNullOrEmpty(update.getDeviceOwner()) && !Strings.isNullOrEmpty(update.getDeviceId())) {
216 if (update.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF)) {
217 handleRouterInterfaceAdded(update);
220 if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(update.getDeviceOwner())) {
221 handleRouterGatewayUpdated(update);
222 } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(update.getDeviceOwner())) {
223 handleFloatingIpPortUpdated(original, update);
226 Set<FixedIps> oldIPs = getFixedIpSet(original.getFixedIps());
227 Set<FixedIps> newIPs = getFixedIpSet(update.getFixedIps());
228 if (!oldIPs.equals(newIPs)) {
229 handleNeutronPortUpdated(original, update);
233 // check if port security enabled/disabled as part of port update
234 boolean origSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(original);
235 boolean updatedSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(update);
237 if (origSecurityEnabled || updatedSecurityEnabled) {
238 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(portName);
239 jobCoordinator.enqueueJob("PORT- " + portName,
240 () -> Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(confTx -> {
241 Optional<Interface> optionalInf =
242 confTx.read(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier).checkedGet();
243 if (optionalInf.isPresent()) {
244 InterfaceBuilder interfaceBuilder = new InterfaceBuilder(optionalInf.get());
245 InterfaceAcl infAcl = handlePortSecurityUpdated(original, update,
246 origSecurityEnabled, updatedSecurityEnabled, interfaceBuilder).build();
247 interfaceBuilder.addAugmentation(InterfaceAcl.class, infAcl);
248 LOG.info("update: Of-port-interface updation for port {}", portName);
249 // Update OFPort interface for this neutron port
250 confTx.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier,
251 interfaceBuilder.build());
253 LOG.warn("update: Interface {} is not present", portName);
259 private void handleFloatingIpPortUpdated(Port original, Port update) {
260 if ((original == null || original.getDeviceId().equals(NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING))
261 && !update.getDeviceId().equals(NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING)) {
262 // populate floating-ip uuid and floating-ip port attributes (uuid, mac and subnet id for the ONLY
263 // fixed IP) to be used by NAT, depopulated in NATService once mac is retrieved in the removal path
264 addToFloatingIpPortInfo(new Uuid(update.getDeviceId()), update.getUuid(), update.getFixedIps().get(0)
265 .getSubnetId(), update.getMacAddress().getValue());
266 elanService.addKnownL3DmacAddress(update.getMacAddress().getValue(), update.getNetworkId().getValue());
270 private void handleRouterInterfaceAdded(Port routerPort) {
271 if (routerPort.getDeviceId() != null) {
272 Uuid routerId = new Uuid(routerPort.getDeviceId());
273 Uuid infNetworkId = routerPort.getNetworkId();
274 Uuid existingVpnId = neutronvpnUtils.getVpnForNetwork(infNetworkId);
276 elanService.addKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
277 if (existingVpnId == null) {
278 Set<Uuid> listVpnIds = new HashSet<>();
279 Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true);
283 listVpnIds.add(vpnId);
284 Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId);
285 List<Subnetmap> subnetMapList = new ArrayList<>();
286 List<FixedIps> portIps = routerPort.getFixedIps();
287 boolean portIsIpv6 = false;
288 for (FixedIps portIP : portIps) {
289 // NOTE: Please donot change the order of calls to updateSubnetNodeWithFixedIP
290 // and addSubnetToVpn here
291 if (internetVpnId != null
292 && portIP.getIpAddress().getIpv6Address() != null) {
295 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
296 Uuid subnetId = portIP.getSubnetId();
297 nvpnManager.updateSubnetNodeWithFixedIp(subnetId, routerId,
298 routerPort.getUuid(), ipValue, routerPort.getMacAddress().getValue(), vpnId);
299 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
300 subnetMapList.add(sn);
303 listVpnIds.add(internetVpnId);
304 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChangeToAdd(
305 IpVersionChoice.IPV6, internetVpnId)) {
306 neutronvpnUtils.updateVpnInstanceWithIpFamily(internetVpnId.getValue(),
307 IpVersionChoice.IPV6, true);
308 neutronvpnUtils.updateVpnInstanceWithFallback(internetVpnId.getValue(), true);
311 if (! subnetMapList.isEmpty()) {
312 nvpnManager.createVpnInterface(listVpnIds, routerPort, null);
314 for (FixedIps portIP : routerPort.getFixedIps()) {
315 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
316 IpVersionChoice version = neutronvpnUtils.getIpVersionFromString(ipValue);
317 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChangeToAdd(version, vpnId)) {
318 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(),
321 if (version.isIpVersionChosen(IpVersionChoice.IPV4)) {
322 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(),
323 null /* internet-vpn-id */);
325 nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(), internetVpnId);
327 LOG.trace("NeutronPortChangeListener Add Subnet Gateway IP {} MAC {} Interface {} VPN {}",
328 ipValue, routerPort.getMacAddress(),
329 routerPort.getUuid().getValue(), vpnId.getValue());
331 nvpnManager.addToNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
332 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
333 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
334 String portInterfaceName = createOfPortInterface(routerPort, confTx);
335 createElanInterface(routerPort, portInterfaceName, confTx);
336 }), LOG, "Error creating ELAN interface for {}", routerPort);
338 LOG.error("Neutron network {} corresponding to router interface port {} for neutron router {}"
339 + " already associated to VPN {}", infNetworkId.getValue(), routerPort.getUuid().getValue(),
340 routerId.getValue(), existingVpnId.getValue());
345 private void handleRouterInterfaceRemoved(Port routerPort) {
346 if (routerPort.getDeviceId() != null) {
347 Uuid routerId = new Uuid(routerPort.getDeviceId());
348 Uuid infNetworkId = routerPort.getNetworkId();
349 elanService.removeKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
350 Uuid vpnId = ObjectUtils.defaultIfNull(neutronvpnUtils.getVpnForRouter(routerId, true), routerId);
351 List<FixedIps> portIps = routerPort.getFixedIps();
352 boolean vpnInstanceInternetIpVersionRemoved = false;
353 Uuid vpnInstanceInternetUuid = null;
354 for (FixedIps portIP : portIps) {
355 // Internet VPN : flush InternetVPN first
356 Uuid subnetId = portIP.getSubnetId();
357 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
358 if (sn != null && sn.getInternetVpnId() != null) {
359 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, sn.getInternetVpnId())) {
360 vpnInstanceInternetIpVersionRemoved = true;
361 vpnInstanceInternetUuid = sn.getInternetVpnId();
363 nvpnManager.updateVpnInternetForSubnet(sn, sn.getInternetVpnId(), false);
366 /* Remove ping responder for router interfaces
367 * A router interface reference in a VPN will have to be removed before the host interface references
368 * for that subnet in the VPN are removed. This is to ensure that the FIB Entry of the router interface
369 * is not the last entry to be removed for that subnet in the VPN.
370 * If router interface FIB entry is the last to be removed for a subnet in a VPN , then all the host
371 * interface references in the vpn will already have been cleared, which will cause failures in
372 * cleanup of router interface flows*/
373 nvpnManager.deleteVpnInterface(routerPort.getUuid().getValue(),
374 null /* vpn-id */, null /* wrtConfigTxn*/);
375 // update RouterInterfaces map
376 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
377 boolean vpnInstanceIpVersionRemoved = false;
378 IpVersionChoice vpnInstanceIpVersionToRemove = IpVersionChoice.UNDEFINED;
379 for (FixedIps portIP : portIps) {
380 Subnetmap sn = neutronvpnUtils.getSubnetmap(portIP.getSubnetId());
381 // router Port have either IPv4 or IPv6, never both
382 if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, vpnId)) {
383 vpnInstanceIpVersionRemoved = true;
384 vpnInstanceIpVersionToRemove = neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
386 String ipValue = String.valueOf(portIP.getIpAddress().getValue());
387 neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipValue, confTx);
388 // NOTE: Please donot change the order of calls to removeSubnetFromVpn and
389 // and updateSubnetNodeWithFixedIP
390 nvpnManager.removeSubnetFromVpn(vpnId, portIP.getSubnetId(),
391 sn != null ? sn.getInternetVpnId() : null);
392 nvpnManager.updateSubnetNodeWithFixedIp(portIP.getSubnetId(), null, null, null, null, null);
394 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
395 deleteElanInterface(routerPort.getUuid().getValue(), confTx);
396 deleteOfPortInterface(routerPort, confTx);
397 nvpnNatManager.handleSubnetsForExternalRouter(routerId);
398 if (vpnInstanceIpVersionRemoved) {
399 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), vpnInstanceIpVersionToRemove,
402 }), LOG, "Error handling interface removal");
403 if (vpnInstanceInternetIpVersionRemoved) {
404 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnInstanceInternetUuid.getValue(),
405 IpVersionChoice.IPV6, false);
406 neutronvpnUtils.updateVpnInstanceWithFallback(vpnInstanceInternetUuid.getValue(), false);
411 private void handleRouterGatewayUpdated(Port routerGwPort) {
412 Uuid routerId = new Uuid(routerGwPort.getDeviceId());
413 Uuid networkId = routerGwPort.getNetworkId();
414 Network network = neutronvpnUtils.getNeutronNetwork(networkId);
415 if (network == null) {
418 boolean isExternal = neutronvpnUtils.getIsExternal(network);
420 Uuid vpnInternetId = neutronvpnUtils.getVpnForNetwork(networkId);
421 if (vpnInternetId != null) {
422 List<Subnetmap> snList = neutronvpnUtils.getNeutronRouterSubnetMaps(routerId);
423 for (Subnetmap sn : snList) {
424 if (sn.getNetworkId() == networkId) {
427 if (neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp()) != IpVersionChoice.IPV6) {
430 nvpnManager.addSubnetToVpn(null, sn.getId(), vpnInternetId);
434 elanService.addKnownL3DmacAddress(routerGwPort.getMacAddress().getValue(), networkId.getValue());
436 Router router = neutronvpnUtils.getNeutronRouter(routerId);
437 if (router == null) {
438 LOG.warn("No router found for router GW port {} for router {}", routerGwPort.getUuid().getValue(),
439 routerId.getValue());
442 gwMacResolver.sendArpRequestsToExtGateways(router);
444 setExternalGwMac(routerGwPort, routerId);
447 private void setExternalGwMac(Port routerGwPort, Uuid routerId) {
448 // During full-sync networking-odl syncs routers before ports. As such,
449 // the MAC of the router's gw port is not available to be set when the
450 // router is written. We catch that here.
451 InstanceIdentifier<Routers> routersId = NeutronvpnUtils.buildExtRoutersIdentifier(routerId);
452 Optional<Routers> optionalRouter = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId);
453 if (!optionalRouter.isPresent()) {
457 Routers extRouters = optionalRouter.get();
458 if (extRouters.getExtGwMacAddress() != null) {
462 RoutersBuilder builder = new RoutersBuilder(extRouters);
463 builder.setExtGwMacAddress(routerGwPort.getMacAddress().getValue());
464 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId, builder.build());
467 private String getPortHostId(final Port port) {
469 PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);
470 if (portBinding != null) {
471 return portBinding.getHostId();
477 private Hostconfig getHostConfig(final Port port) {
478 String hostId = getPortHostId(port);
479 if (hostId == null) {
482 Optional<Hostconfig> hostConfig;
484 hostConfig = this.hostConfigCache.get(hostId);
485 } catch (ReadFailedException e) {
486 LOG.error("failed to read host config from host {}", hostId, e);
489 return hostConfig.isPresent() ? hostConfig.get() : null;
492 private boolean isPortBound(final Port port) {
493 String hostId = getPortHostId(port);
494 return hostId != null && !hostId.isEmpty();
497 private boolean isPortVnicTypeDirect(Port port) {
498 PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);
499 if (portBinding == null || portBinding.getVnicType() == null) {
500 // By default, VNIC_TYPE is NORMAL
503 String vnicType = portBinding.getVnicType().trim().toLowerCase(Locale.getDefault());
504 return vnicType.equals(NeutronConstants.VNIC_TYPE_DIRECT);
507 private boolean isSupportedVnicTypeByHost(final Port port, final String vnicType) {
508 Hostconfig hostConfig = getHostConfig(port);
509 String supportStr = String.format("\"vnic_type\": \"%s\"", vnicType);
510 if (hostConfig != null && hostConfig.getConfig().contains(supportStr)) {
516 private Map<String, JsonElement> unmarshal(final String profile) {
517 if (null == profile) {
520 Gson gson = new Gson();
521 JsonObject jsonObject = gson.fromJson(profile, JsonObject.class);
522 Map<String, JsonElement> map = new HashMap();
523 for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
524 map.put(entry.getKey(), entry.getValue());
529 private boolean isPortTypeSwitchdev(final Port port) {
530 if (!isPortVnicTypeDirect(port)) {
534 PortBindingExtension portBinding = port.getAugmentation(PortBindingExtension.class);
535 String profile = portBinding.getProfile();
536 if (profile == null || profile.isEmpty()) {
537 LOG.debug("Port {} has no binding:profile values", port.getUuid());
541 Map<String, JsonElement> mapProfile = unmarshal(profile);
542 JsonElement capabilities = mapProfile.get(NeutronConstants.BINDING_PROFILE_CAPABILITIES);
543 LOG.debug("Port {} capabilities: {}", port.getUuid(), capabilities);
544 if (capabilities == null || !capabilities.isJsonArray()) {
545 LOG.debug("binding profile capabilities not in array format: {}", capabilities);
549 JsonArray capabilitiesArray = capabilities.getAsJsonArray();
550 Gson gson = new Gson();
551 JsonElement switchdevElement = gson.fromJson(NeutronConstants.SWITCHDEV, JsonElement.class);
552 return capabilitiesArray.contains(switchdevElement);
556 private void handleNeutronPortCreated(final Port port) {
557 final String portName = port.getUuid().getValue();
558 final Uuid portId = port.getUuid();
559 final List<FixedIps> portIpAddrsList = port.getFixedIps();
560 if (NeutronConstants.IS_ODL_DHCP_PORT.test(port)) {
563 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
564 // add direct port to subnetMaps config DS
565 if (!(NeutronUtils.isPortVnicTypeNormal(port)
566 || (isPortTypeSwitchdev(port)
567 && isSupportedVnicTypeByHost(port, NeutronConstants.VNIC_TYPE_DIRECT)))) {
568 for (FixedIps ip: portIpAddrsList) {
569 nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), null, portId);
571 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
572 + "OF Port interfaces are not created", portName);
573 return Collections.emptyList();
575 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
576 LOG.info("Of-port-interface creation for port {}", portName);
577 // Create of-port interface for this neutron port
578 String portInterfaceName = createOfPortInterface(port, tx);
579 LOG.debug("Creating ELAN Interface for port {}", portName);
580 createElanInterface(port, portInterfaceName, tx);
581 Set<Uuid> vpnIdList = new HashSet<>();
582 Set<Uuid> routerIds = new HashSet<>();
583 for (FixedIps ip: portIpAddrsList) {
584 Subnetmap subnetMap = nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), portId, null);
585 if (subnetMap != null && subnetMap.getInternetVpnId() != null) {
586 if (!vpnIdList.contains(subnetMap.getInternetVpnId())) {
587 vpnIdList.add(subnetMap.getInternetVpnId());
590 if (subnetMap != null && subnetMap.getVpnId() != null) {
591 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
592 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
593 Uuid vpnId = subnetMap.getVpnId();
595 vpnIdList.add(vpnId);
598 if (subnetMap != null && subnetMap.getRouterId() != null) {
599 routerIds.add(subnetMap.getRouterId());
602 if (!vpnIdList.isEmpty()) {
603 // create new vpn-interface for neutron port
604 LOG.debug("handleNeutronPortCreated: Adding VPN Interface for port {} from network {}", portName,
605 port.getNetworkId().toString());
606 nvpnManager.createVpnInterface(vpnIdList, port, tx);
607 if (!routerIds.isEmpty()) {
608 for (Uuid routerId : routerIds) {
609 nvpnManager.addToNeutronRouterInterfacesMap(routerId,port.getUuid().getValue());
617 private void handleNeutronPortDeleted(final Port port) {
618 final String portName = port.getUuid().getValue();
619 final Uuid portId = port.getUuid();
620 final List<FixedIps> portIpsList = port.getFixedIps();
621 jobCoordinator.enqueueJob("PORT- " + portName,
622 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
623 if (!(NeutronUtils.isPortVnicTypeNormal(port) || isPortTypeSwitchdev(port))) {
624 for (FixedIps ip : portIpsList) {
625 // remove direct port from subnetMaps config DS
626 nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), null, portId);
628 LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
629 + "Skipping OF Port interfaces removal", portName);
633 Set<Uuid> routerIds = new HashSet<>();
634 Uuid internetVpnId = null;
635 for (FixedIps ip : portIpsList) {
636 Subnetmap subnetMap = nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), portId, null);
637 if (subnetMap == null) {
640 if (subnetMap.getVpnId() != null) {
641 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
642 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
643 vpnId = subnetMap.getVpnId();
645 if (subnetMap.getRouterId() != null) {
646 routerIds.add(subnetMap.getRouterId());
648 internetVpnId = subnetMap.getInternetVpnId();
650 if (vpnId != null || internetVpnId != null) {
651 // remove vpn-interface for this neutron port
652 LOG.debug("removing VPN Interface for port {}", portName);
653 if (!routerIds.isEmpty()) {
654 for (Uuid routerId : routerIds) {
655 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portName);
658 nvpnManager.deleteVpnInterface(portName, null /* vpn-id */, confTx);
660 // Remove of-port interface for this neutron port
661 // ELAN interface is also implicitly deleted as part of this operation
662 LOG.debug("Of-port-interface removal for port {}", portName);
663 deleteOfPortInterface(port, confTx);
664 //dissociate fixedIP from floatingIP if associated
665 nvpnManager.dissociatefixedIPFromFloatingIP(port.getUuid().getValue());
670 private void handleNeutronPortUpdated(final Port portoriginal, final Port portupdate) {
671 final List<FixedIps> portoriginalIps = portoriginal.getFixedIps();
672 final List<FixedIps> portupdateIps = portupdate.getFixedIps();
673 if (portoriginalIps == null || portoriginalIps.isEmpty()) {
674 handleNeutronPortCreated(portupdate);
678 if (portupdateIps == null || portupdateIps.isEmpty()) {
679 LOG.info("Ignoring portUpdate (fixed_ip removal) for port {} as this case is handled "
680 + "during subnet deletion event.", portupdate.getUuid().getValue());
683 jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(),
684 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(confTx -> {
685 final List<Uuid> originalSnMapsIds = portoriginalIps.stream().map(FixedIps::getSubnetId)
686 .collect(Collectors.toList());
687 final List<Uuid> updateSnMapsIds = portupdateIps.stream().map(FixedIps::getSubnetId)
688 .collect(Collectors.toList());
689 Set<Uuid> originalRouterIds = new HashSet<>();
690 Set<Uuid> oldVpnIds = new HashSet<>();
691 for (Uuid snId: originalSnMapsIds) {
692 if (!updateSnMapsIds.remove(snId)) {
693 // snId was present in originalSnMapsIds, but not in updateSnMapsIds
694 Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
696 if (subnetMapOld != null && subnetMapOld.getVpnId() != null) {
697 oldVpnIds.add(subnetMapOld.getVpnId());
699 if (subnetMapOld != null && subnetMapOld.getInternetVpnId() != null) {
700 oldVpnIds.add(subnetMapOld.getInternetVpnId());
702 if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
703 originalRouterIds.add(subnetMapOld.getRouterId());
707 Set<Uuid> newVpnIds = new HashSet();
708 Set<Uuid> newRouterIds = new HashSet<>();
709 for (Uuid snId: updateSnMapsIds) {
710 Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
711 if (subnetMapNew != null) {
712 if (subnetMapNew.getVpnId() != null) {
713 newVpnIds.add(subnetMapNew.getVpnId());
715 if (subnetMapNew.getInternetVpnId() != null) {
716 newVpnIds.add(subnetMapNew.getInternetVpnId());
718 if (subnetMapNew.getRouterId() != null) {
719 newRouterIds.add(subnetMapNew.getRouterId());
723 if (!oldVpnIds.isEmpty()) {
724 LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
725 if (!originalRouterIds.isEmpty()) {
726 for (Uuid routerId : originalRouterIds) {
727 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId,
728 portoriginal.getUuid().getValue());
731 nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(),
732 null /* vpn-id */, confTx);
734 if (!newVpnIds.isEmpty()) {
735 LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
736 nvpnManager.createVpnInterface(newVpnIds, portupdate, confTx);
737 if (!newRouterIds.isEmpty()) {
738 for (Uuid routerId : newRouterIds) {
739 nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
746 private InterfaceAclBuilder handlePortSecurityUpdated(Port portOriginal,
747 Port portUpdated, boolean origSecurityEnabled, boolean updatedSecurityEnabled,
748 InterfaceBuilder interfaceBuilder) {
749 InterfaceAclBuilder interfaceAclBuilder = null;
750 if (origSecurityEnabled != updatedSecurityEnabled) {
751 interfaceAclBuilder = new InterfaceAclBuilder();
752 interfaceAclBuilder.setPortSecurityEnabled(updatedSecurityEnabled);
753 if (updatedSecurityEnabled) {
754 // Handle security group enabled
755 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, portUpdated);
756 neutronvpnUtils.populateSubnetInfo(portUpdated);
758 // Handle security group disabled
759 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
760 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
763 if (updatedSecurityEnabled) {
764 // handle SG add/delete delta
765 InterfaceAcl interfaceAcl = interfaceBuilder.getAugmentation(InterfaceAcl.class);
766 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
767 interfaceAclBuilder.setSecurityGroups(
768 NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
769 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
770 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
771 interfaceAcl.getAllowedAddressPairs(), portOriginal.getAllowedAddressPairs(),
772 portUpdated.getAllowedAddressPairs());
773 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
774 updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
775 portUpdated.getFixedIps()));
777 if (portOriginal.getFixedIps() != null
778 && !portOriginal.getFixedIps().equals(portUpdated.getFixedIps())) {
779 neutronvpnUtils.populateSubnetInfo(portUpdated);
783 return interfaceAclBuilder;
786 private String createOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
787 Interface inf = createInterface(port);
788 String infName = inf.getName();
790 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
792 Optional<Interface> optionalInf =
793 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
794 interfaceIdentifier);
795 if (!optionalInf.isPresent()) {
796 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, inf);
798 LOG.warn("Interface {} is already present", infName);
800 } catch (ReadFailedException e) {
801 LOG.error("failed to create interface {}", infName, e);
806 private Interface createInterface(Port port) {
807 String interfaceName = port.getUuid().getValue();
808 IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
809 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
810 IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
811 ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
813 interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
814 .addAugmentation(IfL2vlan.class, ifL2vlanBuilder.build());
816 if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
817 InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
818 interfaceAclBuilder.setPortSecurityEnabled(true);
819 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
820 interfaceBuilder.addAugmentation(InterfaceAcl.class, interfaceAclBuilder.build());
821 neutronvpnUtils.populateSubnetInfo(port);
823 return interfaceBuilder.build();
826 private void deleteOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
827 String name = port.getUuid().getValue();
828 LOG.debug("Removing OFPort Interface {}", name);
829 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
831 Optional<Interface> optionalInf =
832 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
833 interfaceIdentifier);
834 if (optionalInf.isPresent()) {
835 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier);
837 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
839 } catch (ReadFailedException e) {
840 LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
844 private void createElanInterface(Port port, String name, WriteTransaction wrtConfigTxn) {
845 String elanInstanceName = port.getNetworkId().getValue();
846 List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
848 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
849 .class, new ElanInterfaceKey(name)).build();
850 ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
851 .setName(name).setStaticMacEntries(staticMacEntries).setKey(new ElanInterfaceKey(name)).build();
852 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, id, elanInterface);
853 LOG.debug("Creating new ELan Interface {}", elanInterface);
856 private void deleteElanInterface(String name, WriteTransaction wrtConfigTxn) {
857 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
858 .class, new ElanInterfaceKey(name)).build();
859 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, id);
862 // TODO Clean up the exception handling
863 @SuppressWarnings("checkstyle:IllegalCatch")
864 private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
865 floatingIpPortMacAddress) {
866 InstanceIdentifier id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
868 FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
869 FloatingIpIdToPortMappingBuilder().setKey(new FloatingIpIdToPortMappingKey(floatingIpId))
870 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
871 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
872 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
873 LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
874 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
875 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
876 floatingipIdToPortMacMappingBuilder.build());
877 } catch (Exception e) {
878 LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
879 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
883 private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
884 return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();