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