Merge "Fix junit dependencies in poms. Reuse existing from parent, add missing ones."
[bgpcep.git] / topology / provider-pcep / src / main / java / org / opendaylight / bgpcep / topology / provider / pcep / ServerSessionManager.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.bgpcep.topology.provider.pcep;
9
10 import java.net.InetAddress;
11 import java.util.HashMap;
12 import java.util.Map;
13 import java.util.concurrent.ExecutionException;
14 import java.util.concurrent.Future;
15
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;
47
48 import com.google.common.base.Preconditions;
49
50 /**
51  *
52  */
53 final class ServerSessionManager implements SessionListenerFactory<PCEPSessionListener> {
54         private static String createNodeId(final InetAddress addr) {
55                 return "pcc://" + addr.getHostAddress();
56         }
57
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);
61
62                         // FIXME: after 0.6 yangtools, this cast should not be needed
63                         final Nodes nodes = (Nodes)trans.readOperationalData(inventory);
64
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)) {
68                                         return n;
69                                 }
70
71                                 // FIXME: locate the node by its management IP address
72                         }
73
74                         /*
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
77                          * Runtime data space.
78                          */
79                         LOG.debug("Failed to find inventory node for peer {}, creating a new one at {}", address, pccId);
80
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();
89
90                         trans.putRuntimeData(nii, ret);
91                         ownsInventory = true;
92                         inventoryNodeId = nii;
93                         return ret;
94                 }
95
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);
101
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())) {
105                                         return n;
106                                 }
107                         }
108
109                         /*
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).
112                          */
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();
120
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();
124
125                         trans.putRuntimeData(nti, ret);
126                         ownsTopology = true;
127                         topologyNodeId = nti;
128                         return ret;
129                 }
130
131                 @Override
132                 public void onSessionUp(final PCEPSession session) {
133                         /*
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.
138                          */
139                         final InetAddress peerAddress = session.getRemoteAddress();
140                         final DataModificationTransaction trans = dataProvider.beginTransaction();
141
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);
144
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);
147
148                         // Our augmentation in the topology node
149                         final PccBuilder pb = new PccBuilder();
150
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);
155
156                         // Our augmentation in the inventory node
157                         pccBuilder = new PathComputationClientBuilder();
158
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);
165                         }
166
167                         pccBuilder.setTopologyNode(topoNode.getNodeId());
168
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());
173
174                         // All set, commit the modifications
175                         final Future<RpcResult<TransactionStatus>> s = trans.commit();
176
177                         /*
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.
180                          */
181                         try {
182                                 s.get();
183                         } catch (InterruptedException | ExecutionException e) {
184                                 LOG.error("Failed to update internal state for session {}, terminating it", session, e);
185                                 session.close(TerminationReason.Unknown);
186                         }
187
188                         LOG.info("Session with {} attached to inventory node {} and topology node {}", session.getRemoteAddress(), invNode.getId(), topoNode.getNodeId());
189                 }
190
191                 private void tearDown(final PCEPSession session) {
192                         final DataModificationTransaction trans = dataProvider.beginTransaction();
193
194                         /*
195                          * The session went down. Undo all the Inventory and Topology
196                          * changes we have done.
197                          */
198                         trans.removeRuntimeData(inventoryAugmentId);
199                         if (ownsInventory) {
200                                 trans.removeRuntimeData(inventoryNodeId);
201                         }
202                         trans.removeRuntimeData(topologyAugmentId);
203                         if (ownsTopology) {
204                                 trans.removeRuntimeData(topologyNodeId);
205                         }
206
207                         /*
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.
210                          */
211                         final Future<RpcResult<TransactionStatus>> s = trans.commit();
212                         try {
213                                 s.get();
214                         } catch (InterruptedException | ExecutionException e) {
215                                 LOG.error("Failed to cleanup internal state for session {}", session, e);
216                         }
217                 }
218
219                 @Override
220                 public void onSessionDown(final PCEPSession session, final Exception e) {
221                         LOG.warn("Session {} went down unexpectedly", e);
222                         tearDown(session);
223                 }
224
225                 @Override
226                 public void onSessionTerminated(final PCEPSession session, final PCEPTerminationReason reason) {
227                         LOG.info("Session {} terminated by peer with reason {}", session, reason);
228                         tearDown(session);
229                 }
230
231                 private InstanceIdentifier<Lsps> lspIdentifier(final SymbolicPathName name) {
232                         return InstanceIdentifier.builder(topologyAugmentId).
233                                         node(Lsps.class, new LspsKey(name.getPathName())).toInstance();
234                 }
235
236                 @Override
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);
241                         }
242
243                         final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.pcrpt.message.PcrptMessage rpt =
244                                         ((PcrptMessage)message).getPcrptMessage();
245
246                         final DataModificationTransaction trans = dataProvider.beginTransaction();
247
248                         for (final Reports r : rpt.getReports()) {
249                                 final Lsp lsp = r.getLsp();
250
251                                 if (lsp.isSync() && !synced) {
252                                         // Update synchronization flag
253                                         synced = true;
254                                         inventoryAugmentBuilder.setPathComputationClient(pccBuilder.setStateSync(PccSyncState.Synchronized).build());
255                                         trans.putRuntimeData(inventoryAugmentId, inventoryAugmentBuilder.build());
256                                         LOG.debug("Session {} achieved synchronized state", session);
257                                 }
258
259                                 final PlspId id = lsp.getPlspId();
260                                 if (lsp.isRemove()) {
261                                         final SymbolicPathName name = lsps.remove(id);
262                                         if (name != null) {
263                                                 trans.removeRuntimeData(lspIdentifier(name));
264                                         }
265
266                                         LOG.debug("LSP {} removed", lsp);
267                                 } else {
268                                         if (!lsps.containsKey(id)) {
269                                                 LOG.debug("PLSPID {} not known yet, looking for a symbolic name", id);
270
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();
274                                                 if (name == null) {
275                                                         LOG.error("PLSPID {} seen for the first time, not reporting the LSP");
276                                                         // TODO: what should we do here?
277                                                         continue;
278                                                 }
279                                         }
280
281                                         final SymbolicPathName name = lsps.get(id);
282                                         trans.putRuntimeData(lspIdentifier(name), lsp);
283
284                                         LOG.debug("LSP {} updated");
285                                 }
286                         }
287
288                         /*
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.
291                          */
292                         final Future<RpcResult<TransactionStatus>> s = trans.commit();
293                         try {
294                                 s.get();
295                         } catch (InterruptedException | ExecutionException e) {
296                                 LOG.error("Failed to update internal state for session {}, closing it", session, e);
297                                 session.close(TerminationReason.Unknown);
298                         }
299                 }
300         }
301
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;
309
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;
316
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;
320
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);
326         }
327
328         @Override
329         public PCEPSessionListener getSessionListener() {
330                 return new SessionListener();
331         }
332 }