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.topology.provider.pcep;
10 import java.net.InetAddress;
11 import java.util.HashMap;
13 import java.util.concurrent.ExecutionException;
14 import java.util.concurrent.Future;
16 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
17 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
18 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
19 import org.opendaylight.protocol.framework.SessionListenerFactory;
20 import org.opendaylight.protocol.pcep.PCEPSession;
21 import org.opendaylight.protocol.pcep.PCEPSessionListener;
22 import org.opendaylight.protocol.pcep.PCEPTerminationReason;
23 import org.opendaylight.protocol.pcep.TerminationReason;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.pcep.rev131024.PccSyncState;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.pcep.rev131024.nodes.node.PathComputationClientBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.pcep.rev131024.nodes.node.path.computation.client.StatefulTlvBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.Pcerr;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev131007.PcerrBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.Message;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.PcrptMessage;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.PlspId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.lsp.object.tlvs.SymbolicPathName;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.object.Tlvs;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.open.object.tlvs.Stateful;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.pcerr.message.PcerrMessageBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.pcrpt.message.pcrpt.message.Reports;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.pcrpt.message.pcrpt.message.reports.Lsp;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.PccBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.pcc.Lsps;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.pcc.LspsKey;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.opendaylight.yangtools.yang.common.RpcResult;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 import com.google.common.base.Preconditions;
53 final class ServerSessionManager implements SessionListenerFactory<PCEPSessionListener> {
54 private static String createNodeId(final InetAddress addr) {
55 return "pcc://" + addr.getHostAddress();
58 private final class SessionListener implements PCEPSessionListener {
59 private org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node inventoryNode(final DataModificationTransaction trans, final InetAddress address) {
60 final String pccId = createNodeId(address);
62 // FIXME: after 0.6 yangtools, this cast should not be needed
63 final Nodes nodes = (Nodes)trans.readOperationalData(inventory);
65 for (final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node n : nodes.getNode()) {
66 LOG.debug("Matching inventory node {} to peer {}", n, address);
67 if (n.getId().getValue().equals(pccId)) {
71 // FIXME: locate the node by its management IP address
75 * We failed to find a matching node. Let's create a dynamic one
76 * to have a backer. Note that this node will be created in the
79 LOG.debug("Failed to find inventory node for peer {}, creating a new one at {}", address, pccId);
81 final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId id =
82 new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId(pccId);
83 final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey nk =
84 new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey(id);
85 final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> nii =
86 InstanceIdentifier.builder(inventory).node(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, nk).toInstance();
87 final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node ret =
88 new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder().setId(id).setKey(nk).build();
90 trans.putRuntimeData(nii, ret);
92 inventoryNodeId = nii;
96 final org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node topologyNode(
97 final DataModificationTransaction trans,
98 final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node invNode) {
99 // FIXME: after 0.6 yangtools, this cast should not be needed
100 final Topology topo = (Topology)trans.readOperationalData(topology);
102 for (final org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node n : topo.getNode()) {
103 LOG.debug("Matching topology node {} to inventory node {}", n, invNode);
104 if (n.getNodeId().getValue().equals(invNode.getId().getValue())) {
110 * We failed to find a matching node. Let's create a dynamic one
111 * and note that we are the owner (so we clean it up afterwards).
113 final org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId id =
114 new org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId(invNode.getId().getValue());
115 final org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey nk =
116 new org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey(id);
117 final InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node> nti =
118 InstanceIdentifier.builder(topology).node(
119 org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node.class, nk).toInstance();
121 final org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node ret =
122 new org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder().
123 setKey(nk).setNodeId(id).build();
125 trans.putRuntimeData(nti, ret);
127 topologyNodeId = nti;
132 public void onSessionUp(final PCEPSession session) {
134 * The session went up. Look up the router in Inventory model,
135 * create it if it is not there (marking that fact for later
136 * deletion), and mark it as synchronizing. Also create it in
137 * the topology model, with empty LSP list.
139 final InetAddress peerAddress = session.getRemoteAddress();
140 final DataModificationTransaction trans = dataProvider.beginTransaction();
142 final org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node invNode = inventoryNode(trans, peerAddress);
143 LOG.debug("Peer {} resolved to inventory node {}", peerAddress, invNode);
145 final org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node topoNode = topologyNode(trans, invNode);
146 LOG.debug("Peer {} resolved to topology node {}", peerAddress, topoNode);
148 // Our augmentation in the topology node
149 final PccBuilder pb = new PccBuilder();
151 final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.Node1 topoAugment =
152 new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.Node1Builder().setPcc(pb.build()).build();
153 topologyAugmentId = InstanceIdentifier.builder(topologyNodeId).node(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.Node1.class).toInstance();
154 trans.putRuntimeData(topologyAugmentId, topoAugment);
156 // Our augmentation in the inventory node
157 pccBuilder = new PathComputationClientBuilder();
159 final Tlvs tlvs = session.getRemoteTlvs();
160 final Stateful stateful = tlvs.getStateful();
161 if (stateful != null) {
162 // FIXME: rework once groupings can be used in builders
163 pccBuilder.setStatefulTlv(new StatefulTlvBuilder().setFlags(tlvs.getStateful().getFlags()).build());
164 pccBuilder.setStateSync(PccSyncState.InitialResync);
167 pccBuilder.setTopologyNode(topoNode.getNodeId());
169 inventoryAugmentBuilder = new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.pcep.rev131024.Node1Builder()
170 .setPathComputationClient(pccBuilder.build());
171 inventoryAugmentId = InstanceIdentifier.builder(inventoryNodeId).node(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.pcep.rev131024.Node1.class).toInstance();
172 trans.putRuntimeData(inventoryAugmentId, inventoryAugmentBuilder.build());
174 // All set, commit the modifications
175 final Future<RpcResult<TransactionStatus>> s = trans.commit();
178 * FIXME: once this Future is listenable, attach to it so we can
179 * do cleanup if the commit fails. For now we force a commit.
183 } catch (InterruptedException | ExecutionException e) {
184 LOG.error("Failed to update internal state for session {}, terminating it", session, e);
185 session.close(TerminationReason.Unknown);
188 LOG.info("Session with {} attached to inventory node {} and topology node {}", session.getRemoteAddress(), invNode.getId(), topoNode.getNodeId());
191 private void tearDown(final PCEPSession session) {
192 final DataModificationTransaction trans = dataProvider.beginTransaction();
195 * The session went down. Undo all the Inventory and Topology
196 * changes we have done.
198 trans.removeRuntimeData(inventoryAugmentId);
200 trans.removeRuntimeData(inventoryNodeId);
202 trans.removeRuntimeData(topologyAugmentId);
204 trans.removeRuntimeData(topologyNodeId);
208 * FIXME: once this Future is listenable, attach to it so we can
209 * do cleanup if the commit fails. For now we force a commit.
211 final Future<RpcResult<TransactionStatus>> s = trans.commit();
214 } catch (InterruptedException | ExecutionException e) {
215 LOG.error("Failed to cleanup internal state for session {}", session, e);
220 public void onSessionDown(final PCEPSession session, final Exception e) {
221 LOG.warn("Session {} went down unexpectedly", e);
226 public void onSessionTerminated(final PCEPSession session, final PCEPTerminationReason reason) {
227 LOG.info("Session {} terminated by peer with reason {}", session, reason);
231 private InstanceIdentifier<Lsps> lspIdentifier(final SymbolicPathName name) {
232 return InstanceIdentifier.builder(topologyAugmentId).
233 node(Lsps.class, new LspsKey(name.getPathName())).toInstance();
237 public void onMessage(final PCEPSession session, final Message message) {
238 if (!(message instanceof PcrptMessage)) {
239 LOG.info("Unhandled message {} on session {}", message, session);
240 session.sendMessage(unhandledMessageError);
243 final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.pcrpt.message.PcrptMessage rpt =
244 ((PcrptMessage)message).getPcrptMessage();
246 final DataModificationTransaction trans = dataProvider.beginTransaction();
248 for (final Reports r : rpt.getReports()) {
249 final Lsp lsp = r.getLsp();
251 if (lsp.isSync() && !synced) {
252 // Update synchronization flag
254 inventoryAugmentBuilder.setPathComputationClient(pccBuilder.setStateSync(PccSyncState.Synchronized).build());
255 trans.putRuntimeData(inventoryAugmentId, inventoryAugmentBuilder.build());
256 LOG.debug("Session {} achieved synchronized state", session);
259 final PlspId id = lsp.getPlspId();
260 if (lsp.isRemove()) {
261 final SymbolicPathName name = lsps.remove(id);
263 trans.removeRuntimeData(lspIdentifier(name));
266 LOG.debug("LSP {} removed", lsp);
268 if (!lsps.containsKey(id)) {
269 LOG.debug("PLSPID {} not known yet, looking for a symbolic name", id);
271 final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.lsp.object.Tlvs tlvs =
272 r.getLsp().getTlvs();
273 final SymbolicPathName name = tlvs.getSymbolicPathName();
275 LOG.error("PLSPID {} seen for the first time, not reporting the LSP");
276 // TODO: what should we do here?
281 final SymbolicPathName name = lsps.get(id);
282 trans.putRuntimeData(lspIdentifier(name), lsp);
284 LOG.debug("LSP {} updated");
289 * FIXME: once this Future is listenable, attach to it so we can
290 * do cleanup if the commit fails. For now we force a commit.
292 final Future<RpcResult<TransactionStatus>> s = trans.commit();
295 } catch (InterruptedException | ExecutionException e) {
296 LOG.error("Failed to update internal state for session {}, closing it", session, e);
297 session.close(TerminationReason.Unknown);
302 private static final Logger LOG = LoggerFactory.getLogger(ServerSessionManager.class);
303 private static final Pcerr unhandledMessageError = new PcerrBuilder().setPcerrMessage(
304 new PcerrMessageBuilder().setErrorType(null).build()).build();
305 private final Map<PlspId, SymbolicPathName> lsps = new HashMap<>();
306 private final InstanceIdentifier<Nodes> inventory;
307 private final InstanceIdentifier<Topology> topology;
308 private final DataProviderService dataProvider;
310 private boolean ownsInventory = false;
311 private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> inventoryNodeId;
312 private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.pcep.rev131024.Node1> inventoryAugmentId;
313 private org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.pcep.rev131024.Node1Builder inventoryAugmentBuilder;
314 private PathComputationClientBuilder pccBuilder;
315 private boolean synced = false;
317 private boolean ownsTopology = false;
318 private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node> topologyNodeId;
319 private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.Node1> topologyAugmentId;
321 public ServerSessionManager(final DataProviderService dataProvider,
322 final InstanceIdentifier<Nodes> inventory, final InstanceIdentifier<Topology> topology) {
323 this.dataProvider = Preconditions.checkNotNull(dataProvider);
324 this.inventory = Preconditions.checkNotNull(inventory);
325 this.topology = Preconditions.checkNotNull(topology);
329 public PCEPSessionListener getSessionListener() {
330 return new SessionListener();