This is an active cache, for the purpose of exposing stuff through CLI.
Refactor it to an interface (DpnTracker) and implementation and activate
it via OSGi DS.
JIRA: OPNFLWPLUG-1112
Change-Id: I20594e4f64dab2d688fe90936844da1793ae43da
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
<packaging>bundle</packaging>
<dependencies>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.guicedee.services</groupId>
+ <artifactId>javax.inject</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.annotation</groupId>
+ <artifactId>jakarta.annotation-api</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.opendaylight.mdsal</groupId>
<artifactId>mdsal-binding-api</artifactId>
<groupId>org.opendaylight.openflowplugin.applications</groupId>
<artifactId>forwardingrules-manager</artifactId>
</dependency>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.karaf.shell</groupId>
- <artifactId>org.apache.karaf.shell.console</artifactId>
- <scope>provided</scope>
- </dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.framework</artifactId>
</dependency>
<dependency>
- <groupId>com.guicedee.services</groupId>
- <artifactId>javax.inject</artifactId>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>jakarta.annotation</groupId>
- <artifactId>jakarta.annotation-api</artifactId>
- <optional>true</optional>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.service.component.annotations</artifactId>
</dependency>
<dependency>
*/
package org.opendaylight.openflowplugin.applications.southboundcli;
-import static java.util.Objects.requireNonNull;
-
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
import org.opendaylight.mdsal.binding.api.DataBroker;
-import org.opendaylight.mdsal.binding.api.DataObjectModification;
import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
import org.opendaylight.mdsal.binding.api.DataTreeModification;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.applications.southboundcli.util.OFNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
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.yang.binding.InstanceIdentifier;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class NodeListener implements ClusteredDataTreeChangeListener<FlowCapableNode>, AutoCloseable {
- private static final Logger LOG = LoggerFactory.getLogger(NodeListener.class);
+@Singleton
+@Component(service = DpnTracker.class)
+public final class DefaultDpnTracker
+ implements DpnTracker, ClusteredDataTreeChangeListener<FlowCapableNode>, AutoCloseable {
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultDpnTracker.class);
public static final String DEFAULT_DPN_NAME = "UNKNOWN";
public static final String SEPARATOR = ":";
- private final Map<Long, String> dpnIdToNameCache = new ConcurrentHashMap<>();
- private final DataBroker dataBroker;
- private ListenerRegistration<?> listenerReg;
+ private final Map<Long, String> dpnIdToNameCache = new HashMap<>();
+ private final ListenerRegistration<?> listenerReg;
- public NodeListener(final DataBroker broker) {
- dataBroker = broker;
+ @Inject
+ @Activate
+ public DefaultDpnTracker(@Reference final DataBroker dataBroker) {
+ listenerReg = dataBroker.registerDataTreeChangeListener(
+ DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL,
+ InstanceIdentifier.create(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class)), this);
}
- public void start() {
- final InstanceIdentifier<FlowCapableNode> path = InstanceIdentifier.create(Nodes.class).child(Node.class)
- .augmentation(FlowCapableNode.class);
- final DataTreeIdentifier<FlowCapableNode> identifier =
- DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, path);
- listenerReg = dataBroker.registerDataTreeChangeListener(identifier, NodeListener.this);
+ @PreDestroy
+ @Deactivate
+ @Override
+ public void close() {
+ listenerReg.close();
}
@Override
- public void close() {
- if (listenerReg != null) {
- listenerReg.close();
+ public synchronized List<OFNode> currentNodes() {
+ final var dpnList = new ArrayList<OFNode>();
+ for (var entry : dpnIdToNameCache.entrySet()) {
+ final var dpn = new OFNode(entry.getKey(), entry.getValue());
+ dpnList.add(dpn);
+ LOG.trace("Added OFNode: {} to the list", dpn.getNodeId());
}
+ dpnList.sort(null);
+ return dpnList;
}
@Override
- public void onDataTreeChanged(final Collection<DataTreeModification<FlowCapableNode>> changes) {
- requireNonNull(changes, "Changes may not be null!");
- for (DataTreeModification<FlowCapableNode> change : changes) {
- final InstanceIdentifier<FlowCapableNode> key = change.getRootPath().getRootIdentifier();
- final DataObjectModification<FlowCapableNode> mod = change.getRootNode();
- final InstanceIdentifier<FlowCapableNode> nodeIdent = key.firstIdentifierOf(FlowCapableNode.class);
+ public synchronized void onDataTreeChanged(final Collection<DataTreeModification<FlowCapableNode>> changes) {
+ for (var change : changes) {
+ final var key = change.getRootPath().getRootIdentifier();
+ final var mod = change.getRootNode();
+ final var nodeIdent = key.firstIdentifierOf(FlowCapableNode.class);
switch (mod.getModificationType()) {
case DELETE:
remove(nodeIdent, mod.getDataBefore());
.getValue());
return;
}
- long dpnId = Long.parseLong(node[1]);
- dpnIdToNameCache.remove(dpnId);
+ dpnIdToNameCache.remove(Long.parseLong(node[1]));
}
private void update(final InstanceIdentifier<FlowCapableNode> instId,
}
dpnIdToNameCache.put(dpnId, dpnName);
}
-
- public Map<Long, String> getDpnIdToNameCache() {
- return dpnIdToNameCache;
- }
}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.openflowplugin.applications.southboundcli;
+
+import java.util.List;
+import org.opendaylight.openflowplugin.applications.southboundcli.util.OFNode;
+
+public interface DpnTracker {
+
+ List<OFNode> currentNodes();
+}
import org.opendaylight.openflowplugin.applications.frm.ForwardingRulesManager;
import org.opendaylight.openflowplugin.applications.southboundcli.alarm.NodeReconciliationAlarm;
import org.opendaylight.openflowplugin.applications.southboundcli.util.OFNode;
-import org.opendaylight.openflowplugin.applications.southboundcli.util.ShellUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
}
private final NodeReconciliationAlarm alarm = new NodeReconciliationAlarm();
- private final DataBroker broker;
- private final FlowNodeReconciliation flowNodeReconciliation;
- private final NodeListener nodeListener;
private final Map<String, ReconciliationState> reconciliationStates;
+ private final FlowNodeReconciliation flowNodeReconciliation;
+ private final DpnTracker dpnTracker;
+ private final DataBroker broker;
private ExecutorService executor = Executors.newWorkStealingPool(10);
private boolean unregister;
public ReconciliationServiceImpl(final DataBroker broker, final ForwardingRulesManager frm,
- final NodeListener nodeListener, final FlowGroupCacheManager flowGroupCacheManager) {
+ final DpnTracker dpnTracker, final FlowGroupCacheManager flowGroupCacheManager) {
this.broker = requireNonNull(broker);
flowNodeReconciliation = frm.getFlowNodeReconciliation();
- this.nodeListener = requireNonNull(nodeListener);
+ this.dpnTracker = requireNonNull(dpnTracker);
reconciliationStates = flowGroupCacheManager.getReconciliationStates();
unregister = false;
}
private List<Long> getAllNodes() {
- List<OFNode> nodeList = ShellUtil.getAllNodes(nodeListener);
- List<Long> nodes = nodeList.stream().distinct().map(OFNode::getNodeId).collect(Collectors.toList());
- return nodes;
+ return dpnTracker.currentNodes().stream().distinct().map(OFNode::getNodeId).collect(Collectors.toList());
}
/**
*/
package org.opendaylight.openflowplugin.applications.southboundcli.cli;
+import static java.util.Objects.requireNonNull;
+
import java.util.Formatter;
-import java.util.List;
import org.apache.felix.gogo.commands.Command;
import org.apache.karaf.shell.console.OsgiCommandSupport;
-import org.opendaylight.openflowplugin.applications.southboundcli.NodeListener;
+import org.opendaylight.openflowplugin.applications.southboundcli.DpnTracker;
import org.opendaylight.openflowplugin.applications.southboundcli.util.OFNode;
-import org.opendaylight.openflowplugin.applications.southboundcli.util.ShellUtil;
@Command(scope = "openflow", name = "getallnodes", description = "Print all nodes from the operational datastore")
public class GetAllNodesCommandProvider extends OsgiCommandSupport {
- private NodeListener nodeListener;
+ private final DpnTracker dpnTracker;
- public void setNodeListener(final NodeListener nodeListener) {
- this.nodeListener = nodeListener;
+ public GetAllNodesCommandProvider(final DpnTracker dpnTracker) {
+ this.dpnTracker = requireNonNull(dpnTracker);
}
@SuppressWarnings("checkstyle:RegexpSinglelineJava")
@Override
protected Object doExecute() throws Exception {
- List<OFNode> ofNodeList = ShellUtil.getAllNodes(nodeListener);
+ final var ofNodeList = dpnTracker.currentNodes();
if (ofNodeList.isEmpty()) {
System.out.println("No node is connected yet");
- } else {
- StringBuilder stringBuilder = new StringBuilder();
- Formatter formatter = new Formatter(stringBuilder);
+ return null;
+ }
+
+ final var stringBuilder = new StringBuilder();
+ try (var formatter = new Formatter(stringBuilder)) {
System.out.println("Number of nodes: " + ofNodeList.size());
System.out.println(getAllLocalNodesHeaderOutput());
System.out.println("--------------------------------------------------------------------------");
for (OFNode ofNode : ofNodeList) {
System.out.println(formatter.format("%-15s %3s %-15s %n",
- ofNode.getNodeId(), "", ofNode.getNodeName()).toString());
+ ofNode.getNodeId(), "", ofNode.getNodeName()).toString());
stringBuilder.setLength(0);
}
- formatter.close();
}
return null;
}
private static String getAllLocalNodesHeaderOutput() {
- Formatter formatter = new Formatter();
- String header = formatter.format("%-15s %3s %-15s", "NodeId", "", "NodeName").toString();
- formatter.close();
- return header;
+ try (var formatter = new Formatter()) {
+ return formatter.format("%-15s %3s %-15s", "NodeId", "", "NodeName").toString();
+ }
}
}
\ No newline at end of file
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import org.opendaylight.mdsal.binding.api.DataBroker;
import org.opendaylight.mdsal.binding.api.ReadTransaction;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
-import org.opendaylight.openflowplugin.applications.southboundcli.NodeListener;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
private ShellUtil() {
}
- public static List<OFNode> getAllNodes(final NodeListener nodeListener) {
- List<OFNode> dpnList = new ArrayList<>();
- for (Map.Entry<Long, String> entry : nodeListener.getDpnIdToNameCache().entrySet()) {
- OFNode dpn = new OFNode(entry.getKey(), entry.getValue());
- dpnList.add(dpn);
- LOG.trace("Added OFNode: {} to the list", dpn.getNodeId());
- }
- Collections.sort(dpnList);
- return dpnList;
- }
-
public static OFNode getNode(final long nodeId, final DataBroker broker) {
OFNode nodeInfo = getNodeInfo(nodeId, broker);
if (nodeInfo == null) {
<command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.0.0">
<command name="openflow/getallnodes">
<action class="org.opendaylight.openflowplugin.applications.southboundcli.cli.GetAllNodesCommandProvider">
- <property name="nodeListener" ref="nodeListener"/>
+ <argument ref="dpnTracker"/>
</action>
</command>
<command name="openflow/shownode">
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
- xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
- odl:use-default-for-reference-types="true">
-
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">
<reference id="dataBroker"
interface="org.opendaylight.mdsal.binding.api.DataBroker"/>
<reference id="flowCacheManager"
interface="org.opendaylight.openflowplugin.api.openflow.FlowGroupCacheManager"/>
<reference id="frm"
interface="org.opendaylight.openflowplugin.applications.frm.ForwardingRulesManager"/>
+ <reference id="dpnTracker"
+ interface="org.opendaylight.openflowplugin.applications.southboundcli.DpnTracker"/>
- <bean id="nodeListener"
- class="org.opendaylight.openflowplugin.applications.southboundcli.NodeListener"
- init-method="start"
- destroy-method="close">
- <argument ref="dataBroker"/>
- </bean>
<bean id="reconciliationService"
class="org.opendaylight.openflowplugin.applications.southboundcli.ReconciliationServiceImpl"
destroy-method="close">
<argument ref="dataBroker"/>
<argument ref="frm"/>
- <argument ref="nodeListener"/>
+ <argument ref="dpnTracker"/>
<argument ref="flowCacheManager"/>
</bean>
<odl:rpc-implementation ref="reconciliationService"/>
-
</blueprint>