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