--- /dev/null
+/*
+ * 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 + "]";
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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";
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+ }
+}
--- /dev/null
+/*
+ * 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));
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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));
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+
+}
--- /dev/null
+/*
+ * 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,
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+<?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>
--- /dev/null
+/*
+ * 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;
+ }
+
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
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";