From b697e64a352a88ed7423bc3a3bc15241426ed14b Mon Sep 17 00:00:00 2001 From: abhijith-css Date: Wed, 9 Jul 2025 10:30:33 +0530 Subject: [PATCH 01/15] version changes --- distribution/karaf/pom.xml | 2 +- features/feature-parent/pom.xml | 2 +- features/features-l2switch/pom.xml | 2 +- features/pom.xml | 2 +- parent/pom.xml | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/distribution/karaf/pom.xml b/distribution/karaf/pom.xml index 82a979ad5..ade6a3fc6 100644 --- a/distribution/karaf/pom.xml +++ b/distribution/karaf/pom.xml @@ -4,7 +4,7 @@ org.opendaylight.odlparent karaf4-parent - 13.1.3 + 14.0.9 distribution-karaf diff --git a/features/feature-parent/pom.xml b/features/feature-parent/pom.xml index 40d3c4dd2..b6357d2ce 100644 --- a/features/feature-parent/pom.xml +++ b/features/feature-parent/pom.xml @@ -12,7 +12,7 @@ org.opendaylight.odlparent single-feature-parent - 13.1.3 + 14.0.9 diff --git a/features/features-l2switch/pom.xml b/features/features-l2switch/pom.xml index aa9d5c72d..92c77378b 100644 --- a/features/features-l2switch/pom.xml +++ b/features/features-l2switch/pom.xml @@ -12,7 +12,7 @@ org.opendaylight.odlparent feature-repo-parent - 13.1.3 + 14.0.9 diff --git a/features/pom.xml b/features/pom.xml index 4b93022da..55a2ea931 100644 --- a/features/pom.xml +++ b/features/pom.xml @@ -11,7 +11,7 @@ org.opendaylight.odlparent odlparent-lite - 13.1.3 + 14.0.9 diff --git a/parent/pom.xml b/parent/pom.xml index 11be8f62b..a71ba10c3 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -5,7 +5,7 @@ org.opendaylight.mdsal binding-parent - 12.0.6 + 14.0.12 org.opendaylight.l2switch @@ -18,14 +18,14 @@ org.opendaylight.controller controller-artifacts - 8.0.6 + 10.0.9 import pom org.opendaylight.openflowplugin openflowplugin-artifacts - 0.17.3 + 0.19.2 import pom From 51a495e9c69c20ba5e0e764f0d785e4eb1f8072f Mon Sep 17 00:00:00 2001 From: abhijith-css Date: Wed, 9 Jul 2025 10:52:13 +0530 Subject: [PATCH 02/15] version changes --- hosttracker/implementation/pom.xml | 1 + parent/pom.xml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/hosttracker/implementation/pom.xml b/hosttracker/implementation/pom.xml index c1752f102..1c1624bb4 100644 --- a/hosttracker/implementation/pom.xml +++ b/hosttracker/implementation/pom.xml @@ -20,6 +20,7 @@ org.opendaylight.mdsal yang-binding + 13.0.8 org.opendaylight.mdsal diff --git a/parent/pom.xml b/parent/pom.xml index a71ba10c3..e14229de9 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -13,6 +13,11 @@ 0.8.0-SNAPSHOT pom + + false + true + + From 938ab7c11232fbc16b93b5cef7b42438c0739d3b Mon Sep 17 00:00:00 2001 From: abhijith-css Date: Wed, 9 Jul 2025 11:20:08 +0530 Subject: [PATCH 03/15] packet handler issue fix --- .../l2switch/packethandler/decoders/AbstractPacketDecoder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packethandler/implementation/src/main/java/org/opendaylight/l2switch/packethandler/decoders/AbstractPacketDecoder.java b/packethandler/implementation/src/main/java/org/opendaylight/l2switch/packethandler/decoders/AbstractPacketDecoder.java index 961f180a6..c7720e080 100644 --- a/packethandler/implementation/src/main/java/org/opendaylight/l2switch/packethandler/decoders/AbstractPacketDecoder.java +++ b/packethandler/implementation/src/main/java/org/opendaylight/l2switch/packethandler/decoders/AbstractPacketDecoder.java @@ -13,8 +13,9 @@ import org.opendaylight.mdsal.binding.api.NotificationPublishService; import org.opendaylight.mdsal.binding.api.NotificationService; import org.opendaylight.mdsal.binding.api.NotificationService.Listener; +import org.opendaylight.yangtools.binding.Notification; import org.opendaylight.yangtools.concepts.Registration; -import org.opendaylight.yangtools.yang.binding.Notification; + /** * A base class for all decoders. Each extended decoder should also implement a notification listener that it can From ced9ffbe299d0a5f5d7bd5615bcb9d8b6c517ca6 Mon Sep 17 00:00:00 2001 From: i-am-arathypm Date: Wed, 9 Jul 2025 12:46:39 +0530 Subject: [PATCH 04/15] loopremover fixed --- .../loopremover/LoopRemoverProvider.java | 74 +-- .../loopremover/flow/InitialFlowWriter.java | 393 ++++++------- .../topology/NetworkGraphImpl.java | 315 +++++----- .../TopologyLinkDataChangeHandler.java | 541 +++++++++--------- .../util/InstanceIdentifierUtils.java | 182 +++--- 5 files changed, 746 insertions(+), 759 deletions(-) diff --git a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/LoopRemoverProvider.java b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/LoopRemoverProvider.java index a186bf07f..974a94851 100644 --- a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/LoopRemoverProvider.java +++ b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/LoopRemoverProvider.java @@ -11,7 +11,7 @@ import org.opendaylight.l2switch.loopremover.topology.NetworkGraphImpl; import org.opendaylight.l2switch.loopremover.topology.TopologyLinkDataChangeHandler; import org.opendaylight.mdsal.binding.api.DataBroker; -import org.opendaylight.mdsal.binding.api.RpcConsumerRegistry; +import org.opendaylight.mdsal.binding.api.RpcService; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlow; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.loop.remover.config.rev140528.LoopRemoverConfig; import org.opendaylight.yangtools.concepts.Registration; @@ -20,44 +20,44 @@ // FIXME: @Component(service = { }) once we have the required constructor public final class LoopRemoverProvider implements AutoCloseable { - private static final Logger LOG = LoggerFactory.getLogger(LoopRemoverProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(LoopRemoverProvider.class); - private Registration listenerRegistration; - private Registration topoNodeListnerReg; + private Registration listenerRegistration; + private Registration topoNodeListnerReg; - // FIXME: an @Activate constructor which deals with dynamic config - public LoopRemoverProvider(final DataBroker dataBroker, final RpcConsumerRegistry rpcService, - final LoopRemoverConfig config) { - // Write initial flows - if (config.getIsInstallLldpFlow()) { - LOG.info("LoopRemover will install an lldp flow"); - var initialFlowWriter = new InitialFlowWriter(rpcService.getRpc(AddFlow.class)); - initialFlowWriter.setFlowTableId(config.getLldpFlowTableId()); - initialFlowWriter.setFlowPriority(config.getLldpFlowPriority()); - initialFlowWriter.setFlowIdleTimeout(config.getLldpFlowIdleTimeout()); - initialFlowWriter.setFlowHardTimeout(config.getLldpFlowHardTimeout()); - topoNodeListnerReg = initialFlowWriter.registerAsDataChangeListener(dataBroker); - } + // FIXME: an @Activate constructor which deals with dynamic config + public LoopRemoverProvider(final DataBroker dataBroker, final RpcService rpcService, + final LoopRemoverConfig config) { + // Write initial flows + if (config.getIsInstallLldpFlow()) { + LOG.info("LoopRemover will install an lldp flow"); + var initialFlowWriter = new InitialFlowWriter(rpcService.getRpc(AddFlow.class)); + initialFlowWriter.setFlowTableId(config.getLldpFlowTableId()); + initialFlowWriter.setFlowPriority(config.getLldpFlowPriority()); + initialFlowWriter.setFlowIdleTimeout(config.getLldpFlowIdleTimeout()); + initialFlowWriter.setFlowHardTimeout(config.getLldpFlowHardTimeout()); + topoNodeListnerReg = initialFlowWriter.registerAsDataChangeListener(dataBroker); + } - // Register Topology DataChangeListener - var topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataBroker, new NetworkGraphImpl()); - topologyLinkDataChangeHandler.setGraphRefreshDelay(config.getGraphRefreshDelay().toJava()); - topologyLinkDataChangeHandler.setTopologyId(config.getTopologyId()); - listenerRegistration = topologyLinkDataChangeHandler.registerAsDataChangeListener(); - LOG.info("LoopRemover initialized."); - } + // Register Topology DataChangeListener + var topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataBroker, new NetworkGraphImpl()); + topologyLinkDataChangeHandler.setGraphRefreshDelay(config.getGraphRefreshDelay().toJava()); + topologyLinkDataChangeHandler.setTopologyId(config.getTopologyId()); + listenerRegistration = topologyLinkDataChangeHandler.registerAsDataChangeListener(); + LOG.info("LoopRemover initialized."); + } - // FIXME: @Deactivate - @Override - public void close() { - if (listenerRegistration != null) { - listenerRegistration.close(); - listenerRegistration = null; - } - if (topoNodeListnerReg != null) { - topoNodeListnerReg.close(); - topoNodeListnerReg = null; - } - LOG.info("LoopRemover (instance {}) torn down.", this); - } + // FIXME: @Deactivate + @Override + public void close() { + if (listenerRegistration != null) { + listenerRegistration.close(); + listenerRegistration = null; + } + if (topoNodeListnerReg != null) { + topoNodeListnerReg.close(); + topoNodeListnerReg = null; + } + LOG.info("LoopRemover (instance {}) torn down.", this); + } } diff --git a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/flow/InitialFlowWriter.java b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/flow/InitialFlowWriter.java index fb3ea3246..c64b30443 100644 --- a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/flow/InitialFlowWriter.java +++ b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/flow/InitialFlowWriter.java @@ -9,13 +9,14 @@ import static java.util.Objects.requireNonNull; -import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; + import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.mdsal.binding.api.DataObjectModification; import org.opendaylight.mdsal.binding.api.DataTreeChangeListener; @@ -58,9 +59,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetTypeBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder; -import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.binding.ExactDataObjectStep; +import org.opendaylight.yangtools.binding.util.BindingMap; +import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.util.BindingMap; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.Uint16; import org.opendaylight.yangtools.yang.common.Uint32; @@ -71,206 +74,190 @@ /** * Adds a flow, which sends all LLDP packets to the controller, on all switches. - * Registers as ODL Inventory listener so that it can add flows once a new node i.e. switch is added. + * Registers as ODL Inventory listener so that it can add flows once a new node + * i.e. switch is added. */ public class InitialFlowWriter implements DataTreeChangeListener { - private static final Logger LOG = LoggerFactory.getLogger(InitialFlowWriter.class); - - private static final String FLOW_ID_PREFIX = "L2switch-"; - private static final Uint32 LLDP_ETHER_TYPE = Uint32.valueOf(35020); - - private final ExecutorService initialFlowExecutor = Executors.newCachedThreadPool(); - private final AddFlow addFlow; - private Uint8 flowTableId = Uint8.ZERO; - private Uint16 flowPriority = Uint16.ZERO; - private Uint16 flowIdleTimeout = Uint16.ZERO; - private Uint16 flowHardTimeout = Uint16.ZERO; - - private final AtomicLong flowIdInc = new AtomicLong(); - private final AtomicLong flowCookieInc = new AtomicLong(0x2b00000000000000L); - - public InitialFlowWriter(AddFlow addFlow) { - this.addFlow = requireNonNull(addFlow); - } - - public void setFlowTableId(Uint8 flowTableId) { - this.flowTableId = requireNonNull(flowTableId); - } - - public void setFlowPriority(Uint16 flowPriority) { - this.flowPriority = requireNonNull(flowPriority); - } - - public void setFlowIdleTimeout(Uint16 flowIdleTimeout) { - this.flowIdleTimeout = requireNonNull(flowIdleTimeout); - } - - public void setFlowHardTimeout(Uint16 flowHardTimeout) { - this.flowHardTimeout = requireNonNull(flowHardTimeout); - } - - public ListenerRegistration registerAsDataChangeListener(DataBroker dataBroker) { - InstanceIdentifier nodeInstanceIdentifier = InstanceIdentifier.builder(Nodes.class) - .child(Node.class).build(); - - return dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, - nodeInstanceIdentifier), this); - } - - @Override - public void onDataTreeChanged(Collection> changes) { - Set> nodeIds = new HashSet<>(); - for (DataTreeModification change: changes) { - DataObjectModification rootNode = change.getRootNode(); - final InstanceIdentifier identifier = change.getRootPath().getRootIdentifier(); - switch (rootNode.getModificationType()) { - case WRITE: - if (rootNode.getDataBefore() == null) { - nodeIds.add(identifier); - } - break; - default: - break; - } - } - - if (!nodeIds.isEmpty()) { - initialFlowExecutor.execute(new InitialFlowWriterProcessor(nodeIds)); - } - } - - /** - * A private class to process the node updated event in separate thread. Allows to release the - * thread that invoked the data node updated event. Avoids any thread lock it may cause. - */ - private class InitialFlowWriterProcessor implements Runnable { - private final Set> nodeIds; - - InitialFlowWriterProcessor(Set> nodeIds) { - this.nodeIds = nodeIds; - } - - @Override - public void run() { - - if (nodeIds == null) { - return; - } - - for (InstanceIdentifier nodeId : nodeIds) { - if (Node.class.isAssignableFrom(nodeId.getTargetType())) { - InstanceIdentifier topoNodeId = (InstanceIdentifier)nodeId; - if (topoNodeId.firstKeyOf(Node.class).getId().getValue().contains("openflow:")) { - addInitialFlows(topoNodeId); - } - } - } - - } - - /** - * Adds a flow, which sends all LLDP packets to the controller, to the specified node. - * @param nodeId The node to write the flow on. - */ - public void addInitialFlows(InstanceIdentifier nodeId) { - LOG.debug("adding initial flows for node {} ", nodeId); - - InstanceIdentifier tableId = getTableInstanceId(nodeId); - InstanceIdentifier flowId = getFlowInstanceId(tableId); - - //add lldpToController flow - writeFlowToController(nodeId, tableId, flowId, createLldpToControllerFlow(flowTableId, flowPriority)); - - LOG.debug("Added initial flows for node {} ", nodeId); - } - - private InstanceIdentifier
getTableInstanceId(InstanceIdentifier nodeId) { - // get flow table key - TableKey flowTableKey = new TableKey(flowTableId); - return nodeId.builder() - .augmentation(FlowCapableNode.class) - .child(Table.class, flowTableKey) - .build(); - } - - private InstanceIdentifier getFlowInstanceId(InstanceIdentifier
tableId) { - // generate unique flow key - FlowId flowId = new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement())); - FlowKey flowKey = new FlowKey(flowId); - return tableId.child(Flow.class, flowKey); - } - - private Flow createLldpToControllerFlow(Uint8 tableId, Uint16 priority) { - - // start building flow - FlowBuilder lldpFlow = new FlowBuilder() - .setTableId(tableId) - .setFlowName("lldptocntrl"); - - // use its own hash code for id. - lldpFlow.setId(new FlowId(Long.toString(lldpFlow.hashCode()))); - EthernetMatchBuilder ethernetMatchBuilder = new EthernetMatchBuilder() - .setEthernetType(new EthernetTypeBuilder() - .setType(new EtherType(LLDP_ETHER_TYPE)).build()); - - Match match = new MatchBuilder() - .setEthernetMatch(ethernetMatchBuilder.build()) - .build(); - - // Create an Apply Action - ApplyActions applyActions = new ApplyActionsBuilder() - .setAction(BindingMap.of(getSendToControllerAction())) - .build(); - - // Wrap our Apply Action in an Instruction - Instruction applyActionsInstruction = new InstructionBuilder() - .setOrder(0) - .setInstruction(new ApplyActionsCaseBuilder() - .setApplyActions(applyActions) - .build()) - .build(); - - // Put our Instruction in a list of Instructions - lldpFlow - .setMatch(match) - .setInstructions(new InstructionsBuilder() - .setInstruction(BindingMap.of(applyActionsInstruction)) - .build()) - .setPriority(priority) - .setBufferId(OFConstants.OFP_NO_BUFFER) - .setHardTimeout(flowHardTimeout) - .setIdleTimeout(flowIdleTimeout) - .setCookie(new FlowCookie(Uint64.fromLongBits(flowCookieInc.getAndIncrement()))) - .setFlags(new FlowModFlags(false, false, false, false, false)); - - return lldpFlow.build(); - } - - private Action getSendToControllerAction() { - return new ActionBuilder() - .setOrder(0) - .withKey(new ActionKey(0)) - .setAction(new OutputActionCaseBuilder() - .setOutputAction(new OutputActionBuilder() - .setMaxLength(Uint16.MAX_VALUE) - .setOutputNodeConnector(new Uri(OutputPortValues.CONTROLLER.toString())) - .build()) - .build()) - .build(); - } - - private Future> writeFlowToController(InstanceIdentifier nodeInstanceId, - InstanceIdentifier
tableInstanceId, - InstanceIdentifier flowPath, - Flow flow) { - LOG.trace("Adding flow to node {}", - requireNonNull(nodeInstanceId.firstKeyOf(Node.class)).getId().getValue()); - final AddFlowInputBuilder builder = new AddFlowInputBuilder(flow); - builder.setNode(new NodeRef(nodeInstanceId)); - builder.setFlowRef(new FlowRef(flowPath)); - builder.setFlowTable(new FlowTableRef(tableInstanceId)); - builder.setTransactionUri(new Uri(flow.getId().getValue())); - return addFlow.invoke(builder.build()); - } - } + private static final Logger LOG = LoggerFactory.getLogger(InitialFlowWriter.class); + + private static final String FLOW_ID_PREFIX = "L2switch-"; + private static final Uint32 LLDP_ETHER_TYPE = Uint32.valueOf(35020); + + private final ExecutorService initialFlowExecutor = Executors.newCachedThreadPool(); + private final AddFlow addFlow; + private Uint8 flowTableId = Uint8.ZERO; + private Uint16 flowPriority = Uint16.ZERO; + private Uint16 flowIdleTimeout = Uint16.ZERO; + private Uint16 flowHardTimeout = Uint16.ZERO; + + private final AtomicLong flowIdInc = new AtomicLong(); + private final AtomicLong flowCookieInc = new AtomicLong(0x2b00000000000000L); + + public InitialFlowWriter(AddFlow addFlow) { + this.addFlow = requireNonNull(addFlow); + } + + public void setFlowTableId(Uint8 flowTableId) { + this.flowTableId = requireNonNull(flowTableId); + } + + public void setFlowPriority(Uint16 flowPriority) { + this.flowPriority = requireNonNull(flowPriority); + } + + public void setFlowIdleTimeout(Uint16 flowIdleTimeout) { + this.flowIdleTimeout = requireNonNull(flowIdleTimeout); + } + + public void setFlowHardTimeout(Uint16 flowHardTimeout) { + this.flowHardTimeout = requireNonNull(flowHardTimeout); + } + + public Registration registerAsDataChangeListener(DataBroker dataBroker) { + InstanceIdentifier nodeInstanceIdentifier = InstanceIdentifier.builder(Nodes.class).child(Node.class) + .build(); + + return dataBroker.registerDataTreeChangeListener( + DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, nodeInstanceIdentifier), this); + } + + @Override + public void onDataTreeChanged(List> changes) { + Set> nodeIds = new HashSet<>(); + for (DataTreeModification change : changes) { + DataObjectModification rootNode = change.getRootNode(); + switch (change.getRootNode().getModificationType()) { + case WRITE: + if (rootNode.getDataBefore() == null) { + nodeIds.add(change.path()); + } + break; + default: + break; + } + } + + if (!nodeIds.isEmpty()) { + initialFlowExecutor.execute(new InitialFlowWriterProcessor(nodeIds)); + } + } + + /** + * A private class to process the node updated event in separate thread. Allows + * to release the thread that invoked the data node updated event. Avoids any + * thread lock it may cause. + */ + private class InitialFlowWriterProcessor implements Runnable { + private final Set> nodeIds; + + InitialFlowWriterProcessor(Set> nodeIds) { + this.nodeIds = nodeIds; + } + + @Override + public void run() { + for (var nodeId : nodeIds) { + // Find the last step in the identifier + ExactDataObjectStep lastStep = null; + for (ExactDataObjectStep step : nodeId.steps()) { + lastStep = step; + } + if (lastStep != null && Node.class.equals(lastStep.type())) { + @SuppressWarnings("unchecked") + final var invNodeId = (DataObjectIdentifier) nodeId; + if (invNodeId.firstKeyOf(Node.class).getId().getValue().contains("openflow:")) { + addInitialFlows(invNodeId); + } + } + } + + } + + /** + * Adds a flow, which sends all LLDP packets to the controller, to the specified + * node. + * + * @param nodeId The node to write the flow on. + */ + public void addInitialFlows(DataObjectIdentifier nodeId) { + LOG.debug("adding initial flows for node {} ", nodeId); + + DataObjectIdentifier
tableId = getTableInstanceId(nodeId); + DataObjectIdentifier flowId = getFlowInstanceId(tableId); + + // add lldpToController flow + writeFlowToController(nodeId, tableId, flowId, createLldpToControllerFlow(flowTableId, flowPriority)); + + LOG.debug("Added initial flows for node {} ", nodeId); + } + + private DataObjectIdentifier
getTableInstanceId(DataObjectIdentifier nodeId) { + // get flow table key + TableKey flowTableKey = new TableKey(flowTableId); + return (DataObjectIdentifier
) nodeId.builder().augmentation(FlowCapableNode.class) + .child(Table.class, flowTableKey).build(); + } + + private DataObjectIdentifier getFlowInstanceId(DataObjectIdentifier
tableId) { + // generate unique flow key + FlowId flowId = new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement())); + FlowKey flowKey = new FlowKey(flowId); + return tableId.toBuilder().child(Flow.class, flowKey).build(); + } + + private Flow createLldpToControllerFlow(Uint8 tableId, Uint16 priority) { + + // start building flow + FlowBuilder lldpFlow = new FlowBuilder().setTableId(tableId).setFlowName("lldptocntrl"); + + // use its own hash code for id. + lldpFlow.setId(new FlowId(Long.toString(lldpFlow.hashCode()))); + EthernetMatchBuilder ethernetMatchBuilder = new EthernetMatchBuilder() + .setEthernetType(new EthernetTypeBuilder().setType(new EtherType(LLDP_ETHER_TYPE)).build()); + + Match match = new MatchBuilder().setEthernetMatch(ethernetMatchBuilder.build()).build(); + + // Create an Apply Action + ApplyActions applyActions = new ApplyActionsBuilder().setAction(BindingMap.of(getSendToControllerAction())) + .build(); + + // Wrap our Apply Action in an Instruction + Instruction applyActionsInstruction = new InstructionBuilder().setOrder(0) + .setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyActions).build()).build(); + + // Put our Instruction in a list of Instructions + lldpFlow.setMatch(match) + .setInstructions( + new InstructionsBuilder().setInstruction(BindingMap.of(applyActionsInstruction)).build()) + .setPriority(priority).setBufferId(OFConstants.OFP_NO_BUFFER).setHardTimeout(flowHardTimeout) + .setIdleTimeout(flowIdleTimeout) + .setCookie(new FlowCookie(Uint64.fromLongBits(flowCookieInc.getAndIncrement()))) + .setFlags(new FlowModFlags(false, false, false, false, false)); + + return lldpFlow.build(); + } + + private Action getSendToControllerAction() { + return new ActionBuilder() + .setOrder(0).withKey( + new ActionKey(0)) + .setAction(new OutputActionCaseBuilder() + .setOutputAction(new OutputActionBuilder().setMaxLength(Uint16.MAX_VALUE) + .setOutputNodeConnector(new Uri(OutputPortValues.CONTROLLER.toString())).build()) + .build()) + .build(); + } + + private Future> writeFlowToController(DataObjectIdentifier nodeInstanceId, + DataObjectIdentifier
tableInstanceId, DataObjectIdentifier flowPath, Flow flow) { + LOG.trace("Adding flow to node {}", + requireNonNull(nodeInstanceId.firstKeyOf(Node.class)).getId().getValue()); + final AddFlowInputBuilder builder = new AddFlowInputBuilder(flow); + builder.setNode(new NodeRef(nodeInstanceId)); + builder.setFlowRef(new FlowRef(flowPath)); + builder.setFlowTable(new FlowTableRef(tableInstanceId)); + builder.setTransactionUri(new Uri(flow.getId().getValue())); + return addFlow.invoke(builder.build()); + } + } } diff --git a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/topology/NetworkGraphImpl.java b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/topology/NetworkGraphImpl.java index f9c2f92d1..b9e2c8642 100644 --- a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/topology/NetworkGraphImpl.java +++ b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/topology/NetworkGraphImpl.java @@ -9,178 +9,177 @@ import static java.util.Objects.requireNonNull; -import edu.uci.ics.jung.algorithms.shortestpath.PrimMinimumSpanningTree; -import edu.uci.ics.jung.graph.DelegateTree; -import edu.uci.ics.jung.graph.Graph; -import edu.uci.ics.jung.graph.SparseMultigraph; -import edu.uci.ics.jung.graph.util.EdgeType; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.checkerframework.checker.lock.qual.GuardedBy; + 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.topology.Link; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.errorprone.annotations.concurrent.GuardedBy; + +import edu.uci.ics.jung.algorithms.shortestpath.PrimMinimumSpanningTree; +import edu.uci.ics.jung.graph.DelegateTree; +import edu.uci.ics.jung.graph.Graph; +import edu.uci.ics.jung.graph.SparseMultigraph; +import edu.uci.ics.jung.graph.util.EdgeType; + /** * Implementation of NetworkGraphService - * {@link org.opendaylight.l2switch.loopremover.topology.NetworkGraphService}. It - * uses Jung graph library internally to maintain a graph and optimum way to + * {@link org.opendaylight.l2switch.loopremover.topology.NetworkGraphService}. + * It uses Jung graph library internally to maintain a graph and optimum way to * return shortest path using Dijkstra algorithm. */ public class NetworkGraphImpl implements NetworkGraphService { - private static final Logger LOG = LoggerFactory.getLogger(NetworkGraphImpl.class); - - @GuardedBy("this") - private Graph networkGraph = null; - private final Set linkAdded = new HashSet<>(); - - // Enable following lines when shortest path functionality is required. - // DijkstraShortestPath shortestPath = null; - - /** - * Adds links to existing graph or creates new directed graph with given - * links if graph was not initialized. - * - * @param links - * The links to add. - */ - @Override - public synchronized void addLinks(List links) { - if (links == null || links.isEmpty()) { - LOG.info("In addLinks: No link added as links is null or empty."); - return; - } - - if (networkGraph == null) { - networkGraph = SparseMultigraph.getFactory().get(); - } - - for (Link link : links) { - if (linkAlreadyAdded(link)) { - continue; - } - NodeId sourceNodeId = link.getSource().getSourceNode(); - NodeId destinationNodeId = link.getDestination().getDestNode(); - networkGraph.addVertex(sourceNodeId); - networkGraph.addVertex(destinationNodeId); - networkGraph.addEdge(link, sourceNodeId, destinationNodeId, EdgeType.UNDIRECTED); - } - - /* - * if(shortestPath == null) { shortestPath = new - * DijkstraShortestPath<>(networkGraph); } else { shortestPath.reset(); - * } - */ - } - - @GuardedBy("this") - private boolean linkAlreadyAdded(Link link) { - String linkAddedKey = null; - if (link.getDestination().getDestTp().hashCode() > link.getSource().getSourceTp().hashCode()) { - linkAddedKey = link.getSource().getSourceTp().getValue() + link.getDestination().getDestTp().getValue(); - } else { - linkAddedKey = link.getDestination().getDestTp().getValue() + link.getSource().getSourceTp().getValue(); - } - if (linkAdded.contains(linkAddedKey)) { - return true; - } else { - linkAdded.add(linkAddedKey); - return false; - } - } - - /** - * Removes links from existing graph. - * - * @param links - * The links to remove. - */ - @Override - public synchronized void removeLinks(List links) { - requireNonNull(networkGraph, "Graph is not initialized, add links first."); - - if (links == null || links.isEmpty()) { - LOG.info("In removeLinks: No link removed as links is null or empty."); - return; - } - - for (Link link : links) { - networkGraph.removeEdge(link); - } - /* - * if(shortestPath == null) { shortestPath = new - * DijkstraShortestPath<>(networkGraph); } else { shortestPath.reset(); - * } - */ - - } - - /** - * returns a path between 2 nodes. Uses Dijkstra's algorithm to return - * shortest path. - * - * @param sourceNodeId the source node Id - * @param destinationNodeId the destination node Id - */ - // @Override - /* - * public synchronized List getPath(NodeId sourceNodeId, NodeId - * destinationNodeId) { Preconditions.checkNotNull(shortestPath, - * "Graph is not initialized, add links first."); - * - * if(sourceNodeId == null || destinationNodeId == null) { _logger.info( - * "In getPath: returning null, as sourceNodeId or destinationNodeId is null." - * ); return null; } - * - * return shortestPath.getPath(sourceNodeId, destinationNodeId); } - */ - - /** - * Clears the prebuilt graph, in case same service instance is required to - * process a new graph. - */ - @Override - public synchronized void clear() { - networkGraph = null; - linkAdded.clear(); - // shortestPath = null; - } - - /** - * Forms MST(minimum spanning tree) from network graph and returns links - * that are not in MST. - * - * @return The links in the MST (minimum spanning tree) - */ - @Override - public synchronized List getLinksInMst() { - List linksInMst = new ArrayList<>(); - if (networkGraph != null) { - PrimMinimumSpanningTree networkMst = new PrimMinimumSpanningTree<>( - DelegateTree.getFactory()); - Graph mstGraph = networkMst.apply(networkGraph); - Collection mstLinks = mstGraph.getEdges(); - linksInMst.addAll(mstLinks); - } - return linksInMst; - } - - /** - * Get all the links in the network. - * - * @return The links in the network. - */ - @Override - public synchronized List getAllLinks() { - List allLinks = new ArrayList<>(); - if (networkGraph != null) { - allLinks.addAll(networkGraph.getEdges()); - } - return allLinks; - } + private static final Logger LOG = LoggerFactory.getLogger(NetworkGraphImpl.class); + + @GuardedBy("this") + private Graph networkGraph = null; + private final Set linkAdded = new HashSet<>(); + + // Enable following lines when shortest path functionality is required. + // DijkstraShortestPath shortestPath = null; + + /** + * Adds links to existing graph or creates new directed graph with given links + * if graph was not initialized. + * + * @param links The links to add. + */ + @Override + public synchronized void addLinks(List links) { + if (links == null || links.isEmpty()) { + LOG.info("In addLinks: No link added as links is null or empty."); + return; + } + + if (networkGraph == null) { + networkGraph = SparseMultigraph.getFactory().get(); + } + + for (Link link : links) { + if (linkAlreadyAdded(link)) { + continue; + } + NodeId sourceNodeId = link.getSource().getSourceNode(); + NodeId destinationNodeId = link.getDestination().getDestNode(); + networkGraph.addVertex(sourceNodeId); + networkGraph.addVertex(destinationNodeId); + networkGraph.addEdge(link, sourceNodeId, destinationNodeId, EdgeType.UNDIRECTED); + } + + /* + * if(shortestPath == null) { shortestPath = new + * DijkstraShortestPath<>(networkGraph); } else { shortestPath.reset(); } + */ + } + + @GuardedBy("this") + private boolean linkAlreadyAdded(Link link) { + String linkAddedKey = null; + if (link.getDestination().getDestTp().hashCode() > link.getSource().getSourceTp().hashCode()) { + linkAddedKey = link.getSource().getSourceTp().getValue() + link.getDestination().getDestTp().getValue(); + } else { + linkAddedKey = link.getDestination().getDestTp().getValue() + link.getSource().getSourceTp().getValue(); + } + if (linkAdded.contains(linkAddedKey)) { + return true; + } else { + linkAdded.add(linkAddedKey); + return false; + } + } + + /** + * Removes links from existing graph. + * + * @param links The links to remove. + */ + @Override + public synchronized void removeLinks(List links) { + requireNonNull(networkGraph, "Graph is not initialized, add links first."); + + if (links == null || links.isEmpty()) { + LOG.info("In removeLinks: No link removed as links is null or empty."); + return; + } + + for (Link link : links) { + networkGraph.removeEdge(link); + } + /* + * if(shortestPath == null) { shortestPath = new + * DijkstraShortestPath<>(networkGraph); } else { shortestPath.reset(); } + */ + + } + + /** + * returns a path between 2 nodes. Uses Dijkstra's algorithm to return shortest + * path. + * + * @param sourceNodeId the source node Id + * @param destinationNodeId the destination node Id + */ + // @Override + /* + * public synchronized List getPath(NodeId sourceNodeId, NodeId + * destinationNodeId) { Preconditions.checkNotNull(shortestPath, + * "Graph is not initialized, add links first."); + * + * if(sourceNodeId == null || destinationNodeId == null) { _logger.info( + * "In getPath: returning null, as sourceNodeId or destinationNodeId is null." + * ); return null; } + * + * return shortestPath.getPath(sourceNodeId, destinationNodeId); } + */ + + /** + * Clears the prebuilt graph, in case same service instance is required to + * process a new graph. + */ + @Override + public synchronized void clear() { + networkGraph = null; + linkAdded.clear(); + // shortestPath = null; + } + + /** + * Forms MST(minimum spanning tree) from network graph and returns links that + * are not in MST. + * + * @return The links in the MST (minimum spanning tree) + */ + @Override + public synchronized List getLinksInMst() { + List linksInMst = new ArrayList<>(); + if (networkGraph != null) { + PrimMinimumSpanningTree networkMst = new PrimMinimumSpanningTree<>( + DelegateTree.getFactory()); + Graph mstGraph = networkMst.apply(networkGraph); + Collection mstLinks = mstGraph.getEdges(); + linksInMst.addAll(mstLinks); + } + return linksInMst; + } + + /** + * Get all the links in the network. + * + * @return The links in the network. + */ + @Override + public synchronized List getAllLinks() { + List allLinks = new ArrayList<>(); + if (networkGraph != null) { + allLinks.addAll(networkGraph.getEdges()); + } + return allLinks; + } } diff --git a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/topology/TopologyLinkDataChangeHandler.java b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/topology/TopologyLinkDataChangeHandler.java index 52a2fe4b5..8044b29d9 100644 --- a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/topology/TopologyLinkDataChangeHandler.java +++ b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/topology/TopologyLinkDataChangeHandler.java @@ -9,12 +9,7 @@ import static java.util.Objects.requireNonNull; -import com.google.common.util.concurrent.FluentFuture; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.MoreExecutors; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -22,6 +17,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; + import org.opendaylight.l2switch.loopremover.util.InstanceIdentifierUtils; import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.mdsal.binding.api.DataObjectModification; @@ -43,14 +39,20 @@ 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.Link; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey; -import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.util.concurrent.FluentFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; + /** - * Listens to data change events on topology links {@link - * org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link} + * Listens to data change events on topology links + * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link} * and maintains a topology graph using provided NetworkGraphService * {@link org.opendaylight.l2switch.loopremover.topology.NetworkGraphService}. * It refreshes the graph after a delay(default 10 sec) to accommodate burst of @@ -58,266 +60,265 @@ * graph on a series of change events in short time. */ public class TopologyLinkDataChangeHandler implements DataTreeChangeListener { - private static final Logger LOG = LoggerFactory.getLogger(TopologyLinkDataChangeHandler.class); - private static final String DEFAULT_TOPOLOGY_ID = "flow:1"; - private static final long DEFAULT_GRAPH_REFRESH_DELAY = 1000; - - private final ScheduledExecutorService topologyDataChangeEventProcessor = Executors.newScheduledThreadPool(1); - private final NetworkGraphService networkGraphService; - private final DataBroker dataBroker; - - private volatile boolean networkGraphRefreshScheduled = false; - private volatile boolean threadReschedule = false; - private long graphRefreshDelay; - private String topologyId; - - public TopologyLinkDataChangeHandler(DataBroker dataBroker, NetworkGraphService networkGraphService) { - this.dataBroker = requireNonNull(dataBroker); - this.networkGraphService = requireNonNull(networkGraphService); - } - - public void setGraphRefreshDelay(long graphRefreshDelay) { - if (graphRefreshDelay < 0) { - this.graphRefreshDelay = DEFAULT_GRAPH_REFRESH_DELAY; - } else { - this.graphRefreshDelay = graphRefreshDelay; - } - } - - public void setTopologyId(String topologyId) { - if (topologyId == null || topologyId.isEmpty()) { - this.topologyId = DEFAULT_TOPOLOGY_ID; - } else { - this.topologyId = topologyId; - } - } - - /** - * Registers as a data listener to receive changes done to {@link org.opendaylight.yang.gen.v1.urn.tbd.params - * .xml.ns.yang.network.topology.rev131021.network.topology.topology.Link} - * under - * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology} - * operation data root. - */ - public ListenerRegistration registerAsDataChangeListener() { - InstanceIdentifier linkInstance = InstanceIdentifier.builder(NetworkTopology.class) - .child(Topology.class, new TopologyKey(new TopologyId(topologyId))).child(Link.class).build(); - return dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, - linkInstance), this); - } - - /** - * Handler for onDataChanged events and schedules the building of the - * network graph. - */ - @Override - public void onDataTreeChanged(Collection> changes) { - boolean isGraphUpdated = false; - - for (DataTreeModification change: changes) { - DataObjectModification rootNode = change.getRootNode(); - switch (rootNode.getModificationType()) { - case WRITE: - Link createdLink = rootNode.getDataAfter(); - if (rootNode.getDataBefore() == null && !createdLink.getLinkId().getValue().contains("host")) { - isGraphUpdated = true; - LOG.debug("Graph is updated! Added Link {}", createdLink.getLinkId().getValue()); - } - break; - case DELETE: - Link deletedLink = rootNode.getDataBefore(); - if (!deletedLink.getLinkId().getValue().contains("host")) { - isGraphUpdated = true; - LOG.debug("Graph is updated! Removed Link {}", deletedLink.getLinkId().getValue()); - break; - } - break; - default: - break; - } - } - - if (!isGraphUpdated) { - return; - } - if (!networkGraphRefreshScheduled) { - synchronized (this) { - if (!networkGraphRefreshScheduled) { - topologyDataChangeEventProcessor.schedule(new TopologyDataChangeEventProcessor(), graphRefreshDelay, - TimeUnit.MILLISECONDS); - networkGraphRefreshScheduled = true; - LOG.debug("Scheduled Graph for refresh."); - } - } - } else { - LOG.debug("Already scheduled for network graph refresh."); - threadReschedule = true; - } - } - - private final class TopologyDataChangeEventProcessor implements Runnable { - - @Override - public void run() { - if (threadReschedule) { - topologyDataChangeEventProcessor.schedule(this, graphRefreshDelay, TimeUnit.MILLISECONDS); - threadReschedule = false; - return; - } - LOG.debug("In network graph refresh thread."); - networkGraphRefreshScheduled = false; - networkGraphService.clear(); - List links = getLinksFromTopology(); - if (links == null || links.isEmpty()) { - return; - } - networkGraphService.addLinks(links); - final ReadWriteTransaction readWriteTransaction = dataBroker.newReadWriteTransaction(); - updateNodeConnectorStatus(readWriteTransaction); - Futures.addCallback(readWriteTransaction.commit(), new FutureCallback() { - @Override - public void onSuccess(CommitInfo result) { - LOG.debug("TopologyLinkDataChangeHandler write successful for tx :{}", - readWriteTransaction.getIdentifier()); - } - - @Override - public void onFailure(Throwable throwable) { - LOG.error("TopologyLinkDataChangeHandler write transaction {} failed", - readWriteTransaction.getIdentifier(), throwable.getCause()); - } - }, MoreExecutors.directExecutor()); - LOG.debug("Done with network graph refresh thread."); - } - - private List getLinksFromTopology() { - final InstanceIdentifier topologyInstanceIdentifier = - InstanceIdentifierUtils.generateTopologyInstanceIdentifier(topologyId); - final FluentFuture> readFuture; - try (ReadTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction()) { - readFuture = readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, topologyInstanceIdentifier); - } - - final Optional topologyOptional; - try { - topologyOptional = readFuture.get(); - } catch (InterruptedException | ExecutionException e) { - LOG.error("Error reading topology {}", topologyInstanceIdentifier); - throw new RuntimeException( - "Error reading from operational store, topology : " + topologyInstanceIdentifier, e); - } - - if (topologyOptional.isEmpty()) { - return null; - } - final Topology topology = topologyOptional.orElseThrow(); - final Map links = topology.getLink(); - if (links == null || links.isEmpty()) { - return null; - } - - List internalLinks = new ArrayList<>(); - for (Link link : links.values()) { - if (!link.getLinkId().getValue().contains("host")) { - internalLinks.add(link); - } - } - return internalLinks; - } - - private void updateNodeConnectorStatus(ReadWriteTransaction readWriteTransaction) { - List allLinks = networkGraphService.getAllLinks(); - if (allLinks == null || allLinks.isEmpty()) { - return; - } - - List mstLinks = networkGraphService.getLinksInMst(); - for (Link link : allLinks) { - if (mstLinks != null && !mstLinks.isEmpty() && mstLinks.contains(link)) { - updateNodeConnector(readWriteTransaction, getSourceNodeConnectorRef(link), StpStatus.Forwarding); - updateNodeConnector(readWriteTransaction, getDestNodeConnectorRef(link), StpStatus.Forwarding); - } else { - updateNodeConnector(readWriteTransaction, getSourceNodeConnectorRef(link), StpStatus.Discarding); - updateNodeConnector(readWriteTransaction, getDestNodeConnectorRef(link), StpStatus.Discarding); - } - } - } - - private NodeConnectorRef getSourceNodeConnectorRef(Link link) { - InstanceIdentifier nodeConnectorInstanceIdentifier = InstanceIdentifierUtils - .createNodeConnectorIdentifier(link.getSource().getSourceNode().getValue(), - link.getSource().getSourceTp().getValue()); - return new NodeConnectorRef(nodeConnectorInstanceIdentifier); - } - - private NodeConnectorRef getDestNodeConnectorRef(Link link) { - InstanceIdentifier nodeConnectorInstanceIdentifier = InstanceIdentifierUtils - .createNodeConnectorIdentifier(link.getDestination().getDestNode().getValue(), - link.getDestination().getDestTp().getValue()); - - return new NodeConnectorRef(nodeConnectorInstanceIdentifier); - } - - private void updateNodeConnector(ReadWriteTransaction readWriteTransaction, NodeConnectorRef nodeConnectorRef, - StpStatus stpStatus) { - StpStatusAwareNodeConnectorBuilder stpStatusAwareNodeConnectorBuilder = - new StpStatusAwareNodeConnectorBuilder().setStatus(stpStatus); - checkIfExistAndUpdateNodeConnector(readWriteTransaction, nodeConnectorRef, - stpStatusAwareNodeConnectorBuilder.build()); - } - - private void checkIfExistAndUpdateNodeConnector(ReadWriteTransaction readWriteTransaction, - NodeConnectorRef nodeConnectorRef, StpStatusAwareNodeConnector stpStatusAwareNodeConnector) { - final Optional dataObjectOptional; - try { - dataObjectOptional = readWriteTransaction.read(LogicalDatastoreType.OPERATIONAL, - (InstanceIdentifier) nodeConnectorRef.getValue()).get(); - } catch (InterruptedException | ExecutionException e) { - LOG.error("Error reading node connector {}", nodeConnectorRef.getValue()); - readWriteTransaction.commit(); - throw new RuntimeException("Error reading from operational store, node connector : " + nodeConnectorRef, - e); - } - - if (dataObjectOptional.isEmpty()) { - LOG.error("Unable to update Stp Status node connector {} note present in operational store", - nodeConnectorRef.getValue()); - return; - } - - final NodeConnector nc = dataObjectOptional.orElseThrow(); - if (sameStatusPresent(nc.augmentation(StpStatusAwareNodeConnector.class), - stpStatusAwareNodeConnector.getStatus())) { - return; - } - - // build instance id for StpStatusAwareNodeConnector - InstanceIdentifier stpStatusAwareNcInstanceId = - ((InstanceIdentifier) nodeConnectorRef - .getValue()).augmentation(StpStatusAwareNodeConnector.class); - // update StpStatusAwareNodeConnector in operational store - readWriteTransaction.merge(LogicalDatastoreType.OPERATIONAL, stpStatusAwareNcInstanceId, - stpStatusAwareNodeConnector); - LOG.debug("Merged Stp Status aware node connector in operational {} with status {}", - stpStatusAwareNcInstanceId, stpStatusAwareNodeConnector); - } - - private boolean sameStatusPresent(StpStatusAwareNodeConnector stpStatusAwareNodeConnector, - StpStatus stpStatus) { - - if (stpStatusAwareNodeConnector == null) { - return false; - } - - if (stpStatusAwareNodeConnector.getStatus() == null) { - return false; - } - - if (stpStatus.getIntValue() != stpStatusAwareNodeConnector.getStatus().getIntValue()) { - return false; - } - - return true; - } - } + private static final Logger LOG = LoggerFactory.getLogger(TopologyLinkDataChangeHandler.class); + private static final String DEFAULT_TOPOLOGY_ID = "flow:1"; + private static final long DEFAULT_GRAPH_REFRESH_DELAY = 1000; + + private final ScheduledExecutorService topologyDataChangeEventProcessor = Executors.newScheduledThreadPool(1); + private final NetworkGraphService networkGraphService; + private final DataBroker dataBroker; + + private volatile boolean networkGraphRefreshScheduled = false; + private volatile boolean threadReschedule = false; + private long graphRefreshDelay; + private String topologyId; + + public TopologyLinkDataChangeHandler(DataBroker dataBroker, NetworkGraphService networkGraphService) { + this.dataBroker = requireNonNull(dataBroker); + this.networkGraphService = requireNonNull(networkGraphService); + } + + public void setGraphRefreshDelay(long graphRefreshDelay) { + if (graphRefreshDelay < 0) { + this.graphRefreshDelay = DEFAULT_GRAPH_REFRESH_DELAY; + } else { + this.graphRefreshDelay = graphRefreshDelay; + } + } + + public void setTopologyId(String topologyId) { + if (topologyId == null || topologyId.isEmpty()) { + this.topologyId = DEFAULT_TOPOLOGY_ID; + } else { + this.topologyId = topologyId; + } + } + + /** + * Registers as a data listener to receive changes done to + * {@link org.opendaylight.yang.gen.v1.urn.tbd.params + * .xml.ns.yang.network.topology.rev131021.network.topology.topology.Link} under + * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology} + * operation data root. + */ + public Registration registerAsDataChangeListener() { + InstanceIdentifier linkInstance = InstanceIdentifier.builder(NetworkTopology.class) + .child(Topology.class, new TopologyKey(new TopologyId(topologyId))).child(Link.class).build(); + return dataBroker.registerDataTreeChangeListener( + DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, linkInstance), this); + } + + /** + * Handler for onDataChanged events and schedules the building of the network + * graph. + */ + @Override + public void onDataTreeChanged(List> changes) { + boolean isGraphUpdated = false; + + for (DataTreeModification change : changes) { + DataObjectModification rootNode = change.getRootNode(); + switch (rootNode.getModificationType()) { + case WRITE: + Link createdLink = rootNode.getDataAfter(); + if (rootNode.getDataBefore() == null && !createdLink.getLinkId().getValue().contains("host")) { + isGraphUpdated = true; + LOG.debug("Graph is updated! Added Link {}", createdLink.getLinkId().getValue()); + } + break; + case DELETE: + Link deletedLink = rootNode.getDataBefore(); + if (!deletedLink.getLinkId().getValue().contains("host")) { + isGraphUpdated = true; + LOG.debug("Graph is updated! Removed Link {}", deletedLink.getLinkId().getValue()); + break; + } + break; + default: + break; + } + } + + if (!isGraphUpdated) { + return; + } + if (!networkGraphRefreshScheduled) { + synchronized (this) { + if (!networkGraphRefreshScheduled) { + topologyDataChangeEventProcessor.schedule(new TopologyDataChangeEventProcessor(), graphRefreshDelay, + TimeUnit.MILLISECONDS); + networkGraphRefreshScheduled = true; + LOG.debug("Scheduled Graph for refresh."); + } + } + } else { + LOG.debug("Already scheduled for network graph refresh."); + threadReschedule = true; + } + } + + private final class TopologyDataChangeEventProcessor implements Runnable { + + @Override + public void run() { + if (threadReschedule) { + topologyDataChangeEventProcessor.schedule(this, graphRefreshDelay, TimeUnit.MILLISECONDS); + threadReschedule = false; + return; + } + LOG.debug("In network graph refresh thread."); + networkGraphRefreshScheduled = false; + networkGraphService.clear(); + List links = getLinksFromTopology(); + if (links == null || links.isEmpty()) { + return; + } + networkGraphService.addLinks(links); + final ReadWriteTransaction readWriteTransaction = dataBroker.newReadWriteTransaction(); + updateNodeConnectorStatus(readWriteTransaction); + Futures.addCallback(readWriteTransaction.commit(), new FutureCallback() { + @Override + public void onSuccess(CommitInfo result) { + LOG.debug("TopologyLinkDataChangeHandler write successful for tx :{}", + readWriteTransaction.getIdentifier()); + } + + @Override + public void onFailure(Throwable throwable) { + LOG.error("TopologyLinkDataChangeHandler write transaction {} failed", + readWriteTransaction.getIdentifier(), throwable.getCause()); + } + }, MoreExecutors.directExecutor()); + LOG.debug("Done with network graph refresh thread."); + } + + private List getLinksFromTopology() { + final InstanceIdentifier topologyInstanceIdentifier = InstanceIdentifierUtils + .generateTopologyInstanceIdentifier(topologyId); + final FluentFuture> readFuture; + try (ReadTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction()) { + readFuture = readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, topologyInstanceIdentifier); + } + + final Optional topologyOptional; + try { + topologyOptional = readFuture.get(); + } catch (InterruptedException | ExecutionException e) { + LOG.error("Error reading topology {}", topologyInstanceIdentifier); + throw new RuntimeException( + "Error reading from operational store, topology : " + topologyInstanceIdentifier, e); + } + + if (topologyOptional.isEmpty()) { + return null; + } + final Topology topology = topologyOptional.orElseThrow(); + final Map links = topology.getLink(); + if (links == null || links.isEmpty()) { + return null; + } + + List internalLinks = new ArrayList<>(); + for (Link link : links.values()) { + if (!link.getLinkId().getValue().contains("host")) { + internalLinks.add(link); + } + } + return internalLinks; + } + + private void updateNodeConnectorStatus(ReadWriteTransaction readWriteTransaction) { + List allLinks = networkGraphService.getAllLinks(); + if (allLinks == null || allLinks.isEmpty()) { + return; + } + + List mstLinks = networkGraphService.getLinksInMst(); + for (Link link : allLinks) { + if (mstLinks != null && !mstLinks.isEmpty() && mstLinks.contains(link)) { + updateNodeConnector(readWriteTransaction, getSourceNodeConnectorRef(link), StpStatus.Forwarding); + updateNodeConnector(readWriteTransaction, getDestNodeConnectorRef(link), StpStatus.Forwarding); + } else { + updateNodeConnector(readWriteTransaction, getSourceNodeConnectorRef(link), StpStatus.Discarding); + updateNodeConnector(readWriteTransaction, getDestNodeConnectorRef(link), StpStatus.Discarding); + } + } + } + + private NodeConnectorRef getSourceNodeConnectorRef(Link link) { + DataObjectIdentifier nodeConnectorInstanceIdentifier = InstanceIdentifierUtils + .createNodeConnectorIdentifier(link.getSource().getSourceNode().getValue(), + link.getSource().getSourceTp().getValue()); + return new NodeConnectorRef(nodeConnectorInstanceIdentifier); + } + + private NodeConnectorRef getDestNodeConnectorRef(Link link) { + DataObjectIdentifier nodeConnectorInstanceIdentifier = InstanceIdentifierUtils + .createNodeConnectorIdentifier(link.getDestination().getDestNode().getValue(), + link.getDestination().getDestTp().getValue()); + + return new NodeConnectorRef(nodeConnectorInstanceIdentifier); + } + + private void updateNodeConnector(ReadWriteTransaction readWriteTransaction, NodeConnectorRef nodeConnectorRef, + StpStatus stpStatus) { + StpStatusAwareNodeConnectorBuilder stpStatusAwareNodeConnectorBuilder = new StpStatusAwareNodeConnectorBuilder() + .setStatus(stpStatus); + checkIfExistAndUpdateNodeConnector(readWriteTransaction, nodeConnectorRef, + stpStatusAwareNodeConnectorBuilder.build()); + } + + private void checkIfExistAndUpdateNodeConnector(ReadWriteTransaction readWriteTransaction, + NodeConnectorRef nodeConnectorRef, StpStatusAwareNodeConnector stpStatusAwareNodeConnector) { + final Optional dataObjectOptional; + try { + dataObjectOptional = readWriteTransaction.read(LogicalDatastoreType.OPERATIONAL, + (DataObjectIdentifier) nodeConnectorRef.getValue()).get(); + } catch (InterruptedException | ExecutionException e) { + LOG.error("Error reading node connector {}", nodeConnectorRef.getValue()); + readWriteTransaction.commit(); + throw new RuntimeException("Error reading from operational store, node connector : " + nodeConnectorRef, + e); + } + + if (dataObjectOptional.isEmpty()) { + LOG.error("Unable to update Stp Status node connector {} note present in operational store", + nodeConnectorRef.getValue()); + return; + } + + final NodeConnector nc = dataObjectOptional.orElseThrow(); + if (sameStatusPresent(nc.augmentation(StpStatusAwareNodeConnector.class), + stpStatusAwareNodeConnector.getStatus())) { + return; + } + + // build instance id for StpStatusAwareNodeConnector + DataObjectIdentifier stpStatusAwareNcInstanceId = ((DataObjectIdentifier) nodeConnectorRef + .getValue()).toBuilder().augmentation(StpStatusAwareNodeConnector.class).build(); + // update StpStatusAwareNodeConnector in operational store + readWriteTransaction.merge(LogicalDatastoreType.OPERATIONAL, stpStatusAwareNcInstanceId, + stpStatusAwareNodeConnector); + LOG.debug("Merged Stp Status aware node connector in operational {} with status {}", + stpStatusAwareNcInstanceId, stpStatusAwareNodeConnector); + } + + private boolean sameStatusPresent(StpStatusAwareNodeConnector stpStatusAwareNodeConnector, + StpStatus stpStatus) { + + if (stpStatusAwareNodeConnector == null) { + return false; + } + + if (stpStatusAwareNodeConnector.getStatus() == null) { + return false; + } + + if (stpStatus.getIntValue() != stpStatusAwareNodeConnector.getStatus().getIntValue()) { + return false; + } + + return true; + } + } } diff --git a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtils.java b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtils.java index b2e54f0ff..074a4f0c7 100644 --- a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtils.java +++ b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtils.java @@ -13,7 +13,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; @@ -24,110 +23,111 @@ 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.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.Uint8; /** - * InstanceIdentifierUtils provides utility functions related to InstanceIdentifiers. + * InstanceIdentifierUtils provides utility functions related to + * InstanceIdentifiers. */ public final class InstanceIdentifierUtils { - private InstanceIdentifierUtils() { - // Hidden on purpose - } + private InstanceIdentifierUtils() { + // Hidden on purpose + } - /** - * Creates an Instance Identifier (path) for node with specified id. - * - * @param nodeId the node id - * @return the node InstanceIdentifier - */ - public static InstanceIdentifier createNodePath(final NodeId nodeId) { - return InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(nodeId)).build(); - } + /** + * Creates an Instance Identifier (path) for node with specified id. + * + * @param nodeId the node id + * @return the node InstanceIdentifier + */ + public static DataObjectIdentifier createNodePath(final NodeId nodeId) { + return DataObjectIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(nodeId)).build(); + } - /** - * Shorten's node child path to node path. - * - * @param nodeChild - * child of node, from which we want node path. - * @return the Node InstanceIdentifier - */ - public static InstanceIdentifier getNodePath(final InstanceIdentifier nodeChild) { - return nodeChild.firstIdentifierOf(Node.class); - } + /** + * Shorten's node child path to node path. + * + * @param nodeChild child of node, from which we want node path. + * @return the Node InstanceIdentifier + */ + public static InstanceIdentifier getNodePath(final InstanceIdentifier nodeChild) { + return nodeChild.firstIdentifierOf(Node.class); + } - /** - * Creates a table path by appending table specific location to node path. - * - * @param nodePath the node path - * @param tableKey the table key - * @return the table InstanceIdentifier - */ - public static InstanceIdentifier
createTablePath(final InstanceIdentifier nodePath, - final TableKey tableKey) { - return nodePath.builder().augmentation(FlowCapableNode.class).child(Table.class, tableKey).build(); - } + /** + * Creates a table path by appending table specific location to node path. + * + * @param nodePath the node path + * @param tableKey the table key + * @return the table InstanceIdentifier + */ + public static InstanceIdentifier
createTablePath(final InstanceIdentifier nodePath, + final TableKey tableKey) { + return nodePath.builder().augmentation(FlowCapableNode.class).child(Table.class, tableKey).build(); + } - /** - * Creates a path for particular flow, by appending flow-specific - * information to table path. - * - * @param table the table path - * @param flowKey the floe key - * @return the flow InstanceIdentifier - */ - public static InstanceIdentifier createFlowPath(final InstanceIdentifier
table, - final FlowKey flowKey) { - return table.child(Flow.class, flowKey); - } + /** + * Creates a path for particular flow, by appending flow-specific information to + * table path. + * + * @param table the table path + * @param flowKey the floe key + * @return the flow InstanceIdentifier + */ + public static InstanceIdentifier createFlowPath(final InstanceIdentifier
table, + final FlowKey flowKey) { + return table.child(Flow.class, flowKey); + } - /** - * Extract table id from table path. - * - * @param tablePath the table path - * @return the table id - */ - public static Uint8 getTableId(final InstanceIdentifier
tablePath) { - return tablePath.firstKeyOf(Table.class).getId(); - } + /** + * Extract table id from table path. + * + * @param tablePath the table path + * @return the table id + */ + public static Uint8 getTableId(final InstanceIdentifier
tablePath) { + return tablePath.firstKeyOf(Table.class).getId(); + } - /** - * Extracts NodeConnectorKey from node connector path. - */ - public static NodeConnectorKey getNodeConnectorKey(final InstanceIdentifier nodeConnectorPath) { - return nodeConnectorPath.firstKeyOf(NodeConnector.class); - } + /** + * Extracts NodeConnectorKey from node connector path. + */ + public static NodeConnectorKey getNodeConnectorKey(final InstanceIdentifier nodeConnectorPath) { + return nodeConnectorPath.firstKeyOf(NodeConnector.class); + } - /** - * Extracts NodeKey from node path. - */ - public static NodeKey getNodeKey(final InstanceIdentifier nodePath) { - return nodePath.firstKeyOf(Node.class); - } + /** + * Extracts NodeKey from node path. + */ + public static NodeKey getNodeKey(final InstanceIdentifier nodePath) { + return nodePath.firstKeyOf(Node.class); + } - public static InstanceIdentifier createNodeConnectorIdentifier(final String nodeIdValue, - final String nodeConnectorIdValue) { - return createNodePath(new NodeId(nodeIdValue)).child(NodeConnector.class, - new NodeConnectorKey(new NodeConnectorId(nodeConnectorIdValue))); - } + public static DataObjectIdentifier createNodeConnectorIdentifier(final String nodeIdValue, + final String nodeConnectorIdValue) { + return createNodePath(new NodeId(nodeIdValue)).toBuilder() + .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(nodeConnectorIdValue))).build(); + } - public static InstanceIdentifier generateNodeInstanceIdentifier(final NodeConnectorRef nodeConnectorRef) { - return nodeConnectorRef.getValue().firstIdentifierOf(Node.class); - } +// public static InstanceIdentifier generateNodeInstanceIdentifier(final NodeConnectorRef nodeConnectorRef) { +// return nodeConnectorRef.getValue().firstIdentifierOf(Node.class); +// } +// +// public static InstanceIdentifier
generateFlowTableInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, +// final TableKey flowTableKey) { +// return generateNodeInstanceIdentifier(nodeConnectorRef).builder().augmentation(FlowCapableNode.class) +// .child(Table.class, flowTableKey).build(); +// } +// +// public static InstanceIdentifier generateFlowInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, +// final TableKey flowTableKey, final FlowKey flowKey) { +// return generateFlowTableInstanceIdentifier(nodeConnectorRef, flowTableKey).child(Flow.class, flowKey); +// } - public static InstanceIdentifier
generateFlowTableInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, - final TableKey flowTableKey) { - return generateNodeInstanceIdentifier(nodeConnectorRef).builder().augmentation(FlowCapableNode.class) - .child(Table.class, flowTableKey).build(); - } - - public static InstanceIdentifier generateFlowInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, - final TableKey flowTableKey, final FlowKey flowKey) { - return generateFlowTableInstanceIdentifier(nodeConnectorRef, flowTableKey).child(Flow.class, flowKey); - } - - public static InstanceIdentifier generateTopologyInstanceIdentifier(final String topologyId) { - return InstanceIdentifier.builder(NetworkTopology.class) - .child(Topology.class, new TopologyKey(new TopologyId(topologyId))).build(); - } + public static InstanceIdentifier generateTopologyInstanceIdentifier(final String topologyId) { + return InstanceIdentifier.builder(NetworkTopology.class) + .child(Topology.class, new TopologyKey(new TopologyId(topologyId))).build(); + } } From 959ff397f87312345d88e6e5a9938edae520e2b9 Mon Sep 17 00:00:00 2001 From: abhijith-css Date: Wed, 9 Jul 2025 16:18:28 +0530 Subject: [PATCH 05/15] Arp changes --- .../arphandler/core/ArpHandlerProvider.java | 134 ++--- .../arphandler/core/PacketDispatcher.java | 241 ++++----- .../core/ProactiveFloodFlowWriter.java | 498 +++++++++--------- .../arphandler/flow/InitialFlowWriter.java | 405 +++++++------- .../arphandler/inventory/InventoryReader.java | 410 +++++++------- 5 files changed, 827 insertions(+), 861 deletions(-) diff --git a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/ArpHandlerProvider.java b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/ArpHandlerProvider.java index 1982604d5..2747222fd 100644 --- a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/ArpHandlerProvider.java +++ b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/ArpHandlerProvider.java @@ -11,7 +11,7 @@ import org.opendaylight.l2switch.arphandler.inventory.InventoryReader; import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.mdsal.binding.api.NotificationService; -import org.opendaylight.mdsal.binding.api.RpcConsumerRegistry; +import org.opendaylight.mdsal.binding.api.RpcService; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlow; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.handler.config.rev140528.ArpHandlerConfig; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceived; @@ -22,78 +22,78 @@ import org.slf4j.LoggerFactory; public class ArpHandlerProvider { - private static final Logger LOG = LoggerFactory.getLogger(ArpHandlerProvider.class); - private Registration listenerRegistration; - private Registration floodTopoListenerReg; - private Registration floodInvListenerReg; - private Registration topoNodeListenerReg; + private static final Logger LOG = LoggerFactory.getLogger(ArpHandlerProvider.class); + private Registration listenerRegistration; + private Registration floodTopoListenerReg; + private Registration floodInvListenerReg; + private Registration topoNodeListenerReg; - private final NotificationService notificationService; - private final DataBroker dataBroker; - private final RpcConsumerRegistry rpcService; - private final ArpHandlerConfig arpHandlerConfig; + private final NotificationService notificationService; + private final DataBroker dataBroker; + private final RpcService rpcService; + private final ArpHandlerConfig arpHandlerConfig; - public ArpHandlerProvider(final DataBroker dataBroker, final NotificationService notificationProviderService, - final RpcConsumerRegistry rpcService, final ArpHandlerConfig config) { - this.notificationService = notificationProviderService; - this.dataBroker = dataBroker; - this.rpcService = rpcService; - this.arpHandlerConfig = config; - } + public ArpHandlerProvider(final DataBroker dataBroker, final NotificationService notificationProviderService, + final RpcService rpcService, final ArpHandlerConfig config) { + this.notificationService = notificationProviderService; + this.dataBroker = dataBroker; + this.rpcService = rpcService; + this.arpHandlerConfig = config; + } - public void init() { - if (arpHandlerConfig.getIsProactiveFloodMode()) { - //Setup proactive flow writer, which writes flood flows - LOG.info("ArpHandler is in Proactive Flood Mode"); - ProactiveFloodFlowWriter floodFlowWriter = new ProactiveFloodFlowWriter(dataBroker, - rpcService.getRpc(AddFlow.class)); - floodFlowWriter.setFlowTableId(arpHandlerConfig.getFloodFlowTableId()); - floodFlowWriter.setFlowPriority(arpHandlerConfig.getFloodFlowPriority()); - floodFlowWriter.setFlowIdleTimeout(arpHandlerConfig.getFloodFlowIdleTimeout()); - floodFlowWriter.setFlowHardTimeout(arpHandlerConfig.getFloodFlowHardTimeout()); - floodFlowWriter.setFlowInstallationDelay(arpHandlerConfig.getFloodFlowInstallationDelay().toJava()); - floodTopoListenerReg = floodFlowWriter.registerAsDataChangeListener(); - floodInvListenerReg = notificationService.registerListener(EthernetPacketReceived.class, floodFlowWriter); - } else { - //Write initial flows to send arp to controller - LOG.info("ArpHandler is in Reactive Mode"); - InitialFlowWriter initialFlowWriter = new InitialFlowWriter(rpcService.getRpc(AddFlow.class)); - initialFlowWriter.setFlowTableId(arpHandlerConfig.getArpFlowTableId()); - initialFlowWriter.setFlowPriority(arpHandlerConfig.getArpFlowPriority()); - initialFlowWriter.setFlowIdleTimeout(arpHandlerConfig.getArpFlowIdleTimeout()); - initialFlowWriter.setFlowHardTimeout(arpHandlerConfig.getArpFlowHardTimeout()); - initialFlowWriter.setIsHybridMode(arpHandlerConfig.getIsHybridMode()); - topoNodeListenerReg = initialFlowWriter.registerAsDataChangeListener(dataBroker); + public void init() { + if (arpHandlerConfig.getIsProactiveFloodMode()) { + // Setup proactive flow writer, which writes flood flows + LOG.info("ArpHandler is in Proactive Flood Mode"); + ProactiveFloodFlowWriter floodFlowWriter = new ProactiveFloodFlowWriter(dataBroker, + rpcService.getRpc(AddFlow.class)); + floodFlowWriter.setFlowTableId(arpHandlerConfig.getFloodFlowTableId()); + floodFlowWriter.setFlowPriority(arpHandlerConfig.getFloodFlowPriority()); + floodFlowWriter.setFlowIdleTimeout(arpHandlerConfig.getFloodFlowIdleTimeout()); + floodFlowWriter.setFlowHardTimeout(arpHandlerConfig.getFloodFlowHardTimeout()); + floodFlowWriter.setFlowInstallationDelay(arpHandlerConfig.getFloodFlowInstallationDelay().toJava()); + floodTopoListenerReg = floodFlowWriter.registerAsDataChangeListener(); + floodInvListenerReg = notificationService.registerListener(EthernetPacketReceived.class, floodFlowWriter); + } else { + // Write initial flows to send arp to controller + LOG.info("ArpHandler is in Reactive Mode"); + InitialFlowWriter initialFlowWriter = new InitialFlowWriter(rpcService.getRpc(AddFlow.class)); + initialFlowWriter.setFlowTableId(arpHandlerConfig.getArpFlowTableId()); + initialFlowWriter.setFlowPriority(arpHandlerConfig.getArpFlowPriority()); + initialFlowWriter.setFlowIdleTimeout(arpHandlerConfig.getArpFlowIdleTimeout()); + initialFlowWriter.setFlowHardTimeout(arpHandlerConfig.getArpFlowHardTimeout()); + initialFlowWriter.setIsHybridMode(arpHandlerConfig.getIsHybridMode()); + topoNodeListenerReg = initialFlowWriter.registerAsDataChangeListener(dataBroker); - // Setup InventoryReader - InventoryReader inventoryReader = new InventoryReader(dataBroker); + // Setup InventoryReader + InventoryReader inventoryReader = new InventoryReader(dataBroker); - // Setup PacketDispatcher - PacketDispatcher packetDispatcher = new PacketDispatcher(inventoryReader, - rpcService.getRpc(TransmitPacket.class)); + // Setup PacketDispatcher + PacketDispatcher packetDispatcher = new PacketDispatcher(inventoryReader, + rpcService.getRpc(TransmitPacket.class)); - // Setup ArpPacketHandler - ArpPacketHandler arpPacketHandler = new ArpPacketHandler(packetDispatcher); + // Setup ArpPacketHandler + ArpPacketHandler arpPacketHandler = new ArpPacketHandler(packetDispatcher); - // Register ArpPacketHandler - this.listenerRegistration = notificationService.registerListener(ArpPacketReceived.class, arpPacketHandler); - } - LOG.info("ArpHandler initialized."); - } + // Register ArpPacketHandler + this.listenerRegistration = notificationService.registerListener(ArpPacketReceived.class, arpPacketHandler); + } + LOG.info("ArpHandler initialized."); + } - public void close() throws Exception { - if (listenerRegistration != null) { - listenerRegistration.close(); - } - if (floodTopoListenerReg != null) { - floodTopoListenerReg.close(); - } - if (floodInvListenerReg != null) { - floodInvListenerReg.close(); - } - if (topoNodeListenerReg != null) { - topoNodeListenerReg.close(); - } - LOG.info("ArpHandler (instance {}) torn down.", this); - } + public void close() throws Exception { + if (listenerRegistration != null) { + listenerRegistration.close(); + } + if (floodTopoListenerReg != null) { + floodTopoListenerReg.close(); + } + if (floodInvListenerReg != null) { + floodInvListenerReg.close(); + } + if (topoNodeListenerReg != null) { + topoNodeListenerReg.close(); + } + LOG.info("ArpHandler (instance {}) torn down.", this); + } } diff --git a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java index 29620d0fe..413a6f519 100644 --- a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java +++ b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java @@ -9,10 +9,8 @@ import static java.util.Objects.requireNonNull; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.MoreExecutors; import java.util.List; + import org.opendaylight.l2switch.arphandler.inventory.InventoryReader; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; @@ -22,136 +20,127 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacket; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; + /** * PacketDispatcher sends packets out to the network. */ public class PacketDispatcher { - private static final Logger LOG = LoggerFactory.getLogger(PacketDispatcher.class); - - private final InventoryReader inventoryReader; - private final TransmitPacket transmitPacket; - - public PacketDispatcher(InventoryReader inventoryReader, TransmitPacket transmitPacket) { - this.inventoryReader = requireNonNull(inventoryReader); - this.transmitPacket = requireNonNull(transmitPacket); - } - - /** - * Dispatches the packet in the appropriate way - flood or unicast. - * - * @param payload - * The payload to be sent. - * @param ingress - * The NodeConnector where the payload came from. - * @param srcMac - * The source MacAddress of the packet. - * @param destMac - * The destination MacAddress of the packet. - */ - public void dispatchPacket(byte[] payload, NodeConnectorRef ingress, MacAddress srcMac, MacAddress destMac) { - inventoryReader.readInventory(); - - String nodeId = ingress.getValue().firstIdentifierOf(Node.class).firstKeyOf(Node.class).getId().getValue(); - NodeConnectorRef srcConnectorRef = inventoryReader.getControllerSwitchConnectors().get(nodeId); - - if (srcConnectorRef == null) { - refreshInventoryReader(); - srcConnectorRef = inventoryReader.getControllerSwitchConnectors().get(nodeId); - } - NodeConnectorRef destNodeConnector = inventoryReader - .getNodeConnector(ingress.getValue().firstIdentifierOf(Node.class), destMac); - if (srcConnectorRef != null) { - if (destNodeConnector != null) { - sendPacketOut(payload, srcConnectorRef, destNodeConnector); - } else { - floodPacket(nodeId, payload, ingress, srcConnectorRef); - } - } else { - LOG.info("Cannot send packet out or flood as controller node connector is not available for node {}.", - nodeId); - } - } - - /** - * Floods the packet. - * - * @param nodeId - * The node id - * @param payload - * The payload to be sent. - * @param origIngress - * The NodeConnector where the payload came from. - */ - public void floodPacket(String nodeId, byte[] payload, NodeConnectorRef origIngress, - NodeConnectorRef controllerNodeConnector) { - - List nodeConnectors = inventoryReader.getSwitchNodeConnectors().get(nodeId); - - if (nodeConnectors == null) { - refreshInventoryReader(); - nodeConnectors = inventoryReader.getSwitchNodeConnectors().get(nodeId); - if (nodeConnectors == null) { - LOG.info("Cannot flood packets, as inventory doesn't have any node connectors for node {}", nodeId); - return; - } - } - for (NodeConnectorRef ncRef : nodeConnectors) { - String ncId = ncRef.getValue().firstIdentifierOf(NodeConnector.class) - .firstKeyOf(NodeConnector.class).getId().getValue(); - // Don't flood on discarding node connectors & origIngress - if (!ncId.equals(origIngress.getValue().firstIdentifierOf(NodeConnector.class) - .firstKeyOf(NodeConnector.class).getId().getValue())) { - sendPacketOut(payload, origIngress, ncRef); - } - } - } - - /** - * Sends the specified packet on the specified port. - * - * @param payload - * The payload to be sent. - * @param ingress - * The NodeConnector where the payload came from. - * @param egress - * The NodeConnector where the payload will go. - */ - public void sendPacketOut(byte[] payload, NodeConnectorRef ingress, NodeConnectorRef egress) { - if (ingress == null || egress == null) { - return; - } - InstanceIdentifier egressNodePath = getNodePath(egress.getValue()); - TransmitPacketInput input = new TransmitPacketInputBuilder() - .setPayload(payload) - .setNode(new NodeRef(egressNodePath)) - .setEgress(egress) - .setIngress(ingress) - .build(); - - Futures.addCallback(transmitPacket.invoke(input), new FutureCallback>() { - @Override - public void onSuccess(RpcResult result) { - LOG.debug("transmitPacket was successful"); - } - - @Override - public void onFailure(Throwable failure) { - LOG.debug("transmitPacket for {} failed", input, failure); - } - }, MoreExecutors.directExecutor()); - } - - private void refreshInventoryReader() { - inventoryReader.setRefreshData(true); - inventoryReader.readInventory(); - } - - private static InstanceIdentifier getNodePath(final InstanceIdentifier nodeChild) { - return nodeChild.firstIdentifierOf(Node.class); - } + private static final Logger LOG = LoggerFactory.getLogger(PacketDispatcher.class); + + private final InventoryReader inventoryReader; + private final TransmitPacket transmitPacket; + + public PacketDispatcher(InventoryReader inventoryReader, TransmitPacket transmitPacket) { + this.inventoryReader = requireNonNull(inventoryReader); + this.transmitPacket = requireNonNull(transmitPacket); + } + + /** + * Dispatches the packet in the appropriate way - flood or unicast. + * + * @param payload The payload to be sent. + * @param ingress The NodeConnector where the payload came from. + * @param srcMac The source MacAddress of the packet. + * @param destMac The destination MacAddress of the packet. + */ + public void dispatchPacket(byte[] payload, NodeConnectorRef ingress, MacAddress srcMac, MacAddress destMac) { + inventoryReader.readInventory(); + + String nodeId = ingress.getValue().toString(); + NodeConnectorRef srcConnectorRef = inventoryReader.getControllerSwitchConnectors().get(nodeId); + + if (srcConnectorRef == null) { + refreshInventoryReader(); + srcConnectorRef = inventoryReader.getControllerSwitchConnectors().get(nodeId); + } + NodeConnectorRef destNodeConnector = inventoryReader + .getNodeConnector((DataObjectIdentifier) ingress.getValue(), destMac); + if (srcConnectorRef != null) { + if (destNodeConnector != null) { + sendPacketOut(payload, srcConnectorRef, destNodeConnector); + } else { + floodPacket(nodeId, payload, ingress, srcConnectorRef); + } + } else { + LOG.info("Cannot send packet out or flood as controller node connector is not available for node {}.", + nodeId); + } + } + + /** + * Floods the packet. + * + * @param nodeId The node id + * @param payload The payload to be sent. + * @param origIngress The NodeConnector where the payload came from. + */ + public void floodPacket(String nodeId, byte[] payload, NodeConnectorRef origIngress, + NodeConnectorRef controllerNodeConnector) { + + List nodeConnectors = inventoryReader.getSwitchNodeConnectors().get(nodeId); + + if (nodeConnectors == null) { + refreshInventoryReader(); + nodeConnectors = inventoryReader.getSwitchNodeConnectors().get(nodeId); + if (nodeConnectors == null) { + LOG.info("Cannot flood packets, as inventory doesn't have any node connectors for node {}", nodeId); + return; + } + } + for (NodeConnectorRef ncRef : nodeConnectors) { + String ncId = ncRef.getValue().firstIdentifierOf(NodeConnector.class).firstKeyOf(NodeConnector.class) + .getId().getValue(); + // Don't flood on discarding node connectors & origIngress + if (!ncId.equals(origIngress.getValue().firstIdentifierOf(NodeConnector.class) + .firstKeyOf(NodeConnector.class).getId().getValue())) { + sendPacketOut(payload, origIngress, ncRef); + } + } + } + + /** + * Sends the specified packet on the specified port. + * + * @param payload The payload to be sent. + * @param ingress The NodeConnector where the payload came from. + * @param egress The NodeConnector where the payload will go. + */ + public void sendPacketOut(byte[] payload, NodeConnectorRef ingress, NodeConnectorRef egress) { + if (ingress == null || egress == null) { + return; + } + InstanceIdentifier egressNodePath = getNodePath(egress.getValue()); + TransmitPacketInput input = new TransmitPacketInputBuilder().setPayload(payload) + .setNode(new NodeRef(egressNodePath)).setEgress(egress).setIngress(ingress).build(); + + Futures.addCallback(transmitPacket.invoke(input), new FutureCallback>() { + @Override + public void onSuccess(RpcResult result) { + LOG.debug("transmitPacket was successful"); + } + + @Override + public void onFailure(Throwable failure) { + LOG.debug("transmitPacket for {} failed", input, failure); + } + }, MoreExecutors.directExecutor()); + } + + private void refreshInventoryReader() { + inventoryReader.setRefreshData(true); + inventoryReader.readInventory(); + } + + private static InstanceIdentifier getNodePath(final InstanceIdentifier nodeChild) { + return nodeChild.firstIdentifierOf(Node.class); + } } diff --git a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/ProactiveFloodFlowWriter.java b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/ProactiveFloodFlowWriter.java index 045d2073a..69c359134 100644 --- a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/ProactiveFloodFlowWriter.java +++ b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/ProactiveFloodFlowWriter.java @@ -9,9 +9,8 @@ import static java.util.Objects.requireNonNull; -import com.google.common.util.concurrent.FluentFuture; import java.util.ArrayList; -import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutionException; @@ -20,6 +19,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; + import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.mdsal.binding.api.DataTreeChangeListener; import org.opendaylight.mdsal.binding.api.DataTreeIdentifier; @@ -67,9 +67,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatus; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.EthernetPacketReceived; -import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.binding.util.BindingMap; +import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.util.BindingMap; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.Uint16; import org.opendaylight.yangtools.yang.common.Uint64; @@ -77,257 +78,252 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.util.concurrent.FluentFuture; + /** * ProactiveFloodFlowWriter is used for the proactive mode of L2Switch. In this * mode, flood flows are automatically written to each switch and less traffic * is sent to the controller. */ public class ProactiveFloodFlowWriter implements DataTreeChangeListener, - NotificationService.Listener { - - private static final Logger LOG = LoggerFactory.getLogger(ProactiveFloodFlowWriter.class); - - private static final String FLOW_ID_PREFIX = "L2switch-"; - - private final DataBroker dataBroker; - private final AddFlow addFlow; - private final ScheduledExecutorService stpStatusDataChangeEventProcessor = Executors.newScheduledThreadPool(1); - private volatile boolean flowRefreshScheduled = false; - private volatile boolean threadReschedule = false; - private long flowInstallationDelay; - private Uint8 flowTableId = Uint8.ZERO; - private Uint16 flowPriority = Uint16.ZERO; - private Uint16 flowIdleTimeout = Uint16.ZERO; - private Uint16 flowHardTimeout = Uint16.ZERO; - private final AtomicLong flowIdInc = new AtomicLong(); - private final AtomicLong flowCookieInc = new AtomicLong(0x2b00000000000000L); - - public ProactiveFloodFlowWriter(DataBroker dataBroker, AddFlow addFlow) { - this.dataBroker = requireNonNull(dataBroker); - this.addFlow = requireNonNull(addFlow); - } - - public void setFlowInstallationDelay(long flowInstallationDelay) { - this.flowInstallationDelay = flowInstallationDelay; - } - - public void setFlowTableId(Uint8 flowTableId) { - this.flowTableId = requireNonNull(flowTableId); - } - - public void setFlowPriority(Uint16 flowPriority) { - this.flowPriority = requireNonNull(flowPriority); - } - - public void setFlowIdleTimeout(Uint16 flowIdleTimeout) { - this.flowIdleTimeout = requireNonNull(flowIdleTimeout); - } - - public void setFlowHardTimeout(Uint16 flowHardTimeout) { - this.flowHardTimeout = requireNonNull(flowHardTimeout); - } - - @Override - public void onNotification(EthernetPacketReceived packetRecevied) { - if (!flowRefreshScheduled) { - synchronized (this) { - if (!flowRefreshScheduled) { - stpStatusDataChangeEventProcessor.schedule(new StpStatusDataChangeEventProcessor(), - flowInstallationDelay, TimeUnit.MILLISECONDS); - flowRefreshScheduled = true; - LOG.debug("Scheduled Flows for refresh."); - } - } - } else { - LOG.debug("Already scheduled for flow refresh."); - threadReschedule = true; - } - } - - /** - * Registers as a data listener for Nodes. - */ - public ListenerRegistration registerAsDataChangeListener() { - return dataBroker.registerDataTreeChangeListener( - DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(Nodes.class) - .child(Node.class) - .child(NodeConnector.class) - .augmentation(StpStatusAwareNodeConnector.class) - .build()), this); - } - - /** - * Install flows when a link comes up/down. - */ - @Override - public void onDataTreeChanged(Collection> changes) { - if (!flowRefreshScheduled) { - synchronized (this) { - if (!flowRefreshScheduled) { - stpStatusDataChangeEventProcessor.schedule(new StpStatusDataChangeEventProcessor(), - flowInstallationDelay, TimeUnit.MILLISECONDS); - flowRefreshScheduled = true; - LOG.debug("Scheduled Flows for refresh."); - } - } - } else { - LOG.debug("Already scheduled for flow refresh."); - threadReschedule = true; - } - } - - private final class StpStatusDataChangeEventProcessor implements Runnable { - @Override - public void run() { - LOG.debug("In flow refresh thread."); - if (threadReschedule) { - LOG.debug("Rescheduling thread"); - stpStatusDataChangeEventProcessor.schedule(this, flowInstallationDelay, TimeUnit.MILLISECONDS); - threadReschedule = false; - return; - } - - flowRefreshScheduled = false; - installFloodFlows(); - } - - /** - * Installs a FloodFlow on each node. - */ - private void installFloodFlows() { - final FluentFuture> readFuture; - try (ReadTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction()) { - readFuture = readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, - InstanceIdentifier.create(Nodes.class)); - } - - final Nodes nodes; - try { - nodes = readFuture.get().orElse(null); - } catch (InterruptedException | ExecutionException e) { - LOG.error("Failed to read nodes from Operation data store."); - throw new RuntimeException("Failed to read nodes from Operation data store.", e); - } - - if (nodes == null) { - // Reschedule thread when the data store read had errors - LOG.debug("Rescheduling flow refresh thread because datastore read failed."); - if (!flowRefreshScheduled) { - flowRefreshScheduled = true; - stpStatusDataChangeEventProcessor.schedule(this, flowInstallationDelay, TimeUnit.MILLISECONDS); - } - } else { - for (Node node : nodes.nonnullNode().values()) { - // Install a FloodFlow on each node - final Map nodeConnectors = node.getNodeConnector(); - if (nodeConnectors != null) { - for (NodeConnector outerNodeConnector : nodeConnectors.values()) { - StpStatusAwareNodeConnector outerSaNodeConnector = outerNodeConnector - .augmentation(StpStatusAwareNodeConnector.class); - if (outerSaNodeConnector != null - && StpStatus.Discarding.equals(outerSaNodeConnector.getStatus())) { - continue; - } - if (!outerNodeConnector.getId().toString().contains("LOCAL")) { - int order = 0; - ArrayList outputActions = new ArrayList<>(); - for (NodeConnector nodeConnector : nodeConnectors.values()) { - if (!nodeConnector.getId().toString().contains("LOCAL") - && !outerNodeConnector.equals(nodeConnector)) { - // NodeConnectors without STP status - // (external ports) and NodeConnectors - // that are "forwarding" will be flooded - // on - StpStatusAwareNodeConnector saNodeConnector = nodeConnector - .augmentation(StpStatusAwareNodeConnector.class); - if (saNodeConnector == null - || StpStatus.Forwarding.equals(saNodeConnector.getStatus())) { - outputActions.add(new ActionBuilder() - .setOrder(order++) - .setAction( - new OutputActionCaseBuilder() - .setOutputAction(new OutputActionBuilder() - .setMaxLength(Uint16.MAX_VALUE) - .setOutputNodeConnector(nodeConnector.getId()) - .build()) - .build()) - .build()); - } - } - } - - // Add controller port to outputActions for - // external ports only - if (outerSaNodeConnector == null) { - outputActions.add(new ActionBuilder().withKey(new ActionKey(order++)) - .setAction( - new OutputActionCaseBuilder() - .setOutputAction(new OutputActionBuilder() - .setMaxLength(Uint16.MAX_VALUE) - .setOutputNodeConnector(new Uri(OutputPortValues.CONTROLLER.toString())) - .build()) - .build()) - .build()); - } - - // Create an Apply Action - ApplyActions applyActions = new ApplyActionsBuilder() - .setAction(BindingMap.ordered(outputActions)).build(); - - // Wrap our Apply Action in an Instruction - Instruction applyActionsInstruction = new InstructionBuilder() - .setOrder(0) - .setInstruction(new ApplyActionsCaseBuilder() - .setApplyActions(applyActions) - .build()) - .build(); - - FlowBuilder floodFlowBuilder = createBaseFlowForPortMatch(outerNodeConnector); - floodFlowBuilder.setInstructions(new InstructionsBuilder() - .setInstruction(BindingMap.of(applyActionsInstruction)) - .build()); - - writeFlowToSwitch(node.getId(), floodFlowBuilder.build()); - } - } - } - } - } - } - - private FlowBuilder createBaseFlowForPortMatch(NodeConnector nc) { - FlowBuilder floodFlow = new FlowBuilder().setTableId(flowTableId).setFlowName("flood"); - floodFlow.setId(new FlowId(Long.toString(floodFlow.hashCode()))); - - Match match = new MatchBuilder().setInPort(nc.getId()).build(); - - floodFlow.setMatch(match) - .setPriority(flowPriority) - .setBufferId(OFConstants.OFP_NO_BUFFER) - .setHardTimeout(flowHardTimeout) - .setIdleTimeout(flowIdleTimeout) - .setCookie(new FlowCookie(Uint64.fromLongBits(flowCookieInc.getAndIncrement()))) - .setFlags(new FlowModFlags(false, false, false, false, false)); - return floodFlow; - } - - /** - * Starts and commits data change transaction which modifies provided - * flow path with supplied body. - */ - private Future> writeFlowToSwitch(NodeId nodeId, Flow flow) { - InstanceIdentifier nodeInstanceId = InstanceIdentifier.builder(Nodes.class) - .child(Node.class, new NodeKey(nodeId)).build(); - InstanceIdentifier
tableInstanceId = nodeInstanceId - .augmentation(FlowCapableNode.class) - .child(Table.class, new TableKey(flowTableId)); - InstanceIdentifier flowPath = tableInstanceId.child(Flow.class, - new FlowKey(new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement())))); - - final AddFlowInputBuilder builder = new AddFlowInputBuilder(flow).setNode(new NodeRef(nodeInstanceId)) - .setFlowTable(new FlowTableRef(tableInstanceId)).setFlowRef(new FlowRef(flowPath)) - .setTransactionUri(new Uri(flow.getId().getValue())); - return addFlow.invoke(builder.build()); - } - } + NotificationService.Listener { + + private static final Logger LOG = LoggerFactory.getLogger(ProactiveFloodFlowWriter.class); + + private static final String FLOW_ID_PREFIX = "L2switch-"; + + private final DataBroker dataBroker; + private final AddFlow addFlow; + private final ScheduledExecutorService stpStatusDataChangeEventProcessor = Executors.newScheduledThreadPool(1); + private volatile boolean flowRefreshScheduled = false; + private volatile boolean threadReschedule = false; + private long flowInstallationDelay; + private Uint8 flowTableId = Uint8.ZERO; + private Uint16 flowPriority = Uint16.ZERO; + private Uint16 flowIdleTimeout = Uint16.ZERO; + private Uint16 flowHardTimeout = Uint16.ZERO; + private final AtomicLong flowIdInc = new AtomicLong(); + private final AtomicLong flowCookieInc = new AtomicLong(0x2b00000000000000L); + + public ProactiveFloodFlowWriter(DataBroker dataBroker, AddFlow addFlow) { + this.dataBroker = requireNonNull(dataBroker); + this.addFlow = requireNonNull(addFlow); + } + + public void setFlowInstallationDelay(long flowInstallationDelay) { + this.flowInstallationDelay = flowInstallationDelay; + } + + public void setFlowTableId(Uint8 flowTableId) { + this.flowTableId = requireNonNull(flowTableId); + } + + public void setFlowPriority(Uint16 flowPriority) { + this.flowPriority = requireNonNull(flowPriority); + } + + public void setFlowIdleTimeout(Uint16 flowIdleTimeout) { + this.flowIdleTimeout = requireNonNull(flowIdleTimeout); + } + + public void setFlowHardTimeout(Uint16 flowHardTimeout) { + this.flowHardTimeout = requireNonNull(flowHardTimeout); + } + + @Override + public void onNotification(EthernetPacketReceived packetRecevied) { + if (!flowRefreshScheduled) { + synchronized (this) { + if (!flowRefreshScheduled) { + stpStatusDataChangeEventProcessor.schedule(new StpStatusDataChangeEventProcessor(), + flowInstallationDelay, TimeUnit.MILLISECONDS); + flowRefreshScheduled = true; + LOG.debug("Scheduled Flows for refresh."); + } + } + } else { + LOG.debug("Already scheduled for flow refresh."); + threadReschedule = true; + } + } + + /** + * Registers as a data listener for Nodes. + */ + public Registration registerAsDataChangeListener() { + return dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, + InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class) + .augmentation(StpStatusAwareNodeConnector.class).build()), + this); + } + + /** + * Install flows when a link comes up/down. + */ + @Override + public void onDataTreeChanged(List> changes) { + if (!flowRefreshScheduled) { + synchronized (this) { + if (!flowRefreshScheduled) { + stpStatusDataChangeEventProcessor.schedule(new StpStatusDataChangeEventProcessor(), + flowInstallationDelay, TimeUnit.MILLISECONDS); + flowRefreshScheduled = true; + LOG.debug("Scheduled Flows for refresh."); + } + } + } else { + LOG.debug("Already scheduled for flow refresh."); + threadReschedule = true; + } + } + + private final class StpStatusDataChangeEventProcessor implements Runnable { + @Override + public void run() { + LOG.debug("In flow refresh thread."); + if (threadReschedule) { + LOG.debug("Rescheduling thread"); + stpStatusDataChangeEventProcessor.schedule(this, flowInstallationDelay, TimeUnit.MILLISECONDS); + threadReschedule = false; + return; + } + + flowRefreshScheduled = false; + installFloodFlows(); + } + + /** + * Installs a FloodFlow on each node. + */ + private void installFloodFlows() { + final FluentFuture> readFuture; + try (ReadTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction()) { + readFuture = readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, + InstanceIdentifier.create(Nodes.class)); + } + + final Nodes nodes; + try { + nodes = readFuture.get().orElse(null); + } catch (InterruptedException | ExecutionException e) { + LOG.error("Failed to read nodes from Operation data store."); + throw new RuntimeException("Failed to read nodes from Operation data store.", e); + } + + if (nodes == null) { + // Reschedule thread when the data store read had errors + LOG.debug("Rescheduling flow refresh thread because datastore read failed."); + if (!flowRefreshScheduled) { + flowRefreshScheduled = true; + stpStatusDataChangeEventProcessor.schedule(this, flowInstallationDelay, TimeUnit.MILLISECONDS); + } + } else { + for (Node node : nodes.nonnullNode().values()) { + // Install a FloodFlow on each node + final Map nodeConnectors = node.getNodeConnector(); + if (nodeConnectors != null) { + for (NodeConnector outerNodeConnector : nodeConnectors.values()) { + StpStatusAwareNodeConnector outerSaNodeConnector = outerNodeConnector + .augmentation(StpStatusAwareNodeConnector.class); + if (outerSaNodeConnector != null + && StpStatus.Discarding.equals(outerSaNodeConnector.getStatus())) { + continue; + } + if (!outerNodeConnector.getId().toString().contains("LOCAL")) { + int order = 0; + ArrayList outputActions = new ArrayList<>(); + for (NodeConnector nodeConnector : nodeConnectors.values()) { + if (!nodeConnector.getId().toString().contains("LOCAL") + && !outerNodeConnector.equals(nodeConnector)) { + // NodeConnectors without STP status + // (external ports) and NodeConnectors + // that are "forwarding" will be flooded + // on + StpStatusAwareNodeConnector saNodeConnector = nodeConnector + .augmentation(StpStatusAwareNodeConnector.class); + if (saNodeConnector == null + || StpStatus.Forwarding.equals(saNodeConnector.getStatus())) { + outputActions + .add(new ActionBuilder().setOrder(order++) + .setAction(new OutputActionCaseBuilder() + .setOutputAction(new OutputActionBuilder() + .setMaxLength(Uint16.MAX_VALUE) + .setOutputNodeConnector( + nodeConnector.getId()) + .build()) + .build()) + .build()); + } + } + } + + // Add controller port to outputActions for + // external ports only + if (outerSaNodeConnector == null) { + outputActions.add(new ActionBuilder().withKey(new ActionKey(order++)) + .setAction(new OutputActionCaseBuilder().setOutputAction( + new OutputActionBuilder().setMaxLength(Uint16.MAX_VALUE) + .setOutputNodeConnector( + new Uri(OutputPortValues.CONTROLLER.toString())) + .build()) + .build()) + .build()); + } + + // Create an Apply Action + ApplyActions applyActions = new ApplyActionsBuilder() + .setAction(BindingMap.ordered(outputActions)).build(); + + // Wrap our Apply Action in an Instruction + Instruction applyActionsInstruction = new InstructionBuilder().setOrder(0) + .setInstruction( + new ApplyActionsCaseBuilder().setApplyActions(applyActions).build()) + .build(); + + FlowBuilder floodFlowBuilder = createBaseFlowForPortMatch(outerNodeConnector); + floodFlowBuilder.setInstructions(new InstructionsBuilder() + .setInstruction(BindingMap.of(applyActionsInstruction)).build()); + + writeFlowToSwitch(node.getId(), floodFlowBuilder.build()); + } + } + } + } + } + } + + private FlowBuilder createBaseFlowForPortMatch(NodeConnector nc) { + FlowBuilder floodFlow = new FlowBuilder().setTableId(flowTableId).setFlowName("flood"); + floodFlow.setId(new FlowId(Long.toString(floodFlow.hashCode()))); + + Match match = new MatchBuilder().setInPort(nc.getId()).build(); + + floodFlow.setMatch(match).setPriority(flowPriority).setBufferId(OFConstants.OFP_NO_BUFFER) + .setHardTimeout(flowHardTimeout).setIdleTimeout(flowIdleTimeout) + .setCookie(new FlowCookie(Uint64.fromLongBits(flowCookieInc.getAndIncrement()))) + .setFlags(new FlowModFlags(false, false, false, false, false)); + return floodFlow; + } + + /** + * Starts and commits data change transaction which modifies provided flow path + * with supplied body. + */ + private Future> writeFlowToSwitch(NodeId nodeId, Flow flow) { + DataObjectIdentifier nodeInstanceId = DataObjectIdentifier.builder(Nodes.class) + .child(Node.class, new NodeKey(nodeId)).build(); + DataObjectIdentifier
tableInstanceId = nodeInstanceId.toBuilder().augmentation(FlowCapableNode.class) + .child(Table.class, new TableKey(flowTableId)).build(); + DataObjectIdentifier flowPath = tableInstanceId.toBuilder() + .child(Flow.class, + new FlowKey(new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement())))) + .build(); + + final AddFlowInputBuilder builder = new AddFlowInputBuilder(flow).setNode(new NodeRef(nodeInstanceId)) + .setFlowTable(new FlowTableRef(tableInstanceId)).setFlowRef(new FlowRef(flowPath)) + .setTransactionUri(new Uri(flow.getId().getValue())); + + return addFlow.invoke(builder.build()); + } + } } diff --git a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriter.java b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriter.java index 4f19e06bf..673c37b5c 100644 --- a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriter.java +++ b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriter.java @@ -9,15 +9,14 @@ import static java.util.Objects.requireNonNull; -import com.google.common.util.concurrent.ListenableFuture; import java.util.ArrayList; -import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; + import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.mdsal.binding.api.DataTreeChangeListener; import org.opendaylight.mdsal.binding.api.DataTreeIdentifier; @@ -60,10 +59,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetTypeBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.KnownEtherType; -import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.binding.util.BindingMap; +import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.util.BindingMap; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.Uint16; import org.opendaylight.yangtools.yang.common.Uint32; @@ -72,214 +71,200 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.util.concurrent.ListenableFuture; + /** * Adds a flow, which sends all ARP packets to the controller, on all switches. - * Registers as ODL Inventory listener so that it can add flows once a new node i.e. switch is added + * Registers as ODL Inventory listener so that it can add flows once a new node + * i.e. switch is added */ public class InitialFlowWriter implements DataTreeChangeListener { - private static final Logger LOG = LoggerFactory.getLogger(InitialFlowWriter.class); - - private static final String FLOW_ID_PREFIX = "L2switch-"; - private static final EtherType ARP_ETHER_TYPE = new EtherType(Uint32.valueOf(KnownEtherType.Arp.getIntValue())); - - private final ExecutorService initialFlowExecutor = Executors.newCachedThreadPool(); - private final AddFlow addFlow; - private Uint8 flowTableId = Uint8.ZERO; - private Uint16 flowPriority = Uint16.ZERO; - private Uint16 flowIdleTimeout = Uint16.ZERO; - private Uint16 flowHardTimeout = Uint16.ZERO; - private boolean isHybridMode; - - private final AtomicLong flowIdInc = new AtomicLong(); - private final AtomicLong flowCookieInc = new AtomicLong(0x2b00000000000000L); - - public InitialFlowWriter(AddFlow addFlow) { - this.addFlow = requireNonNull(addFlow); - } - - public void setFlowTableId(Uint8 flowTableId) { - this.flowTableId = requireNonNull(flowTableId); - } - - public void setFlowPriority(Uint16 flowPriority) { - this.flowPriority = requireNonNull(flowPriority); - } - - public void setFlowIdleTimeout(Uint16 flowIdleTimeout) { - this.flowIdleTimeout = requireNonNull(flowIdleTimeout); - } - - public void setFlowHardTimeout(Uint16 flowHardTimeout) { - this.flowHardTimeout = requireNonNull(flowHardTimeout); - } - - public void setIsHybridMode(boolean isHybridMode) { - this.isHybridMode = isHybridMode; - } - - public ListenerRegistration registerAsDataChangeListener(DataBroker dataBroker) { - return dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, - InstanceIdentifier.builder(Nodes.class).child(Node.class).build()), this); - } - - @Override - public void onDataTreeChanged(Collection> changes) { - var nodeIds = new HashSet>(); - for (var change: changes) { - var rootNode = change.getRootNode(); - switch (rootNode.getModificationType()) { - case WRITE: - if (rootNode.getDataBefore() == null) { - nodeIds.add(change.getRootPath().getRootIdentifier()); - } - break; - default: - break; - } - } - - if (!nodeIds.isEmpty()) { - initialFlowExecutor.execute(new InitialFlowWriterProcessor(nodeIds)); - } - } - - /** - * A private class to process the node updated event in separate thread. Allows to release the - * thread that invoked the data node updated event. Avoids any thread lock it may cause. - */ - private class InitialFlowWriterProcessor implements Runnable { - private final Set> nodeIds; - - InitialFlowWriterProcessor(final Set> nodeIds) { - this.nodeIds = nodeIds; - } - - @Override - public void run() { - if (nodeIds == null) { - return; - } - - for (var nodeId : nodeIds) { - if (KeyedInstanceIdentifier.keyOf(nodeId).getId().getValue().contains("openflow:")) { - addInitialFlows(nodeId); - } - } - } - - /** - * Adds a flow, which sends all ARP packets to the controller, to the specified node. - * @param nodeId The node to write the flow on. - */ - public void addInitialFlows(InstanceIdentifier nodeId) { - LOG.debug("adding initial flows for node {} ", nodeId); - - InstanceIdentifier
tableId = getTableInstanceId(nodeId); - InstanceIdentifier flowId = getFlowInstanceId(tableId); - - //add arpToController flow - writeFlowToController(nodeId, tableId, flowId, createArpToControllerFlow(flowTableId, flowPriority)); - LOG.debug("Added initial flows for node {} ", nodeId); - } - - private InstanceIdentifier
getTableInstanceId(InstanceIdentifier nodeId) { - // get flow table key - TableKey flowTableKey = new TableKey(flowTableId); - - return nodeId.builder() - .augmentation(FlowCapableNode.class) - .child(Table.class, flowTableKey) - .build(); - } - - private InstanceIdentifier getFlowInstanceId(InstanceIdentifier
tableId) { - // generate unique flow key - FlowId flowId = new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement())); - FlowKey flowKey = new FlowKey(flowId); - return tableId.child(Flow.class, flowKey); - } - - private Flow createArpToControllerFlow(Uint8 tableId, Uint16 priority) { - - // start building flow - FlowBuilder arpFlow = new FlowBuilder() - .setTableId(tableId) - .setFlowName("arptocntrl"); - - // use its own hash code for id. - arpFlow.setId(new FlowId(Long.toString(arpFlow.hashCode()))); - EthernetMatchBuilder ethernetMatchBuilder = new EthernetMatchBuilder() - .setEthernetType(new EthernetTypeBuilder().setType(ARP_ETHER_TYPE).build()); - - Match match = new MatchBuilder() - .setEthernetMatch(ethernetMatchBuilder.build()) - .build(); - - List actions = new ArrayList<>(); - actions.add(new ActionBuilder() - .setOrder(0) - .withKey(new ActionKey(0)) - .setAction(new OutputActionCaseBuilder() - .setOutputAction(new OutputActionBuilder() - .setMaxLength(Uint16.MAX_VALUE) - .setOutputNodeConnector(new Uri(OutputPortValues.CONTROLLER.toString())) - .build()) - .build()) - .build()); - if (isHybridMode) { - actions.add(new ActionBuilder() - .setOrder(1) - .withKey(new ActionKey(1)) - .setAction(new OutputActionCaseBuilder() - .setOutputAction(new OutputActionBuilder() - .setMaxLength(Uint16.MAX_VALUE) - .setOutputNodeConnector(new Uri(OutputPortValues.NORMAL.toString())) - .build()) - .build()) - .build()); - } - - // Create an Apply Action - ApplyActions applyActions = new ApplyActionsBuilder() - .setAction(BindingMap.ordered(actions)) - .build(); - - // Wrap our Apply Action in an Instruction - Instruction applyActionsInstruction = new InstructionBuilder() - .setOrder(0) - .setInstruction(new ApplyActionsCaseBuilder() - .setApplyActions(applyActions) - .build()) - .build(); - - // Put our Instruction in a list of Instructions - arpFlow - .setMatch(match) - .setInstructions(new InstructionsBuilder() - .setInstruction(BindingMap.of(applyActionsInstruction)) - .build()) - .setPriority(priority) - .setBufferId(OFConstants.OFP_NO_BUFFER) - .setHardTimeout(flowHardTimeout) - .setIdleTimeout(flowIdleTimeout) - .setCookie(new FlowCookie(Uint64.fromLongBits(flowCookieInc.getAndIncrement()))) - .setFlags(new FlowModFlags(false, false, false, false, false)); - - return arpFlow.build(); - } - - private ListenableFuture> writeFlowToController( - InstanceIdentifier nodeInstanceId, InstanceIdentifier
tableInstanceId, - InstanceIdentifier flowPath, Flow flow) { - LOG.trace("Adding flow to node {}", requireNonNull(nodeInstanceId.firstKeyOf(Node.class)) - .getId() - .getValue()); - return addFlow.invoke(new AddFlowInputBuilder(flow) - .setNode(new NodeRef(nodeInstanceId)) - .setFlowRef(new FlowRef(flowPath)) - .setFlowTable(new FlowTableRef(tableInstanceId)) - .setTransactionUri(new Uri(flow.getId().getValue())) - .build()); - } - } + private static final Logger LOG = LoggerFactory.getLogger(InitialFlowWriter.class); + + private static final String FLOW_ID_PREFIX = "L2switch-"; + private static final EtherType ARP_ETHER_TYPE = new EtherType(Uint32.valueOf(KnownEtherType.Arp.getIntValue())); + + private final ExecutorService initialFlowExecutor = Executors.newCachedThreadPool(); + private final AddFlow addFlow; + private Uint8 flowTableId = Uint8.ZERO; + private Uint16 flowPriority = Uint16.ZERO; + private Uint16 flowIdleTimeout = Uint16.ZERO; + private Uint16 flowHardTimeout = Uint16.ZERO; + private boolean isHybridMode; + + private final AtomicLong flowIdInc = new AtomicLong(); + private final AtomicLong flowCookieInc = new AtomicLong(0x2b00000000000000L); + + public InitialFlowWriter(AddFlow addFlow) { + this.addFlow = requireNonNull(addFlow); + } + + public void setFlowTableId(Uint8 flowTableId) { + this.flowTableId = requireNonNull(flowTableId); + } + + public void setFlowPriority(Uint16 flowPriority) { + this.flowPriority = requireNonNull(flowPriority); + } + + public void setFlowIdleTimeout(Uint16 flowIdleTimeout) { + this.flowIdleTimeout = requireNonNull(flowIdleTimeout); + } + + public void setFlowHardTimeout(Uint16 flowHardTimeout) { + this.flowHardTimeout = requireNonNull(flowHardTimeout); + } + + public void setIsHybridMode(boolean isHybridMode) { + this.isHybridMode = isHybridMode; + } + + public Registration registerAsDataChangeListener(DataBroker dataBroker) { + return dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, + InstanceIdentifier.builder(Nodes.class).child(Node.class).build()), this); + } + + @Override + public void onDataTreeChanged(List> changes) { + var nodeIds = new HashSet>(); + for (var change : changes) { + var rootNode = change.getRootNode(); + switch (rootNode.getModificationType()) { + case WRITE: + if (rootNode.getDataBefore() == null) { + nodeIds.add(change.getRootPath().getRootIdentifier()); + } + break; + default: + break; + } + } + + if (!nodeIds.isEmpty()) { + initialFlowExecutor.execute(new InitialFlowWriterProcessor(nodeIds)); + } + } + + /** + * A private class to process the node updated event in separate thread. Allows + * to release the thread that invoked the data node updated event. Avoids any + * thread lock it may cause. + */ + private class InitialFlowWriterProcessor implements Runnable { + private final Set> nodeIds; + + InitialFlowWriterProcessor(final Set> nodeIds) { + this.nodeIds = nodeIds; + } + + @Override + public void run() { + if (nodeIds == null) { + return; + } + + for (var nodeId : nodeIds) { + if (KeyedInstanceIdentifier.keyOf(nodeId).getId().getValue().contains("openflow:")) { + addInitialFlows(nodeId); + } + } + } + + /** + * Adds a flow, which sends all ARP packets to the controller, to the specified + * node. + * + * @param nodeId The node to write the flow on. + */ + public void addInitialFlows(InstanceIdentifier nodeId) { + LOG.debug("adding initial flows for node {} ", nodeId); + + InstanceIdentifier
tableId = getTableInstanceId(nodeId); + InstanceIdentifier flowId = getFlowInstanceId(tableId); + + // add arpToController flow + writeFlowToController(nodeId, tableId, flowId, createArpToControllerFlow(flowTableId, flowPriority)); + LOG.debug("Added initial flows for node {} ", nodeId); + } + + private InstanceIdentifier
getTableInstanceId(InstanceIdentifier nodeId) { + // get flow table key + TableKey flowTableKey = new TableKey(flowTableId); + + return nodeId.builder().augmentation(FlowCapableNode.class).child(Table.class, flowTableKey).build(); + } + + private InstanceIdentifier getFlowInstanceId(InstanceIdentifier
tableId) { + // generate unique flow key + FlowId flowId = new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement())); + FlowKey flowKey = new FlowKey(flowId); + return tableId.child(Flow.class, flowKey); + } + + private Flow createArpToControllerFlow(Uint8 tableId, Uint16 priority) { + + // start building flow + FlowBuilder arpFlow = new FlowBuilder().setTableId(tableId).setFlowName("arptocntrl"); + + // use its own hash code for id. + arpFlow.setId(new FlowId(Long.toString(arpFlow.hashCode()))); + EthernetMatchBuilder ethernetMatchBuilder = new EthernetMatchBuilder() + .setEthernetType(new EthernetTypeBuilder().setType(ARP_ETHER_TYPE).build()); + + Match match = new MatchBuilder().setEthernetMatch(ethernetMatchBuilder.build()).build(); + + List actions = new ArrayList<>(); + actions.add( + new ActionBuilder().setOrder(0).withKey(new ActionKey(0)) + .setAction( + new OutputActionCaseBuilder() + .setOutputAction(new OutputActionBuilder().setMaxLength(Uint16.MAX_VALUE) + .setOutputNodeConnector( + new Uri(OutputPortValues.CONTROLLER.toString())) + .build()) + .build()) + .build()); + if (isHybridMode) { + actions.add( + new ActionBuilder().setOrder(1).withKey(new ActionKey(1)) + .setAction( + new OutputActionCaseBuilder() + .setOutputAction(new OutputActionBuilder() + .setMaxLength(Uint16.MAX_VALUE) + .setOutputNodeConnector( + new Uri(OutputPortValues.NORMAL.toString())) + .build()) + .build()) + .build()); + } + + // Create an Apply Action + ApplyActions applyActions = new ApplyActionsBuilder().setAction(BindingMap.ordered(actions)).build(); + + // Wrap our Apply Action in an Instruction + Instruction applyActionsInstruction = new InstructionBuilder().setOrder(0) + .setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyActions).build()).build(); + + // Put our Instruction in a list of Instructions + arpFlow.setMatch(match) + .setInstructions( + new InstructionsBuilder().setInstruction(BindingMap.of(applyActionsInstruction)).build()) + .setPriority(priority).setBufferId(OFConstants.OFP_NO_BUFFER).setHardTimeout(flowHardTimeout) + .setIdleTimeout(flowIdleTimeout) + .setCookie(new FlowCookie(Uint64.fromLongBits(flowCookieInc.getAndIncrement()))) + .setFlags(new FlowModFlags(false, false, false, false, false)); + + return arpFlow.build(); + } + + private ListenableFuture> writeFlowToController( + InstanceIdentifier nodeInstanceId, InstanceIdentifier
tableInstanceId, + InstanceIdentifier flowPath, Flow flow) { + LOG.trace("Adding flow to node {}", + requireNonNull(nodeInstanceId.firstKeyOf(Node.class)).getId().getValue()); + return addFlow.invoke(new AddFlowInputBuilder(flow).setNode(new NodeRef(nodeInstanceId)) + .setFlowRef(new FlowRef(flowPath)).setFlowTable(new FlowTableRef(tableInstanceId)) + .setTransactionUri(new Uri(flow.getId().getValue())).build()); + } + } } diff --git a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReader.java b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReader.java index 5d624f882..0f0106722 100755 --- a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReader.java +++ b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReader.java @@ -8,7 +8,6 @@ package org.opendaylight.l2switch.arphandler.inventory; -import com.google.common.util.concurrent.FluentFuture; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -20,6 +19,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; + import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.mdsal.binding.api.DataTreeChangeListener; import org.opendaylight.mdsal.binding.api.DataTreeIdentifier; @@ -38,243 +38,239 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatus; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnector; +import org.opendaylight.yangtools.binding.DataObject; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.concepts.Registration; -import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.util.concurrent.FluentFuture; + /** * InventoryReader reads the opendaylight-inventory tree in MD-SAL data store. */ public class InventoryReader implements DataTreeChangeListener { - private static final Logger LOG = LoggerFactory.getLogger(InventoryReader.class); - - private final DataBroker dataService; - // Key: SwitchId, Value: NodeConnectorRef that corresponds to NC between - // controller & switch - private final HashMap controllerSwitchConnectors; - // Key: SwitchId, Value: List of node connectors on this switch - private final HashMap> switchNodeConnectors; - private final List listenerRegistrationList = new CopyOnWriteArrayList<>(); - - private volatile boolean refreshData = false; - private static final long REFRESH_DATA_DELAY = 20L; - private volatile boolean refreshDataScheduled = false; - private final ScheduledExecutorService nodeConnectorDataChangeEventProcessor = Executors.newScheduledThreadPool(1); + private static final Logger LOG = LoggerFactory.getLogger(InventoryReader.class); - /** - * Construct an InventoryService object with the specified inputs. - * - * @param dataService - * The DataBrokerService associated with the InventoryService. - */ - public InventoryReader(DataBroker dataService) { - this.dataService = dataService; - controllerSwitchConnectors = new HashMap<>(); - switchNodeConnectors = new HashMap<>(); - } + private final DataBroker dataService; + // Key: SwitchId, Value: NodeConnectorRef that corresponds to NC between + // controller & switch + private final HashMap controllerSwitchConnectors; + // Key: SwitchId, Value: List of node connectors on this switch + private final HashMap> switchNodeConnectors; + private final List listenerRegistrationList = new CopyOnWriteArrayList<>(); - public void setRefreshData(boolean refreshData) { - this.refreshData = refreshData; - } + private volatile boolean refreshData = false; + private static final long REFRESH_DATA_DELAY = 20L; + private volatile boolean refreshDataScheduled = false; + private final ScheduledExecutorService nodeConnectorDataChangeEventProcessor = Executors.newScheduledThreadPool(1); - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void registerAsDataChangeListener() { - InstanceIdentifier nodeConnector = InstanceIdentifier.builder(Nodes.class) - .child(Node.class) - .child(NodeConnector.class) - .build(); - this.listenerRegistrationList.add(dataService.registerDataTreeChangeListener( - DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL,nodeConnector), - (DataTreeChangeListener)this)); + /** + * Construct an InventoryService object with the specified inputs. + * + * @param dataService The DataBrokerService associated with the + * InventoryService. + */ + public InventoryReader(DataBroker dataService) { + this.dataService = dataService; + controllerSwitchConnectors = new HashMap<>(); + switchNodeConnectors = new HashMap<>(); + } - InstanceIdentifier stpStatusAwareNodeConnecto = - InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class) - .augmentation(StpStatusAwareNodeConnector.class) - .build(); - this.listenerRegistrationList.add(dataService.registerDataTreeChangeListener( - DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, stpStatusAwareNodeConnecto), - (DataTreeChangeListener)this)); - } + public void setRefreshData(boolean refreshData) { + this.refreshData = refreshData; + } + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void registerAsDataChangeListener() { + InstanceIdentifier nodeConnector = InstanceIdentifier.builder(Nodes.class).child(Node.class) + .child(NodeConnector.class).build(); + this.listenerRegistrationList.add(dataService.registerDataTreeChangeListener( + DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, nodeConnector), + (DataTreeChangeListener) this)); - public HashMap getControllerSwitchConnectors() { - return controllerSwitchConnectors; - } + InstanceIdentifier stpStatusAwareNodeConnecto = InstanceIdentifier + .builder(Nodes.class).child(Node.class).child(NodeConnector.class) + .augmentation(StpStatusAwareNodeConnector.class).build(); + this.listenerRegistrationList.add(dataService.registerDataTreeChangeListener( + DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, stpStatusAwareNodeConnecto), + (DataTreeChangeListener) this)); + } - public HashMap> getSwitchNodeConnectors() { - return switchNodeConnectors; - } + public HashMap getControllerSwitchConnectors() { + return controllerSwitchConnectors; + } - @Override - public void onDataTreeChanged(Collection> changes) { - if (!refreshDataScheduled) { - synchronized (this) { - if (!refreshDataScheduled) { - nodeConnectorDataChangeEventProcessor.schedule(new NodeConnectorDataChangeEventProcessor(), - REFRESH_DATA_DELAY, TimeUnit.MILLISECONDS); - refreshDataScheduled = true; - } - } - } - } + public HashMap> getSwitchNodeConnectors() { + return switchNodeConnectors; + } + @Override + public void onDataTreeChanged(Collection> changes) { + if (!refreshDataScheduled) { + synchronized (this) { + if (!refreshDataScheduled) { + nodeConnectorDataChangeEventProcessor.schedule(new NodeConnectorDataChangeEventProcessor(), + REFRESH_DATA_DELAY, TimeUnit.MILLISECONDS); + refreshDataScheduled = true; + } + } + } + } - public void close() { - listenerRegistrationList.forEach(reg -> reg.close()); - } + public void close() { + listenerRegistrationList.forEach(reg -> reg.close()); + } - /** - * Read the Inventory data tree to find information about the Nodes and - * NodeConnectors. Create the list of NodeConnectors for a given switch. - * Also determine the STP status of each NodeConnector. - */ - public void readInventory() { - // Only run once for now - if (!refreshData) { - return; - } - synchronized (this) { - if (!refreshData) { - return; - } - // Read Inventory - Nodes nodes = null; - try (ReadTransaction readOnlyTransaction = dataService.newReadOnlyTransaction()) { - Optional dataObjectOptional = readOnlyTransaction - .read(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(Nodes.class)).get(); - if (dataObjectOptional.isPresent()) { - nodes = dataObjectOptional.orElseThrow(); - } - } catch (InterruptedException e) { - LOG.error("Failed to read nodes from Operation data store."); - throw new RuntimeException("Failed to read nodes from Operation data store.", e); - } catch (ExecutionException e) { - LOG.error("Failed to read nodes from Operation data store."); - throw new RuntimeException("Failed to read nodes from Operation data store.", e); - } + /** + * Read the Inventory data tree to find information about the Nodes and + * NodeConnectors. Create the list of NodeConnectors for a given switch. Also + * determine the STP status of each NodeConnector. + */ + public void readInventory() { + // Only run once for now + if (!refreshData) { + return; + } + synchronized (this) { + if (!refreshData) { + return; + } + // Read Inventory + Nodes nodes = null; + try (ReadTransaction readOnlyTransaction = dataService.newReadOnlyTransaction()) { + Optional dataObjectOptional = readOnlyTransaction + .read(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(Nodes.class)).get(); + if (dataObjectOptional.isPresent()) { + nodes = dataObjectOptional.orElseThrow(); + } + } catch (InterruptedException e) { + LOG.error("Failed to read nodes from Operation data store."); + throw new RuntimeException("Failed to read nodes from Operation data store.", e); + } catch (ExecutionException e) { + LOG.error("Failed to read nodes from Operation data store."); + throw new RuntimeException("Failed to read nodes from Operation data store.", e); + } - if (nodes != null) { - // Get NodeConnectors for each node - for (Node node : nodes.nonnullNode().values()) { - ArrayList nodeConnectorRefs = new ArrayList<>(); - for (NodeConnector nodeConnector : node.nonnullNodeConnector().values()) { - // Read STP status for this NodeConnector - StpStatusAwareNodeConnector saNodeConnector = nodeConnector - .augmentation(StpStatusAwareNodeConnector.class); - if (saNodeConnector != null && StpStatus.Discarding.equals(saNodeConnector.getStatus())) { - continue; - } - if (nodeConnector.key().toString().contains("LOCAL")) { - continue; - } - NodeConnectorRef ncRef = new NodeConnectorRef(InstanceIdentifier.builder(Nodes.class) - .child(Node.class, node.key()) - .child(NodeConnector.class, nodeConnector.key()) - .build()); - nodeConnectorRefs.add(ncRef); - } + if (nodes != null) { + // Get NodeConnectors for each node + for (Node node : nodes.nonnullNode().values()) { + ArrayList nodeConnectorRefs = new ArrayList<>(); + for (NodeConnector nodeConnector : node.nonnullNodeConnector().values()) { + // Read STP status for this NodeConnector + StpStatusAwareNodeConnector saNodeConnector = nodeConnector + .augmentation(StpStatusAwareNodeConnector.class); + if (saNodeConnector != null && StpStatus.Discarding.equals(saNodeConnector.getStatus())) { + continue; + } + if (nodeConnector.key().toString().contains("LOCAL")) { + continue; + } + NodeConnectorRef ncRef = new NodeConnectorRef(InstanceIdentifier.builder(Nodes.class) + .child(Node.class, node.key()) + .child(NodeConnector.class, nodeConnector.key()) + .build()); + nodeConnectorRefs.add(ncRef); + } - switchNodeConnectors.put(node.getId().getValue(), nodeConnectorRefs); - NodeConnectorRef ncRef = new NodeConnectorRef(InstanceIdentifier.builder(Nodes.class) - .child(Node.class, node.key()) - .child(NodeConnector.class, - new NodeConnectorKey(new NodeConnectorId(node.getId().getValue() + ":LOCAL"))) - .build()); - LOG.debug("Local port for node {} is {}", node.key(), ncRef); - controllerSwitchConnectors.put(node.getId().getValue(), ncRef); - } - } + switchNodeConnectors.put(node.getId().getValue(), nodeConnectorRefs); + NodeConnectorRef ncRef = new NodeConnectorRef(InstanceIdentifier.builder(Nodes.class) + .child(Node.class, node.key()) + .child(NodeConnector.class, + new NodeConnectorKey(new NodeConnectorId(node.getId().getValue() + ":LOCAL"))) + .build()); + LOG.debug("Local port for node {} is {}", node.key(), ncRef); + controllerSwitchConnectors.put(node.getId().getValue(), ncRef); + } + } - refreshData = false; + refreshData = false; - if (listenerRegistrationList.isEmpty()) { - registerAsDataChangeListener(); - } - } - } + if (listenerRegistrationList.isEmpty()) { + registerAsDataChangeListener(); + } + } + } - /** - * Get the NodeConnector on the specified node with the specified MacAddress - * observation. - * - * @param nodeInsId - * InstanceIdentifier for the node on which to search for. - * @param macAddress - * MacAddress to be searched for. - * @return NodeConnectorRef that pertains to the NodeConnector containing - * the MacAddress observation. - */ - public NodeConnectorRef getNodeConnector(InstanceIdentifier nodeInsId, MacAddress macAddress) { - if (nodeInsId == null || macAddress == null) { - return null; - } + /** + * Get the NodeConnector on the specified node with the specified MacAddress + * observation. + * + * @param nodeInsId InstanceIdentifier for the node on which to search for. + * @param macAddress MacAddress to be searched for. + * @return NodeConnectorRef that pertains to the NodeConnector containing the + * MacAddress observation. + */ + public NodeConnectorRef getNodeConnector(DataObjectIdentifier nodeInsId, MacAddress macAddress) { + if (nodeInsId == null || macAddress == null) { + return null; + } - final FluentFuture> readFuture; - try (ReadTransaction readOnlyTransaction = dataService.newReadOnlyTransaction()) { - readFuture = readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, nodeInsId); - } + final FluentFuture> readFuture; + try (ReadTransaction readOnlyTransaction = dataService.newReadOnlyTransaction()) { + readFuture = readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, nodeInsId); + } - final Optional dataObjectOptional; - try { - dataObjectOptional = readFuture.get(); - } catch (InterruptedException e) { - LOG.error("Failed to read nodes from Operation data store."); - throw new RuntimeException("Failed to read nodes from Operation data store.", e); - } catch (ExecutionException e) { - LOG.error("Failed to read nodes from Operation data store."); - throw new RuntimeException("Failed to read nodes from Operation data store.", e); - } + final Optional dataObjectOptional; + try { + dataObjectOptional = readFuture.get(); + } catch (InterruptedException e) { + LOG.error("Failed to read nodes from Operation data store."); + throw new RuntimeException("Failed to read nodes from Operation data store.", e); + } catch (ExecutionException e) { + LOG.error("Failed to read nodes from Operation data store."); + throw new RuntimeException("Failed to read nodes from Operation data store.", e); + } - if (dataObjectOptional.isEmpty()) { - return null; - } + if (dataObjectOptional.isEmpty()) { + return null; + } - final Node node = dataObjectOptional.orElseThrow(); - final Map connectors = node.getNodeConnector(); - if (connectors == null) { - LOG.debug("Node connectors data is not present for node {}", node.getId()); - return null; - } + final Node node = dataObjectOptional.orElseThrow(); + final Map connectors = node.getNodeConnector(); + if (connectors == null) { + LOG.debug("Node connectors data is not present for node {}", node.getId()); + return null; + } - long latest = -1; - NodeConnectorRef destNodeConnector = null; - for (NodeConnector nc : connectors.values()) { - // Don't look for mac in discarding node connectors - StpStatusAwareNodeConnector saNodeConnector = nc.augmentation(StpStatusAwareNodeConnector.class); - if (saNodeConnector != null && StpStatus.Discarding.equals(saNodeConnector.getStatus())) { - continue; - } - LOG.debug("Looking address{} in nodeconnector : {}", macAddress, nc.key()); - AddressCapableNodeConnector acnc = nc.augmentation(AddressCapableNodeConnector.class); - if (acnc != null) { - for (Addresses add : acnc.nonnullAddresses().values()) { - if (macAddress.equals(add.getMac())) { - final long lastSeen = add.getLastSeen(); - if (lastSeen > latest) { - latest = lastSeen; - LOG.debug("Found address{} in nodeconnector : {}", macAddress, nc.key()); - destNodeConnector = new NodeConnectorRef(nodeInsId.child(NodeConnector.class, nc.key())); - break; - } - } - } - } - } - return destNodeConnector; - } + long latest = -1; + NodeConnectorRef destNodeConnector = null; + for (NodeConnector nc : connectors.values()) { + // Don't look for mac in discarding node connectors + StpStatusAwareNodeConnector saNodeConnector = nc.augmentation(StpStatusAwareNodeConnector.class); + if (saNodeConnector != null && StpStatus.Discarding.equals(saNodeConnector.getStatus())) { + continue; + } + LOG.debug("Looking address{} in nodeconnector : {}", macAddress, nc.key()); + AddressCapableNodeConnector acnc = nc.augmentation(AddressCapableNodeConnector.class); + if (acnc != null) { + for (Addresses add : acnc.nonnullAddresses().values()) { + if (macAddress.equals(add.getMac())) { + final long lastSeen = add.getLastSeen(); + if (lastSeen > latest) { + latest = lastSeen; + LOG.debug("Found address{} in nodeconnector : {}", macAddress, nc.key()); + destNodeConnector = new NodeConnectorRef(nodeInsId.child(NodeConnector.class, nc.key())); + break; + } + } + } + } + } + return destNodeConnector; + } - private final class NodeConnectorDataChangeEventProcessor implements Runnable { + private final class NodeConnectorDataChangeEventProcessor implements Runnable { - @Override - public void run() { - controllerSwitchConnectors.clear(); - switchNodeConnectors.clear(); - refreshDataScheduled = false; - setRefreshData(true); - readInventory(); - } - } + @Override + public void run() { + controllerSwitchConnectors.clear(); + switchNodeConnectors.clear(); + refreshDataScheduled = false; + setRefreshData(true); + readInventory(); + } + } } From 8d2191c8e813ba7b7e45251be99067a9c95ec1f0 Mon Sep 17 00:00:00 2001 From: i-am-arathypm Date: Wed, 9 Jul 2025 17:22:20 +0530 Subject: [PATCH 06/15] Loopremover fix --- .../util/InstanceIdentifierUtils.java | 30 +- .../TopologyLinkDataChangeHandlerTest.java | 509 +++++++++--------- .../util/InstanceIdentifierUtilsTest.java | 109 ++-- 3 files changed, 317 insertions(+), 331 deletions(-) diff --git a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtils.java b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtils.java index 074a4f0c7..908fa3eb7 100644 --- a/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtils.java +++ b/loopremover/implementation/src/main/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtils.java @@ -13,6 +13,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; @@ -111,20 +112,21 @@ public static DataObjectIdentifier createNodeConnectorIdentifier( .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(nodeConnectorIdValue))).build(); } -// public static InstanceIdentifier generateNodeInstanceIdentifier(final NodeConnectorRef nodeConnectorRef) { -// return nodeConnectorRef.getValue().firstIdentifierOf(Node.class); -// } -// -// public static InstanceIdentifier
generateFlowTableInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, -// final TableKey flowTableKey) { -// return generateNodeInstanceIdentifier(nodeConnectorRef).builder().augmentation(FlowCapableNode.class) -// .child(Table.class, flowTableKey).build(); -// } -// -// public static InstanceIdentifier generateFlowInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, -// final TableKey flowTableKey, final FlowKey flowKey) { -// return generateFlowTableInstanceIdentifier(nodeConnectorRef, flowTableKey).child(Flow.class, flowKey); -// } + public static DataObjectIdentifier generateNodeInstanceIdentifier(final NodeConnectorRef nodeConnectorRef) { + return (DataObjectIdentifier) nodeConnectorRef.getValue(); + } + + public static DataObjectIdentifier
generateFlowTableInstanceIdentifier( + final NodeConnectorRef nodeConnectorRef, final TableKey flowTableKey) { + return generateNodeInstanceIdentifier(nodeConnectorRef).toBuilder().augmentation(FlowCapableNode.class) + .child(Table.class, flowTableKey).build(); + } + + public static DataObjectIdentifier generateFlowInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, + final TableKey flowTableKey, final FlowKey flowKey) { + return generateFlowTableInstanceIdentifier(nodeConnectorRef, flowTableKey).toBuilder() + .child(Flow.class, flowKey).build(); + } public static InstanceIdentifier generateTopologyInstanceIdentifier(final String topologyId) { return InstanceIdentifier.builder(NetworkTopology.class) diff --git a/loopremover/implementation/src/test/java/org/opendaylight/l2switch/loopremover/topology/TopologyLinkDataChangeHandlerTest.java b/loopremover/implementation/src/test/java/org/opendaylight/l2switch/loopremover/topology/TopologyLinkDataChangeHandlerTest.java index da196006d..bec3f4d68 100644 --- a/loopremover/implementation/src/test/java/org/opendaylight/l2switch/loopremover/topology/TopologyLinkDataChangeHandlerTest.java +++ b/loopremover/implementation/src/test/java/org/opendaylight/l2switch/loopremover/topology/TopologyLinkDataChangeHandlerTest.java @@ -13,11 +13,11 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.google.common.util.concurrent.FluentFuture; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; + import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -48,292 +48,275 @@ 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.Link; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder; +import org.opendaylight.yangtools.binding.util.BindingMap; import org.opendaylight.yangtools.util.concurrent.FluentFutures; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.util.BindingMap; + +import com.google.common.util.concurrent.FluentFuture; public class TopologyLinkDataChangeHandlerTest { - @Mock - private DataBroker dataBroker; - @Mock - private NetworkGraphService networkGraphService; - private TopologyLinkDataChangeHandler topologyLinkDataChangeHandler; + @Mock + private DataBroker dataBroker; + @Mock + private NetworkGraphService networkGraphService; + private TopologyLinkDataChangeHandler topologyLinkDataChangeHandler; - @Before - public void initMocks() { - MockitoAnnotations.initMocks(this); - topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataBroker, networkGraphService); - topologyLinkDataChangeHandler.setTopologyId(null); - topologyLinkDataChangeHandler.setGraphRefreshDelay(0); - } + @Before + public void initMocks() { + MockitoAnnotations.initMocks(this); + topologyLinkDataChangeHandler = new TopologyLinkDataChangeHandler(dataBroker, networkGraphService); + topologyLinkDataChangeHandler.setTopologyId(null); + topologyLinkDataChangeHandler.setGraphRefreshDelay(0); + } - @Test - public void testRegisterAsDataChangeListener() throws Exception { - topologyLinkDataChangeHandler.registerAsDataChangeListener(); - verify(dataBroker, times(1)).registerDataTreeChangeListener(any(DataTreeIdentifier.class), - any(DataTreeChangeListener.class)); - } + @Test + public void testRegisterAsDataChangeListener() throws Exception { + topologyLinkDataChangeHandler.registerAsDataChangeListener(); + verify(dataBroker, times(1)).registerDataTreeChangeListener(any(DataTreeIdentifier.class), + any(DataTreeChangeListener.class)); + } - @SuppressWarnings("unchecked") - @Test - public void testOnDataChanged_CreatedDataNoRefresh() throws Exception { - InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class) - .child(Topology.class) - .child(Link.class) - .build(); - Link hostLink = new LinkBuilder().setLinkId(new LinkId("host:1")).build(); - DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); - DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); - when(mockModification.getDataAfter()).thenReturn(hostLink); - when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); - when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, - instanceId)); - when(mockChange.getRootNode()).thenReturn(mockModification); - topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); - Thread.sleep(500); - verify(networkGraphService, times(0)).clear(); - } + @SuppressWarnings("unchecked") + @Test + public void testOnDataChanged_CreatedDataNoRefresh() throws Exception { + InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class) + .child(Link.class).build(); + Link hostLink = new LinkBuilder().setLinkId(new LinkId("host:1")).build(); + DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); + DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); + when(mockModification.getDataAfter()).thenReturn(hostLink); + when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); + when(mockChange.getRootPath()) + .thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, instanceId)); + when(mockChange.getRootNode()).thenReturn(mockModification); + topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); + Thread.sleep(500); + verify(networkGraphService, times(0)).clear(); + } - @SuppressWarnings("unchecked") - @Test - public void testOnDataChanged_CreatedDataRefresh() throws Exception { - InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class) - .child(Topology.class) - .child(Link.class) - .build(); - Link hostLink = new LinkBuilder().setLinkId(new LinkId("openflow:1")).build(); - DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); - DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); - when(mockModification.getDataAfter()).thenReturn(hostLink); - when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); - when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, - instanceId)); - when(mockChange.getRootNode()).thenReturn(mockModification); - topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); - Thread.sleep(500); - verify(networkGraphService, times(1)).clear(); - } + @SuppressWarnings("unchecked") + @Test + public void testOnDataChanged_CreatedDataRefresh() throws Exception { + InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class) + .child(Link.class).build(); + Link hostLink = new LinkBuilder().setLinkId(new LinkId("openflow:1")).build(); + DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); + DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); + when(mockModification.getDataAfter()).thenReturn(hostLink); + when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); + when(mockChange.getRootPath()) + .thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, instanceId)); + when(mockChange.getRootNode()).thenReturn(mockModification); + topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); + Thread.sleep(500); + verify(networkGraphService, times(1)).clear(); + } - @SuppressWarnings("unchecked") - @Test - public void testOnDataChanged_RemovedDataNoRefresh() throws Exception { - InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class) - .child(Topology.class) - .child(Link.class) - .build(); - Link hostLink = new LinkBuilder().setLinkId(new LinkId("host:1")).build(); - DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); - DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); - when(mockModification.getDataBefore()).thenReturn(hostLink); - when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.DELETE); - when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, - instanceId)); - when(mockChange.getRootNode()).thenReturn(mockModification); - topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); - Thread.sleep(500); - verify(networkGraphService, times(0)).clear(); - } + @SuppressWarnings("unchecked") + @Test + public void testOnDataChanged_RemovedDataNoRefresh() throws Exception { + InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class) + .child(Link.class).build(); + Link hostLink = new LinkBuilder().setLinkId(new LinkId("host:1")).build(); + DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); + DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); + when(mockModification.getDataBefore()).thenReturn(hostLink); + when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.DELETE); + when(mockChange.getRootPath()) + .thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, instanceId)); + when(mockChange.getRootNode()).thenReturn(mockModification); + topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); + Thread.sleep(500); + verify(networkGraphService, times(0)).clear(); + } - @SuppressWarnings("unchecked") - @Test - public void testOnDataChanged_RemovedDataRefresh() throws Exception { - InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class) - .child(Topology.class) - .child(Link.class) - .build(); - Link hostLink = new LinkBuilder().setLinkId(new LinkId("openflow:1")).build(); - DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); - DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); - when(mockModification.getDataBefore()).thenReturn(hostLink); - when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.DELETE); - when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, - instanceId)); - when(mockChange.getRootNode()).thenReturn(mockModification); - topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); - Thread.sleep(500); - verify(networkGraphService, times(1)).clear(); - } + @SuppressWarnings("unchecked") + @Test + public void testOnDataChanged_RemovedDataRefresh() throws Exception { + InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class) + .child(Link.class).build(); + Link hostLink = new LinkBuilder().setLinkId(new LinkId("openflow:1")).build(); + DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); + DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); + when(mockModification.getDataBefore()).thenReturn(hostLink); + when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.DELETE); + when(mockChange.getRootPath()) + .thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, instanceId)); + when(mockChange.getRootNode()).thenReturn(mockModification); + topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); + Thread.sleep(500); + verify(networkGraphService, times(1)).clear(); + } - @SuppressWarnings("unchecked") - @Test - public void testUpdateNodeConnectorStatus_NoLinks() throws Exception { - // Setup code to trigger the TopologyDataChangeEventProcessor - InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class) - .child(Topology.class) - .child(Link.class) - .build(); - Link nodeLink = new LinkBuilder().setLinkId(new LinkId("openflow:1")).build(); - DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); - DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); - when(mockModification.getDataAfter()).thenReturn(nodeLink); - when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); - when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, - instanceId)); - when(mockChange.getRootNode()).thenReturn(mockModification); - // End setup code - Topology topology = new TopologyBuilder().setTopologyId(new TopologyId("topo")).setLink(Map.of()).build(); - Optional topologyOptional = Optional.of(topology); - FluentFuture> checkedFuture = FluentFutures.immediateFluentFuture(topologyOptional); - ReadTransaction readOnlyTransaction = Mockito.mock(ReadTransaction.class); - when(readOnlyTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) - .thenReturn(checkedFuture); - when(dataBroker.newReadOnlyTransaction()).thenReturn(readOnlyTransaction); + @SuppressWarnings("unchecked") + @Test + public void testUpdateNodeConnectorStatus_NoLinks() throws Exception { + // Setup code to trigger the TopologyDataChangeEventProcessor + InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class) + .child(Link.class).build(); + Link nodeLink = new LinkBuilder().setLinkId(new LinkId("openflow:1")).build(); + DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); + DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); + when(mockModification.getDataAfter()).thenReturn(nodeLink); + when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); + when(mockChange.getRootPath()) + .thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, instanceId)); + when(mockChange.getRootNode()).thenReturn(mockModification); + // End setup code + Topology topology = new TopologyBuilder().setTopologyId(new TopologyId("topo")).setLink(Map.of()).build(); + Optional topologyOptional = Optional.of(topology); + FluentFuture> checkedFuture = FluentFutures.immediateFluentFuture(topologyOptional); + ReadTransaction readOnlyTransaction = Mockito.mock(ReadTransaction.class); + when(readOnlyTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) + .thenReturn(checkedFuture); + when(dataBroker.newReadOnlyTransaction()).thenReturn(readOnlyTransaction); - topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); - Thread.sleep(500); - verify(dataBroker, times(0)).newReadWriteTransaction(); - verify(networkGraphService, times(0)).addLinks(any(List.class)); - verify(networkGraphService, times(0)).getAllLinks(); - verify(networkGraphService, times(0)).getLinksInMst(); - } + topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); + Thread.sleep(500); + verify(dataBroker, times(0)).newReadWriteTransaction(); + verify(networkGraphService, times(0)).addLinks(any(List.class)); + verify(networkGraphService, times(0)).getAllLinks(); + verify(networkGraphService, times(0)).getLinksInMst(); + } - @SuppressWarnings("unchecked") - @Test - public void testUpdateNodeConnectorStatus_WithLinks() throws Exception { - // Setup code to trigger the TopologyDataChangeEventProcessor - InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class) - .child(Topology.class) - .child(Link.class) - .build(); - Link nodeLink = new LinkBuilder().setLinkId(new LinkId("openflow:1")).build(); - DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); - DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); - when(mockModification.getDataAfter()).thenReturn(nodeLink); - when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); - when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, - instanceId)); - when(mockChange.getRootNode()).thenReturn(mockModification); + @SuppressWarnings("unchecked") + @Test + public void testUpdateNodeConnectorStatus_WithLinks() throws Exception { + // Setup code to trigger the TopologyDataChangeEventProcessor + InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class) + .child(Link.class).build(); + Link nodeLink = new LinkBuilder().setLinkId(new LinkId("openflow:1")).build(); + DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); + DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); + when(mockModification.getDataAfter()).thenReturn(nodeLink); + when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); + when(mockChange.getRootPath()) + .thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, instanceId)); + when(mockChange.getRootNode()).thenReturn(mockModification); - // getLinksFromTopology - Topology topology = new TopologyBuilder() - .setTopologyId(new TopologyId("topo")) - .setLink(BindingMap.of(new LinkBuilder().setLinkId(new LinkId("openflow:1")).build())) - .build(); - Optional topologyOptional = Optional.of(topology); - FluentFuture> checkedFuture = FluentFutures.immediateFluentFuture(topologyOptional); - ReadTransaction readOnlyTransaction = Mockito.mock(ReadTransaction.class); - when(readOnlyTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) - .thenReturn(checkedFuture); - when(dataBroker.newReadOnlyTransaction()).thenReturn(readOnlyTransaction); + // getLinksFromTopology + Topology topology = new TopologyBuilder().setTopologyId(new TopologyId("topo")) + .setLink(BindingMap.of(new LinkBuilder().setLinkId(new LinkId("openflow:1")).build())).build(); + Optional topologyOptional = Optional.of(topology); + FluentFuture> checkedFuture = FluentFutures.immediateFluentFuture(topologyOptional); + ReadTransaction readOnlyTransaction = Mockito.mock(ReadTransaction.class); + when(readOnlyTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) + .thenReturn(checkedFuture); + when(dataBroker.newReadOnlyTransaction()).thenReturn(readOnlyTransaction); - // run - ReadWriteTransaction readWriteTransaction = Mockito.mock(ReadWriteTransaction.class); - when(dataBroker.newReadWriteTransaction()).thenReturn(readWriteTransaction); + // run + ReadWriteTransaction readWriteTransaction = Mockito.mock(ReadWriteTransaction.class); + when(dataBroker.newReadWriteTransaction()).thenReturn(readWriteTransaction); - // updateNodeConnectorStatus - Link link1 = new LinkBuilder().setLinkId(new LinkId("openflow:1")) - .setSource(new SourceBuilder().setSourceNode(new NodeId("openflow:1")) - .setSourceTp(new TpId("openflow:1")).build()) - .setDestination(new DestinationBuilder().setDestNode(new NodeId("openflow:1")) - .setDestTp(new TpId("openflow:1")).build()) - .build(); - Link link2 = new LinkBuilder().setLinkId(new LinkId("openflow:2")) - .setSource(new SourceBuilder().setSourceNode(new NodeId("openflow:2")) - .setSourceTp(new TpId("openflow:2")).build()) - .setDestination(new DestinationBuilder().setDestNode(new NodeId("openflow:2")) - .setDestTp(new TpId("openflow:2")).build()) - .build(); + // updateNodeConnectorStatus + Link link1 = new LinkBuilder().setLinkId(new LinkId("openflow:1")) + .setSource(new SourceBuilder().setSourceNode(new NodeId("openflow:1")) + .setSourceTp(new TpId("openflow:1")).build()) + .setDestination(new DestinationBuilder().setDestNode(new NodeId("openflow:1")) + .setDestTp(new TpId("openflow:1")).build()) + .build(); + Link link2 = new LinkBuilder().setLinkId(new LinkId("openflow:2")) + .setSource(new SourceBuilder().setSourceNode(new NodeId("openflow:2")) + .setSourceTp(new TpId("openflow:2")).build()) + .setDestination(new DestinationBuilder().setDestNode(new NodeId("openflow:2")) + .setDestTp(new TpId("openflow:2")).build()) + .build(); - List allLinks = new ArrayList<>(); - allLinks.add(link1); - allLinks.add(link2); - when(networkGraphService.getAllLinks()).thenReturn(allLinks); - List mstLinks = new ArrayList<>(); - mstLinks.add(link1); - when(networkGraphService.getLinksInMst()).thenReturn(mstLinks); + List allLinks = new ArrayList<>(); + allLinks.add(link1); + allLinks.add(link2); + when(networkGraphService.getAllLinks()).thenReturn(allLinks); + List mstLinks = new ArrayList<>(); + mstLinks.add(link1); + when(networkGraphService.getLinksInMst()).thenReturn(mstLinks); - // checkIfExistAndUpdateNodeConnector - NodeConnector nodeConnector = new NodeConnectorBuilder().setId(new NodeConnectorId("connId")).build(); - Optional optionalNodeConnector = Optional.of(nodeConnector); - FluentFuture> checkedFutureNc = - FluentFutures.immediateFluentFuture(optionalNodeConnector); - when(readWriteTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) - .thenReturn(checkedFutureNc); + // checkIfExistAndUpdateNodeConnector + NodeConnector nodeConnector = new NodeConnectorBuilder().setId(new NodeConnectorId("connId")).build(); + Optional optionalNodeConnector = Optional.of(nodeConnector); + FluentFuture> checkedFutureNc = FluentFutures + .immediateFluentFuture(optionalNodeConnector); + when(readWriteTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) + .thenReturn(checkedFutureNc); - topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); - Thread.sleep(500); - verify(dataBroker, times(1)).newReadWriteTransaction(); - verify(networkGraphService, times(1)).addLinks(any(ArrayList.class)); - verify(networkGraphService, times(1)).getAllLinks(); - verify(networkGraphService, times(1)).getLinksInMst(); - verify(readWriteTransaction, times(4)).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), - any(StpStatusAwareNodeConnector.class)); - verify(readWriteTransaction, times(1)).commit(); - } + topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); + Thread.sleep(500); + verify(dataBroker, times(1)).newReadWriteTransaction(); + verify(networkGraphService, times(1)).addLinks(any(ArrayList.class)); + verify(networkGraphService, times(1)).getAllLinks(); + verify(networkGraphService, times(1)).getLinksInMst(); + verify(readWriteTransaction, times(4)).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), + any(StpStatusAwareNodeConnector.class)); + verify(readWriteTransaction, times(1)).commit(); + } - @Test - public void testUpdateNodeConnectorStatus_WithStpLinks() throws Exception { - // Setup code to trigger the TopologyDataChangeEventProcessor - InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class) - .child(Topology.class) - .child(Link.class) - .build(); - Link nodeLink = new LinkBuilder().setLinkId(new LinkId("openflow:1")).build(); - DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); - DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); - when(mockModification.getDataAfter()).thenReturn(nodeLink); - when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); - when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, - instanceId)); - when(mockChange.getRootNode()).thenReturn(mockModification); + @Test + public void testUpdateNodeConnectorStatus_WithStpLinks() throws Exception { + // Setup code to trigger the TopologyDataChangeEventProcessor + InstanceIdentifier instanceId = InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class) + .child(Link.class).build(); + Link nodeLink = new LinkBuilder().setLinkId(new LinkId("openflow:1")).build(); + DataTreeModification mockChange = Mockito.mock(DataTreeModification.class); + DataObjectModification mockModification = Mockito.mock(DataObjectModification.class); + when(mockModification.getDataAfter()).thenReturn(nodeLink); + when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); + when(mockChange.getRootPath()) + .thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, instanceId)); + when(mockChange.getRootNode()).thenReturn(mockModification); - // getLinksFromTopology - Topology topology = new TopologyBuilder() - .setTopologyId(new TopologyId("topo")) - .setLink(BindingMap.of(new LinkBuilder().setLinkId(new LinkId("openflow:1")).build())) - .build(); - Optional topologyOptional = Optional.of(topology); - FluentFuture> checkedFuture = FluentFutures.immediateFluentFuture(topologyOptional); - ReadTransaction readOnlyTransaction = Mockito.mock(ReadTransaction.class); - when(readOnlyTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) - .thenReturn(checkedFuture); - when(dataBroker.newReadOnlyTransaction()).thenReturn(readOnlyTransaction); + // getLinksFromTopology + Topology topology = new TopologyBuilder().setTopologyId(new TopologyId("topo")) + .setLink(BindingMap.of(new LinkBuilder().setLinkId(new LinkId("openflow:1")).build())).build(); + Optional topologyOptional = Optional.of(topology); + FluentFuture> checkedFuture = FluentFutures.immediateFluentFuture(topologyOptional); + ReadTransaction readOnlyTransaction = Mockito.mock(ReadTransaction.class); + when(readOnlyTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) + .thenReturn(checkedFuture); + when(dataBroker.newReadOnlyTransaction()).thenReturn(readOnlyTransaction); - // run - ReadWriteTransaction readWriteTransaction = Mockito.mock(ReadWriteTransaction.class); - when(dataBroker.newReadWriteTransaction()).thenReturn(readWriteTransaction); + // run + ReadWriteTransaction readWriteTransaction = Mockito.mock(ReadWriteTransaction.class); + when(dataBroker.newReadWriteTransaction()).thenReturn(readWriteTransaction); - // updateNodeConnectorStatus - Link link1 = new LinkBuilder().setLinkId(new LinkId("openflow:1")) - .setSource(new SourceBuilder().setSourceNode(new NodeId("openflow:1")) - .setSourceTp(new TpId("openflow:1")).build()) - .setDestination(new DestinationBuilder().setDestNode(new NodeId("openflow:1")) - .setDestTp(new TpId("openflow:1")).build()) - .build(); - Link link2 = new LinkBuilder().setLinkId(new LinkId("openflow:2")) - .setSource(new SourceBuilder().setSourceNode(new NodeId("openflow:2")) - .setSourceTp(new TpId("openflow:2")).build()) - .setDestination(new DestinationBuilder().setDestNode(new NodeId("openflow:2")) - .setDestTp(new TpId("openflow:2")).build()) - .build(); + // updateNodeConnectorStatus + Link link1 = new LinkBuilder().setLinkId(new LinkId("openflow:1")) + .setSource(new SourceBuilder().setSourceNode(new NodeId("openflow:1")) + .setSourceTp(new TpId("openflow:1")).build()) + .setDestination(new DestinationBuilder().setDestNode(new NodeId("openflow:1")) + .setDestTp(new TpId("openflow:1")).build()) + .build(); + Link link2 = new LinkBuilder().setLinkId(new LinkId("openflow:2")) + .setSource(new SourceBuilder().setSourceNode(new NodeId("openflow:2")) + .setSourceTp(new TpId("openflow:2")).build()) + .setDestination(new DestinationBuilder().setDestNode(new NodeId("openflow:2")) + .setDestTp(new TpId("openflow:2")).build()) + .build(); - List allLinks = new ArrayList<>(); - allLinks.add(link1); - allLinks.add(link2); - when(networkGraphService.getAllLinks()).thenReturn(allLinks); - List mstLinks = new ArrayList<>(); - mstLinks.add(link1); - when(networkGraphService.getLinksInMst()).thenReturn(mstLinks); + List allLinks = new ArrayList<>(); + allLinks.add(link1); + allLinks.add(link2); + when(networkGraphService.getAllLinks()).thenReturn(allLinks); + List mstLinks = new ArrayList<>(); + mstLinks.add(link1); + when(networkGraphService.getLinksInMst()).thenReturn(mstLinks); - // checkIfExistAndUpdateNodeConnector - NodeConnector nodeConnector = new NodeConnectorBuilder() - .setId(new NodeConnectorId("connId")) - .addAugmentation(new StpStatusAwareNodeConnectorBuilder().setStatus(StpStatus.Forwarding).build()) - .build(); - Optional optionalNodeConnector = Optional.of(nodeConnector); - FluentFuture> checkedFutureNc = - FluentFutures.immediateFluentFuture(optionalNodeConnector); - when(readWriteTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) - .thenReturn(checkedFutureNc); + // checkIfExistAndUpdateNodeConnector + NodeConnector nodeConnector = new NodeConnectorBuilder().setId(new NodeConnectorId("connId")) + .addAugmentation(new StpStatusAwareNodeConnectorBuilder().setStatus(StpStatus.Forwarding).build()) + .build(); + Optional optionalNodeConnector = Optional.of(nodeConnector); + FluentFuture> checkedFutureNc = FluentFutures + .immediateFluentFuture(optionalNodeConnector); + when(readWriteTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) + .thenReturn(checkedFutureNc); - topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); - Thread.sleep(500); - verify(dataBroker, times(1)).newReadWriteTransaction(); - verify(networkGraphService, times(1)).addLinks(anyList()); - verify(networkGraphService, times(1)).getAllLinks(); - verify(networkGraphService, times(1)).getLinksInMst(); - verify(readWriteTransaction, times(2)).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), - any(StpStatusAwareNodeConnector.class)); - verify(readWriteTransaction, times(1)).commit(); - } + topologyLinkDataChangeHandler.onDataTreeChanged(List.of(mockChange)); + Thread.sleep(500); + verify(dataBroker, times(1)).newReadWriteTransaction(); + verify(networkGraphService, times(1)).addLinks(anyList()); + verify(networkGraphService, times(1)).getAllLinks(); + verify(networkGraphService, times(1)).getLinksInMst(); + verify(readWriteTransaction, times(2)).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), + any(StpStatusAwareNodeConnector.class)); + verify(readWriteTransaction, times(1)).commit(); + } } diff --git a/loopremover/implementation/src/test/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtilsTest.java b/loopremover/implementation/src/test/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtilsTest.java index 8b64a7ae9..d43cfdb77 100644 --- a/loopremover/implementation/src/test/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtilsTest.java +++ b/loopremover/implementation/src/test/java/org/opendaylight/l2switch/loopremover/util/InstanceIdentifierUtilsTest.java @@ -20,70 +20,71 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.Uint8; public class InstanceIdentifierUtilsTest { - private static final Uint8 NUM_ID_1 = Uint8.ONE; - private static final String STR_ID_1 = "id1"; - private static final String STR_ID_2 = "id2"; + private static final Uint8 NUM_ID_1 = Uint8.ONE; + private static final String STR_ID_1 = "id1"; + private static final String STR_ID_2 = "id2"; - @Test - public void testCreateNodePath() throws Exception { - InstanceIdentifier insId = InstanceIdentifierUtils.createNodePath(new NodeId(STR_ID_1)); - assertNotNull(insId); - assertNotNull(insId.firstIdentifierOf(Nodes.class)); - assertEquals(STR_ID_1, insId.firstKeyOf(Node.class).getId().getValue()); - } + @Test + public void testCreateNodePath() throws Exception { + DataObjectIdentifier insId = InstanceIdentifierUtils.createNodePath(new NodeId(STR_ID_1)); + assertNotNull(insId); +// assertNotNull(insId.firstIdentifierOf(Nodes.class)); + assertEquals(STR_ID_1, insId.firstKeyOf(Node.class).getId().getValue()); + } - @Test - public void testGetNodePath() throws Exception { - InstanceIdentifier ncInsId = InstanceIdentifier.builder(Nodes.class).child(Node.class) - .child(NodeConnector.class).build(); - assertNotNull(InstanceIdentifierUtils.getNodePath(ncInsId)); - } + @Test + public void testGetNodePath() throws Exception { + InstanceIdentifier ncInsId = InstanceIdentifier.builder(Nodes.class).child(Node.class) + .child(NodeConnector.class).build(); + assertNotNull(InstanceIdentifierUtils.getNodePath(ncInsId)); + } - @Test - public void testCreateTablePath() throws Exception { - InstanceIdentifier
tableInsId = InstanceIdentifierUtils.createTablePath( - InstanceIdentifier.builder(Nodes.class).child(Node.class).build(), new TableKey(NUM_ID_1)); - assertNotNull(tableInsId); - assertEquals(NUM_ID_1.shortValue(), tableInsId.firstKeyOf(Table.class).getId().shortValue()); - assertNotNull(tableInsId.firstIdentifierOf(FlowCapableNode.class)); - } + @Test + public void testCreateTablePath() throws Exception { + InstanceIdentifier
tableInsId = InstanceIdentifierUtils.createTablePath( + InstanceIdentifier.builder(Nodes.class).child(Node.class).build(), new TableKey(NUM_ID_1)); + assertNotNull(tableInsId); + assertEquals(NUM_ID_1.shortValue(), tableInsId.firstKeyOf(Table.class).getId().shortValue()); + assertNotNull(tableInsId.firstIdentifierOf(FlowCapableNode.class)); + } - @Test - public void testCreateNodeConnectorIdentifier() throws Exception { - InstanceIdentifier ncInsId = InstanceIdentifierUtils.createNodeConnectorIdentifier(STR_ID_1, - STR_ID_2); - assertNotNull(ncInsId); - assertEquals(STR_ID_1, ncInsId.firstKeyOf(Node.class).getId().getValue()); - assertEquals(STR_ID_2, ncInsId.firstKeyOf(NodeConnector.class).getId().getValue()); - } + @Test + public void testCreateNodeConnectorIdentifier() throws Exception { + DataObjectIdentifier ncInsId = InstanceIdentifierUtils.createNodeConnectorIdentifier(STR_ID_1, + STR_ID_2); + assertNotNull(ncInsId); + assertEquals(STR_ID_1, ncInsId.firstKeyOf(Node.class).getId().getValue()); + assertEquals(STR_ID_2, ncInsId.firstKeyOf(NodeConnector.class).getId().getValue()); + } - @Test - public void testGenerateNodeInstanceIdentifier() throws Exception { - NodeConnectorRef ncRef = new NodeConnectorRef( - InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); - assertNotNull(InstanceIdentifierUtils.generateNodeInstanceIdentifier(ncRef)); - } + @Test + public void testGenerateNodeInstanceIdentifier() throws Exception { + NodeConnectorRef ncRef = new NodeConnectorRef( + DataObjectIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); + assertNotNull(InstanceIdentifierUtils.generateNodeInstanceIdentifier(ncRef)); + } - @Test - public void testGenerateFlowTableInstanceIdentifier() throws Exception { - NodeConnectorRef ncRef = new NodeConnectorRef( - InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); - InstanceIdentifier
tableInsId = InstanceIdentifierUtils.generateFlowTableInstanceIdentifier(ncRef, - new TableKey(NUM_ID_1)); - assertNotNull(tableInsId); - assertEquals(NUM_ID_1, tableInsId.firstKeyOf(Table.class).getId()); - } + @Test + public void testGenerateFlowTableInstanceIdentifier() throws Exception { + NodeConnectorRef ncRef = new NodeConnectorRef( + DataObjectIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); + DataObjectIdentifier
tableInsId = InstanceIdentifierUtils.generateFlowTableInstanceIdentifier(ncRef, + new TableKey(NUM_ID_1)); + assertNotNull(tableInsId); + assertEquals(NUM_ID_1, tableInsId.firstKeyOf(Table.class).getId()); + } - @Test - public void testGenerateTopologyInstanceIdentifier() throws Exception { - InstanceIdentifier topologyInsId = InstanceIdentifierUtils - .generateTopologyInstanceIdentifier(STR_ID_1); - assertNotNull(topologyInsId); - assertEquals(STR_ID_1, topologyInsId.firstKeyOf(Topology.class).getTopologyId().getValue()); - } + @Test + public void testGenerateTopologyInstanceIdentifier() throws Exception { + InstanceIdentifier topologyInsId = InstanceIdentifierUtils + .generateTopologyInstanceIdentifier(STR_ID_1); + assertNotNull(topologyInsId); + assertEquals(STR_ID_1, topologyInsId.firstKeyOf(Topology.class).getTopologyId().getValue()); + } } From 75775c6f3b3a6cc3106620be3bf392f283c3d3fe Mon Sep 17 00:00:00 2001 From: i-am-arathypm Date: Thu, 10 Jul 2025 11:04:13 +0530 Subject: [PATCH 07/15] addresstracker fix --- .../AddressObservationWriter.java | 289 +++++++++--------- 1 file changed, 146 insertions(+), 143 deletions(-) diff --git a/addresstracker/implementation/src/main/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriter.java b/addresstracker/implementation/src/main/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriter.java index d2b04d46b..1722a1468 100644 --- a/addresstracker/implementation/src/main/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriter.java +++ b/addresstracker/implementation/src/main/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriter.java @@ -7,10 +7,6 @@ */ package org.opendaylight.l2switch.addresstracker.addressobserver; -import com.google.common.util.concurrent.FluentFuture; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.MoreExecutors; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -18,6 +14,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicLong; + import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.mdsal.binding.api.ReadTransaction; import org.opendaylight.mdsal.binding.api.WriteTransaction; @@ -32,153 +29,159 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.address.tracker.rev140617.address.node.connector.AddressesKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.Uint64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.util.concurrent.FluentFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; + /** * AddressObservationWriter manages the MD-SAL data tree for address * observations (mac, ip) on each node-connector. */ public class AddressObservationWriter { - private static final Logger LOG = LoggerFactory.getLogger(AddressObservationWriter.class); - - private static final class NodeConnectorLock { - } - - private final AtomicLong addressKey = new AtomicLong(0); - private long timestampUpdateInterval; - private final DataBroker dataService; - private final Map lockMap = new ConcurrentHashMap<>(); - private final Map> futureMap = new ConcurrentHashMap<>(); - - /** - * Construct an AddressTracker with the specified inputs. - * - * @param dataService - * The DataBrokerService for the AddressTracker - */ - public AddressObservationWriter(DataBroker dataService) { - this.dataService = dataService; - } - - public void setTimestampUpdateInterval(long timestampUpdateInterval) { - this.timestampUpdateInterval = timestampUpdateInterval; - } - - /** - * Add addresses into the MD-SAL data tree. - * - * @param macAddress - * The MacAddress of the new L2Address object - * @param ipAddress - * The Source Protocol Address - * @param nodeConnectorRef - * The NodeConnectorRef of the new L2Address object - */ - public void addAddress(MacAddress macAddress, IpAddress ipAddress, NodeConnectorRef nodeConnectorRef) { - if (macAddress == null || ipAddress == null || nodeConnectorRef == null) { - return; - } - - // get the lock for given node connector so at a time only one - // observation can be made on a node connector - NodeConnectorLock nodeConnectorLock = lockMap.computeIfAbsent(nodeConnectorRef, key -> new NodeConnectorLock()); - - synchronized (nodeConnectorLock) { - // Ensure previous transaction finished writing to the db - FluentFuture future = futureMap.get(nodeConnectorLock); - if (future != null) { - try { - future.get(); - } catch (InterruptedException | ExecutionException e) { - LOG.error("Exception while waiting for previous transaction to finish", e); - } - } - - // Initialize builders - long now = new Date().getTime(); - final AddressCapableNodeConnectorBuilder acncBuilder = new AddressCapableNodeConnectorBuilder(); - final AddressesBuilder addressBuilder = new AddressesBuilder().setIp(ipAddress).setMac(macAddress) - .setFirstSeen(now).setLastSeen(now); - - // Read existing address observations from data tree - final FluentFuture> readFuture; - try (ReadTransaction readTransaction = dataService.newReadOnlyTransaction()) { - readFuture = readTransaction.read(LogicalDatastoreType.OPERATIONAL, - (InstanceIdentifier) nodeConnectorRef.getValue()); - } - - final NodeConnector nc; - try { - final Optional dataObjectOptional = readFuture.get(); - if (dataObjectOptional.isEmpty()) { - return; - } - nc = dataObjectOptional.orElseThrow(); - } catch (InterruptedException | ExecutionException e) { - LOG.error("Error reading node connector {}", nodeConnectorRef.getValue()); - throw new RuntimeException("Error reading from operational store, node connector : " + nodeConnectorRef, - e); - } - AddressCapableNodeConnector acnc = nc.augmentation(AddressCapableNodeConnector.class); - - // FIXME: This block is quite inefficient: we have a unique key and yet we end up re-merging the entire - // 'addresses' list over and over again -- which is costly due to the HashMap copy below as well as - // the resulting merge() operation. After issuing the read, we should determine the key of the - // matching entry (or allocate a new key) and issue a simple put() on only the key. - final Map addresses; - if (acnc != null) { - // Address observations exist - addresses = new HashMap<>(acnc.nonnullAddresses()); - // Search for this mac-ip pair in the existing address observations & update last-seen timestamp - for (Addresses existing : addresses.values()) { - if (ipAddress.equals(existing.getIp()) && macAddress.equals(existing.getMac())) { - if ((now - existing.getLastSeen() <= timestampUpdateInterval)) { - // Update interval has not elapsed, do not run update - return; - } - addressBuilder.setFirstSeen(existing.getFirstSeen()).withKey(existing.key()); - break; - } - } - } else { - // Address observations don't exist, so create the list - addresses = new HashMap<>(); - } - - if (addressBuilder.key() == null) { - addressBuilder.withKey(new AddressesKey(Uint64.fromLongBits(addressKey.getAndIncrement()))); - } - - // Add as an augmentation - final Addresses address = addressBuilder.build(); - addresses.put(address.key(), address); - acncBuilder.setAddresses(addresses); - - // build Instance Id for AddressCapableNodeConnector - InstanceIdentifier addressCapableNcInstanceId = - ((InstanceIdentifier) nodeConnectorRef - .getValue()).augmentation(AddressCapableNodeConnector.class); - final WriteTransaction writeTransaction = dataService.newWriteOnlyTransaction(); - // Update this AddressCapableNodeConnector in the MD-SAL data tree - writeTransaction.merge(LogicalDatastoreType.OPERATIONAL, addressCapableNcInstanceId, acncBuilder.build()); - - final FluentFuture writeTxResultFuture = writeTransaction.commit(); - Futures.addCallback(writeTxResultFuture, new FutureCallback() { - @Override - public void onSuccess(CommitInfo notUsed) { - LOG.debug("AddressObservationWriter write successful for tx :{}", writeTransaction.getIdentifier()); - } - - @Override - public void onFailure(Throwable throwable) { - LOG.error("AddressObservationWriter write transaction {} failed", writeTransaction.getIdentifier(), - throwable.getCause()); - } - }, MoreExecutors.directExecutor()); - futureMap.put(nodeConnectorLock, writeTxResultFuture); - } - } + private static final Logger LOG = LoggerFactory.getLogger(AddressObservationWriter.class); + + private static final class NodeConnectorLock { + } + + private final AtomicLong addressKey = new AtomicLong(0); + private long timestampUpdateInterval; + private final DataBroker dataService; + private final Map lockMap = new ConcurrentHashMap<>(); + private final Map> futureMap = new ConcurrentHashMap<>(); + + /** + * Construct an AddressTracker with the specified inputs. + * + * @param dataService The DataBrokerService for the AddressTracker + */ + public AddressObservationWriter(DataBroker dataService) { + this.dataService = dataService; + } + + public void setTimestampUpdateInterval(long timestampUpdateInterval) { + this.timestampUpdateInterval = timestampUpdateInterval; + } + + /** + * Add addresses into the MD-SAL data tree. + * + * @param macAddress The MacAddress of the new L2Address object + * @param ipAddress The Source Protocol Address + * @param nodeConnectorRef The NodeConnectorRef of the new L2Address object + */ + public void addAddress(MacAddress macAddress, IpAddress ipAddress, NodeConnectorRef nodeConnectorRef) { + if (macAddress == null || ipAddress == null || nodeConnectorRef == null) { + return; + } + + // get the lock for given node connector so at a time only one + // observation can be made on a node connector + NodeConnectorLock nodeConnectorLock = lockMap.computeIfAbsent(nodeConnectorRef, key -> new NodeConnectorLock()); + + synchronized (nodeConnectorLock) { + // Ensure previous transaction finished writing to the db + FluentFuture future = futureMap.get(nodeConnectorLock); + if (future != null) { + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { + LOG.error("Exception while waiting for previous transaction to finish", e); + } + } + + // Initialize builders + long now = new Date().getTime(); + final AddressCapableNodeConnectorBuilder acncBuilder = new AddressCapableNodeConnectorBuilder(); + final AddressesBuilder addressBuilder = new AddressesBuilder().setIp(ipAddress).setMac(macAddress) + .setFirstSeen(now).setLastSeen(now); + + // Read existing address observations from data tree + final FluentFuture> readFuture; + try (ReadTransaction readTransaction = dataService.newReadOnlyTransaction()) { + readFuture = readTransaction.read(LogicalDatastoreType.OPERATIONAL, + (DataObjectIdentifier) nodeConnectorRef.getValue()); + } + + final NodeConnector nc; + try { + final Optional dataObjectOptional = readFuture.get(); + if (dataObjectOptional.isEmpty()) { + return; + } + nc = dataObjectOptional.orElseThrow(); + } catch (InterruptedException | ExecutionException e) { + LOG.error("Error reading node connector {}", nodeConnectorRef.getValue()); + throw new RuntimeException("Error reading from operational store, node connector : " + nodeConnectorRef, + e); + } + AddressCapableNodeConnector acnc = nc.augmentation(AddressCapableNodeConnector.class); + + // FIXME: This block is quite inefficient: we have a unique key and yet we end + // up re-merging the entire + // 'addresses' list over and over again -- which is costly due to the HashMap + // copy below as well as + // the resulting merge() operation. After issuing the read, we should determine + // the key of the + // matching entry (or allocate a new key) and issue a simple put() on only the + // key. + final Map addresses; + if (acnc != null) { + // Address observations exist + addresses = new HashMap<>(acnc.nonnullAddresses()); + // Search for this mac-ip pair in the existing address observations & update + // last-seen timestamp + for (Addresses existing : addresses.values()) { + if (ipAddress.equals(existing.getIp()) && macAddress.equals(existing.getMac())) { + if ((now - existing.getLastSeen() <= timestampUpdateInterval)) { + // Update interval has not elapsed, do not run update + return; + } + addressBuilder.setFirstSeen(existing.getFirstSeen()).withKey(existing.key()); + break; + } + } + } else { + // Address observations don't exist, so create the list + addresses = new HashMap<>(); + } + + if (addressBuilder.key() == null) { + addressBuilder.withKey(new AddressesKey(Uint64.fromLongBits(addressKey.getAndIncrement()))); + } + + // Add as an augmentation + final Addresses address = addressBuilder.build(); + addresses.put(address.key(), address); + acncBuilder.setAddresses(addresses); + + // build Instance Id for AddressCapableNodeConnector + DataObjectIdentifier addressCapableNcInstanceId = ((DataObjectIdentifier) nodeConnectorRef + .getValue()); + final WriteTransaction writeTransaction = dataService.newWriteOnlyTransaction(); + // Update this AddressCapableNodeConnector in the MD-SAL data tree + writeTransaction.merge(LogicalDatastoreType.OPERATIONAL, addressCapableNcInstanceId, acncBuilder.build()); + + final FluentFuture writeTxResultFuture = writeTransaction.commit(); + Futures.addCallback(writeTxResultFuture, new FutureCallback() { + @Override + public void onSuccess(CommitInfo notUsed) { + LOG.debug("AddressObservationWriter write successful for tx :{}", writeTransaction.getIdentifier()); + } + + @Override + public void onFailure(Throwable throwable) { + LOG.error("AddressObservationWriter write transaction {} failed", writeTransaction.getIdentifier(), + throwable.getCause()); + } + }, MoreExecutors.directExecutor()); + futureMap.put(nodeConnectorLock, writeTxResultFuture); + } + } } From e2510fe9210b7fd20efed5bc654b012d8a40bbb5 Mon Sep 17 00:00:00 2001 From: abhijith-css Date: Thu, 10 Jul 2025 11:13:51 +0530 Subject: [PATCH 08/15] test error fix --- arphandler/pom.xml | 6 +++ .../arphandler/core/PacketDispatcher.java | 37 +++++++++---- .../arphandler/flow/InitialFlowWriter.java | 52 +++++++++++-------- .../arphandler/inventory/InventoryReader.java | 19 ++++--- .../arphandler/core/PacketDispatcherTest.java | 26 +++++----- .../core/ProactiveFloodFlowWriterTest.java | 2 +- .../flow/InitialFlowWriterTest.java | 5 +- .../inventory/InventoryReaderTest.java | 8 +-- 8 files changed, 94 insertions(+), 61 deletions(-) diff --git a/arphandler/pom.xml b/arphandler/pom.xml index 0a7fb9874..569187408 100644 --- a/arphandler/pom.xml +++ b/arphandler/pom.xml @@ -56,6 +56,12 @@ org.opendaylight.yangtools yang-common + + org.opendaylight.mdsal + yang-binding + 13.0.8 + test + diff --git a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java index 413a6f519..25c76e1d2 100644 --- a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java +++ b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java @@ -15,12 +15,15 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacket; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder; +import org.opendaylight.yangtools.binding.BindingInstanceIdentifier; +import org.opendaylight.yangtools.binding.BindingInstanceIdentifier.Step; import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.binding.KeyStep; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; @@ -96,15 +99,29 @@ public void floodPacket(String nodeId, byte[] payload, NodeConnectorRef origIngr return; } } + String origId = extractNodeConnectorId(origIngress); + if (origId == null) { + LOG.warn("Cannot extract original ingress NodeConnector ID"); + return; + } + for (NodeConnectorRef ncRef : nodeConnectors) { - String ncId = ncRef.getValue().firstIdentifierOf(NodeConnector.class).firstKeyOf(NodeConnector.class) - .getId().getValue(); - // Don't flood on discarding node connectors & origIngress - if (!ncId.equals(origIngress.getValue().firstIdentifierOf(NodeConnector.class) - .firstKeyOf(NodeConnector.class).getId().getValue())) { + String ncId = extractNodeConnectorId(ncRef); + if (ncId != null && !ncId.equals(origId)) { sendPacketOut(payload, origIngress, ncRef); } } + + } + + private String extractNodeConnectorId(NodeConnectorRef ref) { + BindingInstanceIdentifier instanceId = ref.getValue(); + for (Step step : instanceId.steps()) { + if (step instanceof KeyStep keyStep && keyStep.key() instanceof NodeConnectorKey) { + return ((NodeConnectorKey) keyStep.key()).getId().getValue(); + } + } + return null; } /** @@ -118,7 +135,7 @@ public void sendPacketOut(byte[] payload, NodeConnectorRef ingress, NodeConnecto if (ingress == null || egress == null) { return; } - InstanceIdentifier egressNodePath = getNodePath(egress.getValue()); + BindingInstanceIdentifier egressNodePath = egress.getValue(); TransmitPacketInput input = new TransmitPacketInputBuilder().setPayload(payload) .setNode(new NodeRef(egressNodePath)).setEgress(egress).setIngress(ingress).build(); @@ -140,7 +157,7 @@ private void refreshInventoryReader() { inventoryReader.readInventory(); } - private static InstanceIdentifier getNodePath(final InstanceIdentifier nodeChild) { - return nodeChild.firstIdentifierOf(Node.class); - } + //private static InstanceIdentifier getNodePath(final InstanceIdentifier nodeChild) { + // return nodeChild.firstIdentifierOf(Node.class); + //} } diff --git a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriter.java b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriter.java index 673c37b5c..254971a2e 100644 --- a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriter.java +++ b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriter.java @@ -59,10 +59,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetTypeBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.KnownEtherType; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.binding.ExactDataObjectStep; import org.opendaylight.yangtools.binding.util.BindingMap; import org.opendaylight.yangtools.concepts.Registration; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.Uint16; import org.opendaylight.yangtools.yang.common.Uint32; @@ -120,19 +120,19 @@ public void setIsHybridMode(boolean isHybridMode) { } public Registration registerAsDataChangeListener(DataBroker dataBroker) { - return dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, - InstanceIdentifier.builder(Nodes.class).child(Node.class).build()), this); + return dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL, + DataObjectIdentifier.builder(Nodes.class).child(Node.class).build()), this); } @Override public void onDataTreeChanged(List> changes) { - var nodeIds = new HashSet>(); + var nodeIds = new HashSet>(); for (var change : changes) { var rootNode = change.getRootNode(); switch (rootNode.getModificationType()) { case WRITE: if (rootNode.getDataBefore() == null) { - nodeIds.add(change.getRootPath().getRootIdentifier()); + nodeIds.add(change.path()); } break; default: @@ -151,21 +151,26 @@ public void onDataTreeChanged(List> changes) { * thread lock it may cause. */ private class InitialFlowWriterProcessor implements Runnable { - private final Set> nodeIds; + private final Set> nodeIds; - InitialFlowWriterProcessor(final Set> nodeIds) { + InitialFlowWriterProcessor(final Set> nodeIds) { this.nodeIds = nodeIds; } @Override public void run() { - if (nodeIds == null) { - return; - } - for (var nodeId : nodeIds) { - if (KeyedInstanceIdentifier.keyOf(nodeId).getId().getValue().contains("openflow:")) { - addInitialFlows(nodeId); + // Find the last step in the identifier + ExactDataObjectStep lastStep = null; + for (ExactDataObjectStep step : nodeId.steps()) { + lastStep = step; + } + if (lastStep != null && Node.class.equals(lastStep.type())) { + @SuppressWarnings("unchecked") + final var invNodeId = (DataObjectIdentifier) nodeId; + if (invNodeId.firstKeyOf(Node.class).getId().getValue().contains("openflow:")) { + addInitialFlows(invNodeId); + } } } } @@ -176,29 +181,30 @@ public void run() { * * @param nodeId The node to write the flow on. */ - public void addInitialFlows(InstanceIdentifier nodeId) { + public void addInitialFlows(DataObjectIdentifier nodeId) { LOG.debug("adding initial flows for node {} ", nodeId); - InstanceIdentifier
tableId = getTableInstanceId(nodeId); - InstanceIdentifier flowId = getFlowInstanceId(tableId); + DataObjectIdentifier
tableId = getTableInstanceId(nodeId); + DataObjectIdentifier flowId = getFlowInstanceId(tableId); // add arpToController flow writeFlowToController(nodeId, tableId, flowId, createArpToControllerFlow(flowTableId, flowPriority)); LOG.debug("Added initial flows for node {} ", nodeId); } - private InstanceIdentifier
getTableInstanceId(InstanceIdentifier nodeId) { + private DataObjectIdentifier
getTableInstanceId(DataObjectIdentifier nodeId) { // get flow table key TableKey flowTableKey = new TableKey(flowTableId); - return nodeId.builder().augmentation(FlowCapableNode.class).child(Table.class, flowTableKey).build(); + return (DataObjectIdentifier
) nodeId.builder().augmentation(FlowCapableNode.class) + .child(Table.class, flowTableKey).build(); } - private InstanceIdentifier getFlowInstanceId(InstanceIdentifier
tableId) { + private DataObjectIdentifier getFlowInstanceId(DataObjectIdentifier
tableId) { // generate unique flow key FlowId flowId = new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement())); FlowKey flowKey = new FlowKey(flowId); - return tableId.child(Flow.class, flowKey); + return tableId.toBuilder().child(Flow.class, flowKey).build(); } private Flow createArpToControllerFlow(Uint8 tableId, Uint16 priority) { @@ -258,8 +264,8 @@ private Flow createArpToControllerFlow(Uint8 tableId, Uint16 priority) { } private ListenableFuture> writeFlowToController( - InstanceIdentifier nodeInstanceId, InstanceIdentifier
tableInstanceId, - InstanceIdentifier flowPath, Flow flow) { + DataObjectIdentifier nodeInstanceId, DataObjectIdentifier
tableInstanceId, + DataObjectIdentifier flowPath, Flow flow) { LOG.trace("Adding flow to node {}", requireNonNull(nodeInstanceId.firstKeyOf(Node.class)).getId().getValue()); return addFlow.invoke(new AddFlowInputBuilder(flow).setNode(new NodeRef(nodeInstanceId)) diff --git a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReader.java b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReader.java index 0f0106722..66ed2ebfe 100755 --- a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReader.java +++ b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReader.java @@ -9,7 +9,6 @@ package org.opendaylight.l2switch.arphandler.inventory; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -85,17 +84,16 @@ public void setRefreshData(boolean refreshData) { @SuppressWarnings({ "unchecked", "rawtypes" }) private void registerAsDataChangeListener() { - InstanceIdentifier nodeConnector = InstanceIdentifier.builder(Nodes.class).child(Node.class) + DataObjectIdentifier nodeConnector = DataObjectIdentifier.builder(Nodes.class).child(Node.class) .child(NodeConnector.class).build(); this.listenerRegistrationList.add(dataService.registerDataTreeChangeListener( - DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, nodeConnector), - (DataTreeChangeListener) this)); + DataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL, nodeConnector), (DataTreeChangeListener) this)); - InstanceIdentifier stpStatusAwareNodeConnecto = InstanceIdentifier + DataObjectIdentifier stpStatusAwareNodeConnecto = DataObjectIdentifier .builder(Nodes.class).child(Node.class).child(NodeConnector.class) .augmentation(StpStatusAwareNodeConnector.class).build(); this.listenerRegistrationList.add(dataService.registerDataTreeChangeListener( - DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, stpStatusAwareNodeConnecto), + DataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL, stpStatusAwareNodeConnecto), (DataTreeChangeListener) this)); } @@ -108,7 +106,7 @@ public HashMap> getSwitchNodeConnectors() { } @Override - public void onDataTreeChanged(Collection> changes) { + public void onDataTreeChanged(List> changes) { if (!refreshDataScheduled) { synchronized (this) { if (!refreshDataScheduled) { @@ -168,7 +166,7 @@ public void readInventory() { if (nodeConnector.key().toString().contains("LOCAL")) { continue; } - NodeConnectorRef ncRef = new NodeConnectorRef(InstanceIdentifier.builder(Nodes.class) + NodeConnectorRef ncRef = new NodeConnectorRef(DataObjectIdentifier.builder(Nodes.class) .child(Node.class, node.key()) .child(NodeConnector.class, nodeConnector.key()) .build()); @@ -176,7 +174,7 @@ public void readInventory() { } switchNodeConnectors.put(node.getId().getValue(), nodeConnectorRefs); - NodeConnectorRef ncRef = new NodeConnectorRef(InstanceIdentifier.builder(Nodes.class) + NodeConnectorRef ncRef = new NodeConnectorRef(DataObjectIdentifier.builder(Nodes.class) .child(Node.class, node.key()) .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(node.getId().getValue() + ":LOCAL"))) @@ -252,7 +250,8 @@ public NodeConnectorRef getNodeConnector(DataObjectIdentifier nodeInsId, M if (lastSeen > latest) { latest = lastSeen; LOG.debug("Found address{} in nodeconnector : {}", macAddress, nc.key()); - destNodeConnector = new NodeConnectorRef(nodeInsId.child(NodeConnector.class, nc.key())); + destNodeConnector = new NodeConnectorRef( + nodeInsId.toBuilder().child(NodeConnector.class, nc.key()).build()); break; } } diff --git a/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcherTest.java b/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcherTest.java index 568f8be85..c1751f2da 100644 --- a/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcherTest.java +++ b/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcherTest.java @@ -31,6 +31,8 @@ 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.packet.service.rev130709.TransmitPacket; +import org.opendaylight.yangtools.binding.BindingInstanceIdentifier; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; @@ -56,7 +58,7 @@ void beforeEach() { void testSendPacketOut() { doReturn(RpcResultBuilder.success().buildFuture()).when(transmitPacket).invoke(any()); - InstanceIdentifier ncInsId1 = InstanceIdentifier.builder(Nodes.class).child(Node.class) + DataObjectIdentifier ncInsId1 = DataObjectIdentifier.builder(Nodes.class).child(Node.class) .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("1"))).build(); packetDispatcher.sendPacketOut(null, new NodeConnectorRef(ncInsId1), new NodeConnectorRef(ncInsId1)); verify(transmitPacket, times(1)).invoke(any()); @@ -64,7 +66,7 @@ void testSendPacketOut() { @Test void testSendPacketOut_NullIngress() { - packetDispatcher.sendPacketOut(null, null, new NodeConnectorRef(InstanceIdentifier.builder(Nodes.class) + packetDispatcher.sendPacketOut(null, null, new NodeConnectorRef(DataObjectIdentifier.builder(Nodes.class) .child(Node.class) .child(NodeConnector.class) .build())); @@ -73,7 +75,7 @@ void testSendPacketOut_NullIngress() { @Test void testSendPacketOut_NullEgress() { - packetDispatcher.sendPacketOut(null, new NodeConnectorRef(InstanceIdentifier.builder(Nodes.class) + packetDispatcher.sendPacketOut(null, new NodeConnectorRef(DataObjectIdentifier.builder(Nodes.class) .child(Node.class) .child(NodeConnector.class) .build()), null); @@ -85,9 +87,9 @@ void testFloodPacket() { doReturn(RpcResultBuilder.success().buildFuture()).when(transmitPacket).invoke(any()); var nodeConnectors = new ArrayList(); - var ncInsId1 = InstanceIdentifier.builder(Nodes.class).child(Node.class) + var ncInsId1 = DataObjectIdentifier.builder(Nodes.class).child(Node.class) .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("1"))).build(); - var ncInsId2 = InstanceIdentifier.builder(Nodes.class).child(Node.class) + var ncInsId2 = DataObjectIdentifier.builder(Nodes.class).child(Node.class) .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("2"))).build(); nodeConnectors.add(new NodeConnectorRef(ncInsId1)); nodeConnectors.add(new NodeConnectorRef(ncInsId1)); @@ -96,7 +98,7 @@ void testFloodPacket() { when(inventoryReader.getSwitchNodeConnectors()).thenReturn(switchNodeConnectors); packetDispatcher.floodPacket("", null, new NodeConnectorRef(ncInsId2), - new NodeConnectorRef(InstanceIdentifier.builder(Nodes.class) + new NodeConnectorRef(DataObjectIdentifier.builder(Nodes.class) .child(Node.class) .child(NodeConnector.class) .build())); @@ -111,11 +113,11 @@ void testFloodPacket_NullList() { when(inventoryReader.getSwitchNodeConnectors()).thenReturn(switchNodeConnectors); packetDispatcher.floodPacket("", null, - new NodeConnectorRef(InstanceIdentifier.builder(Nodes.class) + new NodeConnectorRef(DataObjectIdentifier.builder(Nodes.class) .child(Node.class) .child(NodeConnector.class) .build()), - new NodeConnectorRef(InstanceIdentifier.builder(Nodes.class) + new NodeConnectorRef(DataObjectIdentifier.builder(Nodes.class) .child(Node.class) .child(NodeConnector.class) .build())); @@ -126,7 +128,7 @@ void testFloodPacket_NullList() { @Test void testDispatchPacket_noDispatch() { - var ncInsId = InstanceIdentifier.builder(Nodes.class) + var ncInsId = DataObjectIdentifier.builder(Nodes.class) .child(Node.class, new NodeKey(new NodeId("1"))) .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("1"))).build(); when(controllerSwitchConnectors.get(any(String.class))).thenReturn(null); @@ -142,7 +144,7 @@ void testDispatchPacket_noDispatch() { void testDispatchPacket_toSendPacketOut() { doReturn(RpcResultBuilder.success().buildFuture()).when(transmitPacket).invoke(any()); - var ncInsId1 = InstanceIdentifier.builder(Nodes.class) + var ncInsId1 = DataObjectIdentifier.builder(Nodes.class) .child(Node.class, new NodeKey(new NodeId("1"))) .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("1"))).build(); var ncRef1 = new NodeConnectorRef(ncInsId1); @@ -160,7 +162,7 @@ void testDispatchPacket_toSendPacketOut() { void testDispatchPacket_toFloodPacket() { doReturn(RpcResultBuilder.success().buildFuture()).when(transmitPacket).invoke(any()); - var ncInsId1 = InstanceIdentifier.builder(Nodes.class) + var ncInsId1 = DataObjectIdentifier.builder(Nodes.class) .child(Node.class, new NodeKey(new NodeId("1"))) .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("1"))).build(); var ncRef1 = new NodeConnectorRef(ncInsId1); @@ -169,7 +171,7 @@ void testDispatchPacket_toFloodPacket() { when(inventoryReader.getNodeConnector(any(), any())).thenReturn(null); var nodeConnectors = new ArrayList(); - var ncInsId2 = InstanceIdentifier.builder(Nodes.class) + var ncInsId2 = DataObjectIdentifier.builder(Nodes.class) .child(Node.class, new NodeKey(new NodeId("2"))) .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("2"))).build(); nodeConnectors.add(new NodeConnectorRef(ncInsId1)); diff --git a/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/core/ProactiveFloodFlowWriterTest.java b/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/core/ProactiveFloodFlowWriterTest.java index 7c7622731..0b568628a 100644 --- a/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/core/ProactiveFloodFlowWriterTest.java +++ b/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/core/ProactiveFloodFlowWriterTest.java @@ -35,9 +35,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatus; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnectorBuilder; +import org.opendaylight.yangtools.binding.util.BindingMap; import org.opendaylight.yangtools.util.concurrent.FluentFutures; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.util.BindingMap; import org.opendaylight.yangtools.yang.common.Uint16; import org.opendaylight.yangtools.yang.common.Uint8; diff --git a/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriterTest.java b/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriterTest.java index afe3622bb..4d5b3ec35 100644 --- a/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriterTest.java +++ b/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/flow/InitialFlowWriterTest.java @@ -28,6 +28,7 @@ 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.inventory.rev130819.nodes.NodeKey; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; public class InitialFlowWriterTest { @@ -50,8 +51,8 @@ public void before() { public void onDataChange_Valid() throws Exception { when(mockModification.getDataAfter()).thenReturn(new NodeBuilder().setId(new NodeId("openflow:1")).build()); when(mockModification.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE); - when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION, - InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1"))).build())); + when(mockChange.getRootPath()).thenReturn(DataTreeIdentifier.of(LogicalDatastoreType.CONFIGURATION, + DataObjectIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1"))).build())); when(mockChange.getRootNode()).thenReturn(mockModification); initialFlowWriter.onDataTreeChanged(List.of(mockChange)); diff --git a/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReaderTest.java b/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReaderTest.java index aded49ec1..bf9dbd86e 100644 --- a/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReaderTest.java +++ b/arphandler/src/test/java/org/opendaylight/l2switch/arphandler/inventory/InventoryReaderTest.java @@ -39,9 +39,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatus; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnectorBuilder; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.binding.util.BindingMap; import org.opendaylight.yangtools.util.concurrent.FluentFutures; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.util.BindingMap; + import org.opendaylight.yangtools.yang.common.Uint64; @RunWith(MockitoJUnitRunner.StrictStubs.class) @@ -51,7 +53,7 @@ public class InventoryReaderTest { @Mock private ReadTransaction readOnlyTransaction; @Mock - private InstanceIdentifier mockInstanceIdentifier; + private DataObjectIdentifier mockInstanceIdentifier; @Mock private MacAddress mockMacAddress; @@ -94,7 +96,7 @@ public void testGetNodeConnector() throws Exception { when(dataBroker.newReadOnlyTransaction()).thenReturn(readOnlyTransaction); assertNotNull(inventoryReader.getNodeConnector( - InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1"))).build(), + DataObjectIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1"))).build(), new MacAddress("aa:bb:cc:dd:ee:ff"))); verify(readOnlyTransaction, times(1)).close(); } From 306ad71657e469afd7a8f3f41f89bab4be6f8be4 Mon Sep 17 00:00:00 2001 From: i-am-arathypm Date: Thu, 10 Jul 2025 11:38:36 +0530 Subject: [PATCH 09/15] addresstracker test fix --- .../addressobserver/AddressObservationWriterTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addresstracker/implementation/src/test/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriterTest.java b/addresstracker/implementation/src/test/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriterTest.java index 4d43b326c..d24725b44 100644 --- a/addresstracker/implementation/src/test/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriterTest.java +++ b/addresstracker/implementation/src/test/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriterTest.java @@ -32,6 +32,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.util.concurrent.FluentFutures; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -54,14 +55,14 @@ public void init() throws Exception { macAddress = new MacAddress("ba:43:52:ce:09:f4"); ipAddress = new IpAddress(new Ipv4Address("10.0.0.1")); realNcRef = new NodeConnectorRef( - InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); + DataObjectIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); readTransaction = mock(ReadTransaction.class); dataService = mock(DataBroker.class); nodeConnector = mock(NodeConnector.class); when(dataService.newReadOnlyTransaction()).thenReturn(readTransaction); checkedFuture = FluentFutures.immediateFluentFuture(Optional.of(nodeConnector)); - when(readTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) + when(readTransaction.read(any(LogicalDatastoreType.class), any(DataObjectIdentifier.class))) .thenReturn(checkedFuture); addrCapableNc = mock(AddressCapableNodeConnector.class); From c0d61befc06d0acbaf136e6100d815d6966119b6 Mon Sep 17 00:00:00 2001 From: abhijith-css Date: Thu, 10 Jul 2025 11:46:17 +0530 Subject: [PATCH 10/15] version changes --- .../addressobserver/AddressObservationWriterTest.java | 3 ++- parent/pom.xml | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/addresstracker/implementation/src/test/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriterTest.java b/addresstracker/implementation/src/test/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriterTest.java index 4d43b326c..9661f0427 100644 --- a/addresstracker/implementation/src/test/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriterTest.java +++ b/addresstracker/implementation/src/test/java/org/opendaylight/l2switch/addresstracker/addressobserver/AddressObservationWriterTest.java @@ -32,6 +32,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.util.concurrent.FluentFutures; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -54,7 +55,7 @@ public void init() throws Exception { macAddress = new MacAddress("ba:43:52:ce:09:f4"); ipAddress = new IpAddress(new Ipv4Address("10.0.0.1")); realNcRef = new NodeConnectorRef( - InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); + DataObjectIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); readTransaction = mock(ReadTransaction.class); dataService = mock(DataBroker.class); diff --git a/parent/pom.xml b/parent/pom.xml index e14229de9..3a63451a4 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -58,6 +58,12 @@ jung-graph-impl 2.1.1 + + org.osgi + org.osgi.service.component.annotations + 1.4.0 + provided + From d39a23f65d95bf9272fa6b154b123b4db3378a20 Mon Sep 17 00:00:00 2001 From: i-am-arathypm Date: Thu, 10 Jul 2025 12:35:09 +0530 Subject: [PATCH 11/15] l2switch-main fixed --- .../l2switch/L2SwitchMainProvider.java | 6 +-- .../l2switch/flow/FlowWriterServiceImpl.java | 16 ++++--- .../l2switch/flow/InitialFlowWriter.java | 47 +++++++++++-------- .../l2switch/flow/ReactiveFlowWriter.java | 10 +++- .../l2switch/inventory/InventoryReader.java | 5 +- .../util/InstanceIdentifierUtils.java | 33 ++++++++++--- 6 files changed, 77 insertions(+), 40 deletions(-) diff --git a/l2switch-main/src/main/java/org/opendaylight/l2switch/L2SwitchMainProvider.java b/l2switch-main/src/main/java/org/opendaylight/l2switch/L2SwitchMainProvider.java index d1a4047f0..968b4f0ce 100644 --- a/l2switch-main/src/main/java/org/opendaylight/l2switch/L2SwitchMainProvider.java +++ b/l2switch-main/src/main/java/org/opendaylight/l2switch/L2SwitchMainProvider.java @@ -13,7 +13,7 @@ import org.opendaylight.l2switch.inventory.InventoryReader; import org.opendaylight.mdsal.binding.api.DataBroker; import org.opendaylight.mdsal.binding.api.NotificationService; -import org.opendaylight.mdsal.binding.api.RpcConsumerRegistry; +import org.opendaylight.mdsal.binding.api.RpcService; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlow; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.l2switch.config.rev140528.L2switchConfig; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.arp.rev140528.ArpPacketReceived; @@ -28,12 +28,12 @@ public class L2SwitchMainProvider { private final DataBroker dataService; private final NotificationService notificationService; - private final RpcConsumerRegistry rpcService; + private final RpcService rpcService; private final L2switchConfig mainConfig; public L2SwitchMainProvider(final DataBroker dataBroker, final NotificationService notificationPublishService, - final RpcConsumerRegistry rpcService, final L2switchConfig config) { + final RpcService rpcService, final L2switchConfig config) { this.dataService = dataBroker; this.notificationService = notificationPublishService; this.rpcService = rpcService; diff --git a/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/FlowWriterServiceImpl.java b/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/FlowWriterServiceImpl.java index 819962108..8fd665893 100644 --- a/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/FlowWriterServiceImpl.java +++ b/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/FlowWriterServiceImpl.java @@ -45,8 +45,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.binding.DataObjectReference; +import org.opendaylight.yangtools.binding.util.BindingMap; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.util.BindingMap; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.Uint16; import org.opendaylight.yangtools.yang.common.Uint64; @@ -108,7 +110,7 @@ public void addMacToMacFlow(MacAddress sourceMac, MacAddress destMac, NodeConnec TableKey flowTableKey = new TableKey(flowTableId); // build a flow path based on node connector to program flow - InstanceIdentifier flowPath = buildFlowPath(destNodeConnectorRef, flowTableKey); + DataObjectIdentifier flowPath = buildFlowPath(destNodeConnectorRef, flowTableKey); // build a flow that target given mac id Flow flowBody = createMacToMacFlow(flowTableKey.getId(), flowPriority, sourceMac, destMac, @@ -147,7 +149,7 @@ public void addBidirectionalMacToMacFlows(MacAddress sourceMac, NodeConnectorRef * @param flowTableKey a reference to the flow table * @return Flow instance identifier */ - private InstanceIdentifier buildFlowPath(NodeConnectorRef nodeConnectorRef, TableKey flowTableKey) { + private DataObjectIdentifier buildFlowPath(NodeConnectorRef nodeConnectorRef, TableKey flowTableKey) { // generate unique flow key FlowId flowId = new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement())); @@ -204,7 +206,7 @@ private Flow createMacToMacFlow(Uint8 tableId, Uint16 priority, MacAddress sourc .setOutputAction(new OutputActionBuilder() .setMaxLength(Uint16.MAX_VALUE) .setOutputNodeConnector( - destPort.getValue().firstKeyOf(NodeConnector.class).getId()) + ((DataObjectReference) destPort.getValue()).firstKeyOf(NodeConnector.class).getId()) .build()) .build()) .build())) @@ -229,11 +231,11 @@ private Flow createMacToMacFlow(Uint8 tableId, Uint16 priority, MacAddress sourc * @param flow the Flow * @return transaction commit */ - private Future> writeFlowToConfigData(InstanceIdentifier flowPath, Flow flow) { + private Future> writeFlowToConfigData(DataObjectIdentifier flowPath, Flow flow) { return addFlow.invoke(new AddFlowInputBuilder(flow) - .setNode(new NodeRef(flowPath.firstIdentifierOf(Node.class))) + .setNode(new NodeRef(InstanceIdentifierUtils.firstIdentifierOf(flowPath, Node.class))) .setFlowRef(new FlowRef(flowPath)) - .setFlowTable(new FlowTableRef(flowPath.firstIdentifierOf(Table.class))) + .setFlowTable(new FlowTableRef(InstanceIdentifierUtils.firstIdentifierOf(flowPath, Table.class))) .setTransactionUri(new Uri(flow.getId().getValue())) .build()); } diff --git a/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/InitialFlowWriter.java b/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/InitialFlowWriter.java index c73cd2f91..813769bcc 100644 --- a/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/InitialFlowWriter.java +++ b/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/InitialFlowWriter.java @@ -12,6 +12,7 @@ import com.google.common.util.concurrent.ListenableFuture; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -47,9 +48,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef; 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.concepts.ListenerRegistration; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.binding.ExactDataObjectStep; +import org.opendaylight.yangtools.binding.util.BindingMap; +import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.util.BindingMap; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.Uint16; import org.opendaylight.yangtools.yang.common.Uint64; @@ -95,20 +98,20 @@ public void setFlowHardTimeout(final Uint16 flowHardTimeout) { this.flowHardTimeout = flowHardTimeout; } - public ListenerRegistration registerAsDataChangeListener(final DataBroker dataBroker) { + public Registration registerAsDataChangeListener(final DataBroker dataBroker) { return dataBroker.registerDataTreeChangeListener(DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(Nodes.class).child(Node.class).build()), this); } @Override - public void onDataTreeChanged(final Collection> changes) { - final var nodeIds = new HashSet>(); + public void onDataTreeChanged(final List> changes) { + final var nodeIds = new HashSet>(); for (var change : changes) { final var rootNode = change.getRootNode(); switch (rootNode.getModificationType()) { case WRITE: - if (rootNode.getDataBefore() == null) { - nodeIds.add(change.getRootPath().getRootIdentifier()); + if (rootNode.dataBefore() == null) { + nodeIds.add(change.path()); } break; default: @@ -126,17 +129,23 @@ public void onDataTreeChanged(final Collection> chang * thread that invoked the data node updated event. Avoids any thread lock it may cause. */ private class InitialFlowWriterProcessor implements Runnable { - private final Set> nodeIds; + private final Set> nodeIds; - InitialFlowWriterProcessor(final Set> nodeIds) { + InitialFlowWriterProcessor(final Set> nodeIds) { this.nodeIds = requireNonNull(nodeIds); } @Override public void run() { for (var nodeId : nodeIds) { - if (Node.class.isAssignableFrom(nodeId.getTargetType())) { - final var invNodeId = (InstanceIdentifier) nodeId; + // Find the last step in the identifier + ExactDataObjectStep lastStep = null; + for (ExactDataObjectStep step : nodeId.steps()) { + lastStep = step; + } + if (lastStep != null && Node.class.equals(lastStep.type())) { + @SuppressWarnings("unchecked") + final var invNodeId = (DataObjectIdentifier) nodeId; if (invNodeId.firstKeyOf(Node.class).getId().getValue().contains("openflow:")) { addInitialFlows(invNodeId); } @@ -148,7 +157,7 @@ public void run() { * Adds a flow, which drops all packets, on the specifide node. * @param nodeId The node to install the flow on. */ - public void addInitialFlows(final InstanceIdentifier nodeId) { + public void addInitialFlows(final DataObjectIdentifier nodeId) { LOG.debug("adding initial flows for node {} ", nodeId); final var tableId = getTableInstanceId(nodeId); @@ -160,18 +169,18 @@ public void addInitialFlows(final InstanceIdentifier nodeId) { LOG.debug("Added initial flows for node {} ", nodeId); } - private InstanceIdentifier
getTableInstanceId(final InstanceIdentifier nodeId) { - return nodeId.builder() + private DataObjectIdentifier
getTableInstanceId(final DataObjectIdentifier nodeId) { + return nodeId.toBuilder() .augmentation(FlowCapableNode.class) // get flow table key .child(Table.class, new TableKey(flowTableId)) .build(); } - private InstanceIdentifier getFlowInstanceId(final InstanceIdentifier
tableId) { - return tableId.child(Flow.class, + private DataObjectIdentifier getFlowInstanceId(final DataObjectIdentifier
tableId) { + return tableId.toBuilder().child(Flow.class, // generate unique flow key - new FlowKey(new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement())))); + new FlowKey(new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement())))).build(); } private Flow createDropAllFlow(final Uint8 tableId, final Uint16 priority) { @@ -210,8 +219,8 @@ private Flow createDropAllFlow(final Uint8 tableId, final Uint16 priority) { } private ListenableFuture> writeFlowToController( - final InstanceIdentifier nodeInstanceId, final InstanceIdentifier
tableInstanceId, - final InstanceIdentifier flowPath, final Flow flow) { + final DataObjectIdentifier nodeInstanceId, final DataObjectIdentifier
tableInstanceId, + final DataObjectIdentifier flowPath, final Flow flow) { LOG.trace("Adding flow to node {}", nodeInstanceId.firstKeyOf(Node.class).getId().getValue()); return addFlow.invoke(new AddFlowInputBuilder(flow) .setNode(new NodeRef(nodeInstanceId)) diff --git a/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/ReactiveFlowWriter.java b/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/ReactiveFlowWriter.java index c8564a251..8132198e1 100644 --- a/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/ReactiveFlowWriter.java +++ b/l2switch-main/src/main/java/org/opendaylight/l2switch/flow/ReactiveFlowWriter.java @@ -8,6 +8,7 @@ package org.opendaylight.l2switch.flow; import org.opendaylight.l2switch.inventory.InventoryReader; +import org.opendaylight.l2switch.util.InstanceIdentifierUtils; import org.opendaylight.mdsal.binding.api.NotificationService.Listener; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; @@ -18,6 +19,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacket; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.RawPacketFields; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacket; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; /** * This class listens to certain type of packets and writes a mac to mac flows. @@ -95,8 +97,12 @@ public void onNotification(ArpPacketReceived packetReceived) { * The destination MacAddress of the packet. */ public void writeFlows(NodeConnectorRef ingress, MacAddress srcMac, MacAddress destMac) { - NodeConnectorRef destNodeConnector = inventoryReader - .getNodeConnector(ingress.getValue().firstIdentifierOf(Node.class), destMac); + DataObjectIdentifier doi = (DataObjectIdentifier) ingress.getValue(); + // Use your utility to get the node identifier + DataObjectIdentifier nodeId = InstanceIdentifierUtils.firstIdentifierOf(doi, Node.class); + + // Use nodeId in your downstream call + NodeConnectorRef destNodeConnector = inventoryReader.getNodeConnector(nodeId, destMac); if (destNodeConnector != null) { flowWriterService.addBidirectionalMacToMacFlows(srcMac, ingress, destMac, destNodeConnector); } diff --git a/l2switch-main/src/main/java/org/opendaylight/l2switch/inventory/InventoryReader.java b/l2switch-main/src/main/java/org/opendaylight/l2switch/inventory/InventoryReader.java index 5f1dac118..cad31e78b 100644 --- a/l2switch-main/src/main/java/org/opendaylight/l2switch/inventory/InventoryReader.java +++ b/l2switch-main/src/main/java/org/opendaylight/l2switch/inventory/InventoryReader.java @@ -21,6 +21,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatus; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnector; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,7 +55,7 @@ public InventoryReader(DataBroker dataService) { * @return NodeConnectorRef that pertains to the NodeConnector containing * the MacAddress observation. */ - public NodeConnectorRef getNodeConnector(InstanceIdentifier nodeInsId, MacAddress macAddress) { + public NodeConnectorRef getNodeConnector(DataObjectIdentifier nodeInsId, MacAddress macAddress) { if (nodeInsId == null || macAddress == null) { return null; } @@ -97,7 +98,7 @@ public NodeConnectorRef getNodeConnector(InstanceIdentifier nodeInsId, Mac if (macAddress.equals(add.getMac())) { final long lastSeen = add.getLastSeen(); if (lastSeen > latest) { - destNodeConnector = new NodeConnectorRef(nodeInsId.child(NodeConnector.class, nc.key())); + destNodeConnector = new NodeConnectorRef(nodeInsId.toBuilder().child(NodeConnector.class, nc.key()).build()); latest = lastSeen; LOG.debug("Found address{} in nodeconnector : {}", macAddress, nc.key()); break; diff --git a/l2switch-main/src/main/java/org/opendaylight/l2switch/util/InstanceIdentifierUtils.java b/l2switch-main/src/main/java/org/opendaylight/l2switch/util/InstanceIdentifierUtils.java index 072448a30..08d4531e4 100644 --- a/l2switch-main/src/main/java/org/opendaylight/l2switch/util/InstanceIdentifierUtils.java +++ b/l2switch-main/src/main/java/org/opendaylight/l2switch/util/InstanceIdentifierUtils.java @@ -7,6 +7,9 @@ */ package org.opendaylight.l2switch.util; +import java.util.ArrayList; +import java.util.List; + import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey; @@ -14,7 +17,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.binding.DataObject; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.binding.ExactDataObjectStep; /** * InstanceIdentifierUtils provides utility functions related to InstanceIdentifiers. @@ -25,18 +30,32 @@ private InstanceIdentifierUtils() { throw new UnsupportedOperationException("Utility class should never be instantiated"); } - public static InstanceIdentifier generateNodeInstanceIdentifier(final NodeConnectorRef nodeConnectorRef) { - return nodeConnectorRef.getValue().firstIdentifierOf(Node.class); + public static DataObjectIdentifier generateNodeInstanceIdentifier(final NodeConnectorRef nodeConnectorRef) { + return firstIdentifierOf((DataObjectIdentifier) nodeConnectorRef.getValue(), Node.class); } - public static InstanceIdentifier
generateFlowTableInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, + public static DataObjectIdentifier
generateFlowTableInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, final TableKey flowTableKey) { - return generateNodeInstanceIdentifier(nodeConnectorRef).builder().augmentation(FlowCapableNode.class) + return generateNodeInstanceIdentifier(nodeConnectorRef).toBuilder().augmentation(FlowCapableNode.class) .child(Table.class, flowTableKey).build(); } - public static InstanceIdentifier generateFlowInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, + public static DataObjectIdentifier generateFlowInstanceIdentifier(final NodeConnectorRef nodeConnectorRef, final TableKey flowTableKey, final FlowKey flowKey) { - return generateFlowTableInstanceIdentifier(nodeConnectorRef, flowTableKey).child(Flow.class, flowKey); + return generateFlowTableInstanceIdentifier(nodeConnectorRef, flowTableKey).toBuilder().child(Flow.class, flowKey).build(); } + public static DataObjectIdentifier firstIdentifierOf(DataObjectIdentifier identifier, + Class type) { + List> collected = new ArrayList<>(); + for (ExactDataObjectStep step : identifier.steps()) { + collected.add(step); + if (type.equals(step.type())) { + @SuppressWarnings("unchecked") + DataObjectIdentifier result = (DataObjectIdentifier) DataObjectIdentifier + .ofUnsafeSteps(List.copyOf(collected)); + return result; + } + } + return null; + } } From 276222d3303972793d0fce32c06ffa450dafbfa0 Mon Sep 17 00:00:00 2001 From: abhijith-css Date: Thu, 10 Jul 2025 18:58:34 +0530 Subject: [PATCH 12/15] host tracker --- hosttracker/implementation/pom.xml | 12 +- .../ConcurrentClusterAwareHostHashMap.java | 21 +- .../ConcurrentClusterAwareLinkHashMap.java | 11 +- .../plugin/internal/HostTrackerImpl.java | 76 ++-- .../plugin/internal/OperationProcessor.java | 81 ++-- .../internal/SimpleAddressObserver.java | 12 +- .../hosttracker/plugin/inventory/Host.java | 3 +- .../plugin/util/SimpleTaskRetryLooper.java | 70 ++++ .../plugin/util/TransactionChainManager.java | 372 ++++++++++++++++++ .../hosttracker/plugin/util/Utilities.java | 42 +- 10 files changed, 576 insertions(+), 124 deletions(-) create mode 100644 hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/SimpleTaskRetryLooper.java create mode 100644 hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/TransactionChainManager.java diff --git a/hosttracker/implementation/pom.xml b/hosttracker/implementation/pom.xml index 1c1624bb4..4083a126a 100644 --- a/hosttracker/implementation/pom.xml +++ b/hosttracker/implementation/pom.xml @@ -17,11 +17,11 @@ org.opendaylight.yangtools yang-common - + org.opendaylight.mdsal mdsal-binding-api @@ -42,6 +42,14 @@ org.opendaylight.mdsal.model ietf-topology + + com.github.spotbugs + spotbugs-annotations + + + org.checkerframework + checker-qual + diff --git a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/ConcurrentClusterAwareHostHashMap.java b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/ConcurrentClusterAwareHostHashMap.java index 259d9e336..57c57c082 100644 --- a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/ConcurrentClusterAwareHostHashMap.java +++ b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/ConcurrentClusterAwareHostHashMap.java @@ -19,6 +19,7 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.yang.gen.v1.urn.opendaylight.host.tracker.rev140624.HostId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +37,7 @@ public class ConcurrentClusterAwareHostHashMap { /** * The instance identifiers for each host submitted to MD-SAL. */ - private final ConcurrentHashMap, HostId> instanceIDs = new ConcurrentHashMap<>(); + private final ConcurrentHashMap, HostId> instanceIDs = new ConcurrentHashMap<>(); /** * The local Hosts' HashMap. @@ -57,7 +58,7 @@ public ConcurrentClusterAwareHostHashMap(OperationProcessor opProcessor, String * the InstanceIdentifier<Node> of the Host to remove. * @return the removed Host if exits, null if it doesn't exist. */ - public synchronized Host removeLocally(InstanceIdentifier iiN) { + public synchronized Host removeLocally(DataObjectIdentifier iiN) { HostId hostId = this.instanceIDs.remove(iiN); if (hostId != null) { return this.hostHashMap.remove(hostId); @@ -74,7 +75,7 @@ public synchronized Host removeLocally(InstanceIdentifier iiN) { * @return the removed Host if exits, null if it doesn't exist. */ public synchronized Host removeLocally(HostId key) { - Iterator, HostId>> iterator = this.instanceIDs.entrySet().iterator(); + Iterator, HostId>> iterator = this.instanceIDs.entrySet().iterator(); while (iterator.hasNext()) { if (iterator.next().getValue().equals(key)) { iterator.remove(); @@ -94,7 +95,7 @@ public synchronized Host removeLocally(HostId key) { * the Host to store locally. * @return the previous value associated with {@code key}, or {@code null} if there was no mapping for {@code key} */ - public synchronized Host putLocally(InstanceIdentifier ii, Host value) { + public synchronized Host putLocally(DataObjectIdentifier ii, Host value) { Host host = value; LOG.trace("Putting locally {}", host.getId()); this.instanceIDs.put(ii, host.getId()); @@ -108,7 +109,7 @@ public synchronized Host putLocally(InstanceIdentifier ii, Host value) { * the hosts to remove. */ public synchronized void removeAll(List hosts) { - for (final Map.Entry, HostId> e : this.instanceIDs.entrySet()) { + for (final Map.Entry, HostId> e : this.instanceIDs.entrySet()) { for (Host h : hosts) { if (e.getValue().equals(h.getId())) { this.opProcessor.enqueueOperation(tx -> tx.delete(LogicalDatastoreType.OPERATIONAL, e.getKey())); @@ -129,7 +130,7 @@ public synchronized void removeAll(List hosts) { public synchronized void submit(HostId hostid) { Host host = this.hostHashMap.get(hostid); final Node hostNode = host.getHostNode(); - final InstanceIdentifier buildNodeIID = Utilities.buildNodeIID(hostNode.key(), topologyId); + final DataObjectIdentifier buildNodeIID = Utilities.buildNodeIID(hostNode.key(), topologyId); this.opProcessor.enqueueOperation( tx -> tx.mergeParentStructureMerge(LogicalDatastoreType.OPERATIONAL, buildNodeIID, hostNode)); putLocally(buildNodeIID, host); @@ -147,7 +148,7 @@ public synchronized void submit(HostId hostid) { public synchronized void putAll(List hosts) { for (Host h : hosts) { final Node hostNode = h.getHostNode(); - final InstanceIdentifier buildNodeIID = Utilities.buildNodeIID(hostNode.key(), topologyId); + final DataObjectIdentifier buildNodeIID = Utilities.buildNodeIID(hostNode.key(), topologyId); this.opProcessor.enqueueOperation( tx -> tx.mergeParentStructureMerge(LogicalDatastoreType.OPERATIONAL, buildNodeIID, hostNode)); putLocally(buildNodeIID, h); @@ -167,7 +168,7 @@ public synchronized void putAll(List hosts) { */ public synchronized Host put(HostId hostId, Host host) { final Node hostNode = host.getHostNode(); - final InstanceIdentifier buildNodeIID = Utilities.buildNodeIID(hostNode.key(), topologyId); + final DataObjectIdentifier buildNodeIID = Utilities.buildNodeIID(hostNode.key(), topologyId); this.opProcessor.enqueueOperation( tx -> tx.mergeParentStructureMerge(LogicalDatastoreType.OPERATIONAL, buildNodeIID, hostNode)); LOG.trace("Putting MD-SAL {}", hostNode.getNodeId()); @@ -186,7 +187,7 @@ public synchronized Host remove(HostId hostId) { Host removedValue = this.hostHashMap.remove(hostId); if (removedValue != null) { Node hostNode = removedValue.getHostNode(); - final InstanceIdentifier hnIID = Utilities.buildNodeIID(hostNode.key(), topologyId); + final DataObjectIdentifier hnIID = Utilities.buildNodeIID(hostNode.key(), topologyId); this.opProcessor.enqueueOperation(tx -> tx.delete(LogicalDatastoreType.OPERATIONAL, hnIID)); this.instanceIDs.remove(hnIID); } @@ -206,7 +207,7 @@ public Host get(HostId key) { * local HashMap will be empty after this call returns. */ public synchronized void clear() { - for (final Map.Entry, HostId> e : this.instanceIDs.entrySet()) { + for (final Map.Entry, HostId> e : this.instanceIDs.entrySet()) { this.opProcessor.enqueueOperation(tx -> tx.delete(LogicalDatastoreType.OPERATIONAL, e.getKey())); } this.hostHashMap.clear(); diff --git a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/ConcurrentClusterAwareLinkHashMap.java b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/ConcurrentClusterAwareLinkHashMap.java index 49d7c743c..e91ff3fe4 100644 --- a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/ConcurrentClusterAwareLinkHashMap.java +++ b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/ConcurrentClusterAwareLinkHashMap.java @@ -16,6 +16,7 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +34,7 @@ public class ConcurrentClusterAwareLinkHashMap { /** * The instance identifiers for each Link submitted to MD-SAL. */ - private final ConcurrentHashMap, LinkId> instanceIDs = new ConcurrentHashMap<>(); + private final ConcurrentHashMap, LinkId> instanceIDs = new ConcurrentHashMap<>(); /** * The local Links' HashMap. @@ -54,7 +55,7 @@ public ConcurrentClusterAwareLinkHashMap(OperationProcessor opProcessor) { * @return the previous value associated with {@code key}, or * {@code null} if there was no mapping for {@code key} */ - public synchronized Link putLocally(InstanceIdentifier ii, Link link) { + public synchronized Link putLocally(DataObjectIdentifier ii, Link link) { LOG.trace("Putting locally {}", link.getLinkId()); this.instanceIDs.put(ii, link.getLinkId()); return this.linkHashMap.put(link.getLinkId(), link); @@ -67,7 +68,7 @@ public synchronized Link putLocally(InstanceIdentifier ii, Link link) { * the links to remove. */ public synchronized void removeAll(List links) { - for (final Map.Entry, LinkId> e : this.instanceIDs.entrySet()) { + for (final Map.Entry, LinkId> e : this.instanceIDs.entrySet()) { LOG.debug("Links to remove from local & MD-SAL database: {}", links); for (Link l : links) { if (e.getValue().equals(l.getLinkId())) { @@ -96,7 +97,7 @@ public synchronized Collection values() { * the InstanceIdentifier<Link> of the Link to remove. * @return the removed Link if exits, null if it doesn't exist. */ - public synchronized Link removeLocally(InstanceIdentifier iiL) { + public synchronized Link removeLocally(DataObjectIdentifier iiL) { LinkId linkId = this.instanceIDs.remove(iiL); if (linkId != null) { return this.linkHashMap.remove(linkId); @@ -113,7 +114,7 @@ public synchronized Link removeLocally(InstanceIdentifier iiL) { * @return the removed Link if exits, null if it doesn't exist. */ public synchronized Link removeLocally(LinkId key) { - Iterator, LinkId>> iterator = this.instanceIDs.entrySet().iterator(); + Iterator, LinkId>> iterator = this.instanceIDs.entrySet().iterator(); while (iterator.hasNext()) { if (iterator.next().getValue().equals(key)) { iterator.remove(); diff --git a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/HostTrackerImpl.java b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/HostTrackerImpl.java index a16866cb5..514a2b74e 100644 --- a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/HostTrackerImpl.java +++ b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/HostTrackerImpl.java @@ -9,6 +9,7 @@ package org.opendaylight.l2switch.hosttracker.plugin.internal; import static java.util.Objects.requireNonNull; +import static org.opendaylight.l2switch.hosttracker.plugin.util.Utilities.firstIdentifierOf; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.FluentFuture; @@ -44,8 +45,9 @@ 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.Link; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; -import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.binding.DataObject; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,9 +72,9 @@ public class HostTrackerImpl implements DataTreeChangeListener { private final ConcurrentClusterAwareLinkHashMap links; private final OperationProcessor opProcessor; private final Thread processorThread; - private ListenerRegistration addrsNodeListenerRegistration; - private ListenerRegistration hostNodeListenerRegistration; - private ListenerRegistration linkNodeListenerRegistration; + private Registration addrsNodeListenerRegistration; + private Registration hostNodeListenerRegistration; + private Registration linkNodeListenerRegistration; /** * It creates hosts using reference to MD-SAl / toplogy module. For every hostPurgeIntervalInput time interval @@ -136,42 +138,14 @@ public void init() { // FIXME: add an specialized object instead of going through raw types! DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, linkIID), (DataTreeChangeListener)this); - //Processing addresses that existed before we register as a data change listener. -// ReadOnlyTransaction newReadOnlyTransaction = dataService.newReadOnlyTransaction(); -// InstanceIdentifier iinc = addrCapableNodeConnectors.firstIdentifierOf(NodeConnector.class); -// InstanceIdentifier iin -// = addrCapableNodeConnectors.firstIdentifierOf( -// org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class); -// ListenableFuture> dataFuture = newReadOnlyTransaction.read( -// LogicalDatastoreType.OPERATIONAL, iinc); -// try { -// NodeConnector get = dataFuture.get().get(); -// log.trace("test "+get); -// } catch (InterruptedException | ExecutionException ex) { -// java.util.logging.Logger.getLogger(HostTrackerImpl.class.getName()).log(Level.SEVERE, null, ex); -// } -// Futures.addCallback(dataFuture, new FutureCallback>() { -// @Override -// public void onSuccess(final Optional result) { -// if (result.isPresent()) { -// log.trace("Processing NEW NODE? " + result.get().getId().getValue()); -//// processHost(result, dataObject, node); -// } -// } - -// @Override -// public void onFailure(Throwable arg0) { -// } -// }); } - @Override - public void onDataTreeChanged(Collection> changes) { + public void onDataTreeChanged(List> changes) { exec.submit(() -> { for (DataTreeModification change: changes) { DataObjectModification rootNode = change.getRootNode(); - final InstanceIdentifier identifier = change.getRootPath().getRootIdentifier(); - switch (rootNode.getModificationType()) { + final DataObjectIdentifier identifier = change.path(); + switch (rootNode.modificationType()) { case SUBTREE_MODIFIED: case WRITE: onModifiedData(identifier, rootNode); @@ -187,42 +161,42 @@ public void onDataTreeChanged(Collection> chang } @SuppressWarnings("unchecked") - private void onModifiedData(InstanceIdentifier iid, DataObjectModification rootNode) { + private void onModifiedData(DataObjectIdentifier iid, DataObjectModification rootNode) { final DataObject dataObject = rootNode.getDataAfter(); if (dataObject instanceof Addresses) { packetReceived((Addresses) dataObject, iid); } else if (dataObject instanceof Node) { - hosts.putLocally((InstanceIdentifier) iid, Host.createHost((Node) dataObject)); + hosts.putLocally((DataObjectIdentifier) iid, Host.createHost((Node) dataObject)); } else if (dataObject instanceof Link) { - links.putLocally((InstanceIdentifier) iid, (Link) dataObject); + links.putLocally((DataObjectIdentifier) iid, (Link) dataObject); } } @SuppressWarnings("unchecked") - private void onDeletedData(InstanceIdentifier iid, DataObjectModification rootNode) { - if (iid.getTargetType().equals(Node.class)) { + private void onDeletedData(DataObjectIdentifier iid, DataObjectModification rootNode) { + if (iid.lastStep().type().equals(NodeConnector.class)) { Node node = (Node) rootNode.getDataBefore(); - InstanceIdentifier iiN = (InstanceIdentifier) iid; + DataObjectIdentifier iiN = (DataObjectIdentifier) iid; HostNode hostNode = node.augmentation(HostNode.class); if (hostNode != null) { hosts.removeLocally(iiN); } - } else if (iid.getTargetType().equals(Link.class)) { + } else if (iid.lastStep().type().equals(Link.class)) { // TODO performance improvement here - InstanceIdentifier iiL = (InstanceIdentifier) iid; + DataObjectIdentifier iiL = (DataObjectIdentifier) iid; links.removeLocally(iiL); - linkRemoved((InstanceIdentifier) iid, (Link) rootNode.getDataBefore()); + linkRemoved((DataObjectIdentifier) iid, (Link) rootNode.getDataBefore()); } } - public void packetReceived(Addresses addrs, InstanceIdentifier ii) { - InstanceIdentifier iinc = ii.firstIdentifierOf(NodeConnector.class); - InstanceIdentifier iin = - ii.firstIdentifierOf(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class); + public void packetReceived(Addresses addrs, DataObjectIdentifier ii) { + + DataObjectIdentifier iinc = firstIdentifierOf(ii,NodeConnector.class); + DataObjectIdentifier iin = firstIdentifierOf(ii,org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class); FluentFuture> futureNodeConnector; FluentFuture> futureNode; + org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node>> futureNode; try (ReadTransaction readTx = dataService.newReadOnlyTransaction()) { futureNodeConnector = readTx.read(LogicalDatastoreType.OPERATIONAL, iinc); futureNode = readTx.read(LogicalDatastoreType.OPERATIONAL, iin); @@ -343,7 +317,7 @@ private void removeNodeConnectorFromHost(List hostsToMod, List hosts } } - private void linkRemoved(InstanceIdentifier iiLink, Link linkRemoved) { + private void linkRemoved(DataObjectIdentifier iiLink, Link linkRemoved) { LOG.trace("linkRemoved"); List hostsToMod = new ArrayList<>(); List hostsToRem = new ArrayList<>(); diff --git a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/OperationProcessor.java b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/OperationProcessor.java index 98d592758..d9bb7ee38 100644 --- a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/OperationProcessor.java +++ b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/OperationProcessor.java @@ -8,24 +8,18 @@ package org.opendaylight.l2switch.hosttracker.plugin.internal; import static java.util.Objects.requireNonNull; - import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.MoreExecutors; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicReference; -import org.opendaylight.mdsal.binding.api.DataBroker; -import org.opendaylight.mdsal.binding.api.ReadWriteTransaction; -import org.opendaylight.mdsal.binding.api.Transaction; -import org.opendaylight.mdsal.binding.api.TransactionChain; -import org.opendaylight.mdsal.binding.api.TransactionChainListener; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.opendaylight.mdsal.binding.api.*; import org.opendaylight.mdsal.common.api.OptimisticLockFailedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class OperationProcessor implements AutoCloseable, Runnable, TransactionChainListener { +public class OperationProcessor implements AutoCloseable, Runnable { private static final Logger LOG = LoggerFactory.getLogger(OperationProcessor.class); private static final int NUM_RETRY_SUBMIT = 2; private static final int OPS_PER_CHAIN = 256; @@ -33,22 +27,12 @@ public class OperationProcessor implements AutoCloseable, Runnable, TransactionC private final BlockingQueue queue = new LinkedBlockingQueue<>(QUEUE_DEPTH); private final DataBroker dataBroker; - private final AtomicReference transactionChain; + private final TransactionFactory transactionChain; @SuppressFBWarnings("MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR") OperationProcessor(final DataBroker dataBroker) { this.dataBroker = requireNonNull(dataBroker); - this.transactionChain = new AtomicReference<>(dataBroker.createTransactionChain(this)); - } - - @Override - public void onTransactionChainFailed(TransactionChain chain, Transaction transaction, Throwable cause) { - chainFailure(); - } - - @Override - public void onTransactionChainSuccessful(TransactionChain chain) { - // no-op + this.transactionChain = dataBroker.createTransactionChain(); } @Override @@ -57,20 +41,18 @@ public void run() { while (!done) { try { HostTrackerOperation op = queue.take(); - final TransactionChain txChain = transactionChain.get(); + final ReadWriteTransaction txChain = dataBroker.newReadWriteTransaction(); if (txChain == null) { break; } - - ReadWriteTransaction tx = txChain.newReadWriteTransaction(); int ops = 0; while (op != null && ops < OPS_PER_CHAIN) { - op.applyOperation(tx); + op.applyOperation(txChain); ops += 1; op = queue.poll(); } - submitTransaction(tx, NUM_RETRY_SUBMIT); + submitTransaction(txChain, NUM_RETRY_SUBMIT); } catch (InterruptedException e) { done = true; } @@ -78,26 +60,25 @@ public void run() { clearQueue(); } - @Override - public void close() { - final TransactionChain txChain = transactionChain.getAndSet(null); - if (txChain != null) { - txChain.close(); - } - } - - private void chainFailure() { - try { - final TransactionChain prevChain = transactionChain.getAndSet( - dataBroker.createTransactionChain(this)); - if (prevChain != null) { - prevChain.close(); - } - clearQueue(); - } catch (IllegalStateException e) { - LOG.warn("Failed to close chain", e); - } - } + /*@Override + public void close() { + final TransactionChain txChain = transactionChain.getAndSet(null); + if (txChain != null) { + txChain.close(); + } + }*/ + + /*private void chainFailure() { + try { + final TransactionChain prevChain = transactionChain.get(); + if (prevChain != null) { + prevChain.close(); + } + clearQueue(); + } catch (IllegalStateException e) { + LOG.warn("Failed to close chain", e); + } + }*/ public void enqueueOperation(HostTrackerOperation op) { try { @@ -124,13 +105,13 @@ public void onFailure(Throwable failure) { } else { LOG.warn("tx {} failed, out of retries", tx.getIdentifier()); // out of retries - chainFailure(); + //chainFailure(); } } else { // failed due to another type of // TransactionCommitFailedException. LOG.warn("tx {} failed: {}", tx.getIdentifier(), failure.getMessage()); - chainFailure(); + //chainFailure(); } } }, MoreExecutors.directExecutor()); @@ -142,4 +123,8 @@ private void clearQueue() { } } + @Override + public void close() throws Exception { + + } } diff --git a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/SimpleAddressObserver.java b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/SimpleAddressObserver.java index b1ae8a907..3167a266c 100644 --- a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/SimpleAddressObserver.java +++ b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/internal/SimpleAddressObserver.java @@ -32,8 +32,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ipv4.rev140528.ipv4.packet.received.packet.chain.packet.Ipv4Packet; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ipv6.rev140528.Ipv6PacketReceived; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ipv6.rev140528.ipv6.packet.received.packet.chain.packet.Ipv6Packet; +import org.opendaylight.yangtools.binding.BindingInstanceIdentifier; +import org.opendaylight.yangtools.binding.DataObject; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.concepts.Registration; -import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.Uint64; @@ -97,7 +99,7 @@ private record MatchedPacket(RawPacket raw, Ether .build(); } - InstanceIdentifier ingress() { + BindingInstanceIdentifier ingress() { return raw.getRawPacketFields().getIngress().getValue(); } } @@ -135,7 +137,7 @@ private void onArpPacketReceived(final ArpPacketReceived received) { final var addrs = matched.createAddresses( new IpAddress(new Ipv4Address(protocol.getSourceProtocolAddress()))); if (addrs != null) { - hostTrackerImpl.packetReceived(addrs, matched.ingress()); + hostTrackerImpl.packetReceived(addrs, (DataObjectIdentifier) matched.ingress()); } } @@ -153,7 +155,7 @@ private void onIpv4PacketReceived(final Ipv4PacketReceived received) { final var addrs = matched.createAddresses(new IpAddress(sourceIp)); if (addrs != null) { - hostTrackerImpl.packetReceived(addrs, matched.ingress()); + hostTrackerImpl.packetReceived(addrs, (DataObjectIdentifier) matched.ingress()); } } @@ -171,7 +173,7 @@ private void onIpv6PacketReceived(final Ipv6PacketReceived received) { final var addrs = matched.createAddresses(new IpAddress(sourceIp)); if (addrs != null) { - hostTrackerImpl.packetReceived(addrs, matched.ingress()); + hostTrackerImpl.packetReceived(addrs, (DataObjectIdentifier) matched.ingress()); } } } diff --git a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/inventory/Host.java b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/inventory/Host.java index bdf3a8703..04bad17ef 100644 --- a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/inventory/Host.java +++ b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/inventory/Host.java @@ -35,7 +35,8 @@ 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; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey; -import org.opendaylight.yangtools.yang.binding.util.BindingMap; + +import org.opendaylight.yangtools.binding.util.BindingMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/SimpleTaskRetryLooper.java b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/SimpleTaskRetryLooper.java new file mode 100644 index 000000000..0ed22ed1c --- /dev/null +++ b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/SimpleTaskRetryLooper.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015, 2017 Cisco Systems, Inc. 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.l2switch.hosttracker.plugin.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.Callable; + +/** + * Created by mirehak on 4/28/15. + */ +public class SimpleTaskRetryLooper { + + private static final Logger LOG = LoggerFactory.getLogger(SimpleTaskRetryLooper.class); + + private final long tick; + private final int maxRetries; + + /** + * It retries the task passed to its method loopUntilNoException a number of + * times (maxRetries). + * + * @param tick + * sleep between steps in miliseconds + * @param maxRetries + * retries limit + */ + public SimpleTaskRetryLooper(long tick, int maxRetries) { + this.tick = tick; + this.maxRetries = maxRetries; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + public T loopUntilNoException(Callable task) throws Exception { + T output = null; + + Exception taskException = null; + for (int i = 0; i < maxRetries; i++) { + taskException = null; + try { + output = task.call(); + break; + } catch (Exception exception) { + LOG.debug("looper step failed: {}", exception.getMessage()); + taskException = exception; + } + + try { + Thread.sleep(tick); + } catch (InterruptedException e) { + LOG.debug("interrupted", e); + Thread.currentThread().interrupt(); + } + } + + if (taskException != null) { + throw taskException; + } + + LOG.debug("looper step succeeded: {}", output); + return output; + } +} diff --git a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/TransactionChainManager.java b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/TransactionChainManager.java new file mode 100644 index 000000000..7ea082271 --- /dev/null +++ b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/TransactionChainManager.java @@ -0,0 +1,372 @@ +package org.opendaylight.l2switch.hosttracker.plugin.util; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.FluentFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.errorprone.annotations.concurrent.GuardedBy; +import org.checkerframework.checker.lock.qual.Holding; +import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.binding.api.ReadWriteTransaction; +import org.opendaylight.mdsal.binding.api.TransactionChain; +import org.opendaylight.mdsal.binding.api.TransactionChainClosedException; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.yangtools.binding.DataObject; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.Empty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Optional; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import static java.util.Objects.requireNonNull; + +public class TransactionChainManager implements AutoCloseable { + private static final Logger LOG = LoggerFactory.getLogger(TransactionChainManager.class); + private static final String CANNOT_WRITE_INTO_TRANSACTION = "Cannot write into transaction."; + + private final ReadWriteLock readWriteTransactionLock = new ReentrantReadWriteLock(); + private final Object txLock = new Object(); + private final DataBroker dataBroker; + private final String nodeId; + + @GuardedBy("txLock") + private ReadWriteTransaction writeTx; + @GuardedBy("txLock") + private TransactionChain transactionChain; + @GuardedBy("txLock") + private boolean submitIsEnabled; + @GuardedBy("txLock") + private FluentFuture lastSubmittedFuture = CommitInfo.emptyFluentFuture(); + @GuardedBy("txLock") + private TransactionChainManagerStatus transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING; + + private volatile boolean initCommit; + + public TransactionChainManager(final DataBroker dataBroker, final String nodeId) { + this.dataBroker = requireNonNull(dataBroker); + this.nodeId = requireNonNull(nodeId); + } + + @Holding("txLock") + private void createTxChain() { + final var prev = transactionChain; + final var next = dataBroker.createTransactionChain(); + transactionChain = next; + if (prev != null) { + prev.close(); + } + next.addCallback(new FutureCallback() { + @Override + public void onSuccess(final Empty result) { + // No-op + } + + @Override + public void onFailure(final Throwable cause) { + onTransactionChainFailed(next, cause); + } + }); + } + + public boolean initialSubmitWriteTransaction() { + enableSubmit(); + return submitTransaction(); + } + + /** + * Method change status for TxChainManager to WORKING and it has to make registration for this instance as + * a {@link TransactionChain} callback to provide possibility a make DS transactions. Call this method for MASTER + * role only. + */ + public void activateTransactionManager() { + LOG.debug("activateTransactionManager for node {} transaction submit is set to {}", nodeId, submitIsEnabled); + synchronized (txLock) { + if (TransactionChainManagerStatus.SLEEPING == transactionChainManagerStatus) { + Preconditions.checkState(transactionChain == null, "TxChainFactory survive last close."); + Preconditions.checkState(writeTx == null, "We have some unexpected WriteTransaction."); + transactionChainManagerStatus = TransactionChainManagerStatus.WORKING; + submitIsEnabled = false; + initCommit = true; + createTxChain(); + } + } + } + + /** + * Method change status for TxChainManger to SLEEPING and it unregisters this instance so it broke a possibility to + * write something to DS. Call this method for SLAVE only. + * + * @return Future competing when deactivation completes + */ + public FluentFuture deactivateTransactionManager() { + LOG.debug("deactivateTransactionManager for node {}", nodeId); + final FluentFuture future; + synchronized (txLock) { + if (TransactionChainManagerStatus.WORKING == transactionChainManagerStatus) { + transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING; + future = txChainShuttingDown(); + Preconditions.checkState(writeTx == null, "We have some unexpected WriteTransaction."); + future.addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + closeTransactionChain(); + } + + @Override + public void onFailure(final Throwable throwable) { + closeTransactionChain(); + } + }, MoreExecutors.directExecutor()); + } else { + // ignoring redundant deactivate invocation + future = CommitInfo.emptyFluentFuture(); + } + } + return future; + } + + private void closeTransactionChain() { + if (writeTx != null) { + writeTx.cancel(); + writeTx = null; + } + if (transactionChain != null) { + transactionChain.close(); + transactionChain = null; + } + } + + @GuardedBy("txLock") + public boolean submitTransaction() { + return submitTransaction(false); + } + + @GuardedBy("txLock") + @SuppressWarnings("checkstyle:IllegalCatch") + public boolean submitTransaction(final boolean doSync) { + synchronized (txLock) { + if (!submitIsEnabled) { + LOG.trace("transaction not committed - submit block issued"); + return false; + } + if (writeTx == null) { + LOG.trace("nothing to commit - submit returns true"); + return true; + } + Preconditions.checkState(TransactionChainManagerStatus.WORKING == transactionChainManagerStatus, + "we have here Uncompleted Transaction for node {} and we are not MASTER", + nodeId); + final FluentFuture submitFuture = writeTx.commit(); + lastSubmittedFuture = submitFuture; + writeTx = null; + + if (initCommit || doSync) { + try { + SimpleTaskRetryLooper looper = new SimpleTaskRetryLooper(500, 6); + looper.loopUntilNoException(() -> submitFuture.get(5L, TimeUnit.SECONDS)); + } catch (Exception ex) { + LOG.error("Exception during INITIAL({}) || doSync({}) transaction submitting for device {}", + initCommit, doSync, nodeId, ex); + return false; + } + initCommit = false; + return true; + } + + submitFuture.addCallback(new FutureCallback() { + @Override + public void onSuccess(final CommitInfo result) { + //NOOP + } + + @Override + public void onFailure(final Throwable throwable) { + if (throwable instanceof InterruptedException || throwable instanceof ExecutionException) { + LOG.error("Transaction commit failed. ", throwable); + } else if (throwable instanceof CancellationException) { + LOG.warn("Submit task was canceled"); + LOG.trace("Submit exception: ", throwable); + } else { + LOG.error("Exception during transaction submitting. ", throwable); + } + } + }, MoreExecutors.directExecutor()); + } + return true; + } + + public void addDeleteOperationToTxChain(final LogicalDatastoreType store, + final DataObjectIdentifier path) { + synchronized (txLock) { + ensureTransaction(); + if (writeTx == null) { + LOG.debug("WriteTx is null for node {}. Delete {} was not realized.", nodeId, path); + throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION); + } + + writeTx.delete(store, path); + } + } + + public void writeToTransaction(final LogicalDatastoreType store, + final DataObjectIdentifier path, + final T data, + final boolean createParents) { + synchronized (txLock) { + ensureTransaction(); + if (writeTx == null) { + LOG.debug("WriteTx is null for node {}. Write data for {} was not realized.", nodeId, path); + throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION); + } + + if (createParents) { + writeTx.mergeParentStructurePut(store, path, data); + } else { + writeTx.put(store, path, data); + } + } + } + + public void mergeToTransaction(final LogicalDatastoreType store, + final DataObjectIdentifier path, + final T data, + final boolean createParents) { + synchronized (txLock) { + ensureTransaction(); + if (writeTx == null) { + LOG.debug("WriteTx is null for node {}. Merge data for {} was not realized.", nodeId, path); + throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION); + } + + if (createParents) { + writeTx.mergeParentStructureMerge(store, path, data); + } else { + writeTx.merge(store, path, data); + } + } + } + + public ListenableFuture> + readFromTransaction(final LogicalDatastoreType store, final DataObjectIdentifier path) { + synchronized (txLock) { + ensureTransaction(); + if (writeTx == null) { + LOG.debug("WriteTx is null for node {}. Read data for {} was not realized.", nodeId, path); + throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION); + } + + return writeTx.read(store, path); + } + } + + @VisibleForTesting + void onTransactionChainFailed(final TransactionChain chain, final Throwable cause) { + synchronized (txLock) { + if (TransactionChainManagerStatus.WORKING == transactionChainManagerStatus + && chain.equals(transactionChain)) { + LOG.warn("Transaction chain failed, recreating chain due to ", cause); + closeTransactionChain(); + createTxChain(); + writeTx = null; + } + } + } + + @Holding("txLock") + private void ensureTransaction() { + if (writeTx == null && TransactionChainManagerStatus.WORKING == transactionChainManagerStatus + && transactionChain != null) { + writeTx = transactionChain.newReadWriteTransaction(); + } + } + + private void enableSubmit() { + synchronized (txLock) { + /* !!!IMPORTANT: never set true without transactionChain */ + submitIsEnabled = transactionChain != null; + } + } + + public FluentFuture shuttingDown() { + if (LOG.isDebugEnabled()) { + LOG.debug("TxManager is going SHUTTING_DOWN for node {}", nodeId); + } + synchronized (txLock) { + transactionChainManagerStatus = TransactionChainManagerStatus.SHUTTING_DOWN; + return txChainShuttingDown(); + } + } + + @GuardedBy("txLock") + private FluentFuture txChainShuttingDown() { + boolean wasSubmitEnabled = submitIsEnabled; + submitIsEnabled = false; + FluentFuture future; + + if (!wasSubmitEnabled || transactionChain == null) { + // stay with actual thread + future = CommitInfo.emptyFluentFuture(); + + if (writeTx != null) { + writeTx.cancel(); + writeTx = null; + } + } else if (writeTx == null) { + // hijack md-sal thread + future = lastSubmittedFuture; + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Submitting all transactions for Node {}", nodeId); + } + // hijack md-sal thread + future = writeTx.commit(); + writeTx = null; + } + + return future; + } + + @Override + public void close() { + if (LOG.isDebugEnabled()) { + LOG.debug("Setting transactionChainManagerStatus to SHUTTING_DOWN for {}", nodeId); + } + synchronized (txLock) { + closeTransactionChain(); + } + } + + private enum TransactionChainManagerStatus { + /** + * txChainManager is working - is active (MASTER). + */ + WORKING, + /** + * txChainManager is sleeping - is not active (SLAVE or default init value). + */ + SLEEPING, + /** + * txChainManager is trying to be closed - device disconnecting. + */ + SHUTTING_DOWN + } + + public void acquireWriteTransactionLock() { + readWriteTransactionLock.writeLock().lock(); + } + + public void releaseWriteTransactionLock() { + readWriteTransactionLock.writeLock().unlock(); + } + +} diff --git a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/Utilities.java b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/Utilities.java index 30e3c8cc5..7a6efb226 100644 --- a/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/Utilities.java +++ b/hosttracker/implementation/src/main/java/org/opendaylight/l2switch/hosttracker/plugin/util/Utilities.java @@ -9,6 +9,9 @@ import java.util.ArrayList; import java.util.List; + +import com.google.common.collect.ImmutableList; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yang.gen.v1.urn.opendaylight.host.tracker.rev140624.host.AttachmentPointsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.host.tracker.rev140624.host.AttachmentPointsKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; @@ -26,6 +29,9 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey; 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.NodeKey; +import org.opendaylight.yangtools.binding.DataObject; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.binding.ExactDataObjectStep; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; public final class Utilities { @@ -53,8 +59,8 @@ public static List createLinks(NodeId srcNId, TpId srcTpId, NodeId dstNId, return links; } - public static InstanceIdentifier buildNodeIID(NodeKey nk, String topologyId) { - return InstanceIdentifier.builder(NetworkTopology.class) + public static DataObjectIdentifier buildNodeIID(NodeKey nk, String topologyId) { + return DataObjectIdentifier.builder(NetworkTopology.class) .child(Topology.class, new TopologyKey(new TopologyId(topologyId))) .child(Node.class, nk).build(); } @@ -72,4 +78,36 @@ public static AttachmentPointsBuilder createAPsfromNodeConnector(NodeConnector n public static AttachmentPointsBuilder createAPsfromTP(TpId tpId) { return new AttachmentPointsBuilder().setTpId(tpId).withKey(new AttachmentPointsKey(tpId)); } + + public static DataObjectIdentifier firstIdentifierOfN( DataObjectIdentifier identifier, Class type) { + List > collected = new ArrayList<>(); + for (ExactDataObjectStep step : identifier.steps()) { + collected.add(step); + if (type.equals(step.type())) { + @SuppressWarnings("unchecked") + DataObjectIdentifier result = (DataObjectIdentifier) DataObjectIdentifier.ofUnsafeSteps(List.copyOf(collected)); + return result; + } + } + return null; + } + public static DataObjectIdentifier firstIdentifierOf(@NonNull final DataObjectIdentifier sourceId, + @NonNull final Class targetType) { + final List> newSteps = new ArrayList<>(); + boolean found = false; + for (final ExactDataObjectStep step : sourceId.steps()) { + newSteps.add(step); + if (targetType.equals(step.type())) { + found = true; + break; // Found the target, stop iterating. + } + } + if (!found) { + return null; + } + @SuppressWarnings("unchecked") + final DataObjectIdentifier result = (DataObjectIdentifier) DataObjectIdentifier.ofUnsafeSteps( + ImmutableList.copyOf(newSteps)); + return result; + } } From c3fb110b4d6ad2cde316aee957add98aeef70f7a Mon Sep 17 00:00:00 2001 From: i-am-arathypm Date: Fri, 11 Jul 2025 10:10:14 +0530 Subject: [PATCH 13/15] l2switch main fixed --- .../l2switch/flow/FlowWriterServiceImplTest.java | 5 +++-- .../l2switch/flow/InventoryReaderTest.java | 7 ++++--- .../l2switch/flow/ReactiveFlowWriterTest.java | 9 +++++---- .../l2switch/util/InstanceIdentifierUtilsTest.java | 12 ++++++------ 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/FlowWriterServiceImplTest.java b/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/FlowWriterServiceImplTest.java index 8ad6cba7d..a3c91c91a 100644 --- a/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/FlowWriterServiceImplTest.java +++ b/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/FlowWriterServiceImplTest.java @@ -26,13 +26,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; 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.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; public class FlowWriterServiceImplTest { @Mock private AddFlow addFlow; private FlowWriterServiceImpl flowWriterService; - private InstanceIdentifier nodeConnectorInstanceIdentifier; + private DataObjectIdentifier nodeConnectorInstanceIdentifier; private NodeConnectorRef nodeConnectorRef; @Before @@ -43,7 +44,7 @@ public void initMocks() { @Test public void addMacToMacFlowTest() { - nodeConnectorInstanceIdentifier = InstanceIdentifier.builder(Nodes.class) + nodeConnectorInstanceIdentifier = DataObjectIdentifier.builder(Nodes.class) .child(Node.class, new NodeKey(new NodeId("node-id"))) .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId("nodeconnector-id"))).build(); nodeConnectorRef = new NodeConnectorRef(nodeConnectorInstanceIdentifier); diff --git a/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/InventoryReaderTest.java b/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/InventoryReaderTest.java index e574e0532..5e368cc20 100644 --- a/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/InventoryReaderTest.java +++ b/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/InventoryReaderTest.java @@ -40,9 +40,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatus; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnectorBuilder; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; +import org.opendaylight.yangtools.binding.util.BindingMap; import org.opendaylight.yangtools.util.concurrent.FluentFutures; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.binding.util.BindingMap; import org.opendaylight.yangtools.yang.common.Uint64; public class InventoryReaderTest { @@ -56,7 +57,7 @@ public class InventoryReaderTest { private Node node; private InventoryReader inventoryReader; - private InstanceIdentifier nodeInstanceIdentifier; + private DataObjectIdentifier nodeInstanceIdentifier; @Before public void init() { @@ -67,7 +68,7 @@ public void init() { @Test public void getNodeConnectorTest() throws Exception { - nodeInstanceIdentifier = InstanceIdentifier.builder(Nodes.class) + nodeInstanceIdentifier = DataObjectIdentifier.builder(Nodes.class) .child(Node.class, new NodeKey(new NodeId("node-id"))).build(); when(dataService.newReadOnlyTransaction()).thenReturn(readOnlyTransaction); when(readOnlyTransaction.read(any(LogicalDatastoreType.class), any(InstanceIdentifier.class))) diff --git a/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/ReactiveFlowWriterTest.java b/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/ReactiveFlowWriterTest.java index 3b12d8610..4366050ff 100644 --- a/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/ReactiveFlowWriterTest.java +++ b/l2switch-main/src/test/java/org/opendaylight/l2switch/flow/ReactiveFlowWriterTest.java @@ -31,6 +31,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacketBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.RawPacketFieldsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacketBuilder; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; public class ReactiveFlowWriterTest { @@ -42,7 +43,7 @@ public class ReactiveFlowWriterTest { @Mock private NodeConnectorRef destNodeConnectorRef; private ReactiveFlowWriter reactiveFlowWriter; - private InstanceIdentifier nodeInstanceIdentifier; + private DataObjectIdentifier nodeInstanceIdentifier; private NodeConnectorRef nodeConnectorRef; private ArrayList packetChainList; @@ -54,7 +55,7 @@ public void initMocks() { MockitoAnnotations.initMocks(this); reactiveFlowWriter = new ReactiveFlowWriter(inventoryReader, flowWriterService); - nodeInstanceIdentifier = InstanceIdentifier.builder(Nodes.class).child(Node.class).build(); + nodeInstanceIdentifier = DataObjectIdentifier.builder(Nodes.class).child(Node.class).build(); nodeConnectorRef = new NodeConnectorRef(nodeInstanceIdentifier); packetChainList = new ArrayList<>(); packetChainList.add(new PacketChainBuilder() @@ -81,12 +82,12 @@ public void onArpPacketReceivedTest() { @Test public void writeFlowsTest() { - when(inventoryReader.getNodeConnector(any(InstanceIdentifier.class), any(MacAddress.class))) + when(inventoryReader.getNodeConnector(any(DataObjectIdentifier.class), any(MacAddress.class))) .thenReturn(destNodeConnectorRef); reactiveFlowWriter.writeFlows(nodeConnectorRef, new MacAddress("00:00:00:00:00:01"), new MacAddress("00:00:00:00:00:02")); - verify(inventoryReader, times(1)).getNodeConnector(any(InstanceIdentifier.class), any(MacAddress.class)); + verify(inventoryReader, times(1)).getNodeConnector(any(DataObjectIdentifier.class), any(MacAddress.class)); verify(flowWriterService, times(1)).addBidirectionalMacToMacFlows(any(MacAddress.class), any(NodeConnectorRef.class), any(MacAddress.class), any(NodeConnectorRef.class)); diff --git a/l2switch-main/src/test/java/org/opendaylight/l2switch/util/InstanceIdentifierUtilsTest.java b/l2switch-main/src/test/java/org/opendaylight/l2switch/util/InstanceIdentifierUtilsTest.java index 5d5207215..d4968997f 100644 --- a/l2switch-main/src/test/java/org/opendaylight/l2switch/util/InstanceIdentifierUtilsTest.java +++ b/l2switch-main/src/test/java/org/opendaylight/l2switch/util/InstanceIdentifierUtilsTest.java @@ -24,7 +24,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; 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.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.yang.common.Uint8; public class InstanceIdentifierUtilsTest { @@ -34,15 +34,15 @@ public class InstanceIdentifierUtilsTest { @Test public void testGenerateNodeInstanceIdentifier() throws Exception { NodeConnectorRef ncRef = new NodeConnectorRef( - InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); + DataObjectIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); assertNotNull(InstanceIdentifierUtils.generateNodeInstanceIdentifier(ncRef)); } @Test public void testGenerateFlowTableInstanceIdentifier() throws Exception { NodeConnectorRef ncRef = new NodeConnectorRef( - InstanceIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); - InstanceIdentifier
tableInsId = InstanceIdentifierUtils.generateFlowTableInstanceIdentifier(ncRef, + DataObjectIdentifier.builder(Nodes.class).child(Node.class).child(NodeConnector.class).build()); + DataObjectIdentifier
tableInsId = InstanceIdentifierUtils.generateFlowTableInstanceIdentifier(ncRef, new TableKey(NUM_ID_1)); assertNotNull(tableInsId); assertEquals(NUM_ID_1, tableInsId.firstKeyOf(Table.class).getId()); @@ -51,9 +51,9 @@ public void testGenerateFlowTableInstanceIdentifier() throws Exception { @Test public void testGenerateFlowInstanceIdentifier() throws Exception { NodeConnectorRef ncRef = new NodeConnectorRef( - InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId(STR_ID_1))) + DataObjectIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId(STR_ID_1))) .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(STR_ID_1))).build()); - InstanceIdentifier flowInsId = InstanceIdentifierUtils.generateFlowInstanceIdentifier(ncRef, + DataObjectIdentifier flowInsId = InstanceIdentifierUtils.generateFlowInstanceIdentifier(ncRef, new TableKey(NUM_ID_1), new FlowKey(new FlowId(STR_ID_1))); assertNotNull(flowInsId); assertEquals(NUM_ID_1, flowInsId.firstKeyOf(Table.class).getId()); From e5a8d9a7d257fbbc8b04685b199ea899d7e115c7 Mon Sep 17 00:00:00 2001 From: i-am-arathypm Date: Fri, 11 Jul 2025 11:02:32 +0530 Subject: [PATCH 14/15] package changed according to the new mdsal --- addresstracker/model/pom.xml | 2 +- .../main/resources/org/opendaylight/blueprint/arp-handler.xml | 2 +- hosttracker/model/pom.xml | 2 +- .../main/resources/org/opendaylight/blueprint/l2switch-impl.xml | 2 +- .../main/resources/org/opendaylight/blueprint/loop-remover.xml | 2 +- loopremover/model/pom.xml | 2 +- packethandler/model/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/addresstracker/model/pom.xml b/addresstracker/model/pom.xml index 502291fe0..7e6c5b5fe 100644 --- a/addresstracker/model/pom.xml +++ b/addresstracker/model/pom.xml @@ -39,7 +39,7 @@ ${project.groupId}.${project.artifactId} - org.opendaylight.yangtools.yang.binding.annotations, * + * diff --git a/arphandler/src/main/resources/org/opendaylight/blueprint/arp-handler.xml b/arphandler/src/main/resources/org/opendaylight/blueprint/arp-handler.xml index 9d436d616..69daa7d69 100644 --- a/arphandler/src/main/resources/org/opendaylight/blueprint/arp-handler.xml +++ b/arphandler/src/main/resources/org/opendaylight/blueprint/arp-handler.xml @@ -6,7 +6,7 @@ + interface="org.opendaylight.mdsal.binding.api.RpcService" /> ${project.groupId}.${project.artifactId} - org.opendaylight.yangtools.yang.binding.annotations, * + * diff --git a/l2switch-main/src/main/resources/org/opendaylight/blueprint/l2switch-impl.xml b/l2switch-main/src/main/resources/org/opendaylight/blueprint/l2switch-impl.xml index b9472af2b..8868775cc 100644 --- a/l2switch-main/src/main/resources/org/opendaylight/blueprint/l2switch-impl.xml +++ b/l2switch-main/src/main/resources/org/opendaylight/blueprint/l2switch-impl.xml @@ -7,7 +7,7 @@ + interface="org.opendaylight.mdsal.binding.api.RpcService" /> diff --git a/loopremover/implementation/src/main/resources/org/opendaylight/blueprint/loop-remover.xml b/loopremover/implementation/src/main/resources/org/opendaylight/blueprint/loop-remover.xml index 222a2540b..f6c6dcb4b 100644 --- a/loopremover/implementation/src/main/resources/org/opendaylight/blueprint/loop-remover.xml +++ b/loopremover/implementation/src/main/resources/org/opendaylight/blueprint/loop-remover.xml @@ -5,7 +5,7 @@ + interface="org.opendaylight.mdsal.binding.api.RpcService" /> diff --git a/loopremover/model/pom.xml b/loopremover/model/pom.xml index 6b5e4b219..51ad1f80b 100644 --- a/loopremover/model/pom.xml +++ b/loopremover/model/pom.xml @@ -35,7 +35,7 @@ ${project.groupId}.${project.artifactId} - org.opendaylight.yangtools.yang.binding.annotations, * + * diff --git a/packethandler/model/pom.xml b/packethandler/model/pom.xml index 832ca8f17..fc597c94c 100644 --- a/packethandler/model/pom.xml +++ b/packethandler/model/pom.xml @@ -31,7 +31,7 @@ ${project.groupId}.${project.artifactId} - org.opendaylight.yangtools.yang.binding.annotations, * + * From c60e1db6b96b06e68485922b3025db8e7fcde45c Mon Sep 17 00:00:00 2001 From: abhijith-css Date: Thu, 7 Aug 2025 14:05:57 +0530 Subject: [PATCH 15/15] package change --- arphandler/pom.xml | 9 ++++++--- .../l2switch/arphandler/core/PacketDispatcher.java | 4 +--- hosttracker/implementation/pom.xml | 5 ----- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/arphandler/pom.xml b/arphandler/pom.xml index 569187408..4bd34b4b9 100644 --- a/arphandler/pom.xml +++ b/arphandler/pom.xml @@ -57,11 +57,14 @@ yang-common - org.opendaylight.mdsal - yang-binding - 13.0.8 + org.opendaylight.yangtools + binding-spec test + + org.opendaylight.yangtools + binding-spec + diff --git a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java index 25c76e1d2..57afda384 100644 --- a/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java +++ b/arphandler/src/main/java/org/opendaylight/l2switch/arphandler/core/PacketDispatcher.java @@ -21,10 +21,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder; import org.opendaylight.yangtools.binding.BindingInstanceIdentifier; -import org.opendaylight.yangtools.binding.BindingInstanceIdentifier.Step; import org.opendaylight.yangtools.binding.DataObjectIdentifier; import org.opendaylight.yangtools.binding.KeyStep; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -116,7 +114,7 @@ public void floodPacket(String nodeId, byte[] payload, NodeConnectorRef origIngr private String extractNodeConnectorId(NodeConnectorRef ref) { BindingInstanceIdentifier instanceId = ref.getValue(); - for (Step step : instanceId.steps()) { + for (BindingInstanceIdentifier.Step step : instanceId.steps()) { if (step instanceof KeyStep keyStep && keyStep.key() instanceof NodeConnectorKey) { return ((NodeConnectorKey) keyStep.key()).getId().getValue(); } diff --git a/hosttracker/implementation/pom.xml b/hosttracker/implementation/pom.xml index 4083a126a..a8d615f56 100644 --- a/hosttracker/implementation/pom.xml +++ b/hosttracker/implementation/pom.xml @@ -17,11 +17,6 @@ org.opendaylight.yangtools yang-common - org.opendaylight.mdsal mdsal-binding-api