import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.GuardedBy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Function;
import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
*
*/
final class ServerSessionManager implements SessionListenerFactory<PCEPSessionListener>, AutoCloseable {
- private static String createNodeId(final InetAddress addr) {
- return "pcc://" + addr.getHostAddress();
- }
-
private final class SessionListener implements PCEPSessionListener {
private final Map<SrpIdNumber, PCEPRequest> waitingRequests = new HashMap<>();
private final Map<SrpIdNumber, PCEPRequest> sendingRequests = new HashMap<>();
private boolean synced = false;
private PCEPSession session;
private long requestId = 1;
- private NodeId nodeId;
+ private TopologyNodeState nodeState;
private Node topologyNode(final DataModificationTransaction trans, final InetAddress address) {
final String pccId = createNodeId(address);
trans.putOperationalData(nti, ret);
this.ownsTopology = true;
this.topologyNode = nti;
- this.nodeId = id;
return ret;
}
}
});
- ServerSessionManager.this.nodes.put(this.nodeId, this);
+ this.nodeState = takeNodeState(topoNode.getNodeId(), this);
this.session = session;
LOG.info("Session with {} attached to topology node {}", session.getRemoteAddress(), topoNode.getNodeId());
}
@GuardedBy("this")
private void tearDown(final PCEPSession session) {
+ releaseNodeState(this.nodeState);
+ this.nodeState = null;
this.session = null;
- ServerSessionManager.this.nodes.remove(this.nodeId);
final DataModificationTransaction trans = ServerSessionManager.this.dataProvider.beginTransaction();
this.synced = true;
this.topologyAugmentBuilder.setPathComputationClient(this.pccBuilder.setStateSync(PccSyncState.Synchronized).build());
trans.putOperationalData(this.topologyAugment, this.topologyAugmentBuilder.build());
+
+ // The node has completed synchronization, cleanup metadata no longer reported back
+ this.nodeState.cleanupExcept(Collections2.transform(this.lsps.values(), new Function<SymbolicPathName, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.SymbolicPathName>() {
+ @Override
+ public org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.SymbolicPathName apply(final SymbolicPathName input) {
+ return input.getPathName();
+ }
+ }));
LOG.debug("Session {} achieved synchronized state", session);
return;
}
if (lsp.isRemove()) {
final SymbolicPathName name = this.lsps.remove(id);
if (name != null) {
+ this.nodeState.removeLspMetadata(name.getPathName());
trans.removeOperationalData(lspIdentifier(new ReportedLspKey(name.getPathName())).build());
}
rlb.setKey(new ReportedLspKey(this.lsps.get(id).getPathName()));
// If this is an unsolicited update. We need to make sure we retain the metadata already present
- if (!solicited) {
- rlb.setMetadata((Metadata) trans.readOperationalData(lspIdentifier(rlb.getKey()).child(Metadata.class).build()));
+ if (solicited) {
+ nodeState.setLspMetadata(rlb.getName(), rlb.getMetadata());
+ } else {
+ rlb.setMetadata(nodeState.getLspMetadata(rlb.getName()));
}
trans.putOperationalData(lspIdentifier(rlb.getKey()).build(), rlb.build());
}
};
+ private static final long DEFAULT_HOLD_STATE_NANOS = TimeUnit.MINUTES.toNanos(5);
private static final OperationResult OPERATION_NOACK = createOperationResult(FailureType.NoAck);
private static final OperationResult OPERATION_SUCCESS = createOperationResult(null);
private static final OperationResult OPERATION_UNSENT = createOperationResult(FailureType.Unsent);
private final Map<NodeId, SessionListener> nodes = new HashMap<>();
+ private final Map<NodeId, TopologyNodeState> state = new HashMap<>();
private final InstanceIdentifier<Topology> topology;
private final DataProviderService dataProvider;
new TopologyBuilder().setKey(k).setTopologyId(k.getTopologyId()).setTopologyTypes(
new TopologyTypesBuilder().addAugmentation(TopologyTypes1.class,
new TopologyTypes1Builder().setTopologyPcep(new TopologyPcepBuilder().build()).build()).build()).setNode(
- new ArrayList<Node>()).build());
+ new ArrayList<Node>()).build());
Futures.addCallback(JdkFutureAdapters.listenInPoolThread(t.commit()), new FutureCallback<RpcResult<TransactionStatus>>() {
@Override
});
}
+ public void releaseNodeState(final TopologyNodeState nodeState) {
+ LOG.debug("Node {} unbound", nodeState.getNodeId());
+ nodes.remove(nodeState.getNodeId());
+ nodeState.released();
+ }
+
+ private synchronized TopologyNodeState takeNodeState(final NodeId id, final SessionListener sessionListener) {
+ LOG.debug("Node {} bound to listener {}", id, sessionListener);
+
+ TopologyNodeState ret = this.state.get(id);
+ if (ret == null) {
+ ret = new TopologyNodeState(id, DEFAULT_HOLD_STATE_NANOS);
+ this.state.put(id, ret);
+ }
+
+ this.nodes.put(id, sessionListener);
+ ret.taken();
+ return ret;
+ }
+
@Override
public PCEPSessionListener getSessionListener() {
return new SessionListener();
};
}
+ private static String createNodeId(final InetAddress addr) {
+ return "pcc://" + addr.getHostAddress();
+ }
+
synchronized ListenableFuture<OperationResult> realRemoveLsp(final RemoveLspArgs input) {
// Get the listener corresponding to the node
final SessionListener l = this.nodes.get(input.getNode());
--- /dev/null
+/*
+ * Copyright (c) 2013 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.bgpcep.pcep.topology.provider;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.SymbolicPathName;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.lsp.metadata.Metadata;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+
+import com.google.common.base.Preconditions;
+
+@ThreadSafe
+final class TopologyNodeState {
+ private final Map<SymbolicPathName, Metadata> metadata = new HashMap<>();
+ private final long holdStateNanos;
+ private final NodeId nodeId;
+ private long lastReleased = 0;
+
+ public TopologyNodeState(final NodeId nodeId, final long holdStateNanos) {
+ Preconditions.checkArgument(holdStateNanos >= 0);
+ this.nodeId = Preconditions.checkNotNull(nodeId);
+ this.holdStateNanos = holdStateNanos;
+ }
+
+ public NodeId getNodeId() {
+ return nodeId;
+ }
+
+ public synchronized Metadata getLspMetadata(final SymbolicPathName name) {
+ return metadata.get(name);
+ }
+
+ public synchronized void setLspMetadata(final SymbolicPathName name, final Metadata value) {
+ if (value == null) {
+ metadata.remove(name);
+ } else {
+ metadata.put(name, value);
+ }
+ }
+
+ public synchronized void removeLspMetadata(final SymbolicPathName name) {
+ metadata.remove(name);
+ }
+
+ public synchronized void cleanupExcept(final Collection<SymbolicPathName> values) {
+ final Iterator<SymbolicPathName> it = metadata.keySet().iterator();
+ while (it.hasNext()) {
+ if (!values.contains(it.next())) {
+ it.remove();
+ }
+ }
+ }
+
+ public synchronized void released() {
+ lastReleased = System.nanoTime();
+ }
+
+ public synchronized void taken() {
+ final long now = System.nanoTime();
+
+ if (now - lastReleased > holdStateNanos) {
+ metadata.clear();
+ }
+ }
+}
\ No newline at end of file