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