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(dataBroker, 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 VNIC Type port; OF Port interfaces are not created", portName);
581 return Collections.emptyList();
583 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
584 LOG.info("Of-port-interface creation for port {}", portName);
585 // Create of-port interface for this neutron port
586 String portInterfaceName = createOfPortInterface(port, tx);
587 LOG.debug("Creating ELAN Interface for port {}", portName);
588 createElanInterface(port, portInterfaceName, tx);
589 Set<Uuid> vpnIdList = new HashSet<>();
590 Set<Uuid> routerIds = new HashSet<>();
591 for (FixedIps ip: portIpAddrsList) {
592 Subnetmap subnetMap = nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), portId, null);
593 if (subnetMap != null && subnetMap.getInternetVpnId() != null) {
594 if (!vpnIdList.contains(subnetMap.getInternetVpnId())) {
595 vpnIdList.add(subnetMap.getInternetVpnId());
598 if (subnetMap != null && subnetMap.getVpnId() != null) {
599 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
600 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
601 Uuid vpnId = subnetMap.getVpnId();
603 vpnIdList.add(vpnId);
606 if (subnetMap != null && subnetMap.getRouterId() != null) {
607 routerIds.add(subnetMap.getRouterId());
610 if (!vpnIdList.isEmpty()) {
611 // create new vpn-interface for neutron port
612 LOG.debug("handleNeutronPortCreated: Adding VPN Interface for port {} from network {}", portName,
613 port.getNetworkId().toString());
614 nvpnManager.createVpnInterface(vpnIdList, port, tx);
615 if (!routerIds.isEmpty()) {
616 for (Uuid routerId : routerIds) {
617 nvpnManager.addToNeutronRouterInterfacesMap(routerId,port.getUuid().getValue());
625 private void handleNeutronPortDeleted(final Port port) {
626 final String portName = port.getUuid().getValue();
627 final Uuid portId = port.getUuid();
628 final List<FixedIps> portIpsList = port.getFixedIps();
629 jobCoordinator.enqueueJob("PORT- " + portName, () -> {
630 WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
631 List<ListenableFuture<Void>> futures = new ArrayList<>();
632 if (!NeutronUtils.isPortVnicTypeNormal(port)) {
633 for (FixedIps ip: portIpsList) {
634 // remove direct port from subnetMaps config DS
635 nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), null, portId);
637 LOG.info("Port {} is not a NORMAL VNIC Type port; OF Port interfaces are not created", portName);
641 Set<Uuid> routerIds = new HashSet<>();
642 Uuid internetVpnId = null;
643 for (FixedIps ip: portIpsList) {
644 Subnetmap subnetMap = nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), portId, null);
645 if (subnetMap == null) {
648 if (subnetMap.getVpnId() != null) {
649 // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
650 // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
651 vpnId = subnetMap.getVpnId();
653 if (subnetMap.getRouterId() != null) {
654 routerIds.add(subnetMap.getRouterId());
656 internetVpnId = subnetMap.getInternetVpnId();
658 if (vpnId != null || internetVpnId != null) {
659 // remove vpn-interface for this neutron port
660 LOG.debug("removing VPN Interface for port {}", portName);
661 if (!routerIds.isEmpty()) {
662 for (Uuid routerId : routerIds) {
663 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portName);
666 nvpnManager.deleteVpnInterface(portName, null /* vpn-id */, wrtConfigTxn);
668 // Remove of-port interface for this neutron port
669 // ELAN interface is also implicitly deleted as part of this operation
670 LOG.debug("Of-port-interface removal for port {}", portName);
671 deleteOfPortInterface(port, wrtConfigTxn);
672 //dissociate fixedIP from floatingIP if associated
673 nvpnManager.dissociatefixedIPFromFloatingIP(port.getUuid().getValue());
674 futures.add(wrtConfigTxn.submit());
680 private void handleNeutronPortUpdated(final Port portoriginal, final Port portupdate) {
681 final List<FixedIps> portoriginalIps = portoriginal.getFixedIps();
682 final List<FixedIps> portupdateIps = portupdate.getFixedIps();
683 if (portoriginalIps == null || portoriginalIps.isEmpty()) {
684 handleNeutronPortCreated(portupdate);
688 if (portupdateIps == null || portupdateIps.isEmpty()) {
689 LOG.info("Ignoring portUpdate (fixed_ip removal) for port {} as this case is handled "
690 + "during subnet deletion event.", portupdate.getUuid().getValue());
693 jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(),
694 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
695 final List<Uuid> originalSnMapsIds = portoriginalIps.stream().map(FixedIps::getSubnetId)
696 .collect(Collectors.toList());
697 final List<Uuid> updateSnMapsIds = portupdateIps.stream().map(FixedIps::getSubnetId)
698 .collect(Collectors.toList());
699 Set<Uuid> originalRouterIds = new HashSet<>();
700 Set<Uuid> oldVpnIds = new HashSet<Uuid>();
701 Uuid oldRouterId = null;
702 for (Uuid snId: originalSnMapsIds) {
703 if (!updateSnMapsIds.remove(snId)) {
704 // snId was present in originalSnMapsIds, but not in updateSnMapsIds
705 Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
707 if (subnetMapOld != null && subnetMapOld.getVpnId() != null) {
708 oldVpnIds.add(subnetMapOld.getVpnId());
710 if (subnetMapOld != null && subnetMapOld.getInternetVpnId() != null) {
711 oldVpnIds.add(subnetMapOld.getInternetVpnId());
713 if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
714 originalRouterIds.add(subnetMapOld.getRouterId());
718 Set<Uuid> newVpnIds = new HashSet();
719 Set<Uuid> newRouterIds = new HashSet<>();
720 for (Uuid snId: updateSnMapsIds) {
721 Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
722 if (subnetMapNew != null) {
723 if (subnetMapNew.getVpnId() != null) {
724 newVpnIds.add(subnetMapNew.getVpnId());
726 if (subnetMapNew.getInternetVpnId() != null) {
727 newVpnIds.add(subnetMapNew.getInternetVpnId());
729 if (subnetMapNew.getRouterId() != null) {
730 newRouterIds.add(subnetMapNew.getRouterId());
734 WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
735 if (!oldVpnIds.isEmpty()) {
736 LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
737 if (!originalRouterIds.isEmpty()) {
738 for (Uuid routerId : originalRouterIds) {
739 nvpnManager.removeFromNeutronRouterInterfacesMap(routerId,
740 portoriginal.getUuid().getValue());
743 nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(),
744 null /* vpn-id */, wrtConfigTxn);
746 if (!newVpnIds.isEmpty()) {
747 LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
748 nvpnManager.createVpnInterface(newVpnIds, portupdate, wrtConfigTxn);
749 if (!newRouterIds.isEmpty()) {
750 for (Uuid routerId : newRouterIds) {
751 nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
758 private static InterfaceAclBuilder handlePortSecurityUpdated(DataBroker dataBroker, Port portOriginal,
759 Port portUpdated, boolean origSecurityEnabled, boolean updatedSecurityEnabled,
760 InterfaceBuilder interfaceBuilder) {
761 InterfaceAclBuilder interfaceAclBuilder = null;
762 if (origSecurityEnabled != updatedSecurityEnabled) {
763 interfaceAclBuilder = new InterfaceAclBuilder();
764 interfaceAclBuilder.setPortSecurityEnabled(updatedSecurityEnabled);
765 if (updatedSecurityEnabled) {
766 // Handle security group enabled
767 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, portUpdated);
769 // Handle security group disabled
770 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
771 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
774 if (updatedSecurityEnabled) {
775 // handle SG add/delete delta
776 InterfaceAcl interfaceAcl = interfaceBuilder.getAugmentation(InterfaceAcl.class);
777 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
778 interfaceAclBuilder.setSecurityGroups(
779 NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
780 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
781 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
782 interfaceAcl.getAllowedAddressPairs(), portOriginal.getAllowedAddressPairs(),
783 portUpdated.getAllowedAddressPairs());
784 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
785 updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
786 portUpdated.getFixedIps()));
789 return interfaceAclBuilder;
792 private String createOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
793 Interface inf = createInterface(port);
794 String infName = inf.getName();
796 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
798 Optional<Interface> optionalInf =
799 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
800 interfaceIdentifier);
801 if (!optionalInf.isPresent()) {
802 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier, inf);
804 LOG.warn("Interface {} is already present", infName);
806 } catch (ReadFailedException e) {
807 LOG.error("failed to create interface {}", infName, e);
812 private Interface createInterface(Port port) {
813 String interfaceName = port.getUuid().getValue();
814 IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
815 InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
816 IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
817 ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
819 interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
820 .addAugmentation(IfL2vlan.class, ifL2vlanBuilder.build());
822 if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
823 InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
824 interfaceAclBuilder.setPortSecurityEnabled(true);
825 NeutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
826 interfaceBuilder.addAugmentation(InterfaceAcl.class, interfaceAclBuilder.build());
827 neutronvpnUtils.populateSubnetIpPrefixes(port);
829 return interfaceBuilder.build();
832 private void deleteOfPortInterface(Port port, WriteTransaction wrtConfigTxn) {
833 String name = port.getUuid().getValue();
834 LOG.debug("Removing OFPort Interface {}", name);
835 InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
837 Optional<Interface> optionalInf =
838 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
839 interfaceIdentifier);
840 if (optionalInf.isPresent()) {
841 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, interfaceIdentifier);
843 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
845 } catch (ReadFailedException e) {
846 LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
850 private void createElanInterface(Port port, String name, WriteTransaction wrtConfigTxn) {
851 String elanInstanceName = port.getNetworkId().getValue();
852 List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
854 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
855 .class, new ElanInterfaceKey(name)).build();
856 ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
857 .setName(name).setStaticMacEntries(staticMacEntries).setKey(new ElanInterfaceKey(name)).build();
858 wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, id, elanInterface);
859 LOG.debug("Creating new ELan Interface {}", elanInterface);
862 private void deleteElanInterface(String name, WriteTransaction wrtConfigTxn) {
863 InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
864 .class, new ElanInterfaceKey(name)).build();
865 wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, id);
868 // TODO Clean up the exception handling
869 @SuppressWarnings("checkstyle:IllegalCatch")
870 private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
871 floatingIpPortMacAddress) {
872 InstanceIdentifier id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
874 FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
875 FloatingIpIdToPortMappingBuilder().setKey(new FloatingIpIdToPortMappingKey(floatingIpId))
876 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
877 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
878 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
879 LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
880 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
881 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
882 floatingipIdToPortMacMappingBuilder.build());
883 } catch (Exception e) {
884 LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
885 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
889 private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
890 return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();