2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.bgpcep.pcep.topology.provider;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.util.concurrent.FluentFuture;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import com.google.common.util.concurrent.SettableFuture;
17 import io.netty.util.Timeout;
18 import java.lang.invoke.MethodHandles;
19 import java.lang.invoke.VarHandle;
20 import java.net.InetAddress;
21 import java.util.HashMap;
22 import java.util.List;
24 import java.util.concurrent.TimeUnit;
25 import org.checkerframework.checker.lock.qual.GuardedBy;
26 import org.eclipse.jdt.annotation.NonNull;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.opendaylight.bgpcep.pcep.server.PceServerProvider;
29 import org.opendaylight.mdsal.binding.api.WriteTransaction;
30 import org.opendaylight.mdsal.common.api.CommitInfo;
31 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
32 import org.opendaylight.protocol.pcep.PCEPSessionListenerFactory;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.graph.rev220720.graph.topology.GraphKey;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.SrpIdNumber;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.AddLspArgs;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.EnsureLspOperationalInput;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.OperationResult;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.RemoveLspArgs;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.TearDownSessionInput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.TearDownSessionInputBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.TopologyTypes1Builder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.TriggerSyncArgs;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.UpdateLspArgs;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.network.topology.topology.topology.types.TopologyPcepBuilder;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.TopologyTypesBuilder;
50 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
51 import org.opendaylight.yangtools.yang.common.ErrorType;
52 import org.opendaylight.yangtools.yang.common.RpcResult;
53 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
57 // Non-final for testing
58 class ServerSessionManager implements PCEPSessionListenerFactory, TopologySessionRPCs {
60 interface RpcTimeout {
61 void run(SrpIdNumber requestId);
64 private static final Logger LOG = LoggerFactory.getLogger(ServerSessionManager.class);
65 private static final long DEFAULT_HOLD_STATE_NANOS = TimeUnit.MINUTES.toNanos(5);
66 private static final VarHandle CLOSED;
70 CLOSED = MethodHandles.lookup().findVarHandle(ServerSessionManager.class, "closed", boolean.class);
71 } catch (NoSuchFieldException | IllegalAccessException e) {
72 throw new ExceptionInInitializerError(e);
76 private final @NonNull KeyedInstanceIdentifier<Topology, TopologyKey> topology;
77 private final @NonNull PCEPTopologyProviderDependencies dependencies;
78 private final @NonNull GraphKey graphKey;
81 private final Map<NodeId, TopologySessionListener> nodes = new HashMap<>();
83 private final Map<NodeId, TopologyNodeState> state = new HashMap<>();
85 private volatile long updateInterval;
86 private volatile short rpcTimeout;
87 private volatile boolean closed;
89 ServerSessionManager(final KeyedInstanceIdentifier<Topology, TopologyKey> topology,
90 final PCEPTopologyProviderDependencies dependencies, final GraphKey graphKey,
91 final short rpcTimeout, final long updateInterval) {
92 this.dependencies = requireNonNull(dependencies);
93 this.topology = requireNonNull(topology);
94 this.graphKey = requireNonNull(graphKey);
95 this.rpcTimeout = rpcTimeout;
96 this.updateInterval = updateInterval;
99 // Initialize the operational view of the topology.
100 final ListenableFuture<Boolean> start() {
101 LOG.info("Creating PCEP Topology {}", topologyId());
102 final var tx = dependencies.getDataBroker().newWriteOnlyTransaction();
103 tx.put(LogicalDatastoreType.OPERATIONAL, topology, new TopologyBuilder()
104 .withKey(topology.getKey())
105 .setTopologyTypes(new TopologyTypesBuilder()
106 .addAugmentation(new TopologyTypes1Builder().setTopologyPcep(new TopologyPcepBuilder().build()).build())
110 final var future = SettableFuture.<Boolean>create();
111 final var txFuture = tx.commit();
112 txFuture.addCallback(new FutureCallback<CommitInfo>() {
114 public void onSuccess(final CommitInfo result) {
115 LOG.info("PCEP Topology {} created successfully.", topologyId());
117 future.set(Boolean.TRUE);
121 public void onFailure(final Throwable failure) {
122 LOG.error("Failed to create PCEP Topology {}.", topologyId(), failure);
124 future.set(Boolean.FALSE);
126 }, MoreExecutors.directExecutor());
128 // Register this new topology to PCE Server
129 final PceServerProvider server = dependencies.getPceServerProvider();
130 if (server != null) {
131 server.registerPcepTopology(topology, graphKey);
136 final boolean isClosed() {
140 final synchronized FluentFuture<? extends CommitInfo> stop() {
141 if (!CLOSED.compareAndSet(this, false, true)) {
142 LOG.error("Session Manager has already been closed.");
143 return CommitInfo.emptyFluentFuture();
147 for (final TopologySessionListener node : nodes.values()) {
152 // Clean up remembered metadata
153 for (final TopologyNodeState topologyNodeState : state.values()) {
154 topologyNodeState.close();
158 // Un-Register Pcep Topology into PCE Server
159 final PceServerProvider server = dependencies.getPceServerProvider();
160 if (server != null) {
161 server.unRegisterPcepTopology(topology);
164 final WriteTransaction t = dependencies.getDataBroker().newWriteOnlyTransaction();
165 t.delete(LogicalDatastoreType.OPERATIONAL, topology);
166 final FluentFuture<? extends CommitInfo> future = t.commit();
167 future.addCallback(new FutureCallback<CommitInfo>() {
169 public void onSuccess(final CommitInfo result) {
170 LOG.debug("Topology {} removed", topology);
174 public void onFailure(final Throwable throwable) {
175 LOG.warn("Failed to remove Topology {}", topology, throwable);
177 }, MoreExecutors.directExecutor());
181 final synchronized void releaseNodeState(final TopologyNodeState nodeState, final InetAddress peerAddress,
182 final boolean persistNode) {
184 LOG.error("Session Manager has already been closed.");
187 final NodeId nodeId = createNodeId(peerAddress);
188 nodes.remove(nodeId);
189 state.remove(nodeId);
190 if (nodeState != null) {
191 LOG.debug("Node {} unbound", nodeState.getNodeId());
192 nodeState.released(persistNode);
196 final synchronized TopologyNodeState takeNodeState(final InetAddress address,
197 final TopologySessionListener sessionListener, final boolean retrieveNode) {
198 final NodeId id = createNodeId(address);
200 LOG.error("Server Session Manager is closed. Unable to create topology node {} with listener {}", id,
205 LOG.debug("Node {} requested by listener {}", id, sessionListener);
206 TopologyNodeState ret = state.get(id);
209 ret = new TopologyNodeState(dependencies.getDataBroker(), topology, id, DEFAULT_HOLD_STATE_NANOS);
210 LOG.debug("Created topology node {} for id {} at {}", ret, id, ret.getNodeId());
213 // if another listener requests the same session, close it
214 final TopologySessionListener existingSessionListener = nodes.get(id);
215 if (existingSessionListener != null && !sessionListener.equals(existingSessionListener)) {
216 LOG.error("New session listener {} is in conflict with existing session listener {} on node {},"
217 + " closing the existing one.", existingSessionListener, sessionListener, id);
218 existingSessionListener.close();
220 ret.taken(retrieveNode);
221 nodes.put(id, sessionListener);
222 LOG.debug("Node {} bound to listener {}", id, sessionListener);
226 // Non-final for testing
228 public PCEPTopologySessionListener getSessionListener() {
229 return new PCEPTopologySessionListener(dependencies.getStateRegistry(), this,
230 dependencies.getPceServerProvider());
233 private synchronized TopologySessionListener checkSessionPresence(final NodeId nodeId) {
234 // Get the listener corresponding to the node
235 final TopologySessionListener l = nodes.get(nodeId);
237 LOG.debug("Session for node {} not found", nodeId);
244 public final synchronized ListenableFuture<OperationResult> addLsp(final AddLspArgs input) {
245 final TopologySessionListener l = checkSessionPresence(input.getNode());
246 return l != null ? l.addLsp(input) : OperationResults.UNSENT.future();
250 public final synchronized ListenableFuture<OperationResult> removeLsp(final RemoveLspArgs input) {
251 final TopologySessionListener l = checkSessionPresence(input.getNode());
252 return l != null ? l.removeLsp(input) : OperationResults.UNSENT.future();
256 public final synchronized ListenableFuture<OperationResult> updateLsp(final UpdateLspArgs input) {
257 final TopologySessionListener l = checkSessionPresence(input.getNode());
258 return l != null ? l.updateLsp(input) : OperationResults.UNSENT.future();
262 public final synchronized ListenableFuture<OperationResult> ensureLspOperational(
263 final EnsureLspOperationalInput input) {
264 final TopologySessionListener l = checkSessionPresence(input.getNode());
265 return l != null ? l.ensureLspOperational(input) : OperationResults.UNSENT.future();
269 public final synchronized ListenableFuture<OperationResult> triggerSync(final TriggerSyncArgs input) {
270 final TopologySessionListener l = checkSessionPresence(input.getNode());
271 return l != null ? l.triggerSync(input) : OperationResults.UNSENT.future();
275 public final ListenableFuture<RpcResult<Void>> tearDownSession(final TearDownSessionInput input) {
276 final NodeId nodeId = input.getNode();
277 final TopologySessionListener listener = checkSessionPresence(nodeId);
278 if (listener != null) {
279 return listener.tearDownSession(input);
282 return RpcResultBuilder.<Void>failed()
283 .withError(ErrorType.RPC, "Failed to find session " + nodeId)
287 final @Nullable Timeout newRpcTimeout(final RpcTimeout task, final SrpIdNumber requestId) {
288 final short localTimeout = rpcTimeout;
289 return localTimeout <= 0 ? null
290 : dependencies.getTimer().newTimeout(ignored -> task.run(requestId), localTimeout, TimeUnit.SECONDS);
293 final long updateInterval() {
294 return isClosed() ? 0 : updateInterval;
297 final void setRpcTimeout(final short rpcTimeout) {
298 this.rpcTimeout = rpcTimeout;
301 final void setUpdateInterval(final long updateInterval) {
302 this.updateInterval = updateInterval;
305 final void tearDownSessions(final List<InetAddress> outdatedNodes) {
306 for (var address : outdatedNodes) {
307 tearDownSession(new TearDownSessionInputBuilder().setNode(createNodeId(address)).build());
311 final PCEPTopologyProviderDependencies getPCEPTopologyProviderDependencies() {
315 static @NonNull NodeId createNodeId(final InetAddress addr) {
316 return new NodeId("pcc://" + addr.getHostAddress());
319 private @NonNull String topologyId() {
320 return TopologyUtils.friendlyId(topology);