neutronvpn: dual stack changes 17/62917/46
authorValentina Krasnobaeva <valentina.krasnobaeva@6wind.com>
Tue, 23 May 2017 09:08:42 +0000 (11:08 +0200)
committerVivekanandan Narasimhan <n.vivekanandan@ericsson.com>
Thu, 9 Nov 2017 17:07:45 +0000 (17:07 +0000)
The vpn interface creation/deletion is decorrelated with the adjacencies
and fixed ips contexts port creation. This becomes necessary since each
neutron port will receive creation of ports, but also updates related to
update of new IPs ( which implies that it does not mean creation of VPN
interface).
Also if fixes in vpnmanager an issue upon some cases where networkId
field from subnetmap was null.

Change-Id: I664421f592ea7da9c34f5400100502593bfeea55
Signed-off-by: Valentina Krasnobaeva <valentina.krasnobaeva@6wind.com>
Signed-off-by: Noel de Prandieres <prandieres@6wind.com>
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
vpnservice/fibmanager/fibmanager-api/src/main/java/org/opendaylight/netvirt/fibmanager/api/FibHelper.java
vpnservice/natservice/natservice-impl/src/main/java/org/opendaylight/netvirt/natservice/internal/NatRouterInterfaceListener.java
vpnservice/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronPortChangeListener.java
vpnservice/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronvpnManager.java
vpnservice/vpnmanager/vpnmanager-impl/src/main/java/org/opendaylight/netvirt/vpnmanager/SubnetmapChangeListener.java

index 2fcc75771ea28dc9213647537caa815ceb4515e9..fc2a44903075faacedd31437137f159ef1055ef0 100644 (file)
@@ -183,13 +183,13 @@ public class FibHelper {
 
     /**get String format IP from prefix as x.x.....x/nn.
      * @param prefix the prefix as IPv4 or IPv6 as x.....x/nn
-     * @return null if "/" is unfindable or the IP only as x.x...x from x.x......x/nn
+     * @return prefix if "/" is unfindable or the IP only as x.x...x from x.x......x/nn
      */
     public static String getIpFromPrefix(String prefix) {
         if (prefix == null || prefix.length() < 2) {
             return null;
         }
-        String rep = null;
+        String rep = prefix;
         String[] prefixValues = prefix.split("/");
         if (prefixValues != null && prefixValues.length > 0) {
             rep = prefixValues[0];
@@ -203,6 +203,16 @@ public class FibHelper {
      * @return true if the param subnet contained the prefixToTest false otherwise
      */
     public static boolean isBelongingPrefix(String prefixToTest, String subnet) {
+        return doesPrefixBelongToSubnet(prefixToTest, subnet, true);
+    }
+
+    /**Return true if this prefix or subnet is belonging the specified subnetwork.
+     * @param prefixToTest the prefix which could be in the subnet
+     * @param subnet the subnet that have to contain the prefixToTest to return true
+     * @param exactMatch boolean set to true if exact match is expected
+     * @return true if the param subnet contained the prefixToTest false otherwise
+     */
+    public static boolean doesPrefixBelongToSubnet(String prefixToTest, String subnet, boolean exactMatch) {
         boolean rep = false;
         if (prefixToTest == null || prefixToTest.length() < 7 || subnet == null || subnet.length() < 7) {
             return rep;
@@ -232,7 +242,7 @@ public class FibHelper {
             try {
                 maskPref = Integer.valueOf(maskPrefString);
                 maskSub = Integer.valueOf(maskSubString);
-                if (maskPref != maskSub) {
+                if (exactMatch && maskPref != maskSub) {
                  /*because the mask must be exactly the same between them, the return type is false. This behavior could
                   * be changed to ignored it in including a boolean options to force or not the same mask control*/
                     rep = false;
index 7067d74e5367a93105fb2643f88f55173a411813..5f72b1252c97621572a74deac44eddf31eaeae02 100644 (file)
@@ -122,4 +122,4 @@ public class NatRouterInterfaceListener
         return new RouterInterfaceBuilder().setKey(new RouterInterfaceKey(interfaceName))
             .setInterfaceName(interfaceName).setRouterName(routerName).build();
     }
-}
\ No newline at end of file
+}
index 571a1f2dced5add570912c6819bffc70c13c220f..68cc57fad6f377f46df275d226b4b8746147cc41 100644 (file)
@@ -260,11 +260,12 @@ public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<P
                     NwConstants.ADD_FLOW);
             if (existingVpnId == null) {
                 Uuid vpnId = NeutronvpnUtils.getVpnForRouter(dataBroker, routerId, true);
-                List<Subnetmap> subnetMapList = new ArrayList<>();
                 if (vpnId == null) {
                     vpnId = routerId;
                 }
-                for (FixedIps portIP : routerPort.getFixedIps()) {
+                List<Subnetmap> subnetMapList = new ArrayList<>();
+                List<FixedIps> portIps = routerPort.getFixedIps();
+                for (FixedIps portIP : portIps) {
                     // NOTE:  Please donot change the order of calls to updateSubnetNodeWithFixedIP
                     // and addSubnetToVpn here
                     String ipValue = String.valueOf(portIP.getIpAddress().getValue());
@@ -289,6 +290,7 @@ public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<P
                             ipValue, routerPort.getMacAddress(),
                             routerPort.getUuid().getValue(), vpnId.getValue());
                 }
+                nvpnManager.addToNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
                 nvpnNatManager.handleSubnetsForExternalRouter(routerId, dataBroker);
                 WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
                 String portInterfaceName = createOfPortInterface(routerPort, wrtConfigTxn);
@@ -296,8 +298,8 @@ public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<P
                 wrtConfigTxn.submit();
                 PhysAddress mac = new PhysAddress(routerPort.getMacAddress().getValue());
             } else {
-                LOG.error("Neutron network {} corresponding to router interface port {} for neutron router {} already"
-                    + " associated to VPN {}", infNetworkId.getValue(), routerPort.getUuid().getValue(),
+                LOG.error("Neutron network {} corresponding to router interface port {} for neutron router {}"
+                    + " already associated to VPN {}", infNetworkId.getValue(), routerPort.getUuid().getValue(),
                     routerId.getValue(), existingVpnId.getValue());
             }
         }
@@ -307,48 +309,45 @@ public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<P
         if (routerPort.getDeviceId() != null) {
             Uuid routerId = new Uuid(routerPort.getDeviceId());
             Uuid infNetworkId = routerPort.getNetworkId();
-
             elanService.handleKnownL3DmacAddress(routerPort.getMacAddress().getValue(), infNetworkId.getValue(),
                     NwConstants.DEL_FLOW);
-            List<Subnetmap> subnetMapList = new ArrayList<>();
             Uuid vpnId = NeutronvpnUtils.getVpnForRouter(dataBroker, routerId, true);
             if (vpnId == null) {
                 vpnId = routerId;
             }
+            /* Remove ping responder for router interfaces
+             *  A router interface reference in a VPN will have to be removed before the host interface references
+             * for that subnet in the VPN are removed. This is to ensure that the FIB Entry of the router interface
+             *  is not the last entry to be removed for that subnet in the VPN.
+             *  If router interface FIB entry is the last to be removed for a subnet in a VPN , then all the host
+             *  interface references in the vpn will already have been cleared, which will cause failures in
+             *  cleanup of router interface flows*/
+            nvpnManager.deleteVpnInterface(routerPort.getUuid().getValue(), null);
+            // update RouterInterfaces map
+            List<FixedIps> portIps = routerPort.getFixedIps();
+            WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
             boolean vpnInstanceIpVersionRemoved = false;
             IpVersionChoice vpnInstanceIpVersionToRemove = IpVersionChoice.UNDEFINED;
-            for (FixedIps portIP : routerPort.getFixedIps()) {
+            for (FixedIps portIP : portIps) {
                 Subnetmap sn = NeutronvpnUtils.getSubnetmap(dataBroker, portIP.getSubnetId());
                 // router Port have either IPv4 or IPv6, never both
                 if (NeutronvpnUtils.shouldVpnHandleIpVersionChangeToRemove(dataBroker, sn, vpnId)) {
                     vpnInstanceIpVersionRemoved = true;
                     vpnInstanceIpVersionToRemove = NeutronvpnUtils.getIpVersionFromString(sn.getSubnetIp());
                 }
-                subnetMapList.add(sn);
-            }
-            /* Remove ping responder for router interfaces
-             *  A router interface reference in a VPN will have to be removed before the host interface references
-             * for that subnet in the VPN are removed. This is to ensure that the FIB Entry of the router interface
-             *  is not the last entry to be removed for that subnet in the VPN.
-             *  If router interface FIB entry is the last to be removed for a subnet in a VPN , then all the host
-             *  interface references in the vpn will already have been cleared, which will cause failures in
-             *  cleanup of router interface flows*/
-            nvpnManager.deleteVpnInterface(vpnId, routerPort, null);
-            for (FixedIps portIP : routerPort.getFixedIps()) {
+                String ipValue = String.valueOf(portIP.getIpAddress().getValue());
+                NeutronvpnUtils.removeVpnPortFixedIpToPort(dataBroker, vpnId.getValue(), ipValue,
+                    wrtConfigTxn);
                 // NOTE:  Please donot change the order of calls to removeSubnetFromVpn and
                 // and updateSubnetNodeWithFixedIP
                 nvpnManager.removeSubnetFromVpn(vpnId, portIP.getSubnetId());
-                nvpnManager.updateSubnetNodeWithFixedIp(portIP.getSubnetId(), null,
-                        null, null, null);
-                WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
-                deleteElanInterface(routerPort.getUuid().getValue(), wrtConfigTxn);
-                deleteOfPortInterface(routerPort, wrtConfigTxn);
-                wrtConfigTxn.submit();
-                nvpnNatManager.handleSubnetsForExternalRouter(routerId, dataBroker);
-                String ipValue = String.valueOf(portIP.getIpAddress().getValue());
-                NeutronvpnUtils.removeVpnPortFixedIpToPort(dataBroker, vpnId.getValue(),
-                        ipValue, null /*writeTransaction*/);
+                nvpnManager.updateSubnetNodeWithFixedIp(portIP.getSubnetId(), null, null, null, null);
             }
+            nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, routerPort.getUuid().getValue());
+            deleteElanInterface(routerPort.getUuid().getValue(), wrtConfigTxn);
+            deleteOfPortInterface(routerPort, wrtConfigTxn);
+            wrtConfigTxn.submit();
+            nvpnNatManager.handleSubnetsForExternalRouter(routerId, dataBroker);
             if (vpnInstanceIpVersionRemoved) {
                 NeutronvpnUtils.updateVpnInstanceWithIpFamily(dataBroker, vpnId.getValue(),
                           vpnInstanceIpVersionToRemove, false);
@@ -395,6 +394,7 @@ public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<P
             LOG.debug("Creating ELAN Interface for port {}", portName);
             createElanInterface(port, portInterfaceName, wrtConfigTxn);
             Uuid vpnId = null;
+            Set<Uuid> routerIds = new HashSet<Uuid>();
             for (FixedIps ip: portIpAddrsList) {
                 Subnetmap subnetMap = nvpnManager.updateSubnetmapNodeWithPorts(ip.getSubnetId(), portId, null);
                 if (subnetMap != null && subnetMap.getVpnId() != null) {
@@ -402,12 +402,20 @@ public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<P
                     // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
                     vpnId = subnetMap.getVpnId();
                 }
+                if (subnetMap != null && subnetMap.getRouterId() != null) {
+                    routerIds.add(subnetMap.getRouterId());
+                }
             }
             if (vpnId != null) {
                 // create new vpn-interface for neutron port
                 LOG.debug("handleNeutronPortCreated: Adding VPN Interface for port {} from network {}", portName,
                            port.getNetworkId().toString());
                 nvpnManager.createVpnInterface(vpnId, port, wrtConfigTxn);
+                if (!routerIds.isEmpty()) {
+                    for (Uuid routerId : routerIds) {
+                        nvpnManager.addToNeutronRouterInterfacesMap(routerId,port.getUuid().getValue());
+                    }
+                }
             }
             futures.add(wrtConfigTxn.submit());
             return futures;
@@ -430,18 +438,27 @@ public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<P
                 return futures;
             }
             Uuid vpnId = null;
+            Set<Uuid> routerIds = new HashSet<Uuid>();
             for (FixedIps ip: portIpsList) {
                 Subnetmap subnetMap = nvpnManager.removePortsFromSubnetmapNode(ip.getSubnetId(), portId, null);
                 if (subnetMap != null && subnetMap.getVpnId() != null) {
                     // can't use NeutronvpnUtils.getVpnForNetwork to optimise here, because it gives BGPVPN id
                     // obtained subnetMaps belongs to one network => vpnId must be the same for each port Ip
                     vpnId = subnetMap.getVpnId();
+                    if (subnetMap.getRouterId() != null) {
+                        routerIds.add(subnetMap.getRouterId());
+                    }
                 }
             }
             if (vpnId != null) {
                 // remove vpn-interface for this neutron port
                 LOG.debug("removing VPN Interface for port {}", portName);
-                nvpnManager.deleteVpnInterface(vpnId, port, wrtConfigTxn);
+                if (!routerIds.isEmpty()) {
+                    for (Uuid routerId : routerIds) {
+                        nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portName);
+                    }
+                }
+                nvpnManager.deleteVpnInterface(portName, wrtConfigTxn);
             }
             // Remove of-port interface for this neutron port
             // ELAN interface is also implicitly deleted as part of this operation
@@ -466,35 +483,57 @@ public class NeutronPortChangeListener extends AsyncDataTreeChangeListenerBase<P
             handleNeutronPortCreated(portupdate);
             return;
         }
+
         if (portupdateIps == null || portupdateIps.isEmpty()) {
             LOG.info("Ignoring portUpdate (fixed_ip removal) for port {} as this case is handled "
                       + "during subnet deletion event.", portupdate.getUuid().getValue());
             return;
         }
         jobCoordinator.enqueueJob("PORT- " + portupdate.getUuid().getValue(), () -> {
-            WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
             final List<Uuid> originalSnMapsIds = portoriginalIps.stream().map(ip -> ip.getSubnetId())
                     .collect(Collectors.toList());
             final List<Uuid> updateSnMapsIds = portupdateIps.stream().map(ip -> ip.getSubnetId())
                     .collect(Collectors.toList());
+            Uuid oldVpnId = null;
+            Set<Uuid> originalRouterIds = new HashSet<Uuid>();
             for (Uuid snId: originalSnMapsIds) {
                 if (!updateSnMapsIds.remove(snId)) {
                     // snId was present in originalSnMapsIds, but not in updateSnMapsIds
-                    nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(), null);
+                    Subnetmap subnetMapOld = nvpnManager.removePortsFromSubnetmapNode(snId, portoriginal.getUuid(),
+                                                null);
+                    oldVpnId = (subnetMapOld != null) ? subnetMapOld.getVpnId() : null;
+                    if (subnetMapOld != null && subnetMapOld.getRouterId() != null) {
+                        originalRouterIds.add(subnetMapOld.getRouterId());
+                    }
                 }
             }
+            Uuid newVpnId = null;
+            Set<Uuid> newRouterIds = new HashSet<>();
             for (Uuid snId: updateSnMapsIds) {
-                nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
+                Subnetmap subnetMapNew = nvpnManager.updateSubnetmapNodeWithPorts(snId, portupdate.getUuid(), null);
+                newVpnId = (subnetMapNew != null) ? subnetMapNew.getVpnId() : null;
+                if (subnetMapNew != null && subnetMapNew.getRouterId() != null) {
+                    newRouterIds.add(subnetMapNew.getRouterId());
+                }
             }
-            final Uuid oldVpnId = NeutronvpnUtils.getVpnForNetwork(dataBroker, portoriginal.getNetworkId());
+            WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
             if (oldVpnId != null) {
                 LOG.info("removing VPN Interface for port {}", portoriginal.getUuid().getValue());
-                nvpnManager.deleteVpnInterface(oldVpnId, portoriginal, wrtConfigTxn);
+                if (!originalRouterIds.isEmpty()) {
+                    for (Uuid routerId : originalRouterIds) {
+                        nvpnManager.removeFromNeutronRouterInterfacesMap(routerId, portoriginal.getUuid().getValue());
+                    }
+                }
+                nvpnManager.deleteVpnInterface(portoriginal.getUuid().getValue(), wrtConfigTxn);
             }
-            final Uuid newVpnId = NeutronvpnUtils.getVpnForNetwork(dataBroker, portupdate.getNetworkId());
             if (newVpnId != null) {
                 LOG.info("Adding VPN Interface for port {}", portupdate.getUuid().getValue());
                 nvpnManager.createVpnInterface(newVpnId, portupdate, wrtConfigTxn);
+                if (!newRouterIds.isEmpty()) {
+                    for (Uuid routerId : newRouterIds) {
+                        nvpnManager.addToNeutronRouterInterfacesMap(routerId,portupdate.getUuid().getValue());
+                    }
+                }
             }
             List<ListenableFuture<Void>> futures = new ArrayList<>();
             futures.add(wrtConfigTxn.submit());
index f215dac97ca447137e5a2aad38a068765923f065..4a2b356077a69b605ba3759c77a8d3887cf27d98 100644 (file)
@@ -38,6 +38,7 @@ import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
 import org.opendaylight.netvirt.elanmanager.api.IElanService;
+import org.opendaylight.netvirt.fibmanager.api.FibHelper;
 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
@@ -731,72 +732,136 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         }
     }
 
-    protected void createVpnInterface(Uuid vpnId, Port port, WriteTransaction wrtConfigTxn) {
-        String infName = port.getUuid().getValue();
+    protected Adjacencies createPortIpAdjacencies(Uuid vpnId, Port port, Boolean isRouterInterface,
+                                  WriteTransaction wrtConfigTxn, Subnetmap sn, VpnInterface vpnIface) {
         List<Adjacency> adjList = new ArrayList<>();
-        Boolean isRouterInterface = false;
-        if (port.getDeviceOwner() != null) {
-            isRouterInterface = port.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF);
+        if (vpnIface != null) {
+            adjList = vpnIface.getAugmentation(Adjacencies.class).getAdjacency();
         }
-        LOG.trace("createVpnInterface - isRouterInterface:{}", isRouterInterface);
-        // create adjacency list
-        Uuid rtrId = null;
+        String infName = port.getUuid().getValue();
+        LOG.trace("neutronVpnManager: create config adjacencies for Port: {}", infName);
         for (FixedIps ip : port.getFixedIps()) {
             String ipValue = String.valueOf(ip.getIpAddress().getValue());
             String ipPrefix = ip.getIpAddress().getIpv4Address() != null ? ipValue + "/32" : ipValue + "/128";
+            if (sn != null && !FibHelper.doesPrefixBelongToSubnet(ipPrefix, sn.getSubnetIp(), false)) {
+                continue;
+            }
             Adjacency vmAdj = new AdjacencyBuilder().setKey(new AdjacencyKey(ipPrefix)).setIpAddress(ipPrefix)
                     .setMacAddress(port.getMacAddress().getValue()).setAdjacencyType(AdjacencyType.PrimaryAdjacency)
                     .setSubnetId(ip.getSubnetId()).build();
-            adjList.add(vmAdj);
-
+            if (!adjList.contains(vmAdj)) {
+                adjList.add(vmAdj);
+            }
             NeutronvpnUtils.createVpnPortFixedIpToPort(dataBroker, vpnId.getValue(), ipValue, infName,
-                    port.getMacAddress().getValue(), isRouterInterface, wrtConfigTxn);
+                port.getMacAddress().getValue(), isRouterInterface, wrtConfigTxn);
 
             Uuid routerId = NeutronvpnUtils.getSubnetmap(dataBroker, ip.getSubnetId()).getRouterId();
-            if (routerId != null && routerId != rtrId) {
-                addToNeutronRouterInterfacesMap(routerId, infName);
+            if (routerId != null) {
                 Router rtr = NeutronvpnUtils.getNeutronRouter(dataBroker, routerId);
                 if (rtr != null && rtr.getRoutes() != null) {
                     List<Routes> routeList = rtr.getRoutes();
+                    // create extraroute Adjacence for each ipValue,
+                    // because router can have IPv4 and IPv6 subnet ports, or can have
+                    // more that one IPv4 subnet port or more than one IPv6 subnet port
                     List<Adjacency> erAdjList = getAdjacencyforExtraRoute(vpnId, routeList, ipValue);
                     if (erAdjList != null && !erAdjList.isEmpty()) {
                         adjList.addAll(erAdjList);
                     }
                 }
-                rtrId = routerId;
             }
         }
-        // create vpn-interface on this neutron port
-        Adjacencies adjs = new AdjacenciesBuilder().setAdjacency(adjList).build();
+        return new AdjacenciesBuilder().setAdjacency(adjList).build();
+    }
+
+    protected void createVpnInterface(Uuid vpnId, Port port, WriteTransaction wrtConfigTxn) {
+        boolean isRouterInterface = false;
+        if (port.getDeviceOwner() != null) {
+            isRouterInterface = port.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF);
+        }
+        Adjacencies adjs = createPortIpAdjacencies(vpnId, port, isRouterInterface, wrtConfigTxn, null, null);
+        String infName = port.getUuid().getValue();
+        LOG.trace("createVpnInterface for Port: {}, isRouterInterface: {}", infName, isRouterInterface);
         writeVpnInterfaceToDs(vpnId, infName, adjs, isRouterInterface, wrtConfigTxn);
     }
 
+    protected void withdrawPortIpFromVpnIface(Uuid vpnId, Port port, Subnetmap sn, WriteTransaction wrtConfigTxn) {
+        boolean isLockAcquired = false;
+        String infName = port.getUuid().getValue();
+        InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
+        Optional<VpnInterface> optionalVpnInterface = null;
+        try {
+            optionalVpnInterface = SingleTransactionDataBroker
+                .syncReadOptional(dataBroker, LogicalDatastoreType
+                .CONFIGURATION, vpnIfIdentifier);
+        } catch (ReadFailedException e) {
+            LOG.error("withdrawPortIpFromVpnIface: Error reading the VPN interface for {}", vpnIfIdentifier, e);
+            return;
+        }
+        if (!optionalVpnInterface.isPresent()) {
+            return;
+        }
+        LOG.trace("withdraw adjacencies for Port: {} subnet {}", port.getUuid().getValue(),
+                sn != null ? sn.getSubnetIp() : "null");
+        List<Adjacency> vpnAdjsList = optionalVpnInterface.get().getAugmentation(Adjacencies.class).getAdjacency();
+        List<Adjacency> updatedAdjsList = new ArrayList();
+        boolean isIpFromAnotherSubnet = false;
+        for (Adjacency adj : vpnAdjsList) {
+            String adjString = FibHelper.getIpFromPrefix(adj.getIpAddress().toString());
+            if (sn == null || !adj.getSubnetId().equals(sn.getId())) {
+                if (adj.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
+                    isIpFromAnotherSubnet = true;
+                }
+                updatedAdjsList.add(adj);
+                continue;
+            }
+            if (adj.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
+                LOG.error("withdrawPortIpFromVpnIface: suppressing primaryAdjacency {} FixedIp for vpnId {}",
+                      adjString, vpnId);
+                NeutronvpnUtils.removeVpnPortFixedIpToPort(dataBroker, vpnId.getValue(),
+                      String.valueOf(adjString), wrtConfigTxn);
+            } else {
+                if (port.getDeviceOwner()
+                    .equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF) && sn.getRouterId() != null)  {
+                    Router rtr = NeutronvpnUtils.getNeutronRouter(dataBroker, sn.getRouterId());
+                    if (rtr == null && rtr.getRoutes() != null) {
+                        List<Routes> extraRoutesToRemove = new ArrayList<>();
+                        for (Routes rt: rtr.getRoutes()) {
+                            if (rt.getNexthop().toString().equals(adjString)) {
+                                extraRoutesToRemove.add(rt);
+                            }
+                        }
+                        LOG.error("withdrawPortIpFromVpnIface: suppressing extraRoute {} for vpnId {}",
+                              extraRoutesToRemove, vpnId);
+                        removeAdjacencyforExtraRoute(vpnId, extraRoutesToRemove);
+                    }
+                }
+            }
+        }
+        Adjacencies adjacencies = new AdjacenciesBuilder().setAdjacency(updatedAdjsList).build();
+        updateVpnInterfaceWithAdjacencies(vpnId, infName, adjacencies, wrtConfigTxn);
+        if (!isIpFromAnotherSubnet) {
+            // no more subnetworks for neutron port
+            if (sn != null && sn.getRouterId() != null) {
+                removeFromNeutronRouterInterfacesMap(sn.getRouterId(), port.getUuid().getValue());
+            }
+            deleteVpnInterface(infName, wrtConfigTxn);
+            return;
+        }
+        return;
+    }
+
     // TODO Clean up the exception handling
     @SuppressWarnings("checkstyle:IllegalCatch")
-    protected void deleteVpnInterface(Uuid vpnId, Port port, WriteTransaction wrtConfigTxn) {
+    protected void deleteVpnInterface(String infName, WriteTransaction wrtConfigTxn) {
         Boolean wrtConfigTxnPresent = true;
         if (wrtConfigTxn == null) {
             wrtConfigTxnPresent = false;
             wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
         }
-        String infName = port.getUuid().getValue();
         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
         try {
             LOG.debug("Deleting vpn interface {}", infName);
             wrtConfigTxn.delete(LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier);
-
-            Uuid rtrId = null;
-            List<FixedIps> ips = port.getFixedIps();
-            for (FixedIps ip : ips) {
-                String ipValue = String.valueOf(ip.getIpAddress().getValue());
-                NeutronvpnUtils.removeVpnPortFixedIpToPort(dataBroker, vpnId.getValue(),
-                        ipValue, wrtConfigTxn);
-                Uuid routerId = NeutronvpnUtils.getSubnetmap(dataBroker, ip.getSubnetId()).getRouterId();
-                if (routerId != null && routerId != rtrId) {
-                    removeFromNeutronRouterInterfacesMap(routerId, infName);
-                    rtrId = routerId;
-                }
-            }
         } catch (Exception ex) {
             LOG.error("Deletion of vpninterface {} failed", infName, ex);
         }
@@ -886,7 +951,6 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             Uuid existingVpn = NeutronvpnUtils.getVpnForRouter(dataBroker, router, true);
             if (existingVpn != null) {
                 // use case when a cluster is rebooted and router add DCN is received, triggering #createL3InternalVpn
-
                 // if before reboot, router was already associated to VPN, should not proceed associating router to
                 // internal VPN. Adding to RouterInterfacesMap is also not needed since it's a config DS and will be
                 // preserved upon reboot.
@@ -910,7 +974,7 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
      * @param rd Route-distinguisher for the VPN
      * @param irt A list of Import Route Targets
      * @param ert A list of Export Route Targets
-     * @param router UUID of the neutron router the VPN may be associated to
+     * @param router neutron router Id to associate with created VPN
      * @param networks UUID of the neutron network the VPN may be associated to
      * @param type Type of the VPN Instance
      * @param l3vni L3VNI for the VPN Instance using VxLAN as the underlay
@@ -921,9 +985,11 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
                                     throws Exception {
 
         IpVersionChoice ipVersChoices = IpVersionChoice.UNDEFINED;
-        IpVersionChoice vers = NeutronvpnUtils.getIpVersionChoicesFromRouterUuid(dataBroker, router);
-        ipVersChoices = ipVersChoices.addVersion(vers);
 
+        if (router != null) {
+            IpVersionChoice vers = NeutronvpnUtils.getIpVersionChoicesFromRouterUuid(dataBroker, router);
+            ipVersChoices = ipVersChoices.addVersion(vers);
+        }
         updateVpnInstanceNode(vpn.getValue(), rd, irt, ert, type, l3vni, ipVersChoices);
 
         // Please note that router and networks will be filled into VPNMaps
@@ -1274,16 +1340,35 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             neutronEvpnUtils.updateElanAndVpn(vpnInstance, sn.getNetworkId().getValue(),
                     NeutronEvpnUtils.Operation.ADD);
         }
-        // Check if there are ports on this subnet and add corresponding
-        // vpn-interfaces
+        // Check if there are ports on this subnet and add corresponding vpn-interfaces
         List<Uuid> portList = sn.getPortList();
         if (portList != null) {
             for (final Uuid portId : portList) {
-                LOG.debug("addSubnetToVpn: adding vpn-interface for vpnId {}", vpnId.getValue());
+                String vpnInfName = portId.getValue();
+                VpnInterface vpnIface = VpnHelper.getVpnInterface(dataBroker, vpnInfName);
+                Port port = NeutronvpnUtils.getNeutronPort(dataBroker, portId);
+                if (port == null) {
+                    LOG.error("addSubnetToVpn: Cannot proceed with addSubnetToVpn for port {} in subnet {} "
+                        + "since port is absent in Neutron config DS", portId.getValue(), subnet.getValue());
+                    continue;
+                }
+                final Boolean isRouterInterface = (port.getDeviceOwner()
+                        .equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF)) ? true : false;
                 jobCoordinator.enqueueJob("PORT-" + portId.getValue(), () -> {
                     WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
                     List<ListenableFuture<Void>> futures = new ArrayList<>();
-                    createVpnInterface(vpnId, NeutronvpnUtils.getNeutronPort(dataBroker, portId), wrtConfigTxn);
+                    Adjacencies portAdj = createPortIpAdjacencies(vpnId, port, isRouterInterface,
+                              wrtConfigTxn, sn, vpnIface);
+                    if (vpnIface == null) {
+                        LOG.trace("create new VpnInterface for Port {}", vpnInfName);
+                        writeVpnInterfaceToDs(vpnId, vpnInfName, portAdj, isRouterInterface, wrtConfigTxn);
+                        if (sn.getRouterId() != null) {
+                            addToNeutronRouterInterfacesMap(sn.getRouterId(),portId.getValue());
+                        }
+                    } else {
+                        LOG.trace("update VpnInterface for Port {} with adj {}", vpnInfName, portAdj);
+                        updateVpnInterfaceWithAdjacencies(vpnId, vpnInfName, portAdj, wrtConfigTxn);
+                    }
                     futures.add(wrtConfigTxn.submit());
                     return futures;
                 });
@@ -1291,6 +1376,54 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         }
     }
 
+    protected void removeSubnetFromVpn(final Uuid vpnId, Uuid subnet) {
+        LOG.debug("Removing subnet {} from vpn {}", subnet.getValue(), vpnId.getValue());
+        VpnMap vpnMap = NeutronvpnUtils.getVpnMap(dataBroker, vpnId);
+        if (vpnMap == null) {
+            LOG.error("No vpnMap for vpnId {}, cannot remove subnet {} from VPN",
+                    vpnId.getValue(), subnet.getValue());
+            return;
+        }
+        Subnetmap sn = NeutronvpnUtils.getSubnetmap(dataBroker, subnet);
+        final VpnInstance vpnInstance = VpnHelper.getVpnInstance(dataBroker, vpnId.getValue());
+        if (isVpnOfTypeL2(vpnInstance)) {
+            neutronEvpnUtils.updateElanAndVpn(vpnInstance, sn.getNetworkId().getValue(),
+                    NeutronEvpnUtils.Operation.DELETE);
+        }
+        if (sn != null) {
+            // Check if there are ports on this subnet; remove corresponding vpn-interfaces
+            List<Uuid> portList = sn.getPortList();
+            if (portList != null) {
+                for (final Uuid portId : portList) {
+                    LOG.debug("withdrawing subnet IP {} from vpn-interface {}", sn.getSubnetIp().toString(),
+                        portId.getValue());
+                    final Port port = NeutronvpnUtils.getNeutronPort(dataBroker, portId);
+                    jobCoordinator.enqueueJob("PORT-" + portId.getValue(), () -> {
+                        WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
+                        List<ListenableFuture<Void>> futures = new ArrayList<>();
+
+                        if (port != null) {
+                            withdrawPortIpFromVpnIface(vpnId, port, sn, wrtConfigTxn);
+                        } else {
+                            LOG.warn("Cannot proceed with withdrawPortIpFromVpnIface for port {} in subnet {} since "
+                                + "port is absent in Neutron config DS", portId.getValue(), subnet.getValue());
+                        }
+                        futures.add(wrtConfigTxn.submit());
+                        return futures;
+                    });
+                    //update subnet-vpn association
+                    if (port != null && port.getDeviceOwner().equals(NeutronConstants.DEVICE_OWNER_ROUTER_INF)) {
+                        removeFromSubnetNode(subnet, sn.getNetworkId(), sn.getRouterId(), vpnId, portId);
+                    } else {
+                        removeFromSubnetNode(subnet, sn.getNetworkId(), sn.getRouterId(), vpnId, null);
+                    }
+                }
+            }
+        } else {
+            LOG.error("Subnetmap for subnet {} not found", subnet.getValue());
+        }
+    }
+
     private Subnetmap updateVpnForSubnet(Uuid oldVpnId, Uuid newVpnId, Uuid subnet, boolean isBeingAssociated) {
         LOG.debug("Moving subnet {} from oldVpn {} to newVpn {} ", subnet.getValue(),
                 oldVpnId.getValue(), newVpnId.getValue());
@@ -1649,49 +1782,6 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         deleteVpnInstance(id);
     }
 
-    protected void removeSubnetFromVpn(final Uuid vpnId, Uuid subnet) {
-        LOG.debug("Removing subnet {} from vpn {}", subnet.getValue(), vpnId.getValue());
-        VpnMap vpnMap = NeutronvpnUtils.getVpnMap(dataBroker, vpnId);
-        if (vpnMap == null) {
-            LOG.error("No vpnMap for vpnId {}, cannot remove subnet {} from VPN",
-                    vpnId.getValue(), subnet.getValue());
-            return;
-        }
-        Subnetmap sn = NeutronvpnUtils.getSubnetmap(dataBroker, subnet);
-        final VpnInstance vpnInstance = VpnHelper.getVpnInstance(dataBroker, vpnId.getValue());
-        if (isVpnOfTypeL2(vpnInstance)) {
-            neutronEvpnUtils.updateElanAndVpn(vpnInstance, sn.getNetworkId().getValue(),
-                    NeutronEvpnUtils.Operation.DELETE);
-        }
-        if (sn != null) {
-            // Check if there are ports on this subnet; remove corresponding vpn-interfaces
-            List<Uuid> portList = sn.getPortList();
-            if (portList != null) {
-                for (final Uuid portId : portList) {
-                    LOG.debug("removing vpn-interface for port {}", portId.getValue());
-                    jobCoordinator.enqueueJob("PORT-" + portId.getValue(), () -> {
-                        WriteTransaction wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
-                        List<ListenableFuture<Void>> futures = new ArrayList<>();
-                        Port port = NeutronvpnUtils.getNeutronPort(dataBroker, portId);
-
-                        if (port != null) {
-                            deleteVpnInterface(vpnId, port, wrtConfigTxn);
-                        } else {
-                            LOG.warn("Cannot proceed with deleteVpnInterface for port {} in subnet {} since port is "
-                                + "absent in Neutron config DS", portId.getValue(), subnet.getValue());
-                        }
-                        futures.add(wrtConfigTxn.submit());
-                        return futures;
-                    });
-                }
-            }
-            // update subnet-vpn association
-            removeFromSubnetNode(subnet, null, null, vpnId, null);
-        } else {
-            LOG.error("Subnetmap for subnet {} not found", subnet.getValue());
-        }
-    }
-
     private boolean isVpnOfTypeL2(VpnInstance vpnInstance) {
         return vpnInstance != null && vpnInstance.getType() == VpnInstance.Type.L2;
     }
@@ -2388,6 +2478,47 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
         }
     }
 
+    private void updateVpnInterfaceWithAdjacencies(Uuid vpnId, String infName, Adjacencies adjacencies,
+            WriteTransaction wrtConfigTxn) {
+        if (vpnId == null || infName == null) {
+            LOG.error("vpn id or interface is null");
+            return;
+        }
+        Boolean wrtConfigTxnPresent = true;
+        if (wrtConfigTxn == null) {
+            wrtConfigTxnPresent = false;
+            wrtConfigTxn = dataBroker.newWriteOnlyTransaction();
+        }
+        InstanceIdentifier<VpnInterface> vpnIfIdentifier = NeutronvpnUtils.buildVpnInterfaceIdentifier(infName);
+        boolean isLockAcquired = false;
+        try {
+            isLockAcquired = NeutronUtils.lock(infName);
+            Optional<VpnInterface> optionalVpnInterface = SingleTransactionDataBroker
+                    .syncReadOptional(dataBroker, LogicalDatastoreType
+                    .CONFIGURATION, vpnIfIdentifier);
+            if (optionalVpnInterface.isPresent()) {
+                VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get());
+                LOG.debug("Updating vpn interface {} with new adjacencies", infName);
+
+                if (adjacencies == null) {
+                    if (isLockAcquired) {
+                        NeutronUtils.unlock(infName);
+                    }
+                    return;
+                }
+                vpnIfBuilder.addAugmentation(Adjacencies.class, adjacencies);
+                LOG.info("Updating vpn interface {} with new adjacencies", infName);
+                wrtConfigTxn.put(LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier, vpnIfBuilder.build());
+            }
+        } catch (IllegalStateException | ReadFailedException ex) {
+            LOG.error("Update of vpninterface {} failed", infName, ex);
+        } finally {
+            if (isLockAcquired) {
+                NeutronUtils.unlock(infName);
+            }
+        }
+    }
+
     private String getshowVpnConfigCLIHelp() {
         StringBuilder help = new StringBuilder("Usage:");
         help.append("display vpn-config [-vid/--vpnid <id>]");
index 3671646c7d598c386b936789d19a285af38440cc..ce63b18f89c42f0dbc37aeaf317cb5ace24822d8 100644 (file)
@@ -105,18 +105,19 @@ public class SubnetmapChangeListener extends AsyncDataTreeChangeListenerBase<Sub
         Uuid vpnIdNew = subnetmapUpdate.getVpnId();
         Uuid vpnIdOld = subnetmapOriginal.getVpnId();
         Uuid subnetId = subnetmapUpdate.getId();
-        String elanInstanceName = subnetmapUpdate.getNetworkId().getValue();
         // SubnetRoute for ExternalSubnets is handled in ExternalSubnetVpnInstanceListener.
         // Here we must handle only InternalVpnSubnetRoute and BGPVPNBasedSubnetRoute
-        Network network = VpnUtil.getNeutronNetwork(dataBroker, subnetmapUpdate.getNetworkId());
-        if (network == null) {
-            LOG.info("update: vpnIdNew: {}, vpnIdOld: {}, networkId: {}, subnetId: {}: network was not found",
-                vpnIdNew.getValue(), vpnIdOld.getValue(), elanInstanceName, subnetId.getValue());
+        Network network = null;
+        if (subnetmapUpdate.getNetworkId() == null
+              || (network = VpnUtil.getNeutronNetwork(dataBroker, subnetmapUpdate.getNetworkId())) == null) {
+            LOG.info("update: vpnIdNew: {}, vpnIdOld: {}, subnetId: {}: network was not found",
+                  vpnIdNew != null ? vpnIdNew.getValue() : "null", vpnIdOld.getValue(), subnetId.getValue());
             return;
         }
         if (VpnUtil.getIsExternal(network)) {
             return;
         }
+        String elanInstanceName = subnetmapUpdate.getNetworkId().getValue();
         Long elanTag = getElanTag(elanInstanceName);
         if (elanTag.equals(0L)) {
             LOG.error("update:Unable to fetch elantag from ElanInstance {} and hence not proceeding with "