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