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 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);
770 neutronvpnUtils.populateSubnetInfo(portUpdated);
772 // Handle security group disabled
773 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
774 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
777 if (updatedSecurityEnabled) {
778 // handle SG add/delete delta
779 InterfaceAcl interfaceAcl = interfaceBuilder.getAugmentation(InterfaceAcl.class);
780 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
781 interfaceAclBuilder.setSecurityGroups(
782 NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
783 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
784 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
785 interfaceAcl.getAllowedAddressPairs(), portOriginal.getAllowedAddressPairs(),
786 portUpdated.getAllowedAddressPairs());
787 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
788 updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
789 portUpdated.getFixedIps()));
791 if (portOriginal.getFixedIps() != null
792 && !portOriginal.getFixedIps().equals(portUpdated.getFixedIps())) {
793 neutronvpnUtils.populateSubnetInfo(portUpdated);
797 return interfaceAclBuilder;
800 private String createOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
801 Interface inf = createInterface(port);
802 String infName = inf.getName();
804 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
806 Optional<Interface> optionalInf =
807 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
808 interfaceIdentifier);
809 if (!optionalInf.isPresent()) {
810 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, inf);
812 LOG.warn("Interface {} is already present", infName);
814 } catch (ReadFailedException e) {
815 LOG.error("failed to create interface {}", infName, e);
820 private Interface createInterface(Port port) {
821 String interfaceName = port.getUuid().getValue();
822 IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
823 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
824 IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
825 ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
827 interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
828 .addAugmentation(IfL2vlan.class, ifL2vlanBuilder.build());
830 if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
831 InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
832 interfaceAclBuilder.setPortSecurityEnabled(true);
833 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
834 interfaceBuilder.addAugmentation(InterfaceAcl.class, interfaceAclBuilder.build());
835 neutronvpnUtils.populateSubnetInfo(port);
837 return interfaceBuilder.build();
840 private void deleteOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
841 String name = port.getUuid().getValue();
842 LOG.debug("Removing OFPort Interface {}", name);
843 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
845 Optional<Interface> optionalInf =
846 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
847 interfaceIdentifier);
848 if (optionalInf.isPresent()) {
849 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier);
851 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
853 } catch (ReadFailedException e) {
854 LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
858 private void createElanInterface(Port port, String name, WriteTransaction wrtConfigTxn) {
859 String elanInstanceName = port.getNetworkId().getValue();
860 List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
862 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
863 .class, new ElanInterfaceKey(name)).build();
864 ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
865 .setName(name).setStaticMacEntries(staticMacEntries).setKey(new ElanInterfaceKey(name)).build();
866 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, id, elanInterface);
867 LOG.debug("Creating new ELan Interface {}", elanInterface);
870 private void deleteElanInterface(String name, WriteTransaction wrtConfigTxn) {
871 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
872 .class, new ElanInterfaceKey(name)).build();
873 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, id);
876 // TODO Clean up the exception handling
877 @SuppressWarnings("checkstyle:IllegalCatch")
878 private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
879 floatingIpPortMacAddress) {
880 InstanceIdentifier id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
882 FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
883 FloatingIpIdToPortMappingBuilder().setKey(new FloatingIpIdToPortMappingKey(floatingIpId))
884 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
885 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
886 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
887 LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
888 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
889 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
890 floatingipIdToPortMacMappingBuilder.build());
891 } catch (Exception e) {
892 LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
893 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
897 private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
898 return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();