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