Freeze upstream versions
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronPortChangeListener.java
1 /*
2  * Copyright © 2015, 2018 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.neutronvpn;
9
10 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
11
12 import com.google.common.base.Strings;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import com.google.gson.Gson;
15 import com.google.gson.JsonArray;
16 import com.google.gson.JsonElement;
17 import com.google.gson.JsonObject;
18 import java.time.Duration;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Locale;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.Optional;
28 import java.util.Set;
29 import java.util.concurrent.ExecutionException;
30 import java.util.stream.Collectors;
31 import javax.annotation.PreDestroy;
32 import javax.inject.Inject;
33 import javax.inject.Singleton;
34 import org.apache.commons.lang3.ObjectUtils;
35 import org.eclipse.jdt.annotation.Nullable;
36 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
37 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
38 import org.opendaylight.genius.mdsalutil.MDSALUtil;
39 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
40 import org.opendaylight.infrautils.utils.concurrent.Executors;
41 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
42 import org.opendaylight.mdsal.binding.api.DataBroker;
43 import org.opendaylight.mdsal.binding.util.Datastore;
44 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
45 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
46 import org.opendaylight.mdsal.binding.util.TypedWriteTransaction;
47 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
48 import org.opendaylight.mdsal.common.api.ReadFailedException;
49 import org.opendaylight.netvirt.elanmanager.api.IElanService;
50 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
51 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
52 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
53 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.L2vlan;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlanBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefsBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.SplitHorizon;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.SplitHorizonBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAclBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.interfaces._interface.AllowedAddressPairs;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceKey;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.elan._interface.StaticMacEntries;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.RoutersBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMappingKey;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.config.rev160806.NeutronvpnConfig;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.port.id.subport.data.PortIdToSubport;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.hostconfig.rev150712.hostconfig.attributes.hostconfigs.Hostconfig;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.networks.rev150712.networks.attributes.networks.Network;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIpsKey;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
88 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
89 import org.slf4j.Logger;
90 import org.slf4j.LoggerFactory;
91
92 @Singleton
93 public class NeutronPortChangeListener extends AbstractAsyncDataTreeChangeListener<Port> {
94     private static final Logger LOG = LoggerFactory.getLogger(NeutronPortChangeListener.class);
95     private final DataBroker dataBroker;
96     private final ManagedNewTransactionRunner txRunner;
97     private final NeutronvpnManager nvpnManager;
98     private final NeutronvpnNatManager nvpnNatManager;
99     private final NeutronSubnetGwMacResolver gwMacResolver;
100     private final IElanService elanService;
101     private final JobCoordinator jobCoordinator;
102     private final NeutronvpnUtils neutronvpnUtils;
103     private final HostConfigCache hostConfigCache;
104     private final DataTreeEventCallbackRegistrar eventCallbacks;
105     private final NeutronvpnConfig neutronvpnConfig;
106
107     @Inject
108     public NeutronPortChangeListener(final DataBroker dataBroker,
109                                      final NeutronvpnManager neutronvpnManager,
110                                      final NeutronvpnNatManager neutronvpnNatManager,
111                                      final NeutronSubnetGwMacResolver gwMacResolver,
112                                      final IElanService elanService,
113                                      final JobCoordinator jobCoordinator,
114                                      final NeutronvpnUtils neutronvpnUtils,
115                                      final HostConfigCache hostConfigCache,
116                                      final DataTreeEventCallbackRegistrar dataTreeEventCallbackRegistrar,
117                                      final NeutronvpnConfig neutronvpnConfig) {
118         super(dataBroker, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Neutron.class)
119                 .child(Ports.class).child(Port.class),
120                 Executors.newSingleThreadExecutor("NeutronPortChangeListener", LOG));
121         this.dataBroker = dataBroker;
122         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
123         nvpnManager = neutronvpnManager;
124         nvpnNatManager = neutronvpnNatManager;
125         this.gwMacResolver = gwMacResolver;
126         this.elanService = elanService;
127         this.jobCoordinator = jobCoordinator;
128         this.neutronvpnUtils = neutronvpnUtils;
129         this.hostConfigCache = hostConfigCache;
130         this.eventCallbacks = dataTreeEventCallbackRegistrar;
131         this.neutronvpnConfig = neutronvpnConfig;
132
133     }
134
135     public void init() {
136         LOG.info("{} init", getClass().getSimpleName());
137     }
138
139     @Override
140     @PreDestroy
141     public void close() {
142         super.close();
143         Executors.shutdownAndAwaitTermination(getExecutorService());
144     }
145
146     @Override
147     public void add(InstanceIdentifier<Port> identifier, Port input) {
148         LOG.trace("Received port add event: port={}", input);
149         String portName = input.getUuid().getValue();
150         LOG.trace("Adding Port : key: {}, value={}", identifier, input);
151         Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
152         if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
153             LOG.warn("neutron vpn received a port add() for a network without a provider extension augmentation "
154                             + "or with an unsupported network type for the port {} which is part of network {}",
155                     portName, network);
156             return;
157         }
158
159         neutronvpnUtils.addToPortCache(input);
160         String portStatus = NeutronUtils.PORT_STATUS_DOWN;
161         if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
162             if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(input.getDeviceOwner())) {
163                 handleRouterInterfaceAdded(input);
164                 NeutronUtils.createPortStatus(input.getUuid().getValue(), NeutronUtils.PORT_STATUS_ACTIVE, dataBroker);
165                 return;
166             }
167             if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())) {
168                 handleRouterGatewayUpdated(input, false);
169                 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
170             } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
171                 handleFloatingIpPortUpdated(null, input);
172                 portStatus = NeutronUtils.PORT_STATUS_ACTIVE;
173             }
174         }
175         // Switchdev ports need to be bounded to a host before creation
176         // in order to validate the supported vnic types from the hostconfig
177         if (input.getFixedIps() != null
178             && !input.getFixedIps().isEmpty()
179             && (!isPortTypeSwitchdev(input) || isPortBound(input))) {
180             handleNeutronPortCreated(input);
181         }
182         NeutronUtils.createPortStatus(input.getUuid().getValue(), portStatus, dataBroker);
183     }
184
185     @Override
186     public void remove(InstanceIdentifier<Port> identifier, Port input) {
187         LOG.trace("Removing Port : key: {}, value={}", identifier, input);
188         Network network = neutronvpnUtils.getNeutronNetwork(input.getNetworkId());
189         // need to proceed with deletion in case network is null for a case where v2 sync happens and a read for
190         // network from NN returns null, but the deletion process for port needs to continue
191         if (network != null && !NeutronvpnUtils.isNetworkTypeSupported(network)) {
192             String portName = input.getUuid().getValue();
193             LOG.warn("neutron vpn received a port remove() for a network without a provider extension augmentation "
194                             + "or with an unsupported network type for the port {} which is part of network {}",
195                     portName, network);
196             return;
197         }
198         neutronvpnUtils.removeFromPortCache(input);
199         NeutronUtils.deletePortStatus(input.getUuid().getValue(), dataBroker);
200
201         if (!Strings.isNullOrEmpty(input.getDeviceOwner()) && !Strings.isNullOrEmpty(input.getDeviceId())) {
202             if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(input.getDeviceOwner())) {
203                 handleRouterInterfaceRemoved(input);
204                 /* nothing else to do here */
205                 return;
206             } else if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(input.getDeviceOwner())
207                     || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(input.getDeviceOwner())) {
208                 handleRouterGatewayUpdated(input, true);
209                 elanService.removeKnownL3DmacAddress(input.getMacAddress().getValue(), input.getNetworkId().getValue());
210             }
211         }
212         if (input.getFixedIps() != null) {
213             handleNeutronPortDeleted(input);
214         }
215     }
216
217     @Override
218     public void update(InstanceIdentifier<Port> identifier, Port original, Port update) {
219         LOG.trace("Received port update event: original={}, update={}", original, update);
220         if (Objects.equals(original, update)) {
221             return;
222         }
223         // Switchdev ports need to be bounded to a host before creation
224         // in order to validate the supported vnic types from the hostconfig
225         if (isPortTypeSwitchdev(original)
226             && !isPortBound(original)
227             && isPortBound(update)) {
228             handleNeutronPortCreated(update);
229         }
230         final String portName = update.getUuid().getValue();
231         Network network = neutronvpnUtils.getNeutronNetwork(update.getNetworkId());
232         if (network == null || !NeutronvpnUtils.isNetworkTypeSupported(network)) {
233             LOG.warn("neutron vpn received a port update() for a network without a provider extension augmentation "
234                     + "or with an unsupported network type for the port {} which is part of network {}",
235                     portName, network);
236             return;
237         }
238         neutronvpnUtils.addToPortCache(update);
239
240         if ((Strings.isNullOrEmpty(original.getDeviceOwner()) || Strings.isNullOrEmpty(original.getDeviceId())
241                 || NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING.equalsIgnoreCase(original.getDeviceId()))
242                 && !Strings.isNullOrEmpty(update.getDeviceOwner()) && !Strings.isNullOrEmpty(update.getDeviceId())) {
243             if (NeutronConstants.DEVICE_OWNER_ROUTER_INF.equals(update.getDeviceOwner())) {
244                 handleRouterInterfaceAdded(update);
245                 return;
246             }
247             if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(update.getDeviceOwner())) {
248                 handleRouterGatewayUpdated(update, false);
249             } else if (NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(update.getDeviceOwner())) {
250                 handleFloatingIpPortUpdated(original, update);
251             }
252         } else {
253             Set<FixedIps> oldIPs = getFixedIpSet(new ArrayList<>(original.nonnullFixedIps().values()));
254             Set<FixedIps> newIPs = getFixedIpSet(new ArrayList<>(update.nonnullFixedIps().values()));
255             if (!oldIPs.equals(newIPs)) {
256                 handleNeutronPortUpdated(original, update);
257             }
258         }
259
260         // check if port security enabled/disabled as part of port update
261         boolean origSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(original);
262         boolean updatedSecurityEnabled = NeutronvpnUtils.getPortSecurityEnabled(update);
263         boolean isDhcpServerPort = neutronvpnConfig.isLimitBumtrafficToDhcpserver()
264                                && NeutronvpnUtils.isDhcpServerPort(update);
265         if (origSecurityEnabled || updatedSecurityEnabled || isDhcpServerPort) {
266             InstanceIdentifier<Interface>  interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(portName);
267             jobCoordinator.enqueueJob("PORT- " + portName, () -> {
268                 ListenableFuture<?> future = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
269                     confTx -> {
270                         Optional<Interface> optionalInf = confTx.read(interfaceIdentifier).get();
271                         if (optionalInf.isPresent()) {
272                             InterfaceBuilder interfaceBuilder = new InterfaceBuilder(optionalInf.get());
273                             if (origSecurityEnabled || updatedSecurityEnabled) {
274                                 InterfaceAcl infAcl = handlePortSecurityUpdated(original, update, origSecurityEnabled,
275                                         updatedSecurityEnabled, interfaceBuilder).build();
276                                 interfaceBuilder.addAugmentation(infAcl);
277                             } else if (isDhcpServerPort) {
278                                 Set<FixedIps> oldIPs = getFixedIpSet(
279                                         new ArrayList<>(original.nonnullFixedIps().values()));
280                                 Set<FixedIps> newIPs = getFixedIpSet(
281                                         new ArrayList<>(update.nonnullFixedIps().values()));
282                                 if (!oldIPs.equals(newIPs)) {
283                                     InterfaceAcl infAcl = neutronvpnUtils.getDhcpInterfaceAcl(update);
284                                     interfaceBuilder.addAugmentation(infAcl);
285                                 }
286                             }
287                             LOG.info("update: Of-port-interface updation for port {}", portName);
288                             // Update OFPort interface for this neutron port
289                             confTx.put(interfaceIdentifier, interfaceBuilder.build());
290                         } else {
291                             LOG.warn("update: Interface {} is not present", portName);
292                         }
293                     });
294                 LoggingFutures.addErrorLogging(future, LOG,
295                         "update: Failed to update interface {} with networkId {}", portName, network);
296                 return Collections.singletonList(future);
297             });
298         }
299     }
300
301     private void handleFloatingIpPortUpdated(@Nullable Port original, Port update) {
302         if ((original == null || NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING.equals(original.getDeviceId()))
303                 && !NeutronConstants.FLOATING_IP_DEVICE_ID_PENDING.equals(update.getDeviceId())) {
304             // populate floating-ip uuid and floating-ip port attributes (uuid, mac and subnet id for the ONLY
305             // fixed IP) to be used by NAT, depopulated in NATService once mac is retrieved in the removal path
306             addToFloatingIpPortInfo(new Uuid(update.getDeviceId()), update.getUuid(),
307                     new ArrayList<>(update.nonnullFixedIps().values()).get(0)
308                     .getSubnetId(), update.getMacAddress().getValue());
309             elanService.addKnownL3DmacAddress(update.getMacAddress().getValue(), update.getNetworkId().getValue());
310         }
311     }
312
313     private void handleRouterInterfaceAdded(Port routerPort) {
314         if (routerPort.getDeviceId() != null) {
315             Uuid routerId = new Uuid(routerPort.getDeviceId());
316             Uuid infNetworkId = routerPort.getNetworkId();
317             Uuid existingVpnId = neutronvpnUtils.getVpnForNetwork(infNetworkId);
318
319             elanService.addKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
320             if (existingVpnId == null) {
321                 Set<Uuid> listVpnIds = new HashSet<>();
322                 Uuid vpnId = neutronvpnUtils.getVpnForRouter(routerId, true);
323                 if (vpnId == null) {
324                     vpnId = routerId;
325                 }
326                 listVpnIds.add(vpnId);
327                 Uuid internetVpnId = neutronvpnUtils.getInternetvpnUuidBoundToRouterId(routerId);
328                 List<Subnetmap> subnetMapList = new ArrayList<>();
329                 boolean portIsIpv6 = false;
330                 for (FixedIps portIP : routerPort.nonnullFixedIps().values()) {
331                     // NOTE:  Please donot change the order of calls to updateSubnetNodeWithFixedIP
332                     // and addSubnetToVpn here
333                     if (internetVpnId != null
334                         && portIP.getIpAddress().getIpv6Address() != null) {
335                         portIsIpv6 = true;
336                     }
337                     String ipValue = portIP.getIpAddress().stringValue();
338                     Uuid subnetId = portIP.getSubnetId();
339                     nvpnManager.updateSubnetNodeWithFixedIp(subnetId, routerId,
340                             routerPort.getUuid(), ipValue, routerPort.getMacAddress().getValue(), vpnId);
341                     Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
342                     subnetMapList.add(sn);
343                 }
344                 if (portIsIpv6) {
345                     listVpnIds.add(internetVpnId);
346                     if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChange(
347                                      IpVersionChoice.IPV6, routerId, true)) {
348                         neutronvpnUtils.updateVpnInstanceWithIpFamily(internetVpnId.getValue(), IpVersionChoice.IPV6,
349                                 true);
350                         neutronvpnUtils.updateVpnInstanceWithFallback(routerId, internetVpnId, true);
351                     }
352                 }
353                 if (! subnetMapList.isEmpty()) {
354                     nvpnManager.createVpnInterface(listVpnIds, routerPort, null);
355                 }
356                 IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
357                 for (FixedIps portIP : routerPort.nonnullFixedIps().values()) {
358                     String ipValue = portIP.getIpAddress().stringValue();
359                     ipVersion = NeutronvpnUtils.getIpVersionFromString(ipValue);
360                     if (ipVersion.isIpVersionChosen(IpVersionChoice.IPV4)) {
361                         nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(),
362                                                         null /* internet-vpn-id */);
363                     } else {
364                         nvpnManager.addSubnetToVpn(vpnId, portIP.getSubnetId(), internetVpnId);
365                     }
366                     LOG.trace("NeutronPortChangeListener Add Subnet Gateway IP {} MAC {} Interface {} VPN {}",
367                             ipValue, routerPort.getMacAddress(),
368                             routerPort.getUuid().getValue(), vpnId.getValue());
369                 }
370                 if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChange(ipVersion, routerId, true)) {
371                     LOG.debug("vpnInstanceOpDataEntry is getting update with ip address family {} for VPN {}",
372                             ipVersion, vpnId.getValue());
373                     neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, true);
374                 }
375                 nvpnManager.addToNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
376                 jobCoordinator.enqueueJob(routerId.toString(), () -> {
377                     nvpnNatManager.handleSubnetsForExternalRouter(routerId);
378                     return Collections.emptyList();
379                 });
380                 LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
381                     confTx -> {
382                         String portInterfaceName = createOfPortInterface(routerPort, confTx);
383                         createElanInterface(routerPort, portInterfaceName, confTx);
384                     }), LOG, "Error creating ELAN interface for {}", routerPort);
385             } else {
386                 LOG.error("Neutron network {} corresponding to router interface port {} for neutron router {}"
387                     + " already associated to VPN {}", infNetworkId.getValue(), routerPort.getUuid().getValue(),
388                     routerId.getValue(), existingVpnId.getValue());
389             }
390         }
391     }
392
393     private void handleRouterInterfaceRemoved(Port routerPort) {
394         if (routerPort.getDeviceId() != null) {
395             Uuid routerId = new Uuid(routerPort.getDeviceId());
396             Uuid infNetworkId = routerPort.getNetworkId();
397             elanService.removeKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue());
398             Uuid vpnId = ObjectUtils.defaultIfNull(neutronvpnUtils.getVpnForRouter(routerId, true),
399                     routerId);
400             Map<FixedIpsKey, FixedIps> keyFixedIpsMap = routerPort.nonnullFixedIps();
401             boolean vpnInstanceInternetIpVersionRemoved = false;
402             Uuid vpnInstanceInternetUuid = null;
403             for (FixedIps portIP : keyFixedIpsMap.values()) {
404                 // Internet VPN : flush InternetVPN first
405                 Uuid subnetId = portIP.getSubnetId();
406                 Subnetmap sn = neutronvpnUtils.getSubnetmap(subnetId);
407                 if (sn != null && sn.getInternetVpnId() != null) {
408                     if (neutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(sn, sn.getInternetVpnId())) {
409                         vpnInstanceInternetIpVersionRemoved = true;
410                         vpnInstanceInternetUuid = sn.getInternetVpnId();
411                     }
412                     nvpnManager.updateVpnInternetForSubnet(sn, sn.getInternetVpnId(), false);
413                 }
414             }
415             /* Remove ping responder for router interfaces
416              *  A router interface reference in a VPN will have to be removed before the host interface references
417              * for that subnet in the VPN are removed. This is to ensure that the FIB Entry of the router interface
418              *  is not the last entry to be removed for that subnet in the VPN.
419              *  If router interface FIB entry is the last to be removed for a subnet in a VPN , then all the host
420              *  interface references in the vpn will already have been cleared, which will cause failures in
421              *  cleanup of router interface flows*/
422             nvpnManager.deleteVpnInterface(routerPort.getUuid().getValue(),
423                                            null /* vpn-id */, null /* wrtConfigTxn*/);
424             // update RouterInterfaces map
425             LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
426                 confTx -> {
427                     IpVersionChoice ipVersion = IpVersionChoice.UNDEFINED;
428                     for (FixedIps portIP : keyFixedIpsMap.values()) {
429                         Subnetmap sn = neutronvpnUtils.getSubnetmap(portIP.getSubnetId());
430                         if (null == sn) {
431                             LOG.error("Subnetmap for subnet {} not found", portIP.getSubnetId().getValue());
432                             continue;
433                         }
434                         // router Port have either IPv4 or IPv6, never both
435                         ipVersion = neutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
436                         String ipValue = portIP.getIpAddress().stringValue();
437                         neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipValue, confTx);
438                         // NOTE:  Please donot change the order of calls to removeSubnetFromVpn and
439                         // and updateSubnetNodeWithFixedIP
440                         nvpnManager.removeSubnetFromVpn(vpnId, sn, sn.getInternetVpnId());
441                         nvpnManager.updateSubnetNodeWithFixedIp(portIP.getSubnetId(), null, null,
442                             null, null, null);
443                     }
444                     nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
445                     deleteElanInterface(routerPort.getUuid().getValue(), confTx);
446                     deleteOfPortInterface(routerPort, confTx);
447                     jobCoordinator.enqueueJob(routerId.toString(), () -> {
448                         nvpnNatManager.handleSubnetsForExternalRouter(routerId);
449                         return Collections.emptyList();
450                     });
451                     if (neutronvpnUtils.shouldVpnHandleIpVersionChoiceChange(ipVersion, routerId, false)) {
452                         LOG.debug("vpnInstanceOpDataEntry is getting update with ip address family {} for VPN {}",
453                                 ipVersion, vpnId.getValue());
454                         neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnId.getValue(), ipVersion, false);
455                     }
456                 }), LOG, "Error handling interface removal");
457             if (vpnInstanceInternetIpVersionRemoved) {
458                 neutronvpnUtils.updateVpnInstanceWithIpFamily(vpnInstanceInternetUuid.getValue(),
459                         IpVersionChoice.IPV6, false);
460                 neutronvpnUtils.updateVpnInstanceWithFallback(routerId, vpnInstanceInternetUuid, false);
461             }
462         }
463     }
464
465     private void handleRouterGatewayUpdated(Port routerGwPort, boolean isRtrGwRemoved) {
466         Uuid routerId = new Uuid(routerGwPort.getDeviceId());
467         Uuid networkId = routerGwPort.getNetworkId();
468         Network network = neutronvpnUtils.getNeutronNetwork(networkId);
469         if (network == null) {
470             return;
471         }
472         boolean isExternal = NeutronvpnUtils.getIsExternal(network);
473         if (isExternal) {
474             Uuid vpnInternetId = neutronvpnUtils.getVpnForNetwork(networkId);
475             if (vpnInternetId != null) {
476                 if (!isRtrGwRemoved) {
477                     nvpnManager.updateVpnMaps(vpnInternetId, null, routerId, null, null);
478                 }
479                 List<Subnetmap> snList = neutronvpnUtils.getNeutronRouterSubnetMaps(routerId);
480                 for (Subnetmap sn : snList) {
481                     if (sn.getNetworkId() == networkId) {
482                         continue;
483                     }
484                     if (NeutronvpnUtils.getIpVersionFromString(sn.getSubnetIp()) != IpVersionChoice.IPV6) {
485                         continue;
486                     }
487                     if (isRtrGwRemoved) {
488                         nvpnManager.removeV6PrivateSubnetToExtNetwork(routerId, vpnInternetId, sn);
489                     } else {
490                         nvpnManager.addV6PrivateSubnetToExtNetwork(routerId, vpnInternetId, sn);
491                     }
492                 }
493                 //Update Internet BGP-VPN
494                 if (isRtrGwRemoved) {
495                     nvpnManager.updateVpnMaps(vpnInternetId, null, null, null, null);
496                 }
497             }
498         }
499         elanService.addKnownL3DmacAddress(routerGwPort.getMacAddress().getValue(), networkId.getValue());
500
501         Router router = neutronvpnUtils.getNeutronRouter(routerId);
502         if (router == null) {
503             LOG.warn("No router found for router GW port {} for router {}", routerGwPort.getUuid().getValue(),
504                     routerId.getValue());
505             // NETVIRT-1249
506             eventCallbacks.onAddOrUpdate(LogicalDatastoreType.CONFIGURATION,
507                     neutronvpnUtils.getNeutronRouterIid(routerId), (unused, newRouter) -> {
508                     setupGwMac(newRouter, routerGwPort, routerId);
509                     return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
510                 }, Duration.ofSeconds(3), iid -> {
511                     LOG.error("GwPort {} added without Router", routerGwPort.getUuid().getValue());
512                 });
513             return;
514         }
515         setupGwMac(router, routerGwPort, routerId);
516     }
517
518     private void setupGwMac(Router router, Port routerGwPort, Uuid routerId) {
519         gwMacResolver.sendArpRequestsToExtGateways(router);
520         jobCoordinator.enqueueJob(routerId.toString(), () -> {
521             setExternalGwMac(routerGwPort, routerId);
522             return Collections.emptyList();
523         });
524     }
525
526     private void setExternalGwMac(Port routerGwPort, Uuid routerId) {
527         // During full-sync networking-odl syncs routers before ports. As such,
528         // the MAC of the router's gw port is not available to be set when the
529         // router is written. We catch that here.
530         InstanceIdentifier<Routers> routersId = NeutronvpnUtils.buildExtRoutersIdentifier(routerId);
531         Optional<Routers> optionalRouter = null;
532         try {
533             optionalRouter = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId);
534             if (!optionalRouter.isPresent()) {
535                 return;
536             }
537             Routers extRouters = optionalRouter.get();
538             if (extRouters.getExtGwMacAddress() != null) {
539                 return;
540             }
541
542             RoutersBuilder builder = new RoutersBuilder(extRouters);
543             builder.setExtGwMacAddress(routerGwPort.getMacAddress().getValue());
544             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, routersId, builder.build());
545         } catch (ExecutionException | InterruptedException e) {
546             LOG.error("setExternalGwMac: failed to read EXT-Routers for router Id {} rout-Gw port {} due to exception",
547                     routerId, routerGwPort, e);
548         }
549
550     }
551
552     @Nullable
553     private String getPortHostId(final Port port) {
554         if (port != null) {
555             PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
556             if (portBinding != null) {
557                 return portBinding.getHostId();
558             }
559         }
560         return null;
561     }
562
563     @Nullable
564     private Hostconfig getHostConfig(final Port port) {
565         String hostId = getPortHostId(port);
566         if (hostId == null) {
567             return null;
568         }
569         Optional<Hostconfig> hostConfig;
570         try {
571             hostConfig = this.hostConfigCache.get(hostId);
572         } catch (ReadFailedException e) {
573             LOG.error("failed to read host config from host {}", hostId, e);
574             return null;
575         }
576         return hostConfig.orElse(null);
577     }
578
579     private boolean isPortBound(final Port port) {
580         String hostId = getPortHostId(port);
581         return hostId != null && !hostId.isEmpty();
582     }
583
584     private boolean isPortVnicTypeDirect(Port port) {
585         PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
586         if (portBinding == null || portBinding.getVnicType() == null) {
587             // By default, VNIC_TYPE is NORMAL
588             return false;
589         }
590         String vnicType = portBinding.getVnicType().trim().toLowerCase(Locale.getDefault());
591         return NeutronConstants.VNIC_TYPE_DIRECT.equals(vnicType);
592     }
593
594     private boolean isSupportedVnicTypeByHost(final Port port, final String vnicType) {
595         Hostconfig hostConfig = getHostConfig(port);
596         String supportStr = String.format("\"vnic_type\": \"%s\"", vnicType);
597         if (hostConfig != null && hostConfig.getConfig().contains(supportStr)) {
598             return true;
599         }
600         return false;
601     }
602
603     @Nullable
604     private Map<String, JsonElement> unmarshal(final String profile) {
605         if (null == profile) {
606             return null;
607         }
608         Gson gson = new Gson();
609         JsonObject jsonObject = gson.fromJson(profile, JsonObject.class);
610         Map<String, JsonElement> map = new HashMap<>();
611         for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
612             map.put(entry.getKey(), entry.getValue());
613         }
614         return map;
615     }
616
617     private boolean isPortTypeSwitchdev(final Port port) {
618         if (!isPortVnicTypeDirect(port)) {
619             return false;
620         }
621
622         PortBindingExtension portBinding = port.augmentation(PortBindingExtension.class);
623         String profile = portBinding.getProfile();
624         if (profile == null || profile.isEmpty()) {
625             LOG.debug("Port {} has no binding:profile values", port.getUuid());
626             return false;
627         }
628
629         Map<String, JsonElement> mapProfile = unmarshal(profile);
630         JsonElement capabilities = mapProfile.get(NeutronConstants.BINDING_PROFILE_CAPABILITIES);
631         LOG.debug("Port {} capabilities: {}", port.getUuid(), capabilities);
632         if (capabilities == null || !capabilities.isJsonArray()) {
633             LOG.debug("binding profile capabilities not in array format: {}", capabilities);
634             return false;
635         }
636
637         JsonArray capabilitiesArray = capabilities.getAsJsonArray();
638         Gson gson = new Gson();
639         JsonElement switchdevElement = gson.fromJson(NeutronConstants.SWITCHDEV, JsonElement.class);
640         return capabilitiesArray.contains(switchdevElement);
641     }
642
643
644     private void handleNeutronPortCreated(final Port port) {
645         final String portName = port.getUuid().getValue();
646         final Uuid portId = port.getUuid();
647         final String networkId = port.getNetworkId().getValue();
648         final Map<FixedIpsKey, FixedIps> keyFixedIpsMap = port.nonnullFixedIps();
649         if (NeutronConstants.IS_ODL_DHCP_PORT.test(port)) {
650             return;
651         }
652         if (!NeutronUtils.isPortVnicTypeNormal(port)
653             && (!isPortTypeSwitchdev(port) || !isSupportedVnicTypeByHost(port, NeutronConstants.VNIC_TYPE_DIRECT))) {
654             for (FixedIps ip: keyFixedIpsMap.values()) {
655                 nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), null, portId);
656             }
657             LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
658                     + "OF Port interfaces are not created", portName);
659             return;
660         }
661         jobCoordinator.enqueueJob("PORT- " + portName, () -> {
662             // add direct port to subnetMaps config DS
663             // TODO: for direct port as well, operations should be carried out per subnet based on port IP
664
665             ListenableFuture<?> future = txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
666                 LOG.info("Of-port-interface creation for port {}", portName);
667                 // Create of-port interface for this neutron port
668                 String portInterfaceName = createOfPortInterface(port, tx);
669                 LOG.debug("Creating ELAN Interface for port {}", portName);
670                 createElanInterface(port, portInterfaceName, tx);
671                 Set<Uuid> vpnIdList =  new HashSet<>();
672                 Set<Uuid> routerIds = new HashSet<>();
673                 for (FixedIps ip: keyFixedIpsMap.values()) {
674                     Subnetmap subnetMap = nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), portId, null);
675                     if (subnetMap != null && subnetMap.getInternetVpnId() != null) {
676                         if (!vpnIdList.contains(subnetMap.getInternetVpnId())) {
677                             vpnIdList.add(subnetMap.getInternetVpnId());
678                         }
679                     }
680                     if (subnetMap != null && subnetMap.getVpnId() != null) {
681                         // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
682                         // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
683                         Uuid vpnId = subnetMap.getVpnId();
684                         if (vpnId != null) {
685                             vpnIdList.add(vpnId);
686                         }
687                     }
688                     if (subnetMap != null && subnetMap.getRouterId() != null) {
689                         routerIds.add(subnetMap.getRouterId());
690                     }
691                 }
692                 if (!vpnIdList.isEmpty()) {
693                     // create new vpn-interface for neutron port
694                     LOG.debug("handleNeutronPortCreated: Adding VPN Interface for port {} from network {}", portName,
695                             networkId);
696                     nvpnManager.createVpnInterface(vpnIdList, port, tx);
697                     for (Uuid routerId : routerIds) {
698                         nvpnManager.addToNeutronRouterInterfacesMap(routerId,port.getUuid().getValue());
699                     }
700                 }
701             });
702             LoggingFutures.addErrorLogging(future, LOG,
703                     "handleNeutronPortCreated: Failed for port {} with networkId {}", portName, networkId);
704             return Collections.singletonList(future);
705         });
706     }
707
708     private void handleNeutronPortDeleted(final Port port) {
709         final String portName = port.getUuid().getValue();
710         final Uuid portId = port.getUuid();
711         final Map<FixedIpsKey, FixedIps> keyFixedIpsMap = port.nonnullFixedIps();
712         if (!NeutronUtils.isPortVnicTypeNormal(port) && !isPortTypeSwitchdev(port)) {
713             for (FixedIps ip : keyFixedIpsMap.values()) {
714                 // remove direct port from subnetMaps config DS
715                 // TODO: for direct port as well, operations should be carried out per subnet based on port IP
716                 nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), null, portId);
717             }
718             LOG.info("Port {} is not a normal and not a direct with switchdev VNIC type ;"
719                     + "Skipping OF Port interfaces removal", portName);
720             return;
721         }
722         jobCoordinator.enqueueJob("PORT- " + portName, () -> {
723             ListenableFuture<?> future = txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
724
725                 Uuid vpnId = null;
726                 Set<Uuid> routerIds = new HashSet<>();
727                 Uuid internetVpnId = null;
728                 for (FixedIps ip : keyFixedIpsMap.values()) {
729                     Subnetmap subnetMap = nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), portId, null);
730                     if (subnetMap == null) {
731                         continue;
732                     }
733                     if (subnetMap.getVpnId() != null) {
734                         // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
735                         // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
736                         vpnId = subnetMap.getVpnId();
737                     }
738                     if (subnetMap.getRouterId() != null) {
739                         routerIds.add(subnetMap.getRouterId());
740                     }
741                     internetVpnId = subnetMap.getInternetVpnId();
742
743                     if (NeutronConstants.DEVICE_OWNER_GATEWAY_INF.equals(port.getDeviceOwner())
744                         || NeutronConstants.DEVICE_OWNER_FLOATING_IP.equals(port.getDeviceOwner())) {
745                         String ipAddress = ip.getIpAddress().stringValue();
746                         if (vpnId != null) {
747                             neutronvpnUtils.removeVpnPortFixedIpToPort(vpnId.getValue(), ipAddress, confTx);
748                         }
749                         if (internetVpnId != null) {
750                             neutronvpnUtils.removeVpnPortFixedIpToPort(internetVpnId.getValue(),
751                                 ipAddress, confTx);
752                         }
753                     }
754                 }
755                 if (vpnId != null || internetVpnId != null) {
756                     // remove vpn-interface for this neutron port
757                     LOG.debug("removing VPN Interface for port {}", portName);
758                     if (!routerIds.isEmpty()) {
759                         for (Uuid routerId : routerIds) {
760                             nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portName);
761                         }
762                     }
763                     nvpnManager.deleteVpnInterface(portName, null /* vpn-id */, confTx);
764                 }
765                 // Remove of-port interface for this neutron port
766                 // ELAN interface is also implicitly deleted as part of this operation
767                 LOG.debug("Of-port-interface removal for port {}", portName);
768                 deleteOfPortInterface(port, confTx);
769                 //dissociate fixedIP from floatingIP if associated
770                 nvpnManager.dissociatefixedIPFromFloatingIP(port.getUuid().getValue());
771             });
772             LoggingFutures.addErrorLogging(future, LOG,
773                     "handleNeutronPortDeleted: Failed to update interface {} with networkId", portName,
774                     port.getNetworkId().getValue());
775             return Collections.singletonList(future);
776         });
777     }
778
779
780     private void handleNeutronPortUpdated(final Port portoriginal, final Port portupdate) {
781         final Map<FixedIpsKey, FixedIps> portoriginalIpsMap = portoriginal.nonnullFixedIps();
782         final Map<FixedIpsKey, FixedIps> portupdateIpsMap = portupdate.nonnullFixedIps();
783         if (portoriginalIpsMap == null || portoriginalIpsMap.isEmpty()) {
784             handleNeutronPortCreated(portupdate);
785             return;
786         }
787
788         if (portupdateIpsMap == null || portupdateIpsMap.isEmpty()) {
789             LOG.info("Ignoring portUpdate (fixed_ip removal) for port {} as this case is handled "
790                       + "during subnet deletion event.", portupdate.getUuid().getValue());
791             return;
792         }
793
794         if (NeutronConstants.IS_ODL_DHCP_PORT.test(portupdate)) {
795             return;
796         }
797
798         jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(),
799             () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, confTx -> {
800                 final List<Uuid> originalSnMapsIds = portoriginalIpsMap.values().stream().map(FixedIps::getSubnetId)
801                         .collect(Collectors.toList());
802                 final List<Uuid> updateSnMapsIds = portupdateIpsMap.values().stream().map(FixedIps::getSubnetId)
803                         .collect(Collectors.toList());
804                 Set<Uuid> originalRouterIds = new HashSet<>();
805                 Set<Uuid> oldVpnIds = new HashSet<>();
806                 for (Uuid snId: originalSnMapsIds) {
807                     if (!updateSnMapsIds.remove(snId)) {
808                         // snId was present in originalSnMapsIds, but not in updateSnMapsIds
809                         Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
810                                 null);
811                         if (subnetMapOld != null && subnetMapOld.getVpnId() != null) {
812                             oldVpnIds.add(subnetMapOld.getVpnId());
813                         }
814                         if (subnetMapOld != null && subnetMapOld.getInternetVpnId() != null) {
815                             oldVpnIds.add(subnetMapOld.getInternetVpnId());
816                         }
817                         if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
818                             originalRouterIds.add(subnetMapOld.getRouterId());
819                         }
820                     }
821                 }
822                 Set<Uuid> newVpnIds = new HashSet<>();
823                 Set<Uuid> newRouterIds = new HashSet<>();
824                 for (Uuid snId: updateSnMapsIds) {
825                     Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
826                     if (subnetMapNew != null) {
827                         if (subnetMapNew.getVpnId() != null) {
828                             newVpnIds.add(subnetMapNew.getVpnId());
829                         }
830                         if (subnetMapNew.getInternetVpnId() != null) {
831                             newVpnIds.add(subnetMapNew.getInternetVpnId());
832                         }
833                         if (subnetMapNew.getRouterId() != null) {
834                             newRouterIds.add(subnetMapNew.getRouterId());
835                         }
836                     }
837                 }
838                 if (!oldVpnIds.isEmpty()) {
839                     LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
840                     if (!originalRouterIds.isEmpty()) {
841                         for (Uuid routerId : originalRouterIds) {
842                             nvpnManager.removeFromNeutronRouterInterfacesMap(routerId,
843                                     portoriginal.getUuid().getValue());
844                         }
845                     }
846                     nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(),
847                                                    null /* vpn-id */, confTx);
848                 }
849                 if (!newVpnIds.isEmpty()) {
850                     LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
851                     nvpnManager.createVpnInterface(newVpnIds, portupdate, confTx);
852                     if (!newRouterIds.isEmpty()) {
853                         for (Uuid routerId : newRouterIds) {
854                             nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
855                         }
856                     }
857                 }
858             })));
859     }
860
861     @Nullable
862     private InterfaceAclBuilder handlePortSecurityUpdated(Port portOriginal,
863             Port portUpdated, boolean origSecurityEnabled, boolean updatedSecurityEnabled,
864             InterfaceBuilder interfaceBuilder) {
865         InterfaceAclBuilder interfaceAclBuilder = null;
866         if (origSecurityEnabled != updatedSecurityEnabled) {
867             interfaceAclBuilder = new InterfaceAclBuilder();
868             interfaceAclBuilder.setPortSecurityEnabled(updatedSecurityEnabled);
869             if (updatedSecurityEnabled) {
870                 // Handle security group enabled
871                 neutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, portUpdated);
872             } else {
873                 // Handle security group disabled
874                 interfaceAclBuilder.setSecurityGroups(new ArrayList<>());
875                 interfaceAclBuilder.setAllowedAddressPairs(new ArrayList<>());
876                 interfaceAclBuilder.setSubnetInfo(new ArrayList<>());
877             }
878         } else {
879             if (updatedSecurityEnabled) {
880                 // handle SG add/delete delta
881                 InterfaceAcl interfaceAcl = interfaceBuilder.augmentation(InterfaceAcl.class);
882                 interfaceAclBuilder = new InterfaceAclBuilder(interfaceAcl);
883                 interfaceAclBuilder.setSecurityGroups(
884                         NeutronvpnUtils.getUpdatedSecurityGroups(interfaceAcl.getSecurityGroups(),
885                                 portOriginal.getSecurityGroups(), portUpdated.getSecurityGroups()));
886                 List<AllowedAddressPairs> updatedAddressPairs = NeutronvpnUtils.getUpdatedAllowedAddressPairs(
887                         new ArrayList<>(interfaceAcl.nonnullAllowedAddressPairs().values()),
888                         new ArrayList<>(portOriginal.nonnullAllowedAddressPairs().values()),
889                         new ArrayList<>(portUpdated.nonnullAllowedAddressPairs().values()));
890                 interfaceAclBuilder.setAllowedAddressPairs(NeutronvpnUtils.getAllowedAddressPairsForFixedIps(
891                         updatedAddressPairs, portOriginal.getMacAddress(), portOriginal.getFixedIps(),
892                         portUpdated.nonnullFixedIps().values()));
893
894                 if (portOriginal.getFixedIps() != null
895                         && !portOriginal.getFixedIps().equals(portUpdated.getFixedIps())) {
896                     neutronvpnUtils.populateSubnetInfo(interfaceAclBuilder, portUpdated);
897                 }
898             }
899         }
900         return interfaceAclBuilder;
901     }
902
903     private String createOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
904         Interface inf = createInterface(port);
905         String infName = inf.getName();
906
907         InstanceIdentifier<Interface> interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(infName);
908         try {
909             Optional<Interface> optionalInf =
910                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
911                             interfaceIdentifier);
912             if (!optionalInf.isPresent()) {
913                 wrtConfigTxn.put(interfaceIdentifier, inf);
914             } else if (isInterfaceUpdated(inf, optionalInf.get())) {
915                /*
916                 Case where an update DTCN wasn't received by this class due to node going down
917                 upon cluster reboot or any other unknown reason
918                 In such a case, updates contained in the missed DTCN won't be processed and have to be handled
919                 explicitly
920                 Update of subports (vlanId, splithorizon tag) is handled here
921                 Update of portSecurity (PortSecurityEnabled, SecurityGroups, AllowedAddressPairs) add is handled
922                 Update of portSecurity update/removed is not handled
923                 Update of parentrefs is not handled as parentrefs updation is handled by IFM Oxygen onwards
924                 */
925                 wrtConfigTxn.put(interfaceIdentifier, inf);
926                 LOG.error("Interface {} is already present and is updated", infName);
927             } else {
928                 LOG.warn("Interface {} is already present", infName);
929             }
930         } catch (ExecutionException | InterruptedException e) {
931             LOG.error("failed to create interface {}", infName, e);
932         }
933         return infName;
934     }
935
936     // Not for generic use. For a special case where update DTCN isn't received
937     private static boolean isInterfaceUpdated(Interface newInterface, Interface oldInterface) {
938         if (newInterface.augmentation(SplitHorizon.class) != null) {
939             if (oldInterface.augmentation(SplitHorizon.class) == null) {
940                 return true;
941             }
942             if (!newInterface.augmentation(SplitHorizon.class).equals(oldInterface
943                     .augmentation(SplitHorizon.class))) {
944                 return true;
945             }
946         }
947         if (!newInterface.augmentation(IfL2vlan.class).equals(oldInterface.augmentation(IfL2vlan.class))) {
948             return true;
949         }
950         if (newInterface.augmentation(InterfaceAcl.class) != null && oldInterface
951                 .augmentation(InterfaceAcl.class) == null) {
952             return true;
953         }
954         return false;
955     }
956
957     private Interface createInterface(Port port) {
958         String interfaceName = port.getUuid().getValue();
959         IfL2vlan.L2vlanMode l2VlanMode = IfL2vlan.L2vlanMode.Trunk;
960         InterfaceBuilder interfaceBuilder = new InterfaceBuilder();
961         IfL2vlanBuilder ifL2vlanBuilder = new IfL2vlanBuilder();
962
963         Network network = neutronvpnUtils.getNeutronNetwork(port.getNetworkId());
964         Boolean isVlanTransparent = network.isVlanTransparent();
965         if (isVlanTransparent != null && isVlanTransparent) {
966             l2VlanMode = IfL2vlan.L2vlanMode.Transparent;
967         } else {
968             PortIdToSubport portIdToSubport = neutronvpnUtils.getPortIdToSubport(port.getUuid());
969             if (portIdToSubport != null) {
970                 l2VlanMode = IfL2vlan.L2vlanMode.TrunkMember;
971                 ifL2vlanBuilder.setVlanId(new VlanId(portIdToSubport.getVlanId().intValue()));
972                 String parentRefName = portIdToSubport.getTrunkPortId().getValue();
973                 ParentRefsBuilder parentRefsBuilder = new ParentRefsBuilder().setParentInterface(parentRefName);
974                 interfaceBuilder.addAugmentation(parentRefsBuilder.build());
975                 SplitHorizon splitHorizon =
976                         new SplitHorizonBuilder().setOverrideSplitHorizonProtection(true).build();
977                 interfaceBuilder.addAugmentation(splitHorizon);
978             }
979         }
980
981         ifL2vlanBuilder.setL2vlanMode(l2VlanMode);
982
983         interfaceBuilder.setEnabled(true).setName(interfaceName).setType(L2vlan.class)
984                 .addAugmentation(ifL2vlanBuilder.build());
985
986         if (NeutronvpnUtils.getPortSecurityEnabled(port)) {
987             InterfaceAclBuilder interfaceAclBuilder = new InterfaceAclBuilder();
988             interfaceAclBuilder.setPortSecurityEnabled(true);
989             neutronvpnUtils.populateInterfaceAclBuilder(interfaceAclBuilder, port);
990             interfaceBuilder.addAugmentation(interfaceAclBuilder.build());
991         } else if (neutronvpnConfig.isLimitBumtrafficToDhcpserver() && NeutronvpnUtils.isDhcpServerPort(port)) {
992             interfaceBuilder.addAugmentation(neutronvpnUtils.getDhcpInterfaceAcl(port));
993         }
994         return interfaceBuilder.build();
995     }
996
997     private void deleteOfPortInterface(Port port, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
998         String name = port.getUuid().getValue();
999         LOG.debug("Removing OFPort Interface {}", name);
1000         InstanceIdentifier<Interface>  interfaceIdentifier = NeutronvpnUtils.buildVlanInterfaceIdentifier(name);
1001         try {
1002             Optional<Interface> optionalInf =
1003                     SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
1004                             interfaceIdentifier);
1005             if (optionalInf.isPresent()) {
1006                 wrtConfigTxn.delete(interfaceIdentifier);
1007             } else {
1008                 LOG.warn("deleteOfPortInterface: Interface {} is not present", name);
1009             }
1010         } catch (ExecutionException | InterruptedException e) {
1011             LOG.error("deleteOfPortInterface: Failed to delete interface {}", name, e);
1012         }
1013     }
1014
1015     private void createElanInterface(Port port, String name,
1016                                      TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
1017         String elanInstanceName = port.getNetworkId().getValue();
1018         List<StaticMacEntries> staticMacEntries = NeutronvpnUtils.buildStaticMacEntry(port);
1019
1020         InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
1021                 .class, new ElanInterfaceKey(name)).build();
1022         ElanInterface elanInterface = new ElanInterfaceBuilder().setElanInstanceName(elanInstanceName)
1023                 .setName(name).setStaticMacEntries(staticMacEntries).withKey(new ElanInterfaceKey(name)).build();
1024         wrtConfigTxn.put(id, elanInterface);
1025         LOG.debug("Creating new ELan Interface {}", elanInterface);
1026     }
1027
1028     private void deleteElanInterface(String name, TypedWriteTransaction<Datastore.Configuration> wrtConfigTxn) {
1029         InstanceIdentifier<ElanInterface> id = InstanceIdentifier.builder(ElanInterfaces.class).child(ElanInterface
1030                 .class, new ElanInterfaceKey(name)).build();
1031         wrtConfigTxn.delete(id);
1032     }
1033
1034     // TODO Clean up the exception handling
1035     @SuppressWarnings("checkstyle:IllegalCatch")
1036     private void addToFloatingIpPortInfo(Uuid floatingIpId, Uuid floatingIpPortId, Uuid floatingIpPortSubnetId, String
1037                                          floatingIpPortMacAddress) {
1038         InstanceIdentifier id = NeutronvpnUtils.buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
1039         try {
1040             FloatingIpIdToPortMappingBuilder floatingipIdToPortMacMappingBuilder = new
1041                 FloatingIpIdToPortMappingBuilder().withKey(new FloatingIpIdToPortMappingKey(floatingIpId))
1042                 .setFloatingIpId(floatingIpId).setFloatingIpPortId(floatingIpPortId)
1043                 .setFloatingIpPortSubnetId(floatingIpPortSubnetId)
1044                 .setFloatingIpPortMacAddress(floatingIpPortMacAddress);
1045             LOG.debug("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
1046                 + " Port Info Config DS", floatingIpId.getValue(), floatingIpPortId.getValue());
1047             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, id,
1048                 floatingipIdToPortMacMappingBuilder.build());
1049         } catch (Exception e) {
1050             LOG.error("Creating floating IP UUID {} to Floating IP neutron port {} mapping in Floating IP"
1051                 + " Port Info Config DS failed", floatingIpId.getValue(), floatingIpPortId.getValue(), e);
1052         }
1053     }
1054
1055     private Set<FixedIps> getFixedIpSet(List<FixedIps> fixedIps) {
1056         return fixedIps != null ? new HashSet<>(fixedIps) : Collections.emptySet();
1057     }
1058 }