Federation plugin 78/52178/22
authorTali <tali.ben-meir@hpe.com>
Wed, 22 Feb 2017 16:31:19 +0000 (18:31 +0200)
committerAlon Kochba <alonko@hpe.com>
Mon, 27 Mar 2017 09:40:48 +0000 (09:40 +0000)
L2 and L3 cross site connectivity using the federation service
 https://wiki.opendaylight.org/view/Federation:Main

Implementation details in the following spec
 https://git.opendaylight.org/gerrit/#/c/52271/

Change-Id: I4cd75b319ca7a1e33211c5523b73385f34e86d24
Signed-off-by: Tali <tali.ben-meir@hpe.com>
52 files changed:
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederatedMappings.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederatedNetworkPair.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationCorruptedStateException.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginCleaner.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginConstants.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginCounters.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginEgress.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginFactory.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginIngress.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginMgr.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginRpcServiceImpl.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginUtils.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/IEntityDeleteDecision.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/IFederationSubscriptionMgr.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/PendingModificationCache.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/SubnetVpnAssociationManager.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationElanInterfaceModificationCreator.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationIetfInterfaceModificationCreator.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationInventoryNodeModificationCreator.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationPluginCreatorRegistry.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationPluginModificationCreator.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationTopologyNodeModificationCreator.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationVpnInterfaceModificationCreator.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FullSyncDataObjectModification.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FullSyncDataTreeModification.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationElanInterfaceFilter.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationIetfInterfaceFilter.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationInventoryNodeFilter.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationPluginFilter.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationPluginFilterRegistry.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationTopologyNodeFilter.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationVpnInterfaceFilter.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FilterResult.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationElanInterfaceIdentifier.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationIetfInterfaceIdentifier.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationInventoryNodeIdentifier.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationPluginIdentifier.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationPluginIdentifierRegistry.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationTopologyNodeIdentifier.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationVpnInterfaceIdentifier.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationElanInterfaceTransformer.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationIetfInterfaceTransformer.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationInventoryNodeTransformer.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationPluginTransformer.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationPluginTransformerRegistry.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationTopologyNodeTransformer.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationVpnInterfaceTransformer.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/main/resources/org/opendaylight/blueprint/federationplugin.xml [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/test/java/org/opendaylight/netvirt/federation/plugin/end2end/AbstractEnd2EndTest.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/test/java/org/opendaylight/netvirt/federation/plugin/end2end/End2EndFullSyncTest.java [new file with mode: 0644]
vpnservice/federation-plugin/impl/src/test/java/org/opendaylight/netvirt/federation/plugin/end2end/End2EndSteadyTest.java [new file with mode: 0644]
vpnservice/neutronvpn/neutronvpn-api/src/main/java/org/opendaylight/netvirt/neutronvpn/api/utils/NeutronConstants.java

diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederatedMappings.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederatedMappings.java
new file mode 100644 (file)
index 0000000..f8b4bd1
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin;
+
+import com.google.common.collect.Maps;
+
+import java.util.List;
+import java.util.Map;
+
+public class FederatedMappings {
+
+    private final Map<String, String> producerToConsumerNetworkMap = Maps.newConcurrentMap();
+    private final Map<String, String> producerToConsumerSubnetMap = Maps.newConcurrentMap();
+
+    public FederatedMappings(List<FederatedNetworkPair> federatedNetworkPairs) {
+        federatedNetworkPairs.stream()
+                .forEach((pair) -> producerToConsumerNetworkMap.put(pair.producerNetworkId, pair.consumerNetworkId));
+        federatedNetworkPairs.stream()
+                .forEach((pair) -> producerToConsumerSubnetMap.put(pair.producerSubnetId, pair.consumerSubnetId));
+    }
+
+    public String getConsumerNetworkId(String producerNetworkId) {
+        return producerToConsumerNetworkMap.get(producerNetworkId);
+    }
+
+    public boolean containsProducerNetworkId(String producerNetworkId) {
+        return producerToConsumerNetworkMap.containsKey(producerNetworkId);
+    }
+
+    public boolean containsConsumerNetworkId(String consumerNetworkId) {
+        return producerToConsumerNetworkMap.containsValue(consumerNetworkId);
+    }
+
+    public String getConsumerSubnetId(String producerSubnetId) {
+        return producerToConsumerSubnetMap.get(producerSubnetId);
+    }
+
+    public boolean containsProducerSubnetId(String producerSubnetId) {
+        return producerToConsumerSubnetMap.containsKey(producerSubnetId);
+    }
+
+    public boolean containsConsumerSubnetId(String consumerSubnetId) {
+        return producerToConsumerSubnetMap.containsValue(consumerSubnetId);
+    }
+
+    @Override
+    public String toString() {
+        return "FederatedMappings [federatedNetworkMap=" + producerToConsumerNetworkMap + ", federatedSubnetMap="
+                + producerToConsumerSubnetMap + "]";
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederatedNetworkPair.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederatedNetworkPair.java
new file mode 100644 (file)
index 0000000..6af1401
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+
+public class FederatedNetworkPair {
+
+    public String consumerNetworkId;
+    public String producerNetworkId;
+    public String consumerSubnetId;
+    public String producerSubnetId;
+    public String consumerTenantId;
+    public String producerTenantId;
+
+    public FederatedNetworkPair(String localNetworkId, String remoteNetworkId, String localSubnetId,
+            String remoteSubnetId, String localTenantId, String remoteTenantId) {
+        this.consumerNetworkId = localNetworkId;
+        this.producerNetworkId = remoteNetworkId;
+        this.consumerSubnetId = localSubnetId;
+        this.producerSubnetId = remoteSubnetId;
+        this.consumerTenantId = localTenantId;
+        this.producerTenantId = remoteTenantId;
+    }
+
+    public FederatedNetworkPair(String localNetworkId, String remoteNetworkId, Uuid localSubnetId, Uuid remoteSubnetId,
+            Uuid localTenantId, Uuid remoteTenantId) {
+        this.consumerNetworkId = localNetworkId;
+        this.producerNetworkId = remoteNetworkId;
+        this.consumerSubnetId = uuidToCleanStr(localSubnetId);
+        this.producerSubnetId = uuidToCleanStr(remoteSubnetId);
+        this.consumerTenantId = uuidToCleanStr(localTenantId);
+        this.producerTenantId = uuidToCleanStr(remoteTenantId);
+    }
+
+    public FederatedNetworkPair(Uuid localNetworkId, Uuid remoteNetworkId, Uuid localSubnetId, Uuid remoteSubnetId,
+            Uuid localTenantId, Uuid remoteTenantId) {
+        this.consumerNetworkId = uuidToCleanStr(localNetworkId);
+        this.producerNetworkId = uuidToCleanStr(remoteNetworkId);
+        this.consumerSubnetId = uuidToCleanStr(localSubnetId);
+        this.producerSubnetId = uuidToCleanStr(remoteSubnetId);
+        this.consumerTenantId = uuidToCleanStr(localTenantId);
+        this.producerTenantId = uuidToCleanStr(remoteTenantId);
+    }
+
+    private String uuidToCleanStr(Uuid uuid) {
+        String uuidStr = uuid.toString();
+        String str = "";
+        if (uuidStr.indexOf('=') != -1 && uuidStr.indexOf(']') != -1) {
+            str = uuidStr.substring(uuidStr.indexOf('=') + 1, uuidStr.indexOf(']'));
+        }
+        return str;
+    }
+
+    @Override
+    public String toString() {
+        return "FederatedNetworkPair " + "[consumerNetworkId=" + consumerNetworkId + ", producerNetworkId="
+                + producerNetworkId + "]" + "[consumerSubnetId=" + consumerSubnetId + ", producerSubnetId="
+                + producerSubnetId + "]" + "[consumerTenantId=" + consumerTenantId + ", producerTenantId="
+                + producerTenantId + "]";
+    }
+
+    public boolean equals(FederatedNetworkPair other) {
+        if (!consumerNetworkId.equals(other.consumerNetworkId)) {
+            return false;
+        }
+        if (!producerNetworkId.equals(other.producerNetworkId)) {
+            return false;
+        }
+        if (!consumerSubnetId.equals(other.consumerSubnetId)) {
+            return false;
+        }
+        if (!producerSubnetId.equals(other.producerSubnetId)) {
+            return false;
+        }
+        if (!consumerTenantId.equals(other.consumerTenantId)) {
+            return false;
+        }
+        if (!producerTenantId.equals(other.producerTenantId)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return this.toString().hashCode();
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationCorruptedStateException.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationCorruptedStateException.java
new file mode 100644 (file)
index 0000000..5b626ac
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin;
+
+public class FederationCorruptedStateException extends Exception {
+
+    private static final long serialVersionUID = -1577242292029134902L;
+
+    public FederationCorruptedStateException() {
+        super();
+    }
+
+    public FederationCorruptedStateException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public FederationCorruptedStateException(String message) {
+        super(message);
+    }
+
+    public FederationCorruptedStateException(Throwable cause) {
+        super(cause);
+    }
+
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginCleaner.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginCleaner.java
new file mode 100644 (file)
index 0000000..57e32a8
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.ElanShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.IfShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.InventoryNodeShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.TopologyNodeShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.VpnShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class FederationPluginCleaner {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationPluginCleaner.class);
+    private static final int MAX_TRANSACTION_DELETE_RETRIES = 5;
+    private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1);
+
+    public static synchronized void removeOldGenerationFederatedEntities(DataBroker db, final int generationNumber,
+        String remoteIp) {
+        boolean somethingDeleted = false;
+
+        if (deleteVpnInterface(db, LogicalDatastoreType.OPERATIONAL, MAX_TRANSACTION_DELETE_RETRIES, vpnInterface -> {
+            VpnShadowProperties vpnShadowProperties = vpnInterface.getAugmentation(VpnShadowProperties.class);
+            return vpnShadowProperties != null && Boolean.TRUE.equals(vpnShadowProperties.isShadow())
+                && generationNumber != vpnShadowProperties.getGenerationNumber()
+                && remoteIp.equals(vpnShadowProperties.getRemoteIp());
+        })) {
+            somethingDeleted = true;
+        }
+
+        if (deleteVpnInterface(db, LogicalDatastoreType.CONFIGURATION, MAX_TRANSACTION_DELETE_RETRIES, vpnInterface -> {
+            VpnShadowProperties vpnShadowProperties = vpnInterface.getAugmentation(VpnShadowProperties.class);
+            return vpnShadowProperties != null && Boolean.TRUE.equals(vpnShadowProperties.isShadow())
+                && generationNumber != vpnShadowProperties.getGenerationNumber()
+                && remoteIp.equals(vpnShadowProperties.getRemoteIp());
+        })) {
+            somethingDeleted = true;
+        }
+
+        if (deleteElanInterfacesShadows(db, LogicalDatastoreType.CONFIGURATION, MAX_TRANSACTION_DELETE_RETRIES,
+            elanInterface -> {
+                ElanShadowProperties elanShadowProperties = elanInterface.getAugmentation(ElanShadowProperties.class);
+                return elanShadowProperties != null && Boolean.TRUE.equals(elanShadowProperties.isShadow())
+                    && generationNumber != elanShadowProperties.getGenerationNumber()
+                    && remoteIp.equals(elanShadowProperties.getRemoteIp());
+            })) {
+            somethingDeleted = true;
+        }
+
+        sleepIfSomethingWasDeleted(somethingDeleted);
+
+        deleteInterfacesShadows(db, LogicalDatastoreType.CONFIGURATION, MAX_TRANSACTION_DELETE_RETRIES, iface -> {
+            IfShadowProperties ifShadowProperties = iface.getAugmentation(IfShadowProperties.class);
+            return ifShadowProperties != null && Boolean.TRUE.equals(ifShadowProperties.isShadow())
+                && generationNumber != ifShadowProperties.getGenerationNumber()
+                && remoteIp.equals(ifShadowProperties.getRemoteIp());
+        });
+
+        EXECUTOR.schedule(() -> {
+
+            deleteInventoryNodes(db, LogicalDatastoreType.OPERATIONAL, MAX_TRANSACTION_DELETE_RETRIES,
+                    new IEntityDeleteDecision<Node>() {
+                        @Override
+                        public boolean shouldDelete(Node node) {
+                            InventoryNodeShadowProperties nodeShadowProperties = node
+                                    .getAugmentation(InventoryNodeShadowProperties.class);
+                            return nodeShadowProperties != null && Boolean.TRUE.equals(nodeShadowProperties.isShadow())
+                                    && (generationNumber != nodeShadowProperties.getGenerationNumber())
+                                    && (remoteIp.equals(nodeShadowProperties.getRemoteIp()));
+                        }
+                    });
+
+            deleteInventoryNodes(db, LogicalDatastoreType.CONFIGURATION, MAX_TRANSACTION_DELETE_RETRIES,
+                    new IEntityDeleteDecision<Node>() {
+                        @Override
+                        public boolean shouldDelete(Node node) {
+                            InventoryNodeShadowProperties nodeShadowProperties = node
+                                    .getAugmentation(InventoryNodeShadowProperties.class);
+                            return nodeShadowProperties != null && Boolean.TRUE.equals(nodeShadowProperties.isShadow())
+                                    && (generationNumber != nodeShadowProperties.getGenerationNumber())
+                                    && (remoteIp.equals(nodeShadowProperties.getRemoteIp()));
+                        }
+                    });
+
+            deleteTopologyShadowNodes(db, LogicalDatastoreType.OPERATIONAL, MAX_TRANSACTION_DELETE_RETRIES,
+                    new IEntityDeleteDecision<org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology
+                    .rev131021.network.topology.topology.Node>() {
+                        @Override
+                        public boolean shouldDelete(
+                                org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021
+                                .network.topology.topology.Node node) {
+                            TopologyNodeShadowProperties nodeShadowProperties = node
+                                    .getAugmentation(TopologyNodeShadowProperties.class);
+                            return nodeShadowProperties != null && Boolean.TRUE.equals(nodeShadowProperties.isShadow())
+                                    && (generationNumber != nodeShadowProperties.getGenerationNumber())
+                                    && (remoteIp.equals(nodeShadowProperties.getRemoteIp()));
+                        }
+                    });
+
+            deleteTopologyShadowNodes(db, LogicalDatastoreType.CONFIGURATION, MAX_TRANSACTION_DELETE_RETRIES,
+                    new IEntityDeleteDecision<org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology
+                    .rev131021.network.topology.topology.Node>() {
+                        @Override
+                        public boolean shouldDelete(
+                                org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021
+                                .network.topology.topology.Node node) {
+                            TopologyNodeShadowProperties nodeShadowProperties = node
+                                    .getAugmentation(TopologyNodeShadowProperties.class);
+                            return nodeShadowProperties != null && Boolean.TRUE.equals(nodeShadowProperties.isShadow())
+                                    && (generationNumber > nodeShadowProperties.getGenerationNumber())
+                                    && (remoteIp.equals(nodeShadowProperties.getRemoteIp()));
+                        }
+                    });
+        } , 120, TimeUnit.SECONDS);
+    }
+
+    private static void sleepIfSomethingWasDeleted(boolean somethingRemoved) {
+        if (somethingRemoved) {
+            LOG.info("Sleeping 10 seconds to let Netvirt listeners process");
+            try {
+                Thread.sleep(10000);
+            } catch (InterruptedException e) {
+                LOG.warn("I can't sleep!", e);
+            }
+        }
+    }
+
+    private static boolean deleteVpnInterface(DataBroker db, LogicalDatastoreType type, int remainingRetries,
+        IEntityDeleteDecision<VpnInterface> entityDeleteDecision) {
+        InstanceIdentifier<VpnInterfaces> path = InstanceIdentifier.create(VpnInterfaces.class);
+        ReadTransaction readTx = db.newReadOnlyTransaction();
+        CheckedFuture<Optional<VpnInterfaces>, ReadFailedException> future = readTx.read(type, path);
+        Optional<VpnInterfaces> optional = null;
+
+        try {
+            optional = future.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("deleteVpnInterface failed to get data");
+            return false;
+        }
+        if (optional.isPresent()) {
+            WriteTransaction deleteTx = db.newWriteOnlyTransaction();
+            VpnInterfaces vpnInterfaces = optional.get();
+            for (VpnInterface iface : vpnInterfaces.getVpnInterface()) {
+                if (entityDeleteDecision.shouldDelete(iface)) {
+                    LOG.debug("Delete shadow vpn Interface: DataStoreType {}, interface {}", type, iface);
+                    FederationPluginCounters.removed_shadow_vpn_interface.inc();
+                    InstanceIdentifier<VpnInterface> iid = InstanceIdentifier.create(VpnInterfaces.class)
+                        .child(VpnInterface.class, new VpnInterfaceKey(iface.getKey()));
+                    deleteTx.delete(type, iid);
+                }
+            }
+            CheckedFuture<Void, TransactionCommitFailedException> future1 = deleteTx.submit();
+            try {
+                future1.checkedGet();
+            } catch (TransactionCommitFailedException e) {
+                if (remainingRetries > 0) {
+                    LOG.warn("deleteVpnInterface - Failed to delete! {} {}" + e.getMessage(), e);
+                    deleteVpnInterface(db, type, --remainingRetries, entityDeleteDecision);
+                } else {
+                    LOG.error("deleteVpnInterface - Failed to delete - no more retries! {} {}" + e.getMessage(), e);
+                }
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private static boolean deleteInterfacesShadows(DataBroker db, LogicalDatastoreType type, int remainingRetries,
+        IEntityDeleteDecision<Interface> entityDeleteDecision) {
+        InstanceIdentifier<Interfaces> path = InstanceIdentifier.create(Interfaces.class);
+        ReadTransaction readTx = db.newReadOnlyTransaction();
+        CheckedFuture<Optional<Interfaces>, ReadFailedException> future = readTx.read(type, path);
+        Optional<Interfaces> optional = null;
+
+        try {
+            optional = future.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("deleteInterfacesShadows failed to get data");
+            return false;
+        }
+        if (optional.isPresent()) {
+            WriteTransaction deleteTx = db.newWriteOnlyTransaction();
+            Interfaces interfaces = optional.get();
+            for (Interface iface : interfaces.getInterface()) {
+                if (entityDeleteDecision.shouldDelete(iface)) {
+                    LOG.debug("Delete shadow interfaces: DataStoreType {}, interface {}", type, iface);
+                    FederationPluginCounters.removed_shadow_ietf_interface.inc();
+                    InstanceIdentifier<Interface> iid = InstanceIdentifier.create(Interfaces.class)
+                        .child(Interface.class, new InterfaceKey(iface.getKey()));
+                    deleteTx.delete(type, iid);
+                }
+            }
+            CheckedFuture<Void, TransactionCommitFailedException> future1 = deleteTx.submit();
+            try {
+                future1.checkedGet();
+            } catch (TransactionCommitFailedException e) {
+                if (remainingRetries > 0) {
+                    LOG.warn("deleteInterfacesShadows - Failed to delete! {} {}" + e.getMessage(), e);
+                    deleteInterfacesShadows(db, type, --remainingRetries, entityDeleteDecision);
+                } else {
+                    LOG.error("deleteInterfacesShadows - Failed to delete - no more retries! {} {}" + e.getMessage(),
+                        e);
+                }
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private static boolean deleteElanInterfacesShadows(DataBroker db, LogicalDatastoreType type, int remainingRetries,
+        IEntityDeleteDecision<ElanInterface> entityDeleteDecision) {
+
+        InstanceIdentifier<ElanInterfaces> path = InstanceIdentifier.create(ElanInterfaces.class);
+        ReadTransaction readTx = db.newReadOnlyTransaction();
+        CheckedFuture<Optional<ElanInterfaces>, ReadFailedException> future = readTx.read(type, path);
+
+        Optional<ElanInterfaces> optional = null;
+
+        try {
+            optional = future.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("deleteElanInterfacesShadows failed to get data");
+            return false;
+        }
+        if (optional.isPresent()) {
+            WriteTransaction deleteTx = db.newWriteOnlyTransaction();
+            ElanInterfaces interfaces = optional.get();
+            for (ElanInterface iface : interfaces.getElanInterface()) {
+                if (entityDeleteDecision.shouldDelete(iface)) {
+                    LOG.debug("Delete shadow elan interface: DataStoreType {}, interface {}", type, iface);
+                    FederationPluginCounters.removed_shadow_elan_interface.inc();
+                    InstanceIdentifier<ElanInterface> iid = InstanceIdentifier.create(ElanInterfaces.class)
+                        .child(ElanInterface.class, new ElanInterfaceKey(iface.getKey()));
+                    deleteTx.delete(type, iid);
+                }
+            }
+            CheckedFuture<Void, TransactionCommitFailedException> future1 = deleteTx.submit();
+            try {
+                future1.checkedGet();
+            } catch (TransactionCommitFailedException e) {
+                if (remainingRetries > 0) {
+                    LOG.warn("deleteElanInterfacesShadows - Failed to delete! {} {}" + e.getMessage(), e);
+                    deleteElanInterfacesShadows(db, type, --remainingRetries, entityDeleteDecision);
+                } else {
+                    LOG.error(
+                        "deleteElanInterfacesShadows - Failed to delete - no more retries! {} {}" + e.getMessage(), e);
+                }
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private static boolean deleteTopologyShadowNodes(DataBroker db, LogicalDatastoreType type, int remainingRetries,
+        IEntityDeleteDecision<org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network
+        .topology.topology.Node> entityDeleteDecision) {
+        InstanceIdentifier<Topology> path = InstanceIdentifier.create(NetworkTopology.class).child(Topology.class,
+            new TopologyKey(new TopologyId(new Uri("ovsdb:1"))));
+        ReadTransaction readTx = db.newReadOnlyTransaction();
+        CheckedFuture<Optional<Topology>, ReadFailedException> future = readTx.read(type, path);
+
+        Optional<Topology> optional = null;
+
+        try {
+            optional = future.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("deleteTopologyShadowNodes failed to get data");
+            return false;
+        }
+        if (optional.isPresent()) {
+            WriteTransaction deleteTx = db.newWriteOnlyTransaction();
+            Topology topology = optional.get();
+            for (org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology
+                     .topology.Node node : topology.getNode()) {
+                if (entityDeleteDecision.shouldDelete(node)) {
+                    LOG.debug("Delete shadow topolog node: DataStoreType {}, node {}", type, node);
+                    FederationPluginCounters.removed_shadow_topology_node.inc();
+                    InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology
+                        .rev131021.network.topology.topology.Node> iid = InstanceIdentifier
+                            .create(NetworkTopology.class)
+                            .child(Topology.class, new TopologyKey(new TopologyId(new Uri("ovsdb:1"))))
+                            .child(org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021
+                                    .network.topology.topology.Node.class, node.getKey());
+                    deleteTx.delete(type, iid);
+                }
+            }
+            CheckedFuture<Void, TransactionCommitFailedException> future1 = deleteTx.submit();
+            try {
+                future1.checkedGet();
+            } catch (org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException e) {
+                if (remainingRetries > 0) {
+                    LOG.warn("deleteTopologyShadowNodes - Failed to delete! {} {}" + e.getMessage(), e);
+                    deleteTopologyShadowNodes(db, type, --remainingRetries, entityDeleteDecision);
+                } else {
+                    LOG.error("deleteTopologyShadowNodes - Failed to delete - no more retries! {} {}" + e.getMessage(),
+                        e);
+                }
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private static void deleteInventoryNodes(DataBroker db, LogicalDatastoreType type, int remainingRetries,
+        IEntityDeleteDecision<
+            org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> entityDeleteDecision) {
+        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes> path =
+            InstanceIdentifier.create(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes.class);
+        ReadTransaction readTx = db.newReadOnlyTransaction();
+        CheckedFuture<Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes>,
+            ReadFailedException> future = readTx.read(type, path);
+
+        Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes> optional = null;
+
+        try {
+            optional = future.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.info("deleteInventoryNodes failed to get data");
+        }
+        if (optional != null && optional.isPresent()) {
+            WriteTransaction deleteTx = db.newWriteOnlyTransaction();
+            org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes nodes = optional.get();
+            for (org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node : nodes.getNode()) {
+                if (entityDeleteDecision.shouldDelete(node)) {
+                    LOG.debug("Delete shadow inventory node: DataStoreType {}, node {}", type, node);
+                    FederationPluginCounters.removed_shadow_inventory_node.inc();
+                    org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey key =
+                        new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey(
+                            node.getId());
+                    InstanceIdentifier<
+                        org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> iid =
+                            InstanceIdentifier
+                                .create(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes.class)
+                                .child(
+                                    org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class,
+                                    key);
+                    deleteTx.delete(type, iid);
+                }
+            }
+            CheckedFuture<Void, TransactionCommitFailedException> future1 = deleteTx.submit();
+            try {
+                future1.checkedGet();
+            } catch (TransactionCommitFailedException e) {
+                if (remainingRetries > 0) {
+                    LOG.warn("deleteInventoryNodes - Failed to delete! {} {}" + e.getMessage(), e);
+                    deleteInventoryNodes(db, type, --remainingRetries, entityDeleteDecision);
+                } else {
+                    LOG.error("deleteInventoryNodes - Failed to delete - no more retries! {} {}" + e.getMessage(), e);
+                }
+            }
+        }
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginConstants.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginConstants.java
new file mode 100644 (file)
index 0000000..fe20e04
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin;
+
+import org.opendaylight.genius.itm.globals.ITMConstants;
+
+public class FederationPluginConstants {
+
+    public static final String PLUGIN_TYPE = "NETVIRT";
+
+    public static final String INVENTORY_NODE_CONFIG_KEY = "INVENTORY_NODE_CONFIG";
+
+    public static final String INVENTORY_NODE_OPER_KEY = "INVENTORY_NODE_OPER";
+
+    public static final String TOPOLOGY_NODE_CONFIG_KEY = "TOPOLOGY_NODE_CONFIG";
+
+    public static final String TOPOLOGY_NODE_OPER_KEY = "TOPOLOGY_NODE_OPER";
+
+    public static final String IETF_INTERFACE_KEY = "IETF_INTERFACE";
+
+    public static final String ELAN_INTERFACE_KEY = "ELAN_INTERFACE";
+
+    public static final String VPN_INTERFACE_KEY = "VPN_INTERFACE";
+
+    public static final String RPC_ROUTE_KEY = "FEDERATION_ROUTE_KEY";
+
+    public static final String INTEGRATION_BRIDGE_PREFIX = ITMConstants.BRIDGE_URI_PREFIX + "/"
+            + ITMConstants.DEFAULT_BRIDGE_NAME;
+
+    public static final String TUNNEL_PREFIX = "tun";
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginCounters.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginCounters.java
new file mode 100644 (file)
index 0000000..388d278
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin;
+
+import org.opendaylight.infrautils.counters.api.OccurenceCounter;
+
+public enum FederationPluginCounters {
+
+    egress_steady_data, //
+    egress_full_sync, //
+    egress_last_full_sync_listener, //
+    egress_process_pending_modification, //
+    egress_transformation_failed, //
+    egress_publish_modification, //
+    egress_filter_result_deny, //
+    egress_filter_result_accept, //
+    egress_filter_result_queue, //
+    egress_steady_data_aborted, //
+    egress_full_sync_aborted, //
+    ingress_begin_tx, //
+    ingress_end_tx, //
+    ingress_consume_msg, //
+    ingress_full_sync_modification, //
+    ingress_full_sync_failed, //
+    ingress_process_modification, //
+    ingress_write_modification, //
+    ingress_filter_result_deny, //
+    ingress_filter_result_accept, //
+    ingress_filter_result_queue, //
+    ingress_add_to_tx_modification, //
+    ingress_delete_modification, //
+    ingress_subnet_vpn_association_changed, //
+    ingress_federated_subnet_vpn_association_changed, //
+    ingress_subnet_vpn_association_aborted, //
+    ingress_consume_msg_aborted, //
+    ingress_full_sync_aborted, //
+    egress_node_filtered_after_transform, //
+    egress_no_existing_data, //
+    removed_shadow_elan_interface, //
+    removed_shadow_ietf_interface, //
+    removed_shadow_inventory_node, //
+    removed_shadow_topology_node, //
+    removed_shadow_vpn_interface, //
+    removed_shadow_vpn_port_ip_to_port, //
+    ;
+
+    private OccurenceCounter counter;
+
+    FederationPluginCounters() {
+        counter = new OccurenceCounter(getClass().getSimpleName(), name(), name());
+    }
+
+    public void inc() {
+        counter.inc();
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginEgress.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginEgress.java
new file mode 100644 (file)
index 0000000..f3861ba
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.CompletableFuture;
+
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.federation.plugin.spi.IFederationPluginEgress;
+import org.opendaylight.federation.service.api.IFederationProducerMgr;
+import org.opendaylight.federation.service.api.federationutil.FederationUtils;
+import org.opendaylight.federation.service.common.api.EntityFederationMessage;
+import org.opendaylight.federation.service.common.api.ListenerData;
+import org.opendaylight.netvirt.federation.plugin.filters.FilterResult;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+
+
+public class FederationPluginEgress implements IFederationPluginEgress {
+
+    private final Logger logger;
+    private final IFederationProducerMgr producerMgr;
+    private final String queueName;
+    private final String contextId;
+    private final FederatedMappings federatedMappings;
+    private final PendingModificationCache<DataTreeModification<? extends DataObject>> pendingModifications = //
+            new PendingModificationCache<>();
+
+    private volatile boolean aborted = false;
+
+    static {
+        FederationPluginUtils.initYangModules();
+    }
+
+    public FederationPluginEgress(final IFederationProducerMgr producerMgr,
+            List<FederatedNetworkPair> federatedNetworkPairs, String queueName, String contextId) {
+        this.producerMgr = producerMgr;
+        this.queueName = queueName;
+        this.contextId = contextId;
+        logger = FederationUtils.createLogger(queueName, FederationPluginEgress.class);
+        federatedMappings = new FederatedMappings(federatedNetworkPairs);
+    }
+
+    @Override
+    public synchronized void steadyData(String listenerKey,
+            Collection<DataTreeModification<? extends DataObject>> dataTreeModifications) {
+        if (!aborted) {
+            FederationPluginCounters.egress_steady_data.inc();
+            processDataTreeModifications(listenerKey, dataTreeModifications, false);
+        } else {
+            FederationPluginCounters.egress_steady_data_aborted.inc();
+        }
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Override
+    public synchronized void fullSyncData(String listenerKey, Optional existingData) {
+        if (aborted) {
+            FederationPluginCounters.egress_full_sync_aborted.inc();
+            return;
+        }
+
+        FederationPluginCounters.egress_full_sync.inc();
+        Collection dataTreeModifications = createModifications(listenerKey, existingData);
+        processDataTreeModifications(listenerKey, dataTreeModifications, true);
+    }
+
+    @Override
+    public List<ListenerData> getListenersData() {
+        List<ListenerData> listenersData = new ArrayList<>();
+        for (String listenerKey : FederationPluginUtils.getOrderedListenerKeys()) {
+            LogicalDatastoreType datastoreType = FederationPluginUtils.getListenerDatastoreType(listenerKey);
+            if (datastoreType == null) {
+                logger.error("Failed to get datastore type for listener {}. Ignoring listener key", listenerKey);
+                continue;
+            }
+
+            InstanceIdentifier<?> instanceIdentifierForListener = FederationPluginUtils
+                    .getInstanceIdentifier(listenerKey);
+            if (instanceIdentifierForListener == null) {
+                logger.error("Failed to get instance identifier of listener for listener key {}. Ignoring listener key",
+                        listenerKey);
+                continue;
+            }
+
+            InstanceIdentifier<?> instanceIdentifierForExistingData = FederationPluginUtils
+                    .getParentInstanceIdentifier(listenerKey);
+            if (instanceIdentifierForExistingData == null) {
+                logger.error(
+                        "Failed to get instance identifier of existing data for listener key {}. Ignoring listener key",
+                        listenerKey);
+                continue;
+            }
+
+            ListenerData listenerData = new ListenerData(listenerKey,
+                    new DataTreeIdentifier<>(datastoreType, instanceIdentifierForListener),
+                    new DataTreeIdentifier<>(datastoreType, instanceIdentifierForExistingData));
+            listenersData.add(listenerData);
+        }
+
+        logger.debug("Listener keys {}", listenersData);
+        return listenersData;
+    }
+
+    @Override
+    public void cleanup() {
+        pendingModifications.cleanup();
+    }
+
+    private void processDataTreeModifications(String listenerKey,
+            Collection<DataTreeModification<? extends DataObject>> dataTreeModifications, boolean isFullSync) {
+        if (dataTreeModifications == null) {
+            return;
+        }
+
+        for (DataTreeModification<? extends DataObject> dataTreeModification : dataTreeModifications) {
+            if (isSpuriousModification(dataTreeModification)) {
+                continue;
+            }
+            processDataTreeModification(listenerKey, dataTreeModification, isFullSync);
+        }
+    }
+
+    private boolean isSpuriousModification(DataTreeModification<? extends DataObject> dataTreeModification) {
+        if (dataTreeModification == null) {
+            return true;
+        }
+        DataObjectModification<? extends DataObject> rootNode = dataTreeModification.getRootNode();
+        if (rootNode.getDataBefore() != null && rootNode.getDataAfter() != null
+                && rootNode.getDataBefore().equals(rootNode.getDataAfter())) {
+            return true;
+        }
+        return false;
+    }
+
+    private <T extends DataObject> void processDataTreeModification(String listenerKey,
+            DataTreeModification<T> dataTreeModification, boolean publishInTx) {
+        T dataObject = FederationPluginUtils.getDataObjectFromModification(dataTreeModification);
+        if (dataObject == null) {
+            logger.warn("Failed to get DataObject from {}", dataObject);
+            return;
+        }
+
+        if (!applyFilter(listenerKey, dataObject, dataTreeModification)) {
+            logger.trace("listener {} filtered out", listenerKey);
+            return;
+        }
+
+        // process queued modifications associated with this modification
+        processPendingDataTreeModifications(listenerKey, dataObject, publishInTx);
+        // queue deleted modification for future use if required
+        if (ModificationType.DELETE.equals(dataTreeModification.getRootNode().getModificationType())
+                && PendingModificationCache.isLiberatorKey(listenerKey)) {
+            addPendingModification(listenerKey, dataObject, dataTreeModification);
+        }
+        // publish the modification to the federation
+        publishDataTreeModification(listenerKey, dataObject, dataTreeModification, publishInTx);
+    }
+
+    private <T extends DataObject> void processPendingDataTreeModifications(String listenerKey, T dataObject,
+            boolean publishInTx) {
+        Map<String, Collection<DataTreeModification<? extends DataObject>>>
+            associatedModifications = removePendingModifications(listenerKey, dataObject);
+        if (associatedModifications != null) {
+            for (Entry<String, Collection<DataTreeModification<? extends DataObject>>> entry : associatedModifications
+                    .entrySet()) {
+                for (DataTreeModification<? extends DataObject> modification : entry.getValue()) {
+                    processPendingDataTreeModification(entry.getKey(), modification, publishInTx);
+                }
+            }
+        }
+    }
+
+    private <T extends DataObject> void processPendingDataTreeModification(String listenerKey,
+            DataTreeModification<T> dataTreeModification, boolean publishInTx) {
+        T dataObject = FederationPluginUtils.getDataObjectFromModification(dataTreeModification);
+        if (dataObject == null) {
+            logger.warn("Failed to get DataObject from {}", dataObject);
+            return;
+        }
+
+        FederationPluginCounters.egress_process_pending_modification.inc();
+        publishDataTreeModification(listenerKey, dataObject, dataTreeModification, publishInTx);
+    }
+
+    private <T extends DataObject, S extends DataObject> void publishDataTreeModification(String listenerKey,
+            S dataObject, DataTreeModification<S> dataTreeModification, boolean publishInTx) {
+        T transformedObject = FederationPluginUtils.applyEgressTransformation(listenerKey, dataObject,
+                federatedMappings, pendingModifications);
+        if (transformedObject == null) {
+            FederationPluginCounters.egress_transformation_failed.inc();
+            logger.error("Failed to transform {} for listener {}", dataObject, listenerKey);
+            return;
+        }
+
+        EntityFederationMessage<T> msg = createEntityFederationMsgFromDataObject(listenerKey, transformedObject,
+                dataTreeModification);
+        FederationPluginCounters.egress_publish_modification.inc();
+        logger.trace("Publishing {} for listener {}", transformedObject, listenerKey);
+        producerMgr.publishMessage(msg, queueName, contextId);
+    }
+
+    private <T extends DataObject> boolean applyFilter(String listenerKey, T dataObject,
+            DataTreeModification<T> dataTreeModification) {
+        FilterResult filterResult = FederationPluginUtils.applyEgressFilter(listenerKey, dataObject, federatedMappings,
+                pendingModifications, dataTreeModification);
+        if (filterResult == null) {
+            logger.warn("Failed to get FilterResult for {} {}", listenerKey, dataObject);
+            return false;
+        }
+
+        logger.trace("{} filter result {}", listenerKey, filterResult);
+        switch (filterResult) {
+            case DENY:
+                FederationPluginCounters.egress_filter_result_deny.inc();
+                return false;
+            case ACCEPT:
+                FederationPluginCounters.egress_filter_result_accept.inc();
+                return true;
+            case QUEUE:
+                FederationPluginCounters.egress_filter_result_queue.inc();
+                addPendingModification(listenerKey, dataObject, dataTreeModification);
+                return false;
+            default:
+                logger.error("Didn't find a match for the filter result {}", filterResult.toString());
+                return false;
+        }
+    }
+
+    private <T extends DataObject> void addPendingModification(String listenerKey, T dataObject,
+            DataTreeModification<? extends DataObject> dataTreeModification) {
+        logger.trace("Add pending modification {} listener {}", dataObject, listenerKey);
+        pendingModifications.add(dataObject, listenerKey, dataTreeModification);
+    }
+
+    private <T extends DataObject> Map<String, Collection<DataTreeModification<? extends DataObject>>>
+        removePendingModifications(String listenerKey, T dataObject) {
+        if (!PendingModificationCache.isLiberatorKey(listenerKey)) {
+            return null;
+        }
+
+        logger.trace("Remove pending modifications for listener {}", listenerKey);
+        return pendingModifications.remove(dataObject);
+    }
+
+    @SuppressWarnings({ "unchecked" })
+    private <T extends DataObject, S extends DataObject> EntityFederationMessage<T>
+        createEntityFederationMsgFromDataObject(String listenerKey, T dataObject,
+                DataTreeModification<S> dataTreeModification) {
+        DataObjectModification<S> dataObjectModification = dataTreeModification.getRootNode();
+        ModificationType modificationType = dataObjectModification.getModificationType();
+        InstanceIdentifier<T> instanceIdentifier = (InstanceIdentifier<T>) FederationPluginUtils
+                .getSubtreeInstanceIdentifier(listenerKey);
+        LogicalDatastoreType datastoreType = FederationPluginUtils.getListenerDatastoreType(listenerKey);
+        EntityFederationMessage<T> msg = createMsgWithRetriesMechanism(dataObject, modificationType, instanceIdentifier,
+                datastoreType, 2);
+        return msg;
+    }
+
+    /**
+     * This attempts to workaround
+     * https://bugs.opendaylight.org/show_bug.cgi?id=7420.
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked", "checkstyle:emptyblock" })
+    private <T extends DataObject, S extends DataObject> EntityFederationMessage<T> createMsgWithRetriesMechanism(
+            T dataObject, ModificationType modificationType, InstanceIdentifier<T> instanceIdentifier,
+            LogicalDatastoreType datastoreType, int remainingRetries) {
+        try {
+            EntityFederationMessage msg = new EntityFederationMessage(datastoreType.toString(),
+                    modificationType.toString(), null, queueName, instanceIdentifier, dataObject);
+            return msg;
+        } catch (UncheckedExecutionException t) {
+            if (remainingRetries > 0) {
+
+                logger.warn("Create EntityFederationMessage failed because of frozen class. retrying...", t);
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                }
+                createMsgWithRetriesMechanism(dataObject, modificationType, instanceIdentifier, datastoreType,
+                        --remainingRetries);
+            }
+        }
+
+        logger.error("Failed to create EntityFederationMessage due to frozen class. aborting creation");
+        return null;
+    }
+
+    @Override
+    public synchronized CompletableFuture<Void> abort() {
+        aborted = true;
+        return CompletableFuture.completedFuture(null);
+    }
+
+    @SuppressWarnings("rawtypes")
+    private <T extends DataObject> Collection<DataTreeModification<T>> createModifications(String listenerKey,
+            Optional existingData) {
+        if (existingData.isPresent()) {
+            return FederationPluginUtils.createModifications(listenerKey, (DataObject) existingData.get());
+        }
+
+        FederationPluginCounters.egress_no_existing_data.inc();
+        return Collections.emptyList();
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginFactory.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginFactory.java
new file mode 100644 (file)
index 0000000..8033f48
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin;
+
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.federation.plugin.spi.IFederationPluginEgress;
+import org.opendaylight.federation.plugin.spi.IPluginFactory;
+import org.opendaylight.federation.service.api.IFederationProducerMgr;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationPluginFactory implements IPluginFactory {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationPluginFactory.class);
+
+    private final IFederationProducerMgr producerMgr;
+
+    @Inject
+    public FederationPluginFactory(final IFederationProducerMgr producerMgr) {
+        this.producerMgr = producerMgr;
+    }
+
+    @PostConstruct
+    public void init() {
+        LOG.info("init");
+        producerMgr.attachPluginFactory(FederationPluginConstants.PLUGIN_TYPE, this);
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Override
+    public IFederationPluginEgress createEgressPlugin(Object payload, String queueName, String contextId) {
+        if (payload instanceof List) {
+            List payloadList = (List) payload;
+            for (Object pair : payloadList) {
+                if (!(pair instanceof FederatedNetworkPair)) {
+                    throw new IllegalArgumentException(
+                            "payload expected to be List<FederatedNetworkPair> but was something else: "
+                                    + pair.getClass().getName());
+                }
+            }
+            return new FederationPluginEgress(producerMgr, (List<FederatedNetworkPair>) payload, queueName, contextId);
+        } else {
+            throw new IllegalArgumentException("payload expected to be List<FederatedNetworkPair>"
+                    + " but was something else: " + payload.getClass().getName());
+        }
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginIngress.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginIngress.java
new file mode 100644 (file)
index 0000000..e3d73e0
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2016 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin;
+
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.federation.plugin.spi.IFederationPluginIngress;
+import org.opendaylight.federation.service.api.federationutil.FederationUtils;
+import org.opendaylight.federation.service.common.api.EntityFederationMessage;
+import org.opendaylight.netvirt.federation.plugin.filters.FilterResult;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federation.generations.RemoteSiteGenerationInfo;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+
+
+public class FederationPluginIngress implements IFederationPluginIngress {
+
+    private static final int MAX_TRANSACTION_SUBMIT_RETRIES = 2;
+
+    private enum State {
+        IDLE, COLLECTING;
+    }
+
+    private final Logger logger;
+    private final IFederationSubscriptionMgr subscriptionMgr;
+    private final DataBroker dataBroker;
+    private final FederatedMappings federatedMappings;
+    private volatile State state = State.IDLE;
+    private volatile boolean aborted = false;
+
+    private final Map<String, Collection<? extends DataObject>> fullSyncModifications = Maps.newConcurrentMap();
+    private final String remoteIp;
+
+    static {
+        FederationPluginUtils.initYangModules();
+    }
+
+    public FederationPluginIngress(final IFederationSubscriptionMgr subscriptionMgr, final DataBroker dataBroker,
+            String remoteId, List<FederatedNetworkPair> pairs) {
+        this.subscriptionMgr = subscriptionMgr;
+        this.dataBroker = dataBroker;
+        this.remoteIp = remoteId;
+        this.federatedMappings = new FederatedMappings(pairs);
+        logger = FederationUtils.createLogger(remoteIp, FederationPluginIngress.class);
+        logger.info("Created new NetvirtPluginIngress instance for remoteIp {}", remoteId);
+    }
+
+    @Override
+    public synchronized void beginFullSync() {
+        FederationPluginCounters.ingress_begin_tx.inc();
+        logger.info("Changing state to COLLECTING for remoteIP {}", remoteIp);
+        state = State.COLLECTING;
+        fullSyncModifications.clear();
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public synchronized void endFullSync() {
+        if (aborted) {
+            FederationPluginCounters.ingress_full_sync_aborted.inc();
+            return;
+        }
+
+        int generationNumber = 1;
+        RemoteSiteGenerationInfo currentGenerationNumber =
+                FederationPluginUtils.getGenerationInfoForRemoteSite(dataBroker, remoteIp);
+        if (currentGenerationNumber != null) {
+            generationNumber = currentGenerationNumber.getGenerationNumber() + 1;
+        }
+        FederationPluginUtils.updateGenerationInfo(dataBroker, remoteIp, generationNumber);
+
+        FederationPluginCounters.ingress_end_tx.inc();
+        try {
+            processFullSyncModifications(generationNumber);
+            logger.info("Changing state to IDLE for remoteIP {}", remoteIp);
+            state = State.IDLE;
+        } catch (Throwable t) {
+            logger.error("Deciding to call Full Sync again because failed in processing pending modifications", t);
+            subscriptionMgr.resubscribe(remoteIp);
+        }
+    }
+
+    @Override
+    public void fullSyncFailed() {
+        FederationPluginCounters.ingress_full_sync_failed.inc();
+        logger.error("Full sync failed");
+        state = State.IDLE;
+    }
+
+    @Override
+    public synchronized CompletableFuture<Void> abort() {
+        logger.info("Abort Netvirt ingress plugin for remoteIp {}", remoteIp);
+        aborted = true;
+        return CompletableFuture.completedFuture(null);
+    }
+
+    @Override
+    @SuppressWarnings({ "unchecked", "rawtypes", "checkstyle:IllegalCatch" })
+    public synchronized void consumeMsg(EntityFederationMessage msg) {
+        if (aborted) {
+            FederationPluginCounters.ingress_consume_msg_aborted.inc();
+            return;
+        }
+
+        FederationPluginCounters.ingress_consume_msg.inc();
+        LogicalDatastoreType datastoreType;
+        try {
+            datastoreType = LogicalDatastoreType.valueOf(msg.getDataStoreType());
+        } catch (IllegalArgumentException e) {
+            logger.error("Failed to get datastore type for {}", msg.getDataStoreType());
+            return;
+        }
+
+        String listenerKey = FederationPluginUtils.getClassListener(msg.getInputClassType(), datastoreType);
+        if (listenerKey == null) {
+            logger.error("Failed to get listener key for {}", msg.getInputClassType());
+            return;
+        }
+
+        ModificationType modificationType;
+        try {
+            modificationType = ModificationType.valueOf(msg.getModificationType());
+        } catch (IllegalArgumentException e) {
+            logger.error("Invalid modification type {}", msg.getModificationType());
+            return;
+        }
+
+        DataObject dataObject = msg.getInput();
+        if (dataObject == null) {
+            logger.error("Failed to create DataObject from msg {}", msg);
+            return;
+        }
+
+        if (State.COLLECTING.equals(state)) {
+            addFullSyncModification(listenerKey, dataObject, modificationType);
+        } else {
+            try {
+                RemoteSiteGenerationInfo currentGenerationNumber =
+                        FederationPluginUtils.getGenerationInfoForRemoteSite(dataBroker, remoteIp);
+                if (currentGenerationNumber != null && currentGenerationNumber.getGenerationNumber() != null) {
+                    processModification(listenerKey, dataObject, modificationType,
+                            currentGenerationNumber.getGenerationNumber());
+                } else {
+                    logger.error("Will call Full Sync again because there is no generation number set");
+                    subscriptionMgr.resubscribe(remoteIp);
+                }
+            } catch (FederationCorruptedStateException e) {
+                logger.error("Deciding to call Full Sync again because transactions failed too many times");
+                subscriptionMgr.resubscribe(remoteIp);
+            } catch (Throwable t) {
+                logger.error("Failed to process modification on listener key {}", listenerKey, t);
+            }
+        }
+    }
+
+    @Override
+    public void resubscribe() {
+        subscriptionMgr.resubscribe(remoteIp);
+    }
+
+    @Override
+    public String getPluginType() {
+        return FederationPluginConstants.PLUGIN_TYPE;
+    }
+
+    public synchronized void cleanShadowData() {
+        logger.info("Removing all shadow entities for Netvirt ingress plugin for remoteIp {}", remoteIp);
+        FederationPluginCleaner.removeOldGenerationFederatedEntities(dataBroker, Integer.MAX_VALUE, remoteIp);
+        FederationPluginUtils.deleteGenerationInfo(dataBroker, remoteIp);
+    }
+
+    void subnetVpnAssociationUpdated(String subnetId, String vpnId) {
+        FederationPluginCounters.ingress_subnet_vpn_association_changed.inc();
+        if (aborted) {
+            FederationPluginCounters.ingress_subnet_vpn_association_aborted.inc();
+            return;
+        }
+
+        if (federatedMappings.containsConsumerSubnetId(subnetId)) {
+            FederationPluginCounters.ingress_federated_subnet_vpn_association_changed.inc();
+            logger.info("Deciding to call Full Sync on subnet <-> vpn mapping change for subnet-id {} vpn-id {}",
+                    subnetId, vpnId);
+            resubscribe();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private synchronized <T extends DataObject> void addFullSyncModification(String listenerKey, T modification,
+            ModificationType modificationType) {
+        Collection<T> listenerModifications = (Collection<T>) fullSyncModifications.get(listenerKey);
+        if (listenerModifications == null) {
+            listenerModifications = new ArrayList<>();
+            fullSyncModifications.put(listenerKey, listenerModifications);
+        }
+
+        FederationPluginCounters.ingress_full_sync_modification.inc();
+        logger.trace("Add modification type {} listener {} data {}", modificationType, listenerKey, modification);
+        listenerModifications.add(modification);
+    }
+
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    private void processFullSyncModifications(int generationNumber) throws FederationCorruptedStateException {
+        for (String listenerKey : FederationPluginUtils.getOrderedListenerKeys()) {
+            Collection<? extends DataObject> listenerModifications = fullSyncModifications.get(listenerKey);
+            if (listenerModifications != null) {
+                try {
+                    logger.debug("Start processing full sync for listener", listenerKey);
+                    processModifications(listenerKey, listenerModifications, ModificationType.WRITE, generationNumber);
+                } catch (Exception e) {
+                    logger.error("Failed to process full sync for listener {}", listenerKey, e);
+                    throw e;
+                }
+            }
+        }
+
+        logger.info("Full sync process finished - generation number {} and remoteIp {}", generationNumber, remoteIp);
+        FederationPluginCleaner.removeOldGenerationFederatedEntities(dataBroker, generationNumber, remoteIp);
+    }
+
+    private <T extends DataObject> void processModifications(String listenerKey,
+            Collection<? extends DataObject> modifications, ModificationType modificationType, int generationNumber)
+            throws FederationCorruptedStateException {
+        attemptProcessModifications(listenerKey, modifications, modificationType, MAX_TRANSACTION_SUBMIT_RETRIES,
+                generationNumber);
+    }
+
+    private void attemptProcessModifications(String listenerKey, Collection<? extends DataObject> modifications,
+            ModificationType modificationType, int remainingRetries, int generationNumber)
+            throws FederationCorruptedStateException {
+        WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+        for (DataObject modification : modifications) {
+            processModification(listenerKey, modification, modificationType, tx, generationNumber);
+        }
+
+        try {
+            tx.submit().checkedGet();
+        } catch (TransactionCommitFailedException e) {
+            if (remainingRetries > 0) {
+                logger.error("Process modification failed, retrying.");
+                attemptProcessModifications(listenerKey, modifications, modificationType, --remainingRetries,
+                        generationNumber);
+            } else {
+                throw new FederationCorruptedStateException("Failed to commit modification for listener " + listenerKey,
+                        e);
+            }
+        }
+    }
+
+    private <T extends DataObject, S extends DataObject> void processModification(String listenerKey, S modification,
+            ModificationType modificationType, int generationNumber) throws FederationCorruptedStateException {
+        processModification(listenerKey, modification, modificationType, null, generationNumber);
+    }
+
+    private <T extends DataObject, S extends DataObject> void processModification(String listenerKey, S modification,
+            ModificationType modificationType, WriteTransaction tx, int generationNumber)
+            throws FederationCorruptedStateException {
+        FederationPluginCounters.ingress_process_modification.inc();
+        LogicalDatastoreType datastoreType = FederationPluginUtils.getListenerDatastoreType(listenerKey);
+        if (datastoreType == null) {
+            logger.error("Failed to get datastore type for {}", listenerKey);
+            return;
+        }
+        if (!applyFilter(listenerKey, modification, modificationType)) {
+            logger.trace("listener {} {} filtered out", listenerKey, modification);
+            return;
+        }
+
+        Pair<InstanceIdentifier<T>, T> transformedModification = FederationPluginUtils
+                .applyIngressTransformation(listenerKey, modification, modificationType, generationNumber, remoteIp);
+        if (transformedModification == null) {
+            logger.error("Failed to apply ingress transformation for {} {}", listenerKey, modification);
+            return;
+        }
+        if (ModificationType.DELETE.equals(modificationType)) {
+            logger.trace("Delete modification listener {} identifier {}", listenerKey,
+                    transformedModification.getKey());
+            deleteModification(datastoreType, transformedModification.getKey(), MAX_TRANSACTION_SUBMIT_RETRIES);
+            return;
+        }
+
+        logger.trace("Write modification type {} listener {} data {}", modificationType, listenerKey,
+                transformedModification);
+        if (tx == null) {
+            writeModification(datastoreType, transformedModification.getKey(), transformedModification.getValue(),
+                    MAX_TRANSACTION_SUBMIT_RETRIES);
+        } else {
+            writeModification(listenerKey, datastoreType, transformedModification.getKey(),
+                    transformedModification.getValue(), tx);
+        }
+    }
+
+    private <R extends DataObject> boolean applyFilter(String listenerKey, R dataObject,
+            ModificationType modificationType) {
+        FilterResult filterResult = FederationPluginUtils.applyIngressFilter(listenerKey, dataObject);
+        if (filterResult == null) {
+            logger.warn("Failed to get FilterResult for {} {}", listenerKey, dataObject);
+            return false;
+        }
+
+        logger.trace("{} filter result {}", listenerKey, filterResult);
+        switch (filterResult) {
+            case DENY:
+                FederationPluginCounters.ingress_filter_result_deny.inc();
+                return false;
+            case ACCEPT:
+                FederationPluginCounters.ingress_filter_result_accept.inc();
+                return true;
+            case QUEUE:
+                FederationPluginCounters.ingress_filter_result_queue.inc();
+                logger.error("Ingress queue not supported");
+                return false;
+            default:
+                break;
+        }
+
+        return false;
+    }
+
+    // This is a workaround for bug https://bugs.opendaylight.org/show_bug.cgi?id=7420
+    @SuppressWarnings("checkstyle:emptyblock")
+    private <T extends DataObject> void retryingMerge(LogicalDatastoreType datastoreType,
+            InstanceIdentifier<T> instanceIdentifier, T dataObject, WriteTransaction tx, int remainingRetries) {
+        try {
+            tx.merge(datastoreType, instanceIdentifier, dataObject);
+        } catch (UncheckedExecutionException t) {
+            if (remainingRetries > 0) {
+                logger.warn("Merge failed due to frozen class bug, sleeping and retrying", t);
+                try {
+                    Thread.sleep(1500);
+                } catch (InterruptedException e) {
+                }
+                retryingMerge(datastoreType, instanceIdentifier, dataObject, tx, --remainingRetries);
+            }
+        }
+    }
+
+    private <T extends DataObject> void writeModification(LogicalDatastoreType datastoreType,
+            InstanceIdentifier<T> instanceIdentifier, T dataObject, int remainingRetries)
+            throws FederationCorruptedStateException {
+        FederationPluginCounters.ingress_write_modification.inc();
+        WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+        retryingMerge(datastoreType, instanceIdentifier, dataObject, tx, 1);
+
+        try {
+            tx.submit().checkedGet();
+        } catch (TransactionCommitFailedException e) {
+            if (remainingRetries > 0) {
+                writeModification(datastoreType, instanceIdentifier, dataObject, --remainingRetries);
+            } else {
+                throw new FederationCorruptedStateException(
+                        "Failed to write modification for " + instanceIdentifier.toString(), e);
+            }
+        }
+    }
+
+    private <T extends DataObject> void writeModification(String listenerKey, LogicalDatastoreType datastoreType,
+            InstanceIdentifier<T> instanceIdentifier, T dataObject, WriteTransaction tx) {
+        FederationPluginCounters.ingress_add_to_tx_modification.inc();
+        retryingMerge(datastoreType, instanceIdentifier, dataObject, tx, 1);
+    }
+
+    private <T extends DataObject> void deleteModification(LogicalDatastoreType datastoreType,
+            InstanceIdentifier<T> instanceIdentifier, int remainingRetries) throws FederationCorruptedStateException {
+        FederationPluginCounters.ingress_delete_modification.inc();
+        WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
+        tx.delete(datastoreType, instanceIdentifier);
+
+        try {
+            tx.submit().checkedGet();
+        } catch (TransactionCommitFailedException e) {
+            if (remainingRetries > 0) {
+                deleteModification(datastoreType, instanceIdentifier, --remainingRetries);
+            } else {
+                throw new FederationCorruptedStateException(
+                        "Failed to delete modification for " + instanceIdentifier.toString(), e);
+            }
+        }
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginMgr.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginMgr.java
new file mode 100644 (file)
index 0000000..9434736
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.federation.service.api.IFederationConsumerMgr;
+import org.opendaylight.federation.service.api.federationutil.FederationConstants;
+import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
+import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
+import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
+import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.FederatedNetworks;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.MgrContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.RoutedContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federated.nets.SiteNetwork;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federated.networks.FederatedNetwork;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federated.networks.FederatedNetworkBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federated.networks.FederatedNetworkKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.routed.container.RouteKeyItem;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.routed.container.RouteKeyItemKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.routed.rpc.rev170219.FederationPluginRoutedRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.routed.rpc.rev170219.UpdateFederatedNetworksInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.routed.rpc.rev170219.update.federated.networks.input.FederatedNetworksIn;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+@Singleton
+public class FederationPluginMgr
+        implements IFederationSubscriptionMgr, FederationPluginRoutedRpcService, ClusterSingletonService {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationPluginMgr.class);
+
+    private final IFederationConsumerMgr consumerMgr;
+    private final DataBroker db;
+
+    private static final ServiceGroupIdentifier IDENT = ServiceGroupIdentifier
+            .create(FederationConstants.CLUSTERING_SERVICE_ID);
+
+    private final HashMap<String, FederationPluginIngress> ingressPlugins = new HashMap<>();
+    private final RpcProviderRegistry rpcRegistry;
+    private final ClusterSingletonServiceProvider clusterSingletonServiceProvider;
+    private RoutedRpcRegistration<FederationPluginRoutedRpcService> routedRpcHandle;
+    private ClusterSingletonServiceRegistration clusterRegistrationHandle;
+    private volatile boolean isLeader = false;
+
+    @Inject
+    public FederationPluginMgr(final DataBroker dataBroker, final RpcProviderRegistry rpcReg,
+            final IFederationConsumerMgr consumerMgr,
+            final ClusterSingletonServiceProvider clusterSingletonServiceProvider) {
+        this.db = dataBroker;
+        this.consumerMgr = consumerMgr;
+        this.clusterSingletonServiceProvider = clusterSingletonServiceProvider;
+        this.rpcRegistry = rpcReg;
+    }
+
+    @PostConstruct
+    public void init() {
+        LOG.info("init");
+        clusterRegistrationHandle = clusterSingletonServiceProvider.registerClusterSingletonService(this);
+    }
+
+    @PreDestroy
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void close() {
+        LOG.info("close");
+        if (clusterRegistrationHandle != null) {
+            try {
+                clusterRegistrationHandle.close();
+            } catch (Exception e) {
+                LOG.error("Couldn't unregister from cluster singleton service", e);
+            }
+        }
+    }
+
+    @Override
+    public void resubscribe(String remoteIp) {
+        LOG.info("Resubscribe called for remoteIp {}", remoteIp);
+        subscribeOneIngressPlugin(remoteIp);
+    }
+
+    @Override
+    public synchronized Future<RpcResult<Void>> updateFederatedNetworks(UpdateFederatedNetworksInput input) {
+        if (!isLeader) {
+            return Futures.immediateFuture(RpcResultBuilder.<Void>failed()
+                    .withError(ErrorType.RPC, "updateFederatedNetworks was called on a non-leader service").build());
+        }
+
+        // Write the new config data
+        LOG.info("updateFederatedNetworks input {}", input);
+        Set<String> candidateSitesToRemove = getRemoteSitesToBeRemovedAndCleanState(input);
+        writeNewConfig(input);
+        subscribeIngressPluginsIfNeeded(candidateSitesToRemove, false);
+        return Futures.immediateFuture(RpcResultBuilder.<Void>success().build());
+    }
+
+    public Map<String, FederationPluginIngress> getIngressPlugins() {
+        return ingressPlugins;
+    }
+
+    private FederatedNetwork getFederatedNetFromConfigDs(String netId) {
+        ReadTransaction readTx = db.newReadOnlyTransaction();
+        InstanceIdentifier<FederatedNetwork> netPath = InstanceIdentifier.create(FederatedNetworks.class)
+                .child(FederatedNetwork.class, new FederatedNetworkKey(netId));
+        CheckedFuture<Optional<FederatedNetwork>, ReadFailedException> future = readTx
+                .read(LogicalDatastoreType.CONFIGURATION, netPath);
+
+        Optional<FederatedNetwork> optionalNetInConfig = null;
+
+        try {
+            optionalNetInConfig = future.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.info("new network was found");
+            return null;
+        }
+        if (optionalNetInConfig != null && optionalNetInConfig.isPresent()) {
+            return optionalNetInConfig.get();
+        } else {
+            return null;
+        }
+    }
+
+    private boolean writeNewConfig(UpdateFederatedNetworksInput input) {
+        if (input.getFederatedNetworksIn() == null) {
+            LOG.info("writeNewConfig - no networks in input!");
+            return false;
+        }
+        LOG.debug("writeNewConfig");
+        WriteTransaction putTx = db.newWriteOnlyTransaction();
+        List<FederatedNetworksIn> newFederatedNetworks = input.getFederatedNetworksIn();
+        for (FederatedNetworksIn net : newFederatedNetworks) {
+            FederatedNetwork netInConfig = getFederatedNetFromConfigDs(net.getSelfNetId());
+
+            if (!isEqualFederatednet(netInConfig, net)) {
+                // network updates or new
+                FederatedNetworkBuilder builder = new FederatedNetworkBuilder();
+                builder.setSelfNetId(net.getSelfNetId());
+                builder.setSelfSubnetId(net.getSelfSubnetId());
+                builder.setSelfTenantId(net.getSelfTenantId());
+                builder.setSiteNetwork(net.getSiteNetwork());
+                InstanceIdentifier<FederatedNetwork> path = InstanceIdentifier.create(FederatedNetworks.class)
+                        .child(FederatedNetwork.class, new FederatedNetworkKey(net.getSelfNetId()));
+                FederatedNetwork newNet = builder.build();
+                LOG.info("writeNewConfig add new network {}", newNet);
+                putTx.put(LogicalDatastoreType.CONFIGURATION, path, builder.build());
+            }
+        }
+        CheckedFuture<Void, TransactionCommitFailedException> future1 = putTx.submit();
+        try {
+            future1.checkedGet();
+        } catch (org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException e) {
+            LOG.error("updateFederatedNetworks - Failed to write new configuration " + e.getMessage(), e);
+            return false;
+        }
+        return true;
+    }
+
+    private void deleteFederatedNetFromConfigDs(String netId) {
+        LOG.info("deleteFederatedNetFromConfigDs {}", netId);
+        WriteTransaction deleteTx = db.newWriteOnlyTransaction();
+        InstanceIdentifier<FederatedNetwork> netPath = InstanceIdentifier.create(FederatedNetworks.class)
+                .child(FederatedNetwork.class, new FederatedNetworkKey(netId));
+        deleteTx.delete(LogicalDatastoreType.CONFIGURATION, netPath);
+        CheckedFuture<Void, TransactionCommitFailedException> future1 = deleteTx.submit();
+        try {
+            future1.checkedGet();
+        } catch (org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException e) {
+            LOG.error("deleteFederatedNetFromConfigDs - Failed to delete network " + e.getMessage(), e);
+        }
+    }
+
+    // This function return the list of sites which were updated (networks
+    // removed)
+    private Set<String> getRemoteSitesToBeRemovedAndCleanState(UpdateFederatedNetworksInput input) {
+        Set<String> candidateSitesToRemove = new HashSet<>();
+        ReadOnlyTransaction readTx = db.newReadOnlyTransaction();
+        InstanceIdentifier<FederatedNetworks> existingNetworksPath = InstanceIdentifier.create(FederatedNetworks.class);
+        CheckedFuture<Optional<FederatedNetworks>, ReadFailedException> existingNetworksFuture = readTx
+                .read(LogicalDatastoreType.CONFIGURATION, existingNetworksPath);
+        readTx.close();
+        Optional<FederatedNetworks> existingNetsOptional = null;
+        try {
+            existingNetsOptional = existingNetworksFuture.checkedGet();
+        } catch (ReadFailedException e) {
+            LOG.error("Error while reading existing networks", e);
+            return candidateSitesToRemove;
+        }
+        if (existingNetsOptional.isPresent()) {
+            for (FederatedNetwork existingNet : existingNetsOptional.get().getFederatedNetwork()) {
+                boolean foundExistingNetInNewInput = false;
+                for (FederatedNetworksIn inputNet : input.getFederatedNetworksIn()) {
+                    if (existingNet.getSelfNetId() == inputNet.getSelfNetId()) {
+                        foundExistingNetInNewInput = true;
+                        break;
+                    }
+                }
+                if (!foundExistingNetInNewInput) {
+                    // Add the sites which was updated to the sites that we
+                    // should check for removal
+                    // subscribeIngressPlugins will make the final decision on
+                    // which sites to keep
+                    for (SiteNetwork siteNet : existingNet.getSiteNetwork()) {
+                        candidateSitesToRemove.add(siteNet.getSiteIp());
+                    }
+                    // delete this network from Config
+                    deleteFederatedNetFromConfigDs(existingNet.getSelfNetId());
+                }
+            }
+        }
+        return candidateSitesToRemove;
+    }
+
+    private void subscribeOneIngressPlugin(String remoteIp) {
+        LOG.info("subscribeOneIngressPlugin ");
+        ReadOnlyTransaction tx = db.newReadOnlyTransaction();
+        InstanceIdentifier<FederatedNetworks> path = InstanceIdentifier.create(FederatedNetworks.class);
+        CheckedFuture<Optional<FederatedNetworks>, ReadFailedException> future = tx
+                .read(LogicalDatastoreType.CONFIGURATION, path);
+        Optional<FederatedNetworks> nets = null;
+
+        try {
+            nets = future.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("subscribeOneIngressPlugin Didn't find any federated nets!");
+        }
+        if (nets != null && nets.isPresent()) {
+            RemoteSite site = new RemoteSite(remoteIp);
+            for (FederatedNetwork net : nets.get().getFederatedNetwork()) {
+                for (SiteNetwork siteNet : net.getSiteNetwork()) {
+                    site.pairs.add(
+                            new FederatedNetworkPair(net.getSelfNetId(), siteNet.getSiteNetId(), net.getSelfSubnetId(),
+                                    siteNet.getSiteSubnetId(), net.getSelfTenantId(), siteNet.getSiteTenantId()));
+                }
+            }
+            LOG.info("Aborting ingress plugin for remote ip {}", remoteIp);
+            ingressPlugins.get(remoteIp).abort();
+            createNewIngressPlugin(site.remoteIp, site.pairs, false);
+        } else {
+            LOG.error("subscribeOneIngressPlugin Didn't find any federated nets!");
+        }
+    }
+
+    private void subscribeIngressPluginsIfNeeded(Set<String> candidateSitesToRemove, boolean fromRecovery) {
+        // This function receives the list of sites which were updated (networks
+        // removed)
+        // if these sites contain no new network we will remove their ingress
+        // plugin
+        LOG.debug("subscribeIngressPlugins ");
+        ReadOnlyTransaction tx = db.newReadOnlyTransaction();
+        InstanceIdentifier<FederatedNetworks> path = InstanceIdentifier.create(FederatedNetworks.class);
+        CheckedFuture<Optional<FederatedNetworks>, ReadFailedException> future = tx
+                .read(LogicalDatastoreType.CONFIGURATION, path);
+        Optional<FederatedNetworks> nets = null;
+
+        try {
+            nets = future.get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("subscribeIngressPlugins Exception Didn't find any federated nets!");
+        }
+        if (nets != null && nets.isPresent()) {
+            HashMap<String, RemoteSite> sites = new HashMap<>();
+            for (FederatedNetwork net : nets.get().getFederatedNetwork()) {
+                for (SiteNetwork siteNet : net.getSiteNetwork()) {
+                    String siteIp = siteNet.getSiteIp();
+                    if (!sites.containsKey(siteIp)) {
+                        sites.put(siteIp, new RemoteSite(siteIp));
+                    }
+                    RemoteSite site = sites.get(siteNet.getSiteIp());
+                    site.pairs.add(
+                            new FederatedNetworkPair(net.getSelfNetId(), siteNet.getSiteNetId(), net.getSelfSubnetId(),
+                                    siteNet.getSiteSubnetId(), net.getSelfTenantId(), siteNet.getSiteTenantId()));
+                    sites.put(siteIp, site);
+                }
+            }
+
+            if (candidateSitesToRemove != null && candidateSitesToRemove.size() > 0) {
+                synchronized (ingressPlugins) {
+                    for (Iterator<String> iterator = ingressPlugins.keySet().iterator(); iterator.hasNext();) {
+                        String valueToCheck = iterator.next();
+                        if (candidateSitesToRemove.contains(valueToCheck) && !sites.containsKey(valueToCheck)) {
+                            this.removeIngressPlugin(valueToCheck);
+                            iterator.remove();
+                        }
+                    }
+                }
+            }
+            for (RemoteSite site : sites.values()) {
+                createNewIngressPlugin(site.remoteIp, site.pairs, fromRecovery);
+            }
+        }
+    }
+
+    private boolean isEqualFederatednet(FederatedNetwork configNet, FederatedNetworksIn inputNet) {
+
+        if (configNet == null && inputNet != null) {
+            return false;
+        }
+        if (configNet != null && inputNet == null) {
+            return false;
+        }
+        if (configNet.getSelfNetId() != inputNet.getSelfNetId()) {
+            return false;
+        }
+        if (configNet.getSelfSubnetId() != inputNet.getSelfSubnetId()) {
+            return false;
+        }
+        if (configNet.getSubnetIp() != inputNet.getSubnetIp()) {
+            return false;
+        }
+        if (configNet.getSiteNetwork().size() != inputNet.getSiteNetwork().size()) {
+            return false;
+        }
+        List<SiteNetwork> inSiteNets = inputNet.getSiteNetwork();
+        List<SiteNetwork> configSiteNets = configNet.getSiteNetwork();
+        if (!inSiteNets.containsAll(configSiteNets)) {
+            return false;
+        }
+        if (!configSiteNets.containsAll(inSiteNets)) {
+            return false;
+        }
+        return true;
+    }
+
+    private void createNewIngressPlugin(String remoteIp, List<FederatedNetworkPair> pairs, boolean fromRecovery) {
+        synchronized (ingressPlugins) {
+            LOG.info("createNewIngressPlugin remoteIp {} pairs {}", remoteIp, pairs);
+            FederationPluginIngress newIngress = new FederationPluginIngress(this, db, remoteIp, pairs);
+            FederationPluginIngress prevPlugin = ingressPlugins.put(remoteIp, newIngress);
+            if (prevPlugin != null) {
+                prevPlugin.abort();
+            }
+            consumerMgr.subscribe(remoteIp, pairs, newIngress, fromRecovery);
+        }
+    }
+
+    private void removeIngressPlugin(String remoteIp) {
+        LOG.info("removeIngressPlugin removing subscription {}", remoteIp);
+        ingressPlugins.get(remoteIp).abort();
+        ingressPlugins.get(remoteIp).cleanShadowData();
+        consumerMgr.unsubscribe(remoteIp);
+    }
+
+    private class RemoteSite {
+        public String remoteIp;
+        public List<FederatedNetworkPair> pairs = new ArrayList<>();
+
+        RemoteSite(String remoteIp) {
+            this.remoteIp = remoteIp;
+        }
+    }
+
+    @Override
+    public ServiceGroupIdentifier getIdentifier() {
+        return IDENT;
+    }
+
+    @Override
+    public ListenableFuture<Void> closeServiceInstance() {
+        isLeader = false;
+        LOG.info("Lost federation leadership, unregistering routed RPCs.");
+        if (routedRpcHandle != null) {
+            routedRpcHandle.close();
+        }
+        return Futures.immediateFuture(null);
+    }
+
+    @Override
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void instantiateServiceInstance() {
+        try {
+            isLeader = true;
+            LOG.info("Gained federation leadership, registering routed RPCs.");
+            routedRpcHandle = rpcRegistry.addRoutedRpcImplementation(FederationPluginRoutedRpcService.class, this);
+            InstanceIdentifier<RouteKeyItem> path = InstanceIdentifier.create(RoutedContainer.class)
+                    .child(RouteKeyItem.class, new RouteKeyItemKey(FederationPluginConstants.RPC_ROUTE_KEY));
+            routedRpcHandle.registerPath(MgrContext.class, path);
+            subscribeIngressPluginsIfNeeded(null, true);
+        } catch (Throwable t) {
+            LOG.error("Error while doing leader init logic", t);
+        }
+
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginRpcServiceImpl.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginRpcServiceImpl.java
new file mode 100644 (file)
index 0000000..554d5b2
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin;
+
+import com.google.common.util.concurrent.Futures;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.RoutedContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.routed.container.RouteKeyItem;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.routed.container.RouteKeyItemKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.routed.rpc.rev170219.FederationPluginRoutedRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.routed.rpc.rev170219.UpdateFederatedNetworksInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rpc.rev170219.FederationPluginRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rpc.rev170219.UpdateFederatedNetworksInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rpc.rev170219.update.federated.networks.input.FederatedNetworksIn;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationPluginRpcServiceImpl implements FederationPluginRpcService {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationPluginRpcServiceImpl.class);
+
+    private final RpcProviderRegistry rpcRegistry;
+
+    @Inject
+    public FederationPluginRpcServiceImpl(final RpcProviderRegistry rpcRegistry) {
+        this.rpcRegistry = rpcRegistry;
+    }
+
+    @PostConstruct
+    public void init() {
+        LOG.info("init");
+        rpcRegistry.addRpcImplementation(FederationPluginRpcService.class, this);
+    }
+
+    @Override
+    public Future<RpcResult<Void>> updateFederatedNetworks(UpdateFederatedNetworksInput input) {
+        FederationPluginRoutedRpcService routedRpcService = rpcRegistry
+                .getRpcService(FederationPluginRoutedRpcService.class);
+        if (routedRpcService == null) {
+            return Futures.immediateFuture(RpcResultBuilder.<Void>failed()
+                    .withError(ErrorType.RPC, "Failed to get routed RPC service for federation plugin").build());
+        }
+
+        List<FederatedNetworksIn> federatedNetworks = input.getFederatedNetworksIn();
+        UpdateFederatedNetworksInputBuilder builder = new UpdateFederatedNetworksInputBuilder()
+                .setFederatedNetworksIn(convertFederatedNetworks(federatedNetworks))
+                .setRouteKeyItem(buildtRouteKeyInstanceIdentifier());
+
+        return routedRpcService.updateFederatedNetworks(builder.build());
+    }
+
+    private static List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.routed.rpc.rev170219
+        .update.federated.networks.input.FederatedNetworksIn> convertFederatedNetworks(
+            List<FederatedNetworksIn> federatedNetworks) {
+        if (federatedNetworks == null) {
+            return null;
+        }
+
+        List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.routed.rpc.rev170219
+            .update.federated.networks.input.FederatedNetworksIn> routedFederatedNetworks = new ArrayList<>();
+        for (FederatedNetworksIn federatedNetwork : federatedNetworks) {
+            routedFederatedNetworks
+                    .add(new org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.routed.rpc
+                            .rev170219.update.federated.networks.input.FederatedNetworksInBuilder(
+                            federatedNetwork).build());
+        }
+
+        return routedFederatedNetworks;
+    }
+
+    private static InstanceIdentifier<RouteKeyItem> buildtRouteKeyInstanceIdentifier() {
+        return InstanceIdentifier.create(RoutedContainer.class).child(RouteKeyItem.class,
+                new RouteKeyItemKey(FederationPluginConstants.RPC_ROUTE_KEY));
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginUtils.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/FederationPluginUtils.java
new file mode 100644 (file)
index 0000000..961371a
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.federation.service.api.message.BindingAwareJsonConverter;
+import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
+import org.opendaylight.genius.itm.globals.ITMConstants;
+import org.opendaylight.netvirt.federation.plugin.creators.FederationPluginCreatorRegistry;
+import org.opendaylight.netvirt.federation.plugin.creators.FederationPluginModificationCreator;
+import org.opendaylight.netvirt.federation.plugin.filters.FederationPluginFilter;
+import org.opendaylight.netvirt.federation.plugin.filters.FederationPluginFilterRegistry;
+import org.opendaylight.netvirt.federation.plugin.filters.FilterResult;
+import org.opendaylight.netvirt.federation.plugin.identifiers.FederationPluginIdentifier;
+import org.opendaylight.netvirt.federation.plugin.identifiers.FederationPluginIdentifierRegistry;
+import org.opendaylight.netvirt.federation.plugin.transformers.FederationPluginTransformer;
+import org.opendaylight.netvirt.federation.plugin.transformers.FederationPluginTransformerRegistry;
+import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfacesBuilder;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableStatisticsGatheringStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableStatisticsGatheringStatusBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfExternal;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfExternalBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlanBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfStackedVlan;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfStackedVlanBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupFeaturesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterFeaturesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAcl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.InterfaceAclBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInstance;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInstanceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeLeafTag;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeLeafTagName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.etree.rev160614.EtreeLeafTagNameBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstances;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInstancesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfacesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanTagNameMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanTagNameMapBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstanceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.tag.name.map.ElanTagName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.tag.name.map.ElanTagNameBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.tag.name.map.ElanTagNameKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.FederationGenerations;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federation.generations.RemoteSiteGenerationInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federation.generations.RemoteSiteGenerationInfoBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federation.generations.RemoteSiteGenerationInfoKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.ElanShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.ElanShadowPropertiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.IfShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.IfShadowPropertiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.InventoryNodeShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.InventoryNodeShadowPropertiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.TopologyNodeShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.TopologyNodeShadowPropertiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.VpnShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.VpnShadowPropertiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.OpState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.OpStateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FederationPluginUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationPluginUtils.class);
+
+    private static final List<String> SORTED_LISTENER_KEYS =
+
+            ImmutableList.of(FederationPluginConstants.TOPOLOGY_NODE_CONFIG_KEY, //
+                    FederationPluginConstants.TOPOLOGY_NODE_OPER_KEY, //
+                    FederationPluginConstants.INVENTORY_NODE_CONFIG_KEY, //
+                    FederationPluginConstants.INVENTORY_NODE_OPER_KEY, //
+                    FederationPluginConstants.IETF_INTERFACE_KEY, //
+                    FederationPluginConstants.ELAN_INTERFACE_KEY, //
+                    FederationPluginConstants.VPN_INTERFACE_KEY);
+
+
+    private static volatile boolean yangModulesInitialized = false;
+
+    private FederationPluginUtils() {
+
+    }
+
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public static synchronized void initYangModules() {
+        if (yangModulesInitialized) {
+            return;
+        }
+
+        ArrayList<YangModuleInfo> moduleInfos = new ArrayList<>();
+        try {
+            moduleInfos.add(BindingReflections.getModuleInfo(Topology.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(OvsdbNodeAugmentation.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(Nodes.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(FlowCapableNodeConnector.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(FlowCapableNodeConnectorStatisticsData.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(NodeMeterFeatures.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(NodeGroupFeatures.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(Interfaces.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(IfExternal.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(InterfaceAcl.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(ParentRefs.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(IfL2vlan.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(IfStackedVlan.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(ElanInterfaces.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(EtreeInterface.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(EtreeInstance.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(EtreeLeafTagName.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(VpnInterfaces.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(Adjacencies.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(SubnetRoute.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(OpState.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(TopologyNodeShadowProperties.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(ElanShadowProperties.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(IfShadowProperties.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(InventoryNodeShadowProperties.class));
+            moduleInfos.add(BindingReflections.getModuleInfo(VpnShadowProperties.class));
+            BindingAwareJsonConverter.init(moduleInfos);
+            bug7420Workaround(5);
+            yangModulesInitialized = true;
+            LOG.info("Finished initializing BindingReflections modules");
+        } catch (Exception e) {
+            LOG.error("Failed to initialized MessageSeralizationUtils", e);
+        }
+    }
+
+    public static List<String> getOrderedListenerKeys() {
+        return SORTED_LISTENER_KEYS;
+    }
+
+    public static LogicalDatastoreType getListenerDatastoreType(String listenerKey) {
+        return FederationPluginIdentifierRegistry.getDatastoreType(listenerKey);
+    }
+
+    public static InstanceIdentifier<? extends DataObject> getInstanceIdentifier(String listenerKey) {
+        FederationPluginIdentifier<? extends DataObject, ? extends DataObject, ? extends DataObject>
+            identifierHandler = FederationPluginIdentifierRegistry.getIdentifier(listenerKey);
+        if (identifierHandler == null) {
+            LOG.error("Failed to get identifier for {}", listenerKey);
+            return null;
+        }
+
+        return identifierHandler.getInstanceIdentifier();
+    }
+
+    public static InstanceIdentifier<? extends DataObject> getParentInstanceIdentifier(String listenerKey) {
+        FederationPluginIdentifier<? extends DataObject, ? extends DataObject, ? extends DataObject>
+            identifierHandler = FederationPluginIdentifierRegistry.getIdentifier(listenerKey);
+        if (identifierHandler == null) {
+            LOG.error("Failed to get identifier for {}", listenerKey);
+            return null;
+        }
+
+        return identifierHandler.getParentInstanceIdentifier();
+    }
+
+    public static InstanceIdentifier<? extends DataObject> getSubtreeInstanceIdentifier(String listenerKey) {
+        FederationPluginIdentifier<? extends DataObject, ? extends DataObject, ? extends DataObject>
+            identifierHandler = FederationPluginIdentifierRegistry.getIdentifier(listenerKey);
+        if (identifierHandler == null) {
+            LOG.error("Failed to get identifier for {}", listenerKey);
+            return null;
+        }
+
+        return identifierHandler.getSubtreeInstanceIdentifier();
+    }
+
+    public static <T extends DataObject> String getClassListener(Class<T> clazz, LogicalDatastoreType datastoreType) {
+        return FederationPluginIdentifierRegistry.getListenerKey(datastoreType, clazz);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T extends DataObject, S extends DataObject> FilterResult applyEgressFilter(String listenerKey,
+            T dataObject, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications,
+            DataTreeModification<T> dataTreeModification) {
+        FederationPluginFilter<T, S> filter = (FederationPluginFilter<T, S>) FederationPluginFilterRegistry
+                .getFilter(listenerKey);
+        if (filter == null) {
+            LOG.error("Filter not found for key {}", listenerKey);
+            return FilterResult.DENY;
+        }
+
+        return filter.applyEgressFilter(dataObject, federatedMappings, pendingModifications, dataTreeModification);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T extends DataObject, R extends DataObject> FilterResult applyIngressFilter(String listenerKey,
+            R dataObject) {
+        FederationPluginFilter<T, R> filter = (FederationPluginFilter<T, R>) FederationPluginFilterRegistry
+                .getFilter(listenerKey);
+        if (filter == null) {
+            LOG.error("Filter not found for key {}", listenerKey);
+            return FilterResult.DENY;
+        }
+
+        return filter.applyIngressFilter(listenerKey, dataObject);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T extends DataObject, S extends DataObject> Pair<InstanceIdentifier<T>, T>
+            applyIngressTransformation(String listenerKey, S dataObject, ModificationType modificationType,
+                    int generationNumber, String remoteIp) {
+        FederationPluginTransformer<T, S> transformer =
+                (FederationPluginTransformer<T, S>) FederationPluginTransformerRegistry.getTransformer(listenerKey);
+        if (transformer == null) {
+            LOG.error("Transformer not found for key {}", listenerKey);
+            return null;
+        }
+
+        return transformer.applyIngressTransformation(dataObject, modificationType, generationNumber, remoteIp);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T extends DataObject, S extends DataObject> S applyEgressTransformation(String listenerKey,
+            T dataObject, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications) {
+        FederationPluginTransformer<T, S> transformer =
+                (FederationPluginTransformer<T, S>) FederationPluginTransformerRegistry.getTransformer(listenerKey);
+        if (transformer == null) {
+            LOG.error("Transformer not found for key {} ", listenerKey);
+            return null;
+        }
+
+        return transformer.applyEgressTransformation(dataObject, federatedMappings, pendingModifications);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T extends DataObject, S extends DataObject> Collection<DataTreeModification<T>> createModifications(
+            String listenerKey, S parentDataObject) {
+        FederationPluginModificationCreator<T, S> creator =
+                (FederationPluginModificationCreator<T, S>) FederationPluginCreatorRegistry.getCreator(listenerKey);
+        if (creator == null) {
+            LOG.error("Modification creator not found for key {} ", listenerKey);
+            return null;
+        }
+
+        return creator.createDataTreeModifications(parentDataObject);
+    }
+
+    public static <T extends DataObject> T getDataObjectFromModification(DataTreeModification<T> dataTreeModification) {
+        DataObjectModification<T> dataObjectModification = dataTreeModification.getRootNode();
+        switch (dataObjectModification.getModificationType()) {
+            case WRITE:
+            case SUBTREE_MODIFIED:
+                return dataObjectModification.getDataAfter();
+            case DELETE:
+                return dataObjectModification.getDataBefore();
+            default:
+                break;
+        }
+
+        return null;
+    }
+
+    /**
+     * Discover if interface is a DHCP port. Should be replaced with type
+     * definition in ietf-model
+     *
+     * @param dataBroker
+     *            - the databroker.
+     * @param interfaceName
+     *            - the interface name .
+     * @return if true
+     */
+    public static boolean isDhcpInterface(DataBroker dataBroker, String interfaceName) {
+        Uuid portId;
+
+        try {
+            portId = new Uuid(interfaceName);
+        } catch (IllegalArgumentException e) {
+            return false;
+        }
+
+        InstanceIdentifier<Port> inst = InstanceIdentifier.create(Neutron.class).child(Ports.class).child(Port.class,
+                new PortKey(portId));
+        Port port;
+        try {
+            port = SingleTransactionDataBroker.syncRead(dataBroker, LogicalDatastoreType.CONFIGURATION, inst);
+            return port != null && NeutronConstants.DEVICE_OWNER_DHCP.equals(port.getDeviceOwner());
+        } catch (ReadFailedException e) {
+            LOG.debug("Interface {} is not associated with any neutron port", interfaceName);
+            return false;
+        }
+    }
+
+    public static RemoteSiteGenerationInfo getGenerationInfoForRemoteSite(DataBroker broker, String remoteIp) {
+        InstanceIdentifier<RemoteSiteGenerationInfo> remoteSiteGenerationNumber = InstanceIdentifier
+                .create(FederationGenerations.class)
+                .child(RemoteSiteGenerationInfo.class, new RemoteSiteGenerationInfoKey(remoteIp));
+        try {
+            return SingleTransactionDataBroker.syncRead(broker,
+                    LogicalDatastoreType.CONFIGURATION, remoteSiteGenerationNumber);
+        } catch (ReadFailedException e) {
+            LOG.debug("No generation info found for remote site {}", remoteIp);
+            return null;
+        }
+    }
+
+    public static String getSubnetIdFromVpnInterface(VpnInterface vpnInterface) {
+        Adjacencies adjacencies = vpnInterface.getAugmentation(Adjacencies.class);
+        if (adjacencies == null) {
+            return null;
+        }
+
+        for (Adjacency adjacency : adjacencies.getAdjacency()) {
+            if (adjacency.isPrimaryAdjacency() != null && adjacency.isPrimaryAdjacency()) {
+                Uuid subnetId = adjacency.getSubnetId();
+                return subnetId != null ? subnetId.getValue() : null;
+            }
+        }
+
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T extends DataObject, S extends DataObject> S getAssociatedDataObjectFromPending(String listenerKey,
+            T dataObject, PendingModificationCache<DataTreeModification<?>> pendingModifications) {
+        S associatedObject = null;
+        Map<String, Collection<DataTreeModification<?>>> modifications = pendingModifications.get(dataObject);
+
+        if (modifications != null) {
+            Collection<DataTreeModification<?>> listenerModifications = modifications.get(listenerKey);
+            if (listenerModifications != null && !listenerModifications.isEmpty()) {
+                DataTreeModification<S> modification = (DataTreeModification<S>) listenerModifications.iterator()
+                        .next();
+                associatedObject = modification.getRootNode().getDataAfter();
+                if (associatedObject == null) {
+                    associatedObject = modification.getRootNode().getDataBefore();
+                }
+            }
+        }
+
+        return associatedObject;
+    }
+
+    public static boolean isPortNameFiltered(String portName) {
+        return portName.startsWith(FederationPluginConstants.TUNNEL_PREFIX)
+                || portName.startsWith(ITMConstants.DEFAULT_BRIDGE_NAME);
+    }
+
+    public static boolean updateGenerationInfo(DataBroker broker, String remoteIp, int generationNumber) {
+        if (remoteIp == null) {
+            LOG.error("Cannot write generation number - remote IP is null");
+            return false;
+        }
+
+        LOG.info("Writing generation number {} for remote site {}", remoteIp, generationNumber);
+        WriteTransaction putTx = broker.newWriteOnlyTransaction();
+        RemoteSiteGenerationInfoBuilder builder = new RemoteSiteGenerationInfoBuilder();
+        builder.setRemoteIp(remoteIp);
+        builder.setGenerationNumber(generationNumber);
+        InstanceIdentifier<RemoteSiteGenerationInfo> path = InstanceIdentifier.create(FederationGenerations.class)
+                .child(RemoteSiteGenerationInfo.class, new RemoteSiteGenerationInfoKey(remoteIp));
+        putTx.put(LogicalDatastoreType.CONFIGURATION, path, builder.build());
+        CheckedFuture<Void, TransactionCommitFailedException> future1 = putTx.submit();
+
+        try {
+            future1.checkedGet();
+        } catch (org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException e) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public static void deleteGenerationInfo(DataBroker broker, String remoteIp) {
+        if (remoteIp == null) {
+            LOG.error("Cannot remove generation number - remote IP is null");
+            return;
+        }
+
+        LOG.info("Deleting generation number for remote site {}", remoteIp);
+        WriteTransaction deleteTx = broker.newWriteOnlyTransaction();
+        InstanceIdentifier<RemoteSiteGenerationInfo> path = InstanceIdentifier.create(FederationGenerations.class)
+                .child(RemoteSiteGenerationInfo.class, new RemoteSiteGenerationInfoKey(remoteIp));
+        deleteTx.delete(LogicalDatastoreType.CONFIGURATION, path);
+        deleteTx.submit();
+    }
+
+    // https://bugs.opendaylight.org/show_bug.cgi?id=7420
+    // SubnetRoute.class; allegedly not federated
+    @SuppressWarnings({ "checkstyle:emptyblock", "deprecation" })
+    private static void bug7420Workaround(int moreRetries) {
+
+        if (moreRetries == 0) {
+            return;
+        }
+        try {
+
+            TopologyBuilder topologyBuilder = new TopologyBuilder();
+            topologyBuilder.setKey(new TopologyKey(new TopologyId(new Uri("ovsdb:1"))));
+            String nodeName = "aaa";
+            NodeBuilder nodeBuilder = new NodeBuilder();
+            nodeBuilder.setNodeId(new NodeId(nodeName));
+            nodeBuilder.addAugmentation(TopologyNodeShadowProperties.class,
+                    new TopologyNodeShadowPropertiesBuilder().setShadow(true).build());
+            nodeBuilder.addAugmentation(OvsdbNodeAugmentation.class, new OvsdbNodeAugmentationBuilder().build());
+            topologyBuilder.setNode(Collections.singletonList(nodeBuilder.build()));
+            NetworkTopology networkTopology = new NetworkTopologyBuilder()
+                    .setTopology(Collections.singletonList(topologyBuilder.build())).build();
+            InstanceIdentifier<NetworkTopology> iid = InstanceIdentifier.create(NetworkTopology.class);
+            BindingAwareJsonConverter.jsonStringFromDataObject(iid, networkTopology);
+
+            ElanInterfaceBuilder eeib = new ElanInterfaceBuilder();
+            eeib.setElanInstanceName("aaa");
+            eeib.setKey(new ElanInterfaceKey("vvv"));
+            eeib.addAugmentation(ElanShadowProperties.class, new ElanShadowPropertiesBuilder().setShadow(true).build());
+            ElanInterfacesBuilder eib = new ElanInterfacesBuilder();
+            eib.setElanInterface(Collections.singletonList(eeib.build()));
+            InstanceIdentifier<ElanInterfaces> iid2 = InstanceIdentifier.create(ElanInterfaces.class);
+            BindingAwareJsonConverter.jsonStringFromDataObject(iid2, eib.build());
+
+            InterfaceBuilder interfaceBuilder = new InterfaceBuilder().setKey(new InterfaceKey("sad"));
+            interfaceBuilder.addAugmentation(IfShadowProperties.class,
+                    new IfShadowPropertiesBuilder().setShadow(true).build());
+            interfaceBuilder.addAugmentation(IfL2vlan.class, new IfL2vlanBuilder().build());
+            interfaceBuilder.addAugmentation(IfExternal.class, new IfExternalBuilder().build());
+            interfaceBuilder.addAugmentation(InterfaceAcl.class, new InterfaceAclBuilder().build());
+            interfaceBuilder.addAugmentation(IfStackedVlan.class, new IfStackedVlanBuilder().build());
+            interfaceBuilder.addAugmentation(ParentRefs.class, new ParentRefsBuilder().build());
+
+            Interfaces interfaces = new InterfacesBuilder()
+                    .setInterface(Collections.singletonList(interfaceBuilder.build())).build();
+            InstanceIdentifier<Interfaces> iid4 = InstanceIdentifier.create(Interfaces.class);
+            BindingAwareJsonConverter.jsonStringFromDataObject(iid4, interfaces);
+
+            org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder inventoryNodeBuilder
+                    = new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder().setKey(
+                            new NodeKey(new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId(
+                                    "asd")));
+            inventoryNodeBuilder.addAugmentation(FlowCapableNode.class, new FlowCapableNodeBuilder().build());
+            inventoryNodeBuilder.addAugmentation(NodeMeterFeatures.class, new NodeMeterFeaturesBuilder().build());
+            inventoryNodeBuilder.addAugmentation(FlowCapableStatisticsGatheringStatus.class,
+                    new FlowCapableStatisticsGatheringStatusBuilder().build());
+            inventoryNodeBuilder.addAugmentation(NodeGroupFeatures.class, new NodeGroupFeaturesBuilder().build());
+
+            List<NodeConnector> newNcList = new ArrayList<>();
+            NodeConnectorBuilder ncBuilder = new NodeConnectorBuilder()
+                    .setKey(new NodeConnectorKey(new NodeConnectorId("asd")));
+            ncBuilder.addAugmentation(FlowCapableNodeConnectorStatisticsData.class,
+                    new FlowCapableNodeConnectorStatisticsDataBuilder().build());
+            ncBuilder.addAugmentation(FlowCapableNodeConnector.class, new FlowCapableNodeConnectorBuilder().build());
+            newNcList.add(ncBuilder.build());
+            inventoryNodeBuilder.setNodeConnector(newNcList);
+            inventoryNodeBuilder.addAugmentation(InventoryNodeShadowProperties.class,
+                    new InventoryNodeShadowPropertiesBuilder().setShadow(true).build());
+            Nodes nodes = new NodesBuilder().setNode(Collections.singletonList(inventoryNodeBuilder.build())).build();
+            InstanceIdentifier<Nodes> iid5 = InstanceIdentifier.create(Nodes.class);
+            BindingAwareJsonConverter.jsonStringFromDataObject(iid5, nodes);
+
+            List<Adjacency> federatedAdjacencies = Collections
+                    .singletonList(new AdjacencyBuilder().setKey(new AdjacencyKey("asd")).build());
+            VpnInterfaceBuilder vpnInterfaceBuilder = new VpnInterfaceBuilder().setKey(new VpnInterfaceKey("asd"));
+            vpnInterfaceBuilder.addAugmentation(Adjacencies.class,
+                    new AdjacenciesBuilder().setAdjacency(federatedAdjacencies).build());
+            vpnInterfaceBuilder.addAugmentation(VpnShadowProperties.class,
+                    new VpnShadowPropertiesBuilder().setShadow(true).build());
+            vpnInterfaceBuilder.addAugmentation(OpState.class, new OpStateBuilder().build());
+            VpnInterfaces vpnInterfaces = new VpnInterfacesBuilder()
+                    .setVpnInterface(Collections.singletonList(vpnInterfaceBuilder.build())).build();
+            InstanceIdentifier<VpnInterfaces> iid6 = InstanceIdentifier.create(VpnInterfaces.class);
+            BindingAwareJsonConverter.jsonStringFromDataObject(iid6, vpnInterfaces);
+
+            InstanceIdentifier<ElanInstances> iid7 = InstanceIdentifier.create(ElanInstances.class);
+            ElanInstanceBuilder elanInstanceBuilder = new ElanInstanceBuilder().addAugmentation(EtreeInstance.class,
+                    new EtreeInstanceBuilder().build());
+            ElanInstancesBuilder instancesBuilder = new ElanInstancesBuilder().setElanInstance(
+                    Collections.singletonList(elanInstanceBuilder.setKey(new ElanInstanceKey("asd")).build()));
+            BindingAwareJsonConverter.jsonStringFromDataObject(iid7, instancesBuilder.build());
+
+            InstanceIdentifier<ElanTagNameMap> iid8 = InstanceIdentifier.create(ElanTagNameMap.class);
+            ElanTagName tagName = new ElanTagNameBuilder().setKey(new ElanTagNameKey(123L))
+                    .addAugmentation(EtreeLeafTagName.class,
+                            new EtreeLeafTagNameBuilder().setEtreeLeafTag(new EtreeLeafTag(23L)).build())
+                    .build();
+            ElanTagNameMapBuilder mapBuilder = new ElanTagNameMapBuilder()
+                    .setElanTagName(Collections.singletonList(tagName));
+            BindingAwareJsonConverter.jsonStringFromDataObject(iid8, mapBuilder.build());
+        } catch (UncheckedExecutionException t) {
+            LOG.info("Frozen issue occured in workaround - this is acceptable, retrying it", t);
+            try {
+                Thread.sleep(200);
+            } catch (InterruptedException e) {
+            }
+            bug7420Workaround(--moreRetries);
+        }
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/IEntityDeleteDecision.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/IEntityDeleteDecision.java
new file mode 100644 (file)
index 0000000..bd8687a
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+public interface IEntityDeleteDecision<T extends DataObject> {
+
+    boolean shouldDelete(T entity);
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/IFederationSubscriptionMgr.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/IFederationSubscriptionMgr.java
new file mode 100644 (file)
index 0000000..1ce6b70
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin;
+
+public interface IFederationSubscriptionMgr {
+
+    void resubscribe(String consumerId);
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/PendingModificationCache.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/PendingModificationCache.java
new file mode 100644 (file)
index 0000000..866184b
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.Maps;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+public class PendingModificationCache<M> {
+
+    /**
+     * liberator Identifier -> (listenerKey -> cached entity).
+     */
+    private final Cache<String, Map<String, Collection<M>>> pendingModifications = //
+            CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build();
+
+    public PendingModificationCache() {
+
+    }
+
+    public <T extends DataObject> void add(T dataObject, String listenerKey, M modification) {
+        String identifier = extractLiberatorIdentifier(dataObject);
+        if (identifier == null) {
+            return;
+        }
+
+        synchronized (pendingModifications) {
+            Map<String, Collection<M>> modificationMap = pendingModifications.getIfPresent(identifier);
+            if (modificationMap == null) {
+                modificationMap = Maps.newConcurrentMap();
+                pendingModifications.put(identifier, modificationMap);
+            }
+
+            Collection<M> listenerModifications = modificationMap.get(listenerKey);
+            if (listenerModifications == null) {
+                listenerModifications = new LinkedBlockingQueue<>();
+                modificationMap.put(listenerKey, listenerModifications);
+            }
+
+            listenerModifications.add(modification);
+        }
+    }
+
+    public <T extends DataObject> Map<String, Collection<M>> remove(T dataObject) {
+        String identifier = extractLiberatorIdentifier(dataObject);
+        // TODO to avoid conflict between keys with same value
+        // from different types, should add a postfix by related types
+        if (identifier == null) {
+            return null;
+        }
+
+        Map<String, Collection<M>> modifications = pendingModifications.getIfPresent(identifier);
+        pendingModifications.invalidate(identifier);
+        return modifications;
+    }
+
+    public <T extends DataObject> Map<String, Collection<M>> get(T dataObject) {
+        String identifier = extractLiberatorIdentifier(dataObject);
+        return identifier != null ? pendingModifications.getIfPresent(identifier) : null;
+    }
+
+    public void cleanup() {
+        pendingModifications.cleanUp();
+    }
+
+    private <T extends DataObject> String extractLiberatorIdentifier(T dataObject) {
+        if (dataObject instanceof ElanInterface) {
+            return ((ElanInterface) dataObject).getKey().getName();
+        }
+        if (dataObject instanceof Interface) {
+            return ((Interface) dataObject).getKey().getName();
+        }
+        if (dataObject instanceof VpnInterface) {
+            return ((VpnInterface) dataObject).getKey().getName();
+        }
+
+        return null;
+    }
+
+    public static boolean isLiberatorKey(String listenerKey) {
+        return FederationPluginConstants.ELAN_INTERFACE_KEY.equals(listenerKey)
+                || FederationPluginConstants.VPN_INTERFACE_KEY.equals(listenerKey);
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/SubnetVpnAssociationManager.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/SubnetVpnAssociationManager.java
new file mode 100644 (file)
index 0000000..51e06e2
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Maps;
+
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.genius.datastoreutils.AsyncClusteredDataTreeChangeListenerBase;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class SubnetVpnAssociationManager
+        extends AsyncClusteredDataTreeChangeListenerBase<Subnetmap, SubnetVpnAssociationManager> {
+    private static final Logger LOG = LoggerFactory.getLogger(SubnetVpnAssociationManager.class);
+
+    private final DataBroker dataBroker;
+    private final FederationPluginMgr pluginMgr;
+    private final Map<String, String> subnetVpnMap = Maps.newConcurrentMap();
+
+    @Inject
+    public SubnetVpnAssociationManager(final DataBroker dataBroker, final FederationPluginMgr pluginMgr) {
+        this.dataBroker = dataBroker;
+        this.pluginMgr = pluginMgr;
+    }
+
+    @PostConstruct
+    public void init() {
+        LOG.info("init");
+        registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
+    }
+
+    public String getSubnetVpn(String subnetId) {
+        return subnetVpnMap.get(subnetId);
+    }
+
+    @Override
+    protected InstanceIdentifier<Subnetmap> getWildCardPath() {
+        return InstanceIdentifier.create(Subnetmaps.class).child(Subnetmap.class);
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmap) {
+        String subnetName = subnetmap.getId().getValue();
+        subnetVpnMap.remove(subnetName);
+        LOG.debug("Subnet {} removed", subnetName);
+
+    }
+
+    @Override
+    protected void update(InstanceIdentifier<Subnetmap> identifier, Subnetmap origSubnetmap,
+            Subnetmap updatedSubnetmap) {
+        Uuid subnetId = updatedSubnetmap.getId();
+        Uuid origVpnId = origSubnetmap.getVpnId();
+        Uuid updatedVpnId = updatedSubnetmap.getVpnId();
+        String subnetName = subnetId.getValue();
+        String vpnName = null;
+        if (origVpnId == null && updatedVpnId != null) {
+            vpnName = updatedVpnId.getValue();
+            subnetVpnMap.put(subnetName, vpnName);
+            LOG.debug("Add subnet {} <-> vpn {} association", subnetName, vpnName);
+
+        } else if (origVpnId != null && updatedVpnId == null) {
+            vpnName = origVpnId.getValue();
+            subnetVpnMap.remove(subnetName);
+            LOG.debug("Remove subnet {} <-> vpn {} association", subnetName, vpnName);
+
+        } else if (origVpnId != null && updatedVpnId != null && !Objects.equal(origVpnId, updatedVpnId)) {
+            vpnName = updatedVpnId.getValue();
+            subnetVpnMap.put(subnetName, vpnName);
+            LOG.debug("Update subnet {} <-> vpn {} association", subnetName, vpnName);
+        }
+
+        if (vpnName != null) {
+            updateSubnetVpnAssociation(subnetName, vpnName);
+        }
+    }
+
+    @Override
+    protected void add(InstanceIdentifier<Subnetmap> identifier, Subnetmap subnetmap) {
+        if (subnetmap.getVpnId() != null) {
+            String subnetName = subnetmap.getId().getValue();
+            String vpnName = subnetmap.getVpnId().getValue();
+            subnetVpnMap.put(subnetName, vpnName);
+            LOG.debug("Add subnet {} <-> vpn {} association", subnetName, vpnName);
+            updateSubnetVpnAssociation(subnetName, vpnName);
+        }
+    }
+
+    @Override
+    protected SubnetVpnAssociationManager getDataTreeChangeListener() {
+        return this;
+    }
+
+    private void updateSubnetVpnAssociation(String subnetName, String vpnName) {
+        LOG.debug("Updating {} ingress plugins on subnet vpn association for subnet {} and vpn {}",
+                pluginMgr.getIngressPlugins().size(), subnetName, vpnName);
+        pluginMgr.getIngressPlugins().values().stream()
+                .forEach((plugin) -> plugin.subnetVpnAssociationUpdated(subnetName, vpnName));
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationElanInterfaceModificationCreator.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationElanInterfaceModificationCreator.java
new file mode 100644 (file)
index 0000000..c4b7c59
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.creators;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationElanInterfaceModificationCreator
+        implements FederationPluginModificationCreator<ElanInterface, ElanInterfaces> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationElanInterfaceModificationCreator.class);
+
+    @Inject
+    public FederationElanInterfaceModificationCreator() {
+        FederationPluginCreatorRegistry.registerCreator(FederationPluginConstants.ELAN_INTERFACE_KEY, this);
+    }
+
+    @Override
+    public Collection<DataTreeModification<ElanInterface>> createDataTreeModifications(ElanInterfaces elanInterfaces) {
+        if (elanInterfaces == null || elanInterfaces.getElanInterface() == null) {
+            LOG.debug("No ELAN interfaces found");
+            return Collections.emptyList();
+        }
+
+        Collection<DataTreeModification<ElanInterface>> modifications = new ArrayList<>();
+        for (ElanInterface elanInterface : elanInterfaces.getElanInterface()) {
+            modifications.add(new FullSyncDataTreeModification<ElanInterface>(elanInterface));
+        }
+
+        return modifications;
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationIetfInterfaceModificationCreator.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationIetfInterfaceModificationCreator.java
new file mode 100644 (file)
index 0000000..a382814
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.creators;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationIetfInterfaceModificationCreator
+        implements FederationPluginModificationCreator<Interface, Interfaces> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationIetfInterfaceModificationCreator.class);
+
+    @Inject
+    public FederationIetfInterfaceModificationCreator() {
+        FederationPluginCreatorRegistry.registerCreator(FederationPluginConstants.IETF_INTERFACE_KEY, this);
+    }
+
+    @Override
+    public Collection<DataTreeModification<Interface>> createDataTreeModifications(Interfaces interfaces) {
+        if (interfaces == null || interfaces.getInterface() == null) {
+            LOG.debug("No IETF interfaces found");
+            return Collections.emptyList();
+        }
+
+        Collection<DataTreeModification<Interface>> modifications = new ArrayList<>();
+        for (Interface iface : interfaces.getInterface()) {
+            modifications.add(new FullSyncDataTreeModification<Interface>(iface));
+        }
+
+        return modifications;
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationInventoryNodeModificationCreator.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationInventoryNodeModificationCreator.java
new file mode 100644 (file)
index 0000000..411d47e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.creators;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+@SuppressWarnings("deprecation")
+public class FederationInventoryNodeModificationCreator implements FederationPluginModificationCreator<Node, Nodes> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationInventoryNodeModificationCreator.class);
+
+    @Inject
+    public FederationInventoryNodeModificationCreator() {
+        FederationPluginCreatorRegistry.registerCreator(FederationPluginConstants.INVENTORY_NODE_CONFIG_KEY, this);
+        FederationPluginCreatorRegistry.registerCreator(FederationPluginConstants.INVENTORY_NODE_OPER_KEY, this);
+    }
+
+    @Override
+    public Collection<DataTreeModification<Node>> createDataTreeModifications(Nodes nodes) {
+        if (nodes == null || nodes.getNode() == null) {
+            LOG.debug("No inventory nodes found");
+            return Collections.emptyList();
+        }
+
+        Collection<DataTreeModification<Node>> modifications = new ArrayList<>();
+        for (Node node : nodes.getNode()) {
+            modifications.add(new FullSyncDataTreeModification<Node>(node));
+        }
+
+        return modifications;
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationPluginCreatorRegistry.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationPluginCreatorRegistry.java
new file mode 100644 (file)
index 0000000..874d74d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.creators;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+public class FederationPluginCreatorRegistry {
+
+    private static final Map<String, FederationPluginModificationCreator<? extends DataObject, ? extends DataObject>>
+        CREATORS = new ConcurrentHashMap<>();
+
+    private FederationPluginCreatorRegistry() {
+
+    }
+
+    public static void registerCreator(String listenerKey,
+            FederationPluginModificationCreator<? extends DataObject, ? extends DataObject> creator) {
+        CREATORS.put(listenerKey, creator);
+    }
+
+    public static FederationPluginModificationCreator<? extends DataObject, ? extends DataObject> getCreator(
+            String listenerKey) {
+        return CREATORS.get(listenerKey);
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationPluginModificationCreator.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationPluginModificationCreator.java
new file mode 100644 (file)
index 0000000..8b73202
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.creators;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Create {@link DataTreeModification} collection from parent subtree.
+ *
+ * @param <T>
+ *            dataObject
+ * @param <P>
+ *            parent dataObject
+ */
+public interface FederationPluginModificationCreator<T extends DataObject, P extends DataObject> {
+
+    Collection<DataTreeModification<T>> createDataTreeModifications(P parentDataObject);
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationTopologyNodeModificationCreator.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationTopologyNodeModificationCreator.java
new file mode 100644 (file)
index 0000000..733c391
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.creators;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationTopologyNodeModificationCreator implements FederationPluginModificationCreator<Node, Topology> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationTopologyNodeModificationCreator.class);
+
+    @Inject
+    public FederationTopologyNodeModificationCreator() {
+        FederationPluginCreatorRegistry.registerCreator(FederationPluginConstants.TOPOLOGY_NODE_CONFIG_KEY, this);
+        FederationPluginCreatorRegistry.registerCreator(FederationPluginConstants.TOPOLOGY_NODE_OPER_KEY, this);
+    }
+
+    @Override
+    public Collection<DataTreeModification<Node>> createDataTreeModifications(Topology topology) {
+        if (topology == null || topology.getNode() == null) {
+            LOG.debug("No topology nodes found");
+            return Collections.emptyList();
+        }
+
+        Collection<DataTreeModification<Node>> modifications = new ArrayList<>();
+        for (Node node : topology.getNode()) {
+            modifications.add(new FullSyncDataTreeModification<Node>(node));
+        }
+
+        return modifications;
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationVpnInterfaceModificationCreator.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FederationVpnInterfaceModificationCreator.java
new file mode 100644 (file)
index 0000000..e7b0978
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.creators;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationVpnInterfaceModificationCreator
+        implements FederationPluginModificationCreator<VpnInterface, VpnInterfaces> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationVpnInterfaceModificationCreator.class);
+
+    @Inject
+    public FederationVpnInterfaceModificationCreator() {
+        FederationPluginCreatorRegistry.registerCreator(FederationPluginConstants.VPN_INTERFACE_KEY, this);
+    }
+
+    @Override
+    public Collection<DataTreeModification<VpnInterface>> createDataTreeModifications(VpnInterfaces vpnInterfaces) {
+        if (vpnInterfaces == null || vpnInterfaces.getVpnInterface() == null) {
+            LOG.debug("No VPN interfaces found");
+            return Collections.emptyList();
+        }
+
+        Collection<DataTreeModification<VpnInterface>> modifications = new ArrayList<>();
+        for (VpnInterface vpnInterface : vpnInterfaces.getVpnInterface()) {
+            modifications.add(new FullSyncDataTreeModification<VpnInterface>(vpnInterface));
+        }
+
+        return modifications;
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FullSyncDataObjectModification.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FullSyncDataObjectModification.java
new file mode 100644 (file)
index 0000000..8f857b0
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.creators;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+
+public class FullSyncDataObjectModification<T extends DataObject> implements DataObjectModification<T> {
+
+    private T dataObject;
+
+    public FullSyncDataObjectModification(T dataObject) {
+        this.dataObject = dataObject;
+    }
+
+    @Override
+    public PathArgument getIdentifier() {
+        return null;
+    }
+
+    @Override
+    public Class<T> getDataType() {
+        return null;
+    }
+
+    @Override
+    public ModificationType getModificationType() {
+        return ModificationType.WRITE;
+    }
+
+    @Override
+    public T getDataBefore() {
+        return null;
+    }
+
+    @Override
+    public T getDataAfter() {
+        return dataObject;
+    }
+
+    @Override
+    public Collection<DataObjectModification<? extends DataObject>> getModifiedChildren() {
+        return null;
+    }
+
+    @Override
+    public <C extends ChildOf<? super T>> DataObjectModification<C> getModifiedChildContainer(Class<C> child) {
+        return null;
+    }
+
+    @Override
+    public <C extends Augmentation<T> & DataObject> DataObjectModification<C> getModifiedAugmentation(
+            Class<C> augmentation) {
+        return null;
+    }
+
+    @Override
+    public <C extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<C>> DataObjectModification<C>
+            getModifiedChildListItem(Class<C> listItem, K listKey) {
+        return null;
+    }
+
+    @Override
+    public DataObjectModification<? extends DataObject> getModifiedChild(PathArgument childArgument) {
+        return null;
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FullSyncDataTreeModification.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/creators/FullSyncDataTreeModification.java
new file mode 100644 (file)
index 0000000..daf66bc
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.creators;
+
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+public class FullSyncDataTreeModification<T extends DataObject> implements DataTreeModification<T> {
+
+    private DataObjectModification<T> dataObjectModification;
+
+    public FullSyncDataTreeModification(T dataObject) {
+        dataObjectModification = new FullSyncDataObjectModification<>(dataObject);
+    }
+
+    @Override
+    public DataTreeIdentifier<T> getRootPath() {
+        // no need to access since all the info can be taken from the listener key
+        return null;
+    }
+
+    @Override
+    public DataObjectModification<T> getRootNode() {
+        return dataObjectModification;
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationElanInterfaceFilter.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationElanInterfaceFilter.java
new file mode 100644 (file)
index 0000000..d7da85b
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.filters;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.elanmanager.api.IElanService;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginUtils;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.ElanShadowProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationElanInterfaceFilter implements FederationPluginFilter<ElanInterface, ElanInterfaces> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationElanInterfaceFilter.class);
+
+    private final DataBroker dataBroker;
+    private final IElanService elanService;
+
+    @Inject
+    public FederationElanInterfaceFilter(final DataBroker dataBroker, final IElanService elanService) {
+        this.dataBroker = dataBroker;
+        this.elanService = elanService;
+        FederationPluginFilterRegistry.registerFilter(FederationPluginConstants.ELAN_INTERFACE_KEY, this);
+    }
+
+    @Override
+    public FilterResult applyEgressFilter(ElanInterface elanInterface, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications,
+            DataTreeModification<ElanInterface> dataTreeModification) {
+        if (isShadow(elanInterface)) {
+            LOG.trace("Interface {} filtered out. Reason: shadow interface", elanInterface.getName());
+            return FilterResult.DENY;
+        }
+
+        String elanInstanceName = elanInterface.getElanInstanceName();
+        if (!federatedMappings.containsProducerNetworkId(elanInstanceName)) {
+            LOG.trace("Interface {} filtered out. Reason: network {} not federated", elanInterface.getName(),
+                    elanInstanceName);
+            return FilterResult.DENY;
+        }
+
+        if (elanService.isExternalInterface(elanInterface.getName())) {
+            LOG.trace("Interface {} filtered out. Reason: external interface", elanInterface.getName());
+            return FilterResult.DENY;
+        }
+
+        if (FederationPluginUtils.isDhcpInterface(dataBroker, elanInterface.getName())) {
+            LOG.trace("Interface {} filtered out. Reason: dhcp interface", elanInterface.getName());
+            return FilterResult.DENY;
+        }
+
+        return FilterResult.ACCEPT;
+    }
+
+    @Override
+    public FilterResult applyIngressFilter(String listenerKey, ElanInterfaces elanInterface) {
+        return FilterResult.ACCEPT;
+    }
+
+    private boolean isShadow(ElanInterface elanInterface) {
+        ElanShadowProperties elanShadowProperties = elanInterface.getAugmentation(ElanShadowProperties.class);
+        return elanShadowProperties != null && Boolean.TRUE.equals(elanShadowProperties.isShadow());
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationIetfInterfaceFilter.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationIetfInterfaceFilter.java
new file mode 100644 (file)
index 0000000..68d114b
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.filters;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.elanmanager.api.IElanService;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginUtils;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.IfShadowProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationIetfInterfaceFilter implements FederationPluginFilter<Interface, Interfaces> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationIetfInterfaceFilter.class);
+
+    private final DataBroker dataBroker;
+    private final IElanService elanService;
+
+    @Inject
+    public FederationIetfInterfaceFilter(final DataBroker dataBroker, final IElanService elanService) {
+        this.dataBroker = dataBroker;
+        this.elanService = elanService;
+        FederationPluginFilterRegistry.registerFilter(FederationPluginConstants.IETF_INTERFACE_KEY, this);
+    }
+
+    @Override
+    public FilterResult applyEgressFilter(Interface iface, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications,
+            DataTreeModification<Interface> dataTreeModification) {
+        String interfaceName = iface.getName();
+        if (isShadow(iface)) {
+            LOG.trace("Interface {} filtered out. Reason: shadow interface", interfaceName);
+            return FilterResult.DENY;
+        }
+
+        if (!L2vlan.class.equals(iface.getType())) {
+            LOG.trace("Interface {} filtered out. Reason: type {}", interfaceName, iface.getType());
+            return FilterResult.DENY;
+        }
+
+        if (elanService.isExternalInterface(interfaceName)) {
+            LOG.trace("Interface {} filtered out. Reason: external interface", interfaceName);
+            return FilterResult.DENY;
+        }
+
+        if (FederationPluginUtils.isDhcpInterface(dataBroker, interfaceName)) {
+            LOG.trace("Interface {} filtered out. Reason: dhcp interface", interfaceName);
+            return FilterResult.DENY;
+        }
+
+        ElanInterface elanInterface = elanService.getElanInterfaceByElanInterfaceName(interfaceName);
+        if (elanInterface == null) {
+            elanInterface = FederationPluginUtils.getAssociatedDataObjectFromPending(
+                    FederationPluginConstants.ELAN_INTERFACE_KEY, iface, pendingModifications);
+            if (elanInterface == null) {
+                LOG.debug("ELAN Interface {} not found. Queueing IETF interface", interfaceName);
+                return FilterResult.QUEUE;
+            }
+        }
+
+        String elanInstanceName = elanInterface.getElanInstanceName();
+        if (!federatedMappings.containsProducerNetworkId(elanInstanceName)) {
+            LOG.trace("Interface {} filtered out. Reason: network {} not federated", elanInterface.getName(),
+                    elanInstanceName);
+            return FilterResult.DENY;
+        }
+
+        return FilterResult.ACCEPT;
+    }
+
+    @Override
+    public FilterResult applyIngressFilter(String listenerKey, Interfaces iface) {
+        return FilterResult.ACCEPT;
+    }
+
+    private boolean isShadow(Interface iface) {
+        IfShadowProperties ifShadowProperties = iface.getAugmentation(IfShadowProperties.class);
+        return ifShadowProperties != null && Boolean.TRUE.equals(ifShadowProperties.isShadow());
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationInventoryNodeFilter.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationInventoryNodeFilter.java
new file mode 100644 (file)
index 0000000..6f61e52
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.filters;
+
+import com.google.common.base.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginCounters;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginUtils;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.netvirt.federation.plugin.transformers.FederationInventoryNodeTransformer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.InventoryNodeShadowProperties;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+@SuppressWarnings("deprecation")
+public class FederationInventoryNodeFilter implements FederationPluginFilter<Node, Nodes> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationInventoryNodeFilter.class);
+
+    private final DataBroker dataBroker;
+    private final FederationInventoryNodeTransformer transformer;
+
+    @Inject
+    public FederationInventoryNodeFilter(final DataBroker dataBroker,
+            final FederationInventoryNodeTransformer transformer) {
+        this.dataBroker = dataBroker;
+        this.transformer = transformer;
+        FederationPluginFilterRegistry.registerFilter(FederationPluginConstants.INVENTORY_NODE_CONFIG_KEY, this);
+        FederationPluginFilterRegistry.registerFilter(FederationPluginConstants.INVENTORY_NODE_OPER_KEY, this);
+    }
+
+    @Override
+    public FilterResult applyEgressFilter(Node newNode, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications,
+            DataTreeModification<Node> dataTreeModification) {
+        String nodeName = newNode.getKey().getId().getValue();
+        if (isShadow(newNode)) {
+            LOG.trace("Node {} filtered out. Reason: shadow node", nodeName);
+            return FilterResult.DENY;
+        }
+        if (dataTreeModification != null
+                && dataTreeModification.getRootNode().getModificationType() == ModificationType.SUBTREE_MODIFIED) {
+            Node oldNode = dataTreeModification.getRootNode().getDataBefore();
+            Nodes oldNodes = transformer.applyEgressTransformation(oldNode, federatedMappings, pendingModifications);
+            Nodes newNodes = transformer.applyEgressTransformation(newNode, federatedMappings, pendingModifications);
+            if (oldNodes.equals(newNodes)) {
+                FederationPluginCounters.egress_node_filtered_after_transform.inc();
+                return FilterResult.DENY;
+            }
+        }
+        return FilterResult.ACCEPT;
+    }
+
+    @Override
+    public FilterResult applyIngressFilter(String listenerKey, Nodes node) {
+        KeyedInstanceIdentifier<Node, NodeKey> nodeId = InstanceIdentifier.create(Nodes.class).child(Node.class,
+                node.getNode().get(0).getKey());
+        ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction();
+        try {
+            LogicalDatastoreType dsType = FederationPluginUtils.getListenerDatastoreType(listenerKey);
+            Optional<Node> checkedGet = readTx.read(dsType, nodeId).checkedGet();
+            if (checkedGet.isPresent()) {
+                Node persistedNode = checkedGet.get();
+                if (!isShadow(persistedNode)) {
+                    LOG.error("trying to update my own node - SOMETHING IS WRONG. nodeId: {}", nodeId);
+                    LOG.error("original node id {}", node.getNode().get(0).getId());
+                    LOG.error("persistedNode id {}", persistedNode.getId());
+                    return FilterResult.DENY;
+                }
+            }
+        } catch (ReadFailedException e) {
+            LOG.error("can't read node {}", nodeId);
+        }
+        return FilterResult.ACCEPT;
+    }
+
+    private boolean isShadow(Node node) {
+        InventoryNodeShadowProperties nodeShadowProperties = node.getAugmentation(InventoryNodeShadowProperties.class);
+        return nodeShadowProperties != null && Boolean.TRUE.equals(nodeShadowProperties.isShadow());
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationPluginFilter.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationPluginFilter.java
new file mode 100644 (file)
index 0000000..fa02c33
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.filters;
+
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Filter federated dataObject. Federated entities filtered on the egress side
+ * won't be sent to remote sites. Federated entities filtered on the ingress
+ * side won't be written to the datastore.
+ *
+ * @param <T>
+ *            dataObject
+ * @param <R>
+ *            root dataObject
+ */
+public interface FederationPluginFilter<T extends DataObject, R extends DataObject> {
+
+    FilterResult applyEgressFilter(T dataObject, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications,
+            DataTreeModification<T> dataTreeModification);
+
+    FilterResult applyIngressFilter(String listenerKey, R dataObject);
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationPluginFilterRegistry.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationPluginFilterRegistry.java
new file mode 100644 (file)
index 0000000..4f88b84
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.filters;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+public class FederationPluginFilterRegistry {
+
+    private static final Map<String, FederationPluginFilter<? extends DataObject, ? extends DataObject>>
+        FILTERS = new ConcurrentHashMap<>();
+
+    private FederationPluginFilterRegistry() {
+
+    }
+
+    public static void registerFilter(String listenerKey,
+            FederationPluginFilter<? extends DataObject, ? extends DataObject> filter) {
+        FILTERS.put(listenerKey, filter);
+    }
+
+    public static FederationPluginFilter<? extends DataObject, ? extends DataObject> getFilter(String listenerKey) {
+        return FILTERS.get(listenerKey);
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationTopologyNodeFilter.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationTopologyNodeFilter.java
new file mode 100644 (file)
index 0000000..b8aa2c6
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.filters;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.genius.itm.globals.ITMConstants;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.TopologyNodeShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationTopologyNodeFilter implements FederationPluginFilter<Node, NetworkTopology> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationTopologyNodeFilter.class);
+
+    @Inject
+    public FederationTopologyNodeFilter() {
+        FederationPluginFilterRegistry.registerFilter(FederationPluginConstants.TOPOLOGY_NODE_CONFIG_KEY, this);
+        FederationPluginFilterRegistry.registerFilter(FederationPluginConstants.TOPOLOGY_NODE_OPER_KEY, this);
+    }
+
+    @Override
+    public FilterResult applyEgressFilter(Node node, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications,
+            DataTreeModification<Node> dataTreeModification) {
+        String nodeName = node.getNodeId().getValue();
+        if (isShadow(node)) {
+            LOG.trace("Node {} filtered out. Reason: shadow node", nodeName);
+            return FilterResult.DENY;
+        }
+
+        if (nodeName.contains(ITMConstants.BRIDGE_URI_PREFIX)
+                && !nodeName.contains(FederationPluginConstants.INTEGRATION_BRIDGE_PREFIX)) {
+            LOG.trace("Node {} filtered out. Reason: bridge that is not integration bridge", nodeName);
+            return FilterResult.DENY;
+        }
+
+        return FilterResult.ACCEPT;
+    }
+
+    @Override
+    public FilterResult applyIngressFilter(String listenerKey, NetworkTopology topology) {
+        return FilterResult.ACCEPT;
+    }
+
+    private boolean isShadow(Node node) {
+        TopologyNodeShadowProperties nodeShadowProperties = node.getAugmentation(TopologyNodeShadowProperties.class);
+        return nodeShadowProperties != null && Boolean.TRUE.equals(nodeShadowProperties.isShadow());
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationVpnInterfaceFilter.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FederationVpnInterfaceFilter.java
new file mode 100644 (file)
index 0000000..12025d5
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.filters;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginUtils;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.netvirt.federation.plugin.SubnetVpnAssociationManager;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.VpnShadowProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationVpnInterfaceFilter implements FederationPluginFilter<VpnInterface, VpnInterfaces> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationVpnInterfaceFilter.class);
+
+    private final DataBroker dataBroker;
+    private final SubnetVpnAssociationManager subnetVpnAssociationManager;
+
+    @Inject
+    public FederationVpnInterfaceFilter(final DataBroker dataBroker,
+            final SubnetVpnAssociationManager subnetVpnAssociationManager) {
+        this.dataBroker = dataBroker;
+        this.subnetVpnAssociationManager = subnetVpnAssociationManager;
+        FederationPluginFilterRegistry.registerFilter(FederationPluginConstants.VPN_INTERFACE_KEY, this);
+    }
+
+    @Override
+    public FilterResult applyEgressFilter(VpnInterface vpnInterface, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications,
+            DataTreeModification<VpnInterface> dataTreeModification) {
+        String vpnInterfaceName = vpnInterface.getName();
+        String interfaceName = vpnInterfaceName;
+        if (isShadow(vpnInterface)) {
+            LOG.trace("Interface {} filtered out. Reason: shadow interface", interfaceName);
+            return FilterResult.DENY;
+        }
+        Boolean isRouterInterface = vpnInterface.isIsRouterInterface();
+        if (isRouterInterface != null && vpnInterface.isIsRouterInterface()) {
+            LOG.trace("Interface {} filtered out. Reason: router interface", interfaceName);
+            return FilterResult.DENY;
+        }
+
+        String subnetId = FederationPluginUtils.getSubnetIdFromVpnInterface(vpnInterface);
+        if (subnetId == null) {
+            LOG.trace("Interface {} filtered out. Reason: subnet id missing", interfaceName);
+            return FilterResult.DENY;
+        }
+
+        if (!federatedMappings.containsProducerSubnetId(subnetId)) {
+            LOG.trace("Interface {} filtered out. Reason:  subnet {} not federated", vpnInterfaceName, subnetId);
+            return FilterResult.DENY;
+        }
+
+        if (FederationPluginUtils.isDhcpInterface(dataBroker, vpnInterfaceName)) {
+            LOG.trace("Interface {} filtered out. Reason: dhcp interface", vpnInterfaceName);
+            return FilterResult.DENY;
+        }
+
+        return FilterResult.ACCEPT;
+    }
+
+    @Override
+    public FilterResult applyIngressFilter(String listenerKey, VpnInterfaces vpnInterfaces) {
+        VpnInterface vpnInterface = vpnInterfaces.getVpnInterface().get(0);
+        String subnetId = FederationPluginUtils.getSubnetIdFromVpnInterface(vpnInterface);
+        if (subnetId == null) {
+            LOG.warn("Interface {} filtered out. Reason: subnet id not found", vpnInterface.getName());
+            return FilterResult.DENY;
+        }
+
+        String vpnId = subnetVpnAssociationManager.getSubnetVpn(subnetId);
+        if (vpnId == null) {
+            LOG.debug("Interface {} filtered out. Reason: VPN id not found for subnet id {}", vpnInterface.getName(),
+                    subnetId);
+            return FilterResult.DENY;
+        }
+
+        return FilterResult.ACCEPT;
+    }
+
+    private boolean isShadow(VpnInterface vpnInterface) {
+        VpnShadowProperties vpnShadowProperties = vpnInterface.getAugmentation(VpnShadowProperties.class);
+        return vpnShadowProperties != null && Boolean.TRUE.equals(vpnShadowProperties.isShadow());
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FilterResult.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/filters/FilterResult.java
new file mode 100644 (file)
index 0000000..5042c34
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.filters;
+
+/**
+ * Applicable values for applyEgressFilter and applyIngressFilter
+ * .<br>
+ * Indicates weather DataObject should be federated to remote sites
+ *
+ */
+public enum FilterResult {
+
+    /**
+     * Accept DataObject.
+     */
+    ACCEPT,
+    /**
+     * Filter out DataObject.
+     */
+    DENY,
+    /**
+     * No decision could be made based on the current state. Queue for future
+     * processing
+     */
+    QUEUE,
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationElanInterfaceIdentifier.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationElanInterfaceIdentifier.java
new file mode 100644 (file)
index 0000000..bdbff54
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin.identifiers;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Singleton
+public class FederationElanInterfaceIdentifier
+        implements FederationPluginIdentifier<ElanInterface, ElanInterfaces, ElanInterfaces> {
+
+    @Inject
+    public FederationElanInterfaceIdentifier() {
+        FederationPluginIdentifierRegistry.registerIdentifier(FederationPluginConstants.ELAN_INTERFACE_KEY,
+                LogicalDatastoreType.CONFIGURATION, this);
+    }
+
+    @Override
+    public InstanceIdentifier<ElanInterface> getInstanceIdentifier() {
+        return InstanceIdentifier.create(ElanInterfaces.class).child(ElanInterface.class);
+    }
+
+    @Override
+    public InstanceIdentifier<ElanInterfaces> getParentInstanceIdentifier() {
+        return InstanceIdentifier.create(ElanInterfaces.class);
+    }
+
+    @Override
+    public InstanceIdentifier<ElanInterfaces> getSubtreeInstanceIdentifier() {
+        return InstanceIdentifier.create(ElanInterfaces.class);
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationIetfInterfaceIdentifier.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationIetfInterfaceIdentifier.java
new file mode 100644 (file)
index 0000000..180346e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin.identifiers;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Singleton
+public class FederationIetfInterfaceIdentifier
+        implements FederationPluginIdentifier<Interface, Interfaces, Interfaces> {
+
+    @Inject
+    public FederationIetfInterfaceIdentifier() {
+        FederationPluginIdentifierRegistry.registerIdentifier(FederationPluginConstants.IETF_INTERFACE_KEY,
+                LogicalDatastoreType.CONFIGURATION, this);
+    }
+
+    @Override
+    public InstanceIdentifier<Interface> getInstanceIdentifier() {
+        return InstanceIdentifier.create(Interfaces.class).child(Interface.class);
+    }
+
+    @Override
+    public InstanceIdentifier<Interfaces> getParentInstanceIdentifier() {
+        return InstanceIdentifier.create(Interfaces.class);
+    }
+
+    @Override
+    public InstanceIdentifier<Interfaces> getSubtreeInstanceIdentifier() {
+        return InstanceIdentifier.create(Interfaces.class);
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationInventoryNodeIdentifier.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationInventoryNodeIdentifier.java
new file mode 100644 (file)
index 0000000..96524b4
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin.identifiers;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Singleton
+@SuppressWarnings("deprecation")
+public class FederationInventoryNodeIdentifier implements FederationPluginIdentifier<Node, Nodes, Nodes> {
+
+    @Inject
+    public FederationInventoryNodeIdentifier() {
+        FederationPluginIdentifierRegistry.registerIdentifier(FederationPluginConstants.INVENTORY_NODE_CONFIG_KEY,
+                LogicalDatastoreType.CONFIGURATION, this);
+        FederationPluginIdentifierRegistry.registerIdentifier(FederationPluginConstants.INVENTORY_NODE_OPER_KEY,
+                LogicalDatastoreType.OPERATIONAL, this);
+    }
+
+    @Override
+    public InstanceIdentifier<Node> getInstanceIdentifier() {
+        return InstanceIdentifier.create(Nodes.class).child(Node.class);
+    }
+
+    @Override
+    public InstanceIdentifier<Nodes> getParentInstanceIdentifier() {
+        return InstanceIdentifier.create(Nodes.class);
+    }
+
+    @Override
+    public InstanceIdentifier<Nodes> getSubtreeInstanceIdentifier() {
+        return InstanceIdentifier.create(Nodes.class);
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationPluginIdentifier.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationPluginIdentifier.java
new file mode 100644 (file)
index 0000000..426eaf3
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.identifiers;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * {@link DataObject} hierarchical instance identifiers.
+ *
+ * @param <T>
+ *            dataObject
+ * @param <P>
+ *            parent dataObject
+ * @param <R>
+ *            root dataObject
+ */
+public interface FederationPluginIdentifier<T extends DataObject, P extends DataObject, R extends DataObject> {
+
+    InstanceIdentifier<T> getInstanceIdentifier();
+
+    InstanceIdentifier<P> getParentInstanceIdentifier();
+
+    InstanceIdentifier<R> getSubtreeInstanceIdentifier();
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationPluginIdentifierRegistry.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationPluginIdentifierRegistry.java
new file mode 100644 (file)
index 0000000..371819f
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.identifiers;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+public class FederationPluginIdentifierRegistry {
+
+    private static final //
+        Map<String, FederationPluginIdentifier<? extends DataObject, ? extends DataObject, ? extends DataObject>>
+        IDENTIFIERS = new ConcurrentHashMap<>();
+    private static final Map<String, LogicalDatastoreType> DSTYPES = new ConcurrentHashMap<>();
+    private static final Map<Pair<LogicalDatastoreType, Class<? extends DataObject>>, String> LISTENERS
+            = new ConcurrentHashMap<>();
+
+    private FederationPluginIdentifierRegistry() {
+
+    }
+
+    public static void registerIdentifier(String listenerKey, LogicalDatastoreType datastoreType,
+            FederationPluginIdentifier<? extends DataObject, ? extends DataObject, ? extends DataObject> identifier) {
+        IDENTIFIERS.put(listenerKey, identifier);
+        DSTYPES.put(listenerKey, datastoreType);
+        LISTENERS.put(Pair.of(datastoreType, getSubtreeClass(identifier)), listenerKey);
+    }
+
+    public static
+        FederationPluginIdentifier<? extends DataObject, ? extends DataObject, ? extends DataObject> getIdentifier(
+            String listenerKey) {
+        return IDENTIFIERS.get(listenerKey);
+    }
+
+    public static LogicalDatastoreType getDatastoreType(String listenerKey) {
+        return DSTYPES.get(listenerKey);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static String getListenerKey(LogicalDatastoreType datastoreType, Class<? extends DataObject> subtreeClass) {
+        String listenerKey = LISTENERS.get(Pair.of(datastoreType, subtreeClass));
+        if (listenerKey != null) {
+            return listenerKey;
+        }
+
+        Class<?>[] interfaces = subtreeClass.getInterfaces();
+        if (interfaces != null) {
+            for (Class<?> iface : interfaces) {
+                return getListenerKey(datastoreType, (Class<? extends DataObject>) iface);
+
+            }
+        }
+
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Class<? extends DataObject> getSubtreeClass(
+            FederationPluginIdentifier<? extends DataObject, ? extends DataObject, ? extends DataObject> identifier) {
+        for (Type type : identifier.getClass().getGenericInterfaces()) {
+            ParameterizedType parameterizedType = (ParameterizedType) type;
+            if (parameterizedType.getRawType().equals(FederationPluginIdentifier.class)) {
+                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+                if (actualTypeArguments != null && actualTypeArguments.length > 1) {
+                    return (Class<? extends DataObject>) actualTypeArguments[2];
+                }
+            }
+        }
+
+        return null;
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationTopologyNodeIdentifier.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationTopologyNodeIdentifier.java
new file mode 100644 (file)
index 0000000..49c23f6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin.identifiers;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Singleton
+public class FederationTopologyNodeIdentifier implements FederationPluginIdentifier<Node, Topology, NetworkTopology> {
+
+    @Inject
+    public FederationTopologyNodeIdentifier() {
+        FederationPluginIdentifierRegistry.registerIdentifier(FederationPluginConstants.TOPOLOGY_NODE_CONFIG_KEY,
+                LogicalDatastoreType.CONFIGURATION, this);
+        FederationPluginIdentifierRegistry.registerIdentifier(FederationPluginConstants.TOPOLOGY_NODE_OPER_KEY,
+                LogicalDatastoreType.OPERATIONAL, this);
+    }
+
+    @Override
+    public InstanceIdentifier<Node> getInstanceIdentifier() {
+        return InstanceIdentifier.create(NetworkTopology.class)
+                .child(Topology.class, new TopologyKey(new TopologyId(new Uri("ovsdb:1")))).child(Node.class);
+    }
+
+    @Override
+    public InstanceIdentifier<Topology> getParentInstanceIdentifier() {
+        return InstanceIdentifier.create(NetworkTopology.class).child(Topology.class,
+                new TopologyKey(new TopologyId(new Uri("ovsdb:1"))));
+    }
+
+    @Override
+    public InstanceIdentifier<NetworkTopology> getSubtreeInstanceIdentifier() {
+        return InstanceIdentifier.create(NetworkTopology.class);
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationVpnInterfaceIdentifier.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/identifiers/FederationVpnInterfaceIdentifier.java
new file mode 100644 (file)
index 0000000..2ef73ab
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin.identifiers;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Singleton
+public class FederationVpnInterfaceIdentifier
+        implements FederationPluginIdentifier<VpnInterface, VpnInterfaces, VpnInterfaces> {
+
+    @Inject
+    public FederationVpnInterfaceIdentifier() {
+        FederationPluginIdentifierRegistry.registerIdentifier(FederationPluginConstants.VPN_INTERFACE_KEY,
+                LogicalDatastoreType.CONFIGURATION, this);
+    }
+
+    @Override
+    public InstanceIdentifier<VpnInterface> getInstanceIdentifier() {
+        return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
+    }
+
+    @Override
+    public InstanceIdentifier<VpnInterfaces> getParentInstanceIdentifier() {
+        return InstanceIdentifier.create(VpnInterfaces.class);
+    }
+
+    @Override
+    public InstanceIdentifier<VpnInterfaces> getSubtreeInstanceIdentifier() {
+        return InstanceIdentifier.create(VpnInterfaces.class);
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationElanInterfaceTransformer.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationElanInterfaceTransformer.java
new file mode 100644 (file)
index 0000000..182e5ca
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.transformers;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfacesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.ElanShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.ElanShadowPropertiesBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationElanInterfaceTransformer implements FederationPluginTransformer<ElanInterface, ElanInterfaces> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationElanInterfaceTransformer.class);
+
+    @Inject
+    public FederationElanInterfaceTransformer() {
+        FederationPluginTransformerRegistry.registerTransformer(FederationPluginConstants.ELAN_INTERFACE_KEY, this);
+    }
+
+    @Override
+    public ElanInterfaces applyEgressTransformation(ElanInterface elanInterface, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications) {
+        String elanInstanceName = elanInterface.getElanInstanceName();
+        String consumerElanInstaceName = federatedMappings.getConsumerNetworkId(elanInstanceName);
+        if (consumerElanInstaceName == null) {
+            LOG.error("Failed to transform ELAN interface {}. No transformation found for ELAN instance {}",
+                    elanInterface.getName(), consumerElanInstaceName);
+            return null;
+        }
+
+        ElanInterfaceBuilder interfaceBuilder = new ElanInterfaceBuilder(elanInterface);
+        interfaceBuilder.setElanInstanceName(consumerElanInstaceName);
+        interfaceBuilder.addAugmentation(ElanShadowProperties.class,
+                new ElanShadowPropertiesBuilder().setShadow(true).build());
+        return new ElanInterfacesBuilder().setElanInterface(Collections.singletonList(interfaceBuilder.build()))
+                .build();
+    }
+
+    @Override
+    public Pair<InstanceIdentifier<ElanInterface>, ElanInterface> applyIngressTransformation(
+            ElanInterfaces elanInterfaces, ModificationType modificationType, int generationNumber, String remoteIp) {
+        List<ElanInterface> elanInterfaceList = elanInterfaces.getElanInterface();
+        if (elanInterfaceList == null || elanInterfaceList.isEmpty()) {
+            LOG.error("ELAN interfaces is empty");
+            return null;
+        }
+
+        ElanInterface elanInterface = elanInterfaceList.get(0);
+        ElanInterfaceBuilder interfaceBuilder = new ElanInterfaceBuilder(elanInterface);
+        interfaceBuilder.addAugmentation(ElanShadowProperties.class,
+                new ElanShadowPropertiesBuilder(interfaceBuilder.getAugmentation(ElanShadowProperties.class))
+                        .setShadow(true).setGenerationNumber(generationNumber).setRemoteIp(remoteIp).build());
+        elanInterface = interfaceBuilder.build();
+        return Pair.of(
+                InstanceIdentifier.create(ElanInterfaces.class).child(ElanInterface.class, elanInterface.getKey()),
+                elanInterface);
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationIetfInterfaceTransformer.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationIetfInterfaceTransformer.java
new file mode 100644 (file)
index 0000000..f721a72
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.transformers;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.IfShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.IfShadowPropertiesBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationIetfInterfaceTransformer implements FederationPluginTransformer<Interface, Interfaces> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationIetfInterfaceTransformer.class);
+
+    @Inject
+    public FederationIetfInterfaceTransformer() {
+        FederationPluginTransformerRegistry.registerTransformer(FederationPluginConstants.IETF_INTERFACE_KEY, this);
+    }
+
+    @Override
+    public Interfaces applyEgressTransformation(Interface iface, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications) {
+        InterfaceBuilder interfaceBuilder = new InterfaceBuilder(iface);
+        interfaceBuilder.addAugmentation(IfShadowProperties.class,
+                new IfShadowPropertiesBuilder().setShadow(true).build());
+        return new InterfacesBuilder().setInterface(Collections.singletonList(interfaceBuilder.build())).build();
+    }
+
+    @Override
+    public Pair<InstanceIdentifier<Interface>, Interface> applyIngressTransformation(Interfaces interfaces,
+            ModificationType modificationType, int generationNumber, String remoteIp) {
+        List<Interface> interfaceList = interfaces.getInterface();
+        if (interfaceList == null || interfaceList.isEmpty()) {
+            LOG.error("IETF interfaces is empty");
+            return null;
+        }
+
+        Interface iface = interfaceList.get(0);
+        InterfaceBuilder interfaceBuilder = new InterfaceBuilder(iface);
+        interfaceBuilder.addAugmentation(IfShadowProperties.class,
+                new IfShadowPropertiesBuilder(interfaceBuilder.getAugmentation(IfShadowProperties.class))
+                        .setShadow(true).setGenerationNumber(generationNumber).setRemoteIp(remoteIp).build());
+        return Pair.of(InstanceIdentifier.create(Interfaces.class).child(Interface.class, iface.getKey()),
+                interfaceBuilder.build());
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationInventoryNodeTransformer.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationInventoryNodeTransformer.java
new file mode 100644 (file)
index 0000000..dea87ab
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.transformers;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginUtils;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableStatisticsGatheringStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.InventoryNodeShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.InventoryNodeShadowPropertiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+@SuppressWarnings("deprecation")
+public class FederationInventoryNodeTransformer implements FederationPluginTransformer<Node, Nodes> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationInventoryNodeTransformer.class);
+
+    @Inject
+    public FederationInventoryNodeTransformer() {
+        FederationPluginTransformerRegistry.registerTransformer(FederationPluginConstants.INVENTORY_NODE_CONFIG_KEY,
+                this);
+        FederationPluginTransformerRegistry.registerTransformer(FederationPluginConstants.INVENTORY_NODE_OPER_KEY,
+                this);
+    }
+
+    @Override
+    public Nodes applyEgressTransformation(Node node, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications) {
+        NodeBuilder nodeBuilder = new NodeBuilder(node);
+        nodeBuilder.addAugmentation(FlowCapableNode.class, null);
+        nodeBuilder.addAugmentation(FlowCapableStatisticsGatheringStatus.class, null);
+        List<NodeConnector> ncList = node.getNodeConnector();
+        List<NodeConnector> newNcList = new ArrayList<>();
+        if (ncList != null) {
+            for (NodeConnector nc : ncList) {
+                FlowCapableNodeConnector flowCapableAugmentation = nc.getAugmentation(FlowCapableNodeConnector.class);
+                if (flowCapableAugmentation != null) {
+                    String portName = flowCapableAugmentation.getName();
+                    if (FederationPluginUtils.isPortNameFiltered(portName)) {
+                        continue;
+                    }
+                }
+                NodeConnectorBuilder ncBuilder = new NodeConnectorBuilder(nc);
+                ncBuilder.addAugmentation(FlowCapableNodeConnectorStatisticsData.class, null);
+                newNcList.add(ncBuilder.build());
+            }
+        }
+        nodeBuilder.setNodeConnector(newNcList);
+        nodeBuilder.addAugmentation(InventoryNodeShadowProperties.class,
+                new InventoryNodeShadowPropertiesBuilder().setShadow(true).build());
+        return new NodesBuilder().setNode(Collections.singletonList(nodeBuilder.build())).build();
+    }
+
+    @Override
+    public Pair<InstanceIdentifier<Node>, Node> applyIngressTransformation(Nodes nodes,
+            ModificationType modificationType, int generationNumber, String remoteIp) {
+        List<Node> nodeList = nodes.getNode();
+        if (nodeList == null || nodeList.isEmpty()) {
+            LOG.error("Inventory nodes is empty");
+            return null;
+        }
+
+        Node node = nodeList.get(0);
+        NodeBuilder nodeBuilder = new NodeBuilder(node);
+        nodeBuilder.addAugmentation(InventoryNodeShadowProperties.class,
+                new InventoryNodeShadowPropertiesBuilder(
+                        nodeBuilder.getAugmentation(InventoryNodeShadowProperties.class)).setShadow(true)
+                                .setGenerationNumber(generationNumber).setRemoteIp(remoteIp).build());
+        return Pair.of(InstanceIdentifier.create(Nodes.class).child(Node.class, node.getKey()), nodeBuilder.build());
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationPluginTransformer.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationPluginTransformer.java
new file mode 100644 (file)
index 0000000..09a01f5
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.transformers;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Transform federated dataObject.
+ *
+ * @param <T>
+ *            dataObject
+ * @param <R>
+ *            root dataObject
+ */
+public interface FederationPluginTransformer<T extends DataObject, R extends DataObject> {
+
+    R applyEgressTransformation(T dataObject, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications);
+
+    Pair<InstanceIdentifier<T>, T> applyIngressTransformation(R dataObject, ModificationType modificationType,
+            int generationNumber, String remoteIp);
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationPluginTransformerRegistry.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationPluginTransformerRegistry.java
new file mode 100644 (file)
index 0000000..00ac26a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.transformers;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+public class FederationPluginTransformerRegistry {
+
+    private static final Map<String, FederationPluginTransformer<? extends DataObject, ? extends DataObject>>
+        TRANSFORMERS = new ConcurrentHashMap<>();
+
+    private FederationPluginTransformerRegistry() {
+
+    }
+
+    public static void registerTransformer(String listenerKey,
+            FederationPluginTransformer<? extends DataObject, ? extends DataObject> transformer) {
+        TRANSFORMERS.put(listenerKey, transformer);
+    }
+
+    public static FederationPluginTransformer<? extends DataObject, ? extends DataObject> getTransformer(
+            String listenerKey) {
+        return TRANSFORMERS.get(listenerKey);
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationTopologyNodeTransformer.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationTopologyNodeTransformer.java
new file mode 100644 (file)
index 0000000..a940baf
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.transformers;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginUtils;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.TopologyNodeShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.TopologyNodeShadowPropertiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentationBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class FederationTopologyNodeTransformer implements FederationPluginTransformer<Node, NetworkTopology> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationTopologyNodeTransformer.class);
+
+    @Inject
+    public FederationTopologyNodeTransformer() {
+        FederationPluginTransformerRegistry.registerTransformer(FederationPluginConstants.TOPOLOGY_NODE_CONFIG_KEY,
+                this);
+        FederationPluginTransformerRegistry.registerTransformer(FederationPluginConstants.TOPOLOGY_NODE_OPER_KEY, this);
+    }
+
+    @Override
+    public NetworkTopology applyEgressTransformation(Node node, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications) {
+        TopologyBuilder topologyBuilder = new TopologyBuilder();
+        topologyBuilder.setKey(new TopologyKey(new TopologyId(new Uri("ovsdb:1"))));
+        String nodeName = node.getNodeId().getValue();
+        NodeBuilder nodeBuilder;
+        if (nodeName.contains(FederationPluginConstants.INTEGRATION_BRIDGE_PREFIX)) {
+            nodeBuilder = getNodeBuilderForEgressTransformationOnBrIntNode(node);
+        } else {
+            nodeBuilder = getNodeBuilderForEgressTransformationOnBrIntParentNode(node);
+        }
+
+        nodeBuilder.addAugmentation(TopologyNodeShadowProperties.class,
+                new TopologyNodeShadowPropertiesBuilder().setShadow(true).build());
+        topologyBuilder.setNode(Collections.singletonList(nodeBuilder.build()));
+        return new NetworkTopologyBuilder().setTopology(Collections.singletonList(topologyBuilder.build())).build();
+    }
+
+    @Override
+    public Pair<InstanceIdentifier<Node>, Node> applyIngressTransformation(NetworkTopology networkTopology,
+            ModificationType modificationType, int generationNumber, String remoteIp) {
+        List<Topology> topologyList = networkTopology.getTopology();
+        if (topologyList == null || topologyList.isEmpty()) {
+            LOG.error("Topology network is empty");
+            return null;
+        }
+
+        Topology topology = topologyList.get(0);
+        List<Node> nodeList = topology.getNode();
+        if (nodeList == null || nodeList.isEmpty()) {
+            LOG.error("Topology node is empty");
+            return null;
+        }
+
+        Node node = nodeList.get(0);
+        NodeBuilder nodeBuilder = new NodeBuilder(node);
+        nodeBuilder
+                .addAugmentation(TopologyNodeShadowProperties.class,
+                        new TopologyNodeShadowPropertiesBuilder(
+                                nodeBuilder.getAugmentation(TopologyNodeShadowProperties.class)).setShadow(true)
+                                        .setGenerationNumber(generationNumber).setRemoteIp(remoteIp).build());
+        return Pair.of(InstanceIdentifier.create(NetworkTopology.class)
+                .child(Topology.class, new TopologyKey(new TopologyId(new Uri("ovsdb:1"))))
+                .child(Node.class, node.getKey()), nodeBuilder.build());
+    }
+
+    private NodeBuilder getNodeBuilderForEgressTransformationOnBrIntParentNode(Node node) {
+        NodeBuilder nodeBuilder = new NodeBuilder();
+        nodeBuilder.setKey(node.getKey());
+        nodeBuilder.setNodeId(node.getNodeId());
+        OvsdbNodeAugmentation ovsdbNodeAugmentation = node.getAugmentation(OvsdbNodeAugmentation.class);
+        if (ovsdbNodeAugmentation != null) {
+            nodeBuilder.addAugmentation(OvsdbNodeAugmentation.class, new OvsdbNodeAugmentationBuilder()
+                    .setOpenvswitchOtherConfigs(ovsdbNodeAugmentation.getOpenvswitchOtherConfigs()).build());
+        } else {
+            LOG.warn("OvsdbNodeAugmentation for node ID {} is null", node.getNodeId());
+        }
+
+        return nodeBuilder;
+    }
+
+    private NodeBuilder getNodeBuilderForEgressTransformationOnBrIntNode(Node node) {
+        NodeBuilder nodeBuilder = new NodeBuilder(node);
+        List<TerminationPoint> tps = node.getTerminationPoint();
+        List<TerminationPoint> newTps = new ArrayList<>();
+
+        if (tps != null) {
+            for (TerminationPoint tp : tps) {
+                String portName = tp.getTpId().getValue();
+                if (!FederationPluginUtils.isPortNameFiltered(portName)) {
+                    newTps.add(tp);
+                } else {
+                    LOG.trace("Not copying because it is not vm port: " + tp.getTpId());
+                }
+            }
+            nodeBuilder.setTerminationPoint(newTps);
+        }
+
+        return nodeBuilder;
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationVpnInterfaceTransformer.java b/vpnservice/federation-plugin/impl/src/main/java/org/opendaylight/netvirt/federation/plugin/transformers/FederationVpnInterfaceTransformer.java
new file mode 100644 (file)
index 0000000..259b48f
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netvirt.federation.plugin.transformers;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.netvirt.federation.plugin.FederatedMappings;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginUtils;
+import org.opendaylight.netvirt.federation.plugin.PendingModificationCache;
+import org.opendaylight.netvirt.federation.plugin.SubnetVpnAssociationManager;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfacesBuilder;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.VpnShadowProperties;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.rev170219.VpnShadowPropertiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+@Singleton
+public class FederationVpnInterfaceTransformer implements FederationPluginTransformer<VpnInterface, VpnInterfaces> {
+    private static final Logger LOG = LoggerFactory.getLogger(FederationVpnInterfaceTransformer.class);
+
+    private final SubnetVpnAssociationManager subnetVpnAssociationManager;
+
+    @Inject
+    public FederationVpnInterfaceTransformer(final SubnetVpnAssociationManager subnetVpnAssociationManager) {
+        this.subnetVpnAssociationManager = subnetVpnAssociationManager;
+        FederationPluginTransformerRegistry.registerTransformer(FederationPluginConstants.VPN_INTERFACE_KEY, this);
+    }
+
+    @Override
+    public VpnInterfaces applyEgressTransformation(VpnInterface vpnInterface, FederatedMappings federatedMappings,
+            PendingModificationCache<DataTreeModification<?>> pendingModifications) {
+        Adjacencies adjacencies = vpnInterface.getAugmentation(Adjacencies.class);
+        if (adjacencies == null) {
+            LOG.error("No adjacencies found for VPN interface {}", vpnInterface.getName());
+            return null;
+        }
+
+        List<Adjacency> federatedAdjacencies = transformAdjacencies(vpnInterface, federatedMappings, adjacencies);
+        if (federatedAdjacencies.isEmpty()) {
+            LOG.error("No federated adjacencies found for VPN interface {} federated mappings {}", vpnInterface,
+                    federatedMappings);
+            return null;
+        }
+
+        VpnInterfaceBuilder vpnInterfaceBuilder = new VpnInterfaceBuilder(vpnInterface);
+        vpnInterfaceBuilder.addAugmentation(Adjacencies.class,
+                new AdjacenciesBuilder().setAdjacency(federatedAdjacencies).build());
+        vpnInterfaceBuilder.addAugmentation(VpnShadowProperties.class,
+                new VpnShadowPropertiesBuilder().setShadow(true).build());
+        return new VpnInterfacesBuilder().setVpnInterface(Collections.singletonList(vpnInterfaceBuilder.build()))
+                .build();
+    }
+
+    @Override
+    public Pair<InstanceIdentifier<VpnInterface>, VpnInterface> applyIngressTransformation(VpnInterfaces vpnInterfaces,
+            ModificationType modificationType, int generationNumber, String remoteIp) {
+        List<VpnInterface> vpnInterfaceList = vpnInterfaces.getVpnInterface();
+        if (vpnInterfaceList == null || vpnInterfaceList.isEmpty()) {
+            LOG.error("VPN interfaces is empty");
+            return null;
+        }
+
+        VpnInterface vpnInterface = vpnInterfaceList.get(0);
+        VpnInterface transformedVpnInterface = null;
+        if (!ModificationType.DELETE.equals(modificationType)) {
+            transformedVpnInterface = transformVpnInterface(vpnInterface, generationNumber, remoteIp);
+            if (transformedVpnInterface == null) {
+                return null;
+            }
+        }
+
+        return Pair.of(InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class, vpnInterface.getKey()),
+                transformedVpnInterface);
+    }
+
+    private List<Adjacency> transformAdjacencies(VpnInterface vpnInterface, FederatedMappings federatedMappings,
+            Adjacencies adjacencies) {
+        List<Adjacency> federatedAdjacencies = new ArrayList<>();
+        for (Adjacency adjacency : adjacencies.getAdjacency()) {
+            Uuid subnetId = adjacency.getSubnetId();
+            if (subnetId != null) {
+                String federatedSubnetId = federatedMappings.getConsumerSubnetId(subnetId.getValue());
+                if (federatedSubnetId != null) {
+                    Adjacency federatedAdjacency = new AdjacencyBuilder(adjacency)
+                            .setSubnetId(new Uuid(federatedSubnetId)).build();
+                    federatedAdjacencies.add(federatedAdjacency);
+                } else {
+                    LOG.warn("Adjacency {} not federated. Federated subnet id not found", adjacency);
+                }
+            } else {
+                federatedAdjacencies.add(adjacency);
+            }
+        }
+
+        return federatedAdjacencies;
+    }
+
+    private VpnInterface transformVpnInterface(VpnInterface vpnInterface, int generationNumber, String remoteIp) {
+        String subnetId = FederationPluginUtils.getSubnetIdFromVpnInterface(vpnInterface);
+        if (subnetId == null) {
+            LOG.error("Subnet id not found for VPN interface {}", vpnInterface.getName());
+            return null;
+        }
+
+        String vpnId = subnetVpnAssociationManager.getSubnetVpn(subnetId);
+        if (vpnId == null) {
+            LOG.error("No VPN id found for subnet id {} for interface {}", subnetId, vpnInterface.getName());
+            return null;
+        }
+
+        VpnInterfaceBuilder vpnInterfaceBuilder = new VpnInterfaceBuilder(vpnInterface);
+        vpnInterfaceBuilder.setVpnInstanceName(vpnId);
+        vpnInterfaceBuilder.addAugmentation(VpnShadowProperties.class,
+                new VpnShadowPropertiesBuilder(vpnInterfaceBuilder.getAugmentation(VpnShadowProperties.class))
+                        .setShadow(true).setGenerationNumber(generationNumber).setRemoteIp(remoteIp).build());
+        return vpnInterfaceBuilder.build();
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/main/resources/org/opendaylight/blueprint/federationplugin.xml b/vpnservice/federation-plugin/impl/src/main/resources/org/opendaylight/blueprint/federationplugin.xml
new file mode 100644 (file)
index 0000000..8ad4164
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
+           odl:use-default-for-reference-types="true">
+
+    <reference id="dataBroker"
+        interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"
+        odl:type="default" />
+
+    <reference id="rpcProviderRegistry"
+        interface="org.opendaylight.controller.sal.binding.api.RpcProviderRegistry" />
+
+    <reference id="iElanService"
+        interface="org.opendaylight.netvirt.elanmanager.api.IElanService" />
+
+    <reference id="clusterSingletonServiceProvider"
+        interface="org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider"
+        odl:type="default" />
+
+    <reference id="iFederationConsumerMgr"
+        interface="org.opendaylight.federation.service.api.IFederationConsumerMgr" />
+
+    <reference id="iFederationProducerMgr"
+        interface="org.opendaylight.federation.service.api.IFederationProducerMgr" />
+
+    <odl:rpc-implementation ref="federationPluginMgr"/>
+
+</blueprint>
diff --git a/vpnservice/federation-plugin/impl/src/test/java/org/opendaylight/netvirt/federation/plugin/end2end/AbstractEnd2EndTest.java b/vpnservice/federation-plugin/impl/src/test/java/org/opendaylight/netvirt/federation/plugin/end2end/AbstractEnd2EndTest.java
new file mode 100644 (file)
index 0000000..9f42acf
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin.end2end;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.federation.service.api.IConsumerManagement;
+import org.opendaylight.federation.service.api.message.WrapperEntityFederationMessage;
+import org.opendaylight.federation.service.impl.FederationProducerMgr;
+import org.opendaylight.federation.service.impl.WrapperConsumer;
+import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
+import org.opendaylight.messagequeue.AbstractFederationMessage;
+import org.opendaylight.messagequeue.IMessageBusClient;
+import org.opendaylight.netvirt.elanmanager.api.IElanService;
+import org.opendaylight.netvirt.federation.plugin.FederatedNetworkPair;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginEgress;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginIngress;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginMgr;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginUtils;
+import org.opendaylight.netvirt.federation.plugin.SubnetVpnAssociationManager;
+import org.opendaylight.netvirt.federation.plugin.creators.FederationElanInterfaceModificationCreator;
+import org.opendaylight.netvirt.federation.plugin.creators.FederationIetfInterfaceModificationCreator;
+import org.opendaylight.netvirt.federation.plugin.creators.FederationInventoryNodeModificationCreator;
+import org.opendaylight.netvirt.federation.plugin.creators.FederationTopologyNodeModificationCreator;
+import org.opendaylight.netvirt.federation.plugin.creators.FederationVpnInterfaceModificationCreator;
+import org.opendaylight.netvirt.federation.plugin.filters.FederationElanInterfaceFilter;
+import org.opendaylight.netvirt.federation.plugin.filters.FederationIetfInterfaceFilter;
+import org.opendaylight.netvirt.federation.plugin.filters.FederationInventoryNodeFilter;
+import org.opendaylight.netvirt.federation.plugin.filters.FederationTopologyNodeFilter;
+import org.opendaylight.netvirt.federation.plugin.filters.FederationVpnInterfaceFilter;
+import org.opendaylight.netvirt.federation.plugin.identifiers.FederationElanInterfaceIdentifier;
+import org.opendaylight.netvirt.federation.plugin.identifiers.FederationIetfInterfaceIdentifier;
+import org.opendaylight.netvirt.federation.plugin.identifiers.FederationInventoryNodeIdentifier;
+import org.opendaylight.netvirt.federation.plugin.identifiers.FederationTopologyNodeIdentifier;
+import org.opendaylight.netvirt.federation.plugin.identifiers.FederationVpnInterfaceIdentifier;
+import org.opendaylight.netvirt.federation.plugin.transformers.FederationElanInterfaceTransformer;
+import org.opendaylight.netvirt.federation.plugin.transformers.FederationIetfInterfaceTransformer;
+import org.opendaylight.netvirt.federation.plugin.transformers.FederationInventoryNodeTransformer;
+import org.opendaylight.netvirt.federation.plugin.transformers.FederationTopologyNodeTransformer;
+import org.opendaylight.netvirt.federation.plugin.transformers.FederationVpnInterfaceTransformer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.FederationGenerations;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federation.generations.RemoteSiteGenerationInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federation.generations.RemoteSiteGenerationInfoBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.federation.plugin.manager.rev170219.federation.generations.RemoteSiteGenerationInfoKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.federation.service.config.rev161110.FederationConfigData;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+
+public class AbstractEnd2EndTest {
+
+    protected DataBroker dataBroker = mock(DataBroker.class);
+    protected IElanService elanService = mock(IElanService.class);
+    protected SubnetVpnAssociationManager associationMgr = mock(SubnetVpnAssociationManager.class);
+    protected LinkedList<AbstractFederationMessage> sentMessages;
+    public LinkedList<MergedObject> mergedObjects = new LinkedList<>();
+
+    protected FederationProducerMgr producer;
+    protected FederationPluginEgress egressPlugin;
+    protected final HashMap<String, DataTreeChangeListener<?>> keyToListener = new HashMap<>();
+
+    @Mock
+    protected ReadOnlyTransaction mockReadTx;
+
+    protected WriteTransaction stubWriteTx = mock(WriteTransaction.class);
+
+    protected IMessageBusClient msgBusConsumerMock = mock(IMessageBusClient.class);
+
+    protected ClusterSingletonServiceProvider singletonService = mock(ClusterSingletonServiceProvider.class);
+
+    protected IConsumerManagement consumerMgr = mock(IConsumerManagement.class);
+
+    @Mock
+    protected FederationPluginMgr mgr;
+
+    protected FederationPluginIngress ingressPlugin;
+
+    protected WrapperConsumer wrapperConsumer;
+
+    @Mock
+    FederationConfigData configMock;
+
+    @SuppressWarnings("rawtypes")
+    protected HashMap<String, ArgumentCaptor<DataTreeChangeListener>> listenerKeyToCaptor;
+
+    protected final ArgumentCaptor<AbstractFederationMessage> msgCaptor = ArgumentCaptor
+            .forClass(AbstractFederationMessage.class);
+
+    protected static final String REMOTE_IP = "1.1.1.1";
+    protected static final String DUMMYINTERFACE = "dummyinterface";
+    protected static final String REMOTE_TENANT_ID = "remoteTenantId";
+    protected static final String LOCAL_TENANT_ID = "localTenantId";
+    protected static final String PRODUCER_SUBNET_ID = "11112222-ffff-aaaa-2222-333444555666";
+    protected static final String CONSUMER_SUBNET_ID = "11113333-ffff-aaaa-2222-333444555666";
+    protected static final String PRODUCER_NETWORK_ID = "remoteNetworkId";
+    protected static final String CONSUMER_NETWORK_ID = "localNetworkId";
+    protected static final String CONSUMER1_QUEUE = "consumer1Queue";
+    protected static final String CONSUMER2_QUEUE = "consumer2Queue";
+    protected static final String INTEGRATION_BRIDGE_PREFIX = "bridge/br-int";
+
+    public AbstractEnd2EndTest() {
+        FederationInventoryNodeTransformer inventoryNodeTransformer = new FederationInventoryNodeTransformer();
+        new FederationInventoryNodeFilter(dataBroker, inventoryNodeTransformer);
+        new FederationInventoryNodeModificationCreator();
+        new FederationInventoryNodeIdentifier();
+        new FederationTopologyNodeTransformer();
+        new FederationTopologyNodeFilter();
+        new FederationTopologyNodeModificationCreator();
+        new FederationTopologyNodeIdentifier();
+        new FederationIetfInterfaceTransformer();
+        new FederationIetfInterfaceFilter(dataBroker, elanService);
+        new FederationIetfInterfaceModificationCreator();
+        new FederationIetfInterfaceIdentifier();
+        new FederationElanInterfaceTransformer();
+        new FederationElanInterfaceFilter(dataBroker, elanService);
+        new FederationElanInterfaceModificationCreator();
+        new FederationElanInterfaceIdentifier();
+        new FederationVpnInterfaceFilter(dataBroker, associationMgr);
+        new FederationVpnInterfaceTransformer(associationMgr);
+        new FederationVpnInterfaceModificationCreator();
+        new FederationVpnInterfaceIdentifier();
+    }
+
+    public void initialization() {
+        sentMessages = new LinkedList<>();
+        producer = new FederationProducerMgr(msgBusConsumerMock, dataBroker, configMock, singletonService, consumerMgr);
+        producer.attachPluginFactory("netvirt", (payload, queueName, contextId) -> egressPlugin);
+        List<FederatedNetworkPair> federatedNetworkPairs = Arrays.asList(new FederatedNetworkPair(CONSUMER_NETWORK_ID,
+                PRODUCER_NETWORK_ID, CONSUMER_SUBNET_ID, PRODUCER_SUBNET_ID, LOCAL_TENANT_ID, REMOTE_TENANT_ID));
+        egressPlugin = new FederationPluginEgress(producer, federatedNetworkPairs, CONSUMER1_QUEUE, CONSUMER1_QUEUE);
+        ingressPlugin = new FederationPluginIngress(mgr, dataBroker, REMOTE_IP, federatedNetworkPairs);
+        wrapperConsumer = new WrapperConsumer(REMOTE_IP, ingressPlugin);
+
+        prepareMocks();
+    }
+
+    private void prepareMocks() {
+        HashMap<String, DataTreeIdentifier<?>> listenerKeyToIdentifer = new HashMap<>();
+        listenerKeyToCaptor = new HashMap<>();
+        egressPlugin.getListenersData().forEach(p -> listenerKeyToIdentifer.put(p.listenerId, p.listenerPath));
+        egressPlugin.getListenersData().forEach(
+            p -> listenerKeyToCaptor.put(p.listenerId, ArgumentCaptor.forClass(DataTreeChangeListener.class)));
+        for (String key : FederationPluginUtils.getOrderedListenerKeys()) {
+            when(dataBroker.registerDataTreeChangeListener(eq(listenerKeyToIdentifer.get(key)),
+                    listenerKeyToCaptor.get(key).capture())).thenReturn(null);
+        }
+
+        doAnswer(invocation -> {
+            AbstractFederationMessage msg = (AbstractFederationMessage) invocation.getArguments()[0];
+            sentMessages.add(msg);
+            wrapperConsumer.consumeMsg(msg);
+            return null;
+        }).when(msgBusConsumerMock).sendMsg(any(), anyString());
+
+        doReturn("2.2.2.2").when(configMock).getMqBrokerIp();
+        doReturn(stubWriteTx).when(dataBroker).newWriteOnlyTransaction();
+        when(associationMgr.getSubnetVpn(any())).thenReturn("dummySubnetVpn");
+        doReturn(mockReadTx).when(dataBroker).newReadOnlyTransaction();
+
+        when(stubWriteTx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+        doAnswer(invocation -> {
+            InstanceIdentifier<?> path = (InstanceIdentifier<?>) invocation.getArguments()[1];
+            DataObject data = (DataObject) invocation.getArguments()[2];
+            mergedObjects.add(new MergedObject(data, path));
+            return null;
+        }).when(stubWriteTx).merge(any(), any(), any());
+    }
+
+    protected class MergedObject {
+        public DataObject obj;
+        public InstanceIdentifier<?> insId;
+
+        public MergedObject(DataObject obj, InstanceIdentifier<?> insId) {
+            this.obj = obj;
+            this.insId = insId;
+        }
+    }
+
+    public int mergedObjectsAmount() {
+        return mergedObjects.size();
+    }
+
+    protected <T extends DataObject> T removeLastMerged(Class<T> type) {
+        return type.cast(mergedObjects.removeLast().obj);
+    }
+
+    protected <T extends DataObject> T removeFirstMerged(Class<T> type) {
+        return type.cast(mergedObjects.removeFirst().obj);
+    }
+
+    protected String lastSentJson() {
+        AbstractFederationMessage lastMsg = sentMessages.getLast();
+        WrapperEntityFederationMessage wrapper = (WrapperEntityFederationMessage) lastMsg;
+        return wrapper.getPayload().getJsonInput();
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    protected void dcn(String listenerKey, DataObject newObject) {
+        DataTreeChangeListener listener = getListenerForKey(listenerKey);
+        listener.onDataTreeChanged(change(newObject, FederationPluginUtils.getListenerDatastoreType(listenerKey),
+                FederationPluginUtils.getInstanceIdentifier(listenerKey)));
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    protected void dcns(String listenerKey, List<? extends DataObject> newObjects) {
+        DataTreeChangeListener listener = getListenerForKey(listenerKey);
+        listener.onDataTreeChanged(changes(FederationPluginUtils.getListenerDatastoreType(listenerKey),
+                FederationPluginUtils.getInstanceIdentifier(listenerKey), newObjects));
+    }
+
+    protected DataTreeChangeListener<?> getListenerForKey(String listenerKey) {
+        return listenerKeyToCaptor.get(listenerKey).getValue();
+    }
+
+    protected Collection<DataTreeModification<?>> changes(LogicalDatastoreType datastoreType,
+            InstanceIdentifier<? extends DataObject> instanceIdentifier, List<? extends DataObject> dataObjects) {
+        ArrayList<DataTreeModification<?>> changes = new ArrayList<>();
+        dataObjects.forEach(data -> changes.add(new FakeDataTreeModification(data, datastoreType, instanceIdentifier)));
+        return changes;
+    }
+
+    protected Collection<?> change(DataObject newObject, LogicalDatastoreType datastoreType,
+            InstanceIdentifier<? extends DataObject> instanceIdentifier) {
+        ArrayList<DataTreeModification<?>> changes = new ArrayList<>();
+        changes.add(new FakeDataTreeModification(newObject, datastoreType, instanceIdentifier));
+        return changes;
+    }
+
+    protected void setGenerationNumberMock() {
+        int generationNumberValue = 2;
+        KeyedInstanceIdentifier<RemoteSiteGenerationInfo, RemoteSiteGenerationInfoKey>
+            generationNumberPath = InstanceIdentifier.create(FederationGenerations.class)
+                .child(RemoteSiteGenerationInfo.class, new RemoteSiteGenerationInfoKey(REMOTE_IP));
+        RemoteSiteGenerationInfo generationNumber = new RemoteSiteGenerationInfoBuilder().setRemoteIp(REMOTE_IP)
+                .setGenerationNumber(generationNumberValue).build();
+        when(mockReadTx.read(LogicalDatastoreType.CONFIGURATION, generationNumberPath))
+                .thenReturn(Futures.immediateCheckedFuture(Optional.of(generationNumber)));
+    }
+
+    @SuppressWarnings("rawtypes")
+    public class FakeDataTreeModification implements DataTreeModification {
+
+        private final FakeDataObjectModification fakeMod;
+        private final LogicalDatastoreType dataStoreType;
+        private final InstanceIdentifier<? extends DataObject> instanceIdentifier;
+
+        public FakeDataTreeModification(DataObject theObject, LogicalDatastoreType datastoreType,
+                InstanceIdentifier<? extends DataObject> instanceIdentifier) {
+            fakeMod = new FakeDataObjectModification(theObject);
+            this.dataStoreType = datastoreType;
+            this.instanceIdentifier = instanceIdentifier;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public DataTreeIdentifier<?> getRootPath() {
+            return new DataTreeIdentifier(dataStoreType, instanceIdentifier);
+        }
+
+        @Override
+        public DataObjectModification<?> getRootNode() {
+            return fakeMod;
+        }
+
+    }
+
+    @SuppressWarnings("rawtypes")
+    public class FakeDataObjectModification implements DataObjectModification {
+
+        private final DataObject theObject;
+
+        private final ModificationType modType = ModificationType.WRITE;
+
+        public FakeDataObjectModification(DataObject theObject) {
+            this.theObject = theObject;
+        }
+
+        @Override
+        public PathArgument getIdentifier() {
+            return null;
+        }
+
+        @Override
+        public Class getDataType() {
+            return null;
+        }
+
+        @Override
+        public ModificationType getModificationType() {
+            return modType;
+        }
+
+        @Override
+        public DataObject getDataBefore() {
+            return null;
+        }
+
+        @Override
+        public DataObject getDataAfter() {
+            return theObject;
+        }
+
+        @Override
+        public Collection<?> getModifiedChildren() {
+            return null;
+        }
+
+        @Override
+        public DataObjectModification<?> getModifiedChildContainer(Class child) {
+            return null;
+        }
+
+        @Override
+        public DataObjectModification<?> getModifiedAugmentation(Class augmentation) {
+            return null;
+        }
+
+        @Override
+        public DataObjectModification<?> getModifiedChildListItem(Class listItem, Identifier listKey) {
+            return null;
+        }
+
+        @Override
+        public DataObjectModification<?> getModifiedChild(PathArgument childArgument) {
+            return null;
+        }
+
+    }
+}
diff --git a/vpnservice/federation-plugin/impl/src/test/java/org/opendaylight/netvirt/federation/plugin/end2end/End2EndFullSyncTest.java b/vpnservice/federation-plugin/impl/src/test/java/org/opendaylight/netvirt/federation/plugin/end2end/End2EndFullSyncTest.java
new file mode 100644 (file)
index 0000000..b24d8f0
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin.end2end;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map.Entry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.federation.service.api.message.EndFullSyncFederationMessage;
+import org.opendaylight.federation.service.api.message.SubscribeMessage;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginUtils;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfacesBuilder;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.ElanInterfacesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@RunWith(MockitoJUnitRunner.class)
+public class End2EndFullSyncTest extends AbstractEnd2EndTest {
+
+    private final HashMap<String, DataObject> listenerToObjects = new HashMap<>();
+    private final HashMap<String, LogicalDatastoreType> listenerToDataStoreType = new HashMap<>();
+
+    @Before
+    public void setUp() {
+        super.initialization();
+        listenerToObjects.clear();
+        egressPlugin.getListenersData().forEach(p -> {
+            listenerToObjects.put(p.listenerId, null);
+            listenerToDataStoreType.put(p.listenerId, p.checkExistingDataPath.getDatastoreType());
+        });
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void mixedEntitiesOrderAndAmount() {
+        Adjacencies adjacencies = new AdjacenciesBuilder().setAdjacency(Arrays.asList(new AdjacencyBuilder()
+                .setSubnetId(new Uuid(PRODUCER_SUBNET_ID)).setPrimaryAdjacency(true).setIpAddress("7.7.7.7").build()))
+                .build();
+
+        VpnInterface vpnIface = new VpnInterfaceBuilder().addAugmentation(Adjacencies.class, adjacencies)
+                .setName(DUMMYINTERFACE).build();
+        prepareDataObject(FederationPluginConstants.VPN_INTERFACE_KEY,
+                new VpnInterfacesBuilder().setVpnInterface(Arrays.asList(vpnIface)).build());
+
+        Node operNode = new NodeBuilder().setNodeId(new NodeId("bridge/br-int-opernode")).build();
+        prepareDataObject(FederationPluginConstants.TOPOLOGY_NODE_OPER_KEY,
+                new TopologyBuilder().setNode(Arrays.asList(operNode)).build());
+
+        ElanInterface syncedElanInterface = new ElanInterfaceBuilder().setElanInstanceName(PRODUCER_NETWORK_ID)
+                .setName(DUMMYINTERFACE).setKey(new ElanInterfaceKey(DUMMYINTERFACE)).build();
+        prepareDataObject(FederationPluginConstants.ELAN_INTERFACE_KEY,
+                new ElanInterfacesBuilder().setElanInterface(Arrays.asList(syncedElanInterface)).build());
+
+        Interface iface = new InterfaceBuilder().setType(L2vlan.class).setName(DUMMYINTERFACE)
+                .setKey(new InterfaceKey(DUMMYINTERFACE)).build();
+        prepareDataObject(FederationPluginConstants.IETF_INTERFACE_KEY,
+                new InterfacesBuilder().setInterface(Arrays.asList(iface)).build());
+
+        Node configNode = new NodeBuilder().setNodeId(new NodeId("bridge/br-int-confignode")).build();
+        prepareDataObject(FederationPluginConstants.TOPOLOGY_NODE_CONFIG_KEY,
+                new TopologyBuilder().setNode(Arrays.asList(configNode)).build());
+
+        org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node
+            inventoryNode = new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder()
+                .setId(new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId("j")).build();
+        prepareDataObject(FederationPluginConstants.INVENTORY_NODE_CONFIG_KEY,
+                new NodesBuilder().setNode(Arrays.asList(inventoryNode)).build());
+
+        when(mockReadTx.read(any(), any())).thenReturn(Futures.immediateCheckedFuture(Optional.absent()));
+        when(mockReadTx.read(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.builder(VpnInterfaces.class)
+                        .child(VpnInterface.class, new VpnInterfaceKey(DUMMYINTERFACE)).build()))
+                                .thenReturn(Futures.immediateCheckedFuture(Optional.of(vpnIface)));
+        when(mockReadTx.read(LogicalDatastoreType.CONFIGURATION,
+                InstanceIdentifier.builder(VpnInterfaces.class)
+                        .child(VpnInterface.class, new VpnInterfaceKey("other interface")).build()))
+                                .thenReturn(Futures.immediateCheckedFuture(Optional.of(vpnIface)));
+        subscribe();
+        assertEquals(8, sentMessages.size());
+        assertEquals(6, mergedObjectsAmount());
+        assertTrue(lastMessageIsCommitMessage());
+        assertEquals("bridge/br-int-confignode", removeFirstMerged(Node.class).getNodeId().getValue());
+        assertEquals("bridge/br-int-opernode", removeFirstMerged(Node.class).getNodeId().getValue());
+        assertEquals("j",
+                removeFirstMerged(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class)
+                        .getId().getValue());
+        assertEquals(DUMMYINTERFACE, removeFirstMerged(Interface.class).getName());
+        assertEquals(DUMMYINTERFACE, removeFirstMerged(ElanInterface.class).getName());
+        assertEquals(DUMMYINTERFACE, removeFirstMerged(VpnInterface.class).getName());
+    }
+
+    private boolean lastMessageIsCommitMessage() {
+        return sentMessages.removeLast() instanceof EndFullSyncFederationMessage;
+    }
+
+    @SuppressWarnings("unchecked")
+    private void subscribe() {
+        for (Entry<String, DataObject> listener : listenerToObjects.entrySet()) {
+            if (listener.getValue() == null) {
+                when(mockReadTx.read(listenerToDataStoreType.get(listener.getKey()),
+                        FederationPluginUtils.getParentInstanceIdentifier(listener.getKey())))
+                                .thenReturn(Futures.immediateCheckedFuture(Optional.absent()));
+            } else {
+                when(mockReadTx.read(listenerToDataStoreType.get(listener.getKey()),
+                        FederationPluginUtils.getParentInstanceIdentifier(listener.getKey()))).thenReturn(
+                                Futures.immediateCheckedFuture(Optional.of(preparedObject(listener.getKey()))),
+                                Futures.immediateCheckedFuture(Optional.absent()));
+            }
+        }
+        producer.handleSubscribeMsg(
+                new SubscribeMessage(CONSUMER1_QUEUE, "netvirt", CONSUMER1_QUEUE, "1.2.3.4", CONSUMER1_QUEUE));
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends DataObject> T preparedObject(String key) {
+        return (T) listenerToObjects.get(key);
+    }
+
+    private void prepareDataObject(String listenerKey, DataObject containerNode) {
+        listenerToObjects.put(listenerKey, containerNode);
+    }
+
+}
diff --git a/vpnservice/federation-plugin/impl/src/test/java/org/opendaylight/netvirt/federation/plugin/end2end/End2EndSteadyTest.java b/vpnservice/federation-plugin/impl/src/test/java/org/opendaylight/netvirt/federation/plugin/end2end/End2EndSteadyTest.java
new file mode 100644 (file)
index 0000000..303ffe5
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2017 Hewlett Packard Enterprise, Co. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.federation.plugin.end2end;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.federation.service.api.message.SubscribeMessage;
+import org.opendaylight.netvirt.federation.plugin.FederationPluginConstants;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableStatisticsGatheringStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableStatisticsGatheringStatusBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.snapshot.gathering.status.grouping.SnapshotGatheringStatusEndBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.interfaces.ElanInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder;
+
+@RunWith(MockitoJUnitRunner.class)
+public class End2EndSteadyTest extends AbstractEnd2EndTest {
+
+    public End2EndSteadyTest() {
+
+    }
+
+    @Before
+    public void setUp() {
+        super.initialization();
+        prepareMocks();
+        producer.handleSubscribeMsg(
+                new SubscribeMessage(CONSUMER1_QUEUE, "netvirt", CONSUMER1_QUEUE, "1.2.3.4", CONSUMER1_QUEUE));
+        sentMessages.clear();// Clear the commit message
+    }
+
+    private void prepareMocks() {
+        when(mockReadTx.read(any(), any())).thenReturn(Futures.immediateCheckedFuture(Optional.absent()));
+        setGenerationNumberMock();
+    }
+
+    @Test
+    public void topologyNodeWithFilteredInName() { // Every nodeId should pass
+        Node node = new NodeBuilder().setNodeId(new NodeId(INTEGRATION_BRIDGE_PREFIX)).build();
+        dcn(FederationPluginConstants.TOPOLOGY_NODE_CONFIG_KEY, node);
+        assertEquals(1, sentMessages.size());
+        assertEquals(INTEGRATION_BRIDGE_PREFIX, removeLastMerged(Node.class).getNodeId().getValue());
+    }
+
+    @Test
+    public void vpnInterfaceWithoutAdjacenciesShouldBeFilteredOut() {
+        VpnInterface vpnIface = new VpnInterfaceBuilder().build();
+        dcn(FederationPluginConstants.VPN_INTERFACE_KEY, vpnIface);
+        assertEquals(0, sentMessages.size());
+    }
+
+    @Test
+    public void vpnInterfaceValid() {
+        Adjacencies adjacencies = new AdjacenciesBuilder().setAdjacency(Arrays.asList(new AdjacencyBuilder()
+                .setSubnetId(new Uuid(PRODUCER_SUBNET_ID)).setPrimaryAdjacency(true).setIpAddress("7.7.7.7").build()))
+                .build();
+        VpnInterface vpnIface = new VpnInterfaceBuilder().addAugmentation(Adjacencies.class, adjacencies)
+                .setName(DUMMYINTERFACE).build();
+        dcn(FederationPluginConstants.VPN_INTERFACE_KEY, vpnIface);
+        assertEquals(1, sentMessages.size());
+        assertEquals(CONSUMER_SUBNET_ID, removeLastMerged(VpnInterface.class).getAugmentation(Adjacencies.class)
+                .getAdjacency().get(0).getSubnetId().getValue());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void inventoryNodeTruncatedStatisticsAugmentations() {
+        org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder
+            nodeBuilder = new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder()
+                .setId(new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId("dummynode"));
+        FlowCapableStatisticsGatheringStatus statistics = new FlowCapableStatisticsGatheringStatusBuilder()
+                .setSnapshotGatheringStatusEnd(new SnapshotGatheringStatusEndBuilder()
+                        .setEnd(new DateAndTime("1986-09-04T15:12:10.5Z")).build())
+                .build();
+        nodeBuilder.addAugmentation(FlowCapableStatisticsGatheringStatus.class, statistics);
+        dcn(FederationPluginConstants.INVENTORY_NODE_CONFIG_KEY, nodeBuilder.build());
+        assertEquals(1, sentMessages.size());
+        org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node lastMergedNode = removeLastMerged(
+                org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class);
+        assertNull(lastMergedNode.getAugmentation(FlowCapableStatisticsGatheringStatus.class));
+    }
+
+    @Test
+    public void topologyNodeWithFilteredOutTerminationPoint() {
+        List<TerminationPoint> tps = Arrays.asList(new TerminationPointBuilder().setTpId(new TpId("tunDummy")).build());
+        Node node = new NodeBuilder().setNodeId(new NodeId(INTEGRATION_BRIDGE_PREFIX)).setTerminationPoint(tps).build();
+        dcn(FederationPluginConstants.TOPOLOGY_NODE_CONFIG_KEY, node);
+
+        assertEquals(1, sentMessages.size());
+        assertEquals(0, removeLastMerged(Node.class).getTerminationPoint().size());
+    }
+
+    @Test
+    public void topologyNodeWithFilteredInTerminationPoint() {
+        List<TerminationPoint> tps = Arrays
+                .asList(new TerminationPointBuilder().setTpId(new TpId("nottunDummy")).build());
+        Node node = new NodeBuilder().setNodeId(new NodeId(INTEGRATION_BRIDGE_PREFIX)).setTerminationPoint(tps).build();
+        dcn(FederationPluginConstants.TOPOLOGY_NODE_CONFIG_KEY, node);
+        assertEquals(1, sentMessages.size());
+        assertEquals("nottunDummy", removeLastMerged(Node.class).getTerminationPoint().get(0).getTpId().getValue());
+    }
+
+    @Test
+    public void elanInterfaceWrongNetworkFilteredOut() {
+        ElanInterface elanIface = new ElanInterfaceBuilder().setElanInstanceName("dummynetwork").setName(DUMMYINTERFACE)
+                .setKey(new ElanInterfaceKey(DUMMYINTERFACE)).build();
+        dcn(FederationPluginConstants.ELAN_INTERFACE_KEY, elanIface);
+        assertEquals(0, sentMessages.size());
+    }
+
+    @Test
+    public void interfaceQueuedAndThenReleased() {
+        Interface iface = new InterfaceBuilder().setType(L2vlan.class).setName(DUMMYINTERFACE)
+                .setKey(new InterfaceKey(DUMMYINTERFACE)).build();
+        when(elanService.isExternalInterface(any())).thenReturn(false);
+        when(elanService.getElanInterfaceByElanInterfaceName(any())).thenReturn(null);
+        dcn(FederationPluginConstants.IETF_INTERFACE_KEY, iface);
+        assertEquals(0, sentMessages.size());
+
+        ElanInterface elanIface = new ElanInterfaceBuilder().setElanInstanceName(PRODUCER_NETWORK_ID)
+                .setName(DUMMYINTERFACE).setKey(new ElanInterfaceKey(DUMMYINTERFACE)).build();
+        dcn(FederationPluginConstants.ELAN_INTERFACE_KEY, elanIface);
+        assertEquals(2, sentMessages.size());
+        assertEquals(DUMMYINTERFACE, removeFirstMerged(Interface.class).getName());
+        assertEquals(CONSUMER_NETWORK_ID, removeFirstMerged(ElanInterface.class).getElanInstanceName());
+
+        when(elanService.getElanInterfaceByElanInterfaceName(any())).thenReturn(elanIface);
+        dcn(FederationPluginConstants.IETF_INTERFACE_KEY, iface);
+        assertEquals(3, sentMessages.size());
+        assertEquals(DUMMYINTERFACE, removeFirstMerged(Interface.class).getName());
+    }
+}
index 5ddb61891c54a63bde5d27f15360173611f8aa4a..f704aaff2b99889e30387233b063d1f93157aaec 100644 (file)
@@ -12,6 +12,7 @@ public class NeutronConstants {
     public static final String DEVICE_OWNER_GATEWAY_INF = "network:router_gateway";
     public static final String DEVICE_OWNER_ROUTER_INF = "network:router_interface";
     public static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
+    public static final String DEVICE_OWNER_DHCP = "network:dhcp";
     public static final String FLOATING_IP_DEVICE_ID_PENDING = "PENDING";
     public static final String PREFIX_TAP = "tap";
     public static final String PREFIX_VHOSTUSER = "vhu";