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