Implement TopologySessionStats in AbstractTopologySessionListener
[bgpcep.git] / pcep / topology / topology-provider / src / main / java / org / opendaylight / bgpcep / pcep / topology / provider / PCEPTopologySessionListener.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.pcep.topology.provider;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.util.concurrent.AsyncFunction;
15 import com.google.common.util.concurrent.FluentFuture;
16 import com.google.common.util.concurrent.Futures;
17 import com.google.common.util.concurrent.ListenableFuture;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
20 import java.nio.ByteBuffer;
21 import java.nio.charset.StandardCharsets;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Optional;
26 import java.util.concurrent.atomic.AtomicLong;
27 import org.checkerframework.checker.lock.qual.GuardedBy;
28 import org.checkerframework.checker.lock.qual.Holding;
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.opendaylight.bgpcep.pcep.server.PathComputation;
31 import org.opendaylight.bgpcep.pcep.server.PceServerProvider;
32 import org.opendaylight.protocol.pcep.spi.PCEPErrors;
33 import org.opendaylight.protocol.pcep.spi.PSTUtil;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.sync.optimizations.rev200720.PathComputationClient1Builder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.pcep.sync.optimizations.rev200720.lsp.db.version.tlv.LspDbVersion;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.initiated.rev200720.Lsp1;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.initiated.rev200720.PcinitiateBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.initiated.rev200720.Srp1;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.initiated.rev200720.Srp1Builder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.initiated.rev200720.pcinitiate.message.PcinitiateMessageBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.initiated.rev200720.pcinitiate.message.pcinitiate.message.Requests;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.initiated.rev200720.pcinitiate.message.pcinitiate.message.RequestsBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Arguments1;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Arguments2;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Arguments3;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.OperationalStatus;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Path1;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Path1Builder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.PcrptMessage;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.PcupdBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.PlspId;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.SrpIdNumber;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.SymbolicPathName;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.object.Lsp;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.object.LspBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.object.lsp.TlvsBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.pcerr.pcerr.message.error.type.StatefulCase;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.pcerr.pcerr.message.error.type.stateful._case.stateful.Srps;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.pcrpt.message.pcrpt.message.Reports;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.pcupd.message.PcupdMessageBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.pcupd.message.pcupd.message.UpdatesBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.pcupd.message.pcupd.message.updates.PathBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.srp.object.Srp;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.srp.object.SrpBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.symbolic.path.name.tlv.SymbolicPathNameBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev181109.Pcerr;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev181109.PcerrBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.message.rev181109.Pcreq;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.Message;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.PcerrMessage;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.RequestId;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.explicit.route.object.EroBuilder;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.path.setup.type.tlv.PathSetupType;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.pcep.error.object.ErrorObjectBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.pcerr.message.PcerrMessageBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.pcerr.message.pcerr.message.ErrorsBuilder;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.pcerr.message.pcerr.message.error.type.RequestCaseBuilder;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.pcerr.message.pcerr.message.error.type.request._case.RequestBuilder;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.pcerr.message.pcerr.message.error.type.request._case.request.RpsBuilder;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.pcreq.message.PcreqMessage;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.rp.object.RpBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.AddLspArgs;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.EnsureLspOperationalInput;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.LspId;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.Node1;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.OperationResult;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.PccSyncState;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.RemoveLspArgs;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.TriggerSyncArgs;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.UpdateLspArgs;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.ensure.lsp.operational.args.Arguments;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client.attributes.PathComputationClient;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client.attributes.PathComputationClientBuilder;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client.attributes.path.computation.client.ReportedLsp;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client.attributes.path.computation.client.ReportedLspBuilder;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client.attributes.path.computation.client.reported.lsp.Path;
97 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
98 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
99 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
100 import org.opendaylight.yangtools.yang.common.Uint32;
101 import org.slf4j.Logger;
102 import org.slf4j.LoggerFactory;
103
104 // Non-final for testing
105 class PCEPTopologySessionListener extends AbstractTopologySessionListener {
106     private static final Logger LOG = LoggerFactory.getLogger(PCEPTopologySessionListener.class);
107     private static final PlspId PLSPID_ZERO = new PlspId(Uint32.ZERO);
108     private static final SrpIdNumber SRPID_ZERO = new SrpIdNumber(Uint32.ZERO);
109
110     private final AtomicLong requestId = new AtomicLong(1L);
111
112     @GuardedBy("this")
113     private final List<PlspId> staleLsps = new ArrayList<>();
114
115     private final PceServerProvider pceServerProvider;
116
117     /**
118      * Creates a new stateful topology session listener for given server session manager.
119      */
120     PCEPTopologySessionListener(final ServerSessionManager serverSessionManager) {
121         super(serverSessionManager);
122         pceServerProvider = serverSessionManager.getPCEPTopologyProviderDependencies().getPceServerProvider();
123     }
124
125     private static LspDbVersion geLspDbVersionTlv(final Lsp lsp) {
126         final var tlvs = lsp.getTlvs();
127         if (tlvs != null) {
128             final var tlvs1 = tlvs.augmentation(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang
129                 .controller.pcep.sync.optimizations.rev200720.Tlvs1.class);
130             if (tlvs1 != null) {
131                 return tlvs1.getLspDbVersion();
132             }
133         }
134         return null;
135     }
136
137     @Override
138     public synchronized ListenableFuture<OperationResult> triggerSync(final TriggerSyncArgs input) {
139         if (isTriggeredInitialSynchro() && !isSynchronized()) {
140             return triggerSynchronization(input);
141         } else if (isSessionSynchronized() && isTriggeredReSyncEnabled()) {
142             checkArgument(input != null && input.getNode() != null, MISSING_XML_TAG);
143             return input.getName() == null ? triggerResyncronization(input) : triggerLspSyncronization(input);
144         }
145         return OperationResults.UNSENT.future();
146     }
147
148     private ListenableFuture<OperationResult> triggerLspSyncronization(final TriggerSyncArgs input) {
149         LOG.trace("Trigger Lsp Resynchronization {}", input);
150
151         // Make sure the LSP exists
152         final InstanceIdentifier<ReportedLsp> lsp = lspIdentifier(input.getName());
153         final FluentFuture<Optional<ReportedLsp>> f = readOperationalData(lsp);
154         if (f == null) {
155             return OperationResults.createUnsent(PCEPErrors.LSP_INTERNAL_ERROR).future();
156         }
157         return Futures.transformAsync(f, new ResyncLspFunction(input), MoreExecutors.directExecutor());
158     }
159
160     private ListenableFuture<OperationResult> triggerResyncronization(final TriggerSyncArgs input) {
161         LOG.trace("Trigger Resynchronization {}", input);
162         markAllLspAsStale();
163         updatePccState(PccSyncState.PcepTriggeredResync);
164         final PcupdMessageBuilder pcupdMessageBuilder = new PcupdMessageBuilder(MESSAGE_HEADER);
165         final SrpIdNumber srpIdNumber = createUpdateMessageSync(pcupdMessageBuilder);
166         final Message msg = new PcupdBuilder().setPcupdMessage(pcupdMessageBuilder.build()).build();
167         return sendMessage(msg, srpIdNumber, null);
168     }
169
170     private ListenableFuture<OperationResult> triggerSynchronization(final TriggerSyncArgs input) {
171         LOG.trace("Trigger Initial Synchronization {}", input);
172         final PcupdMessageBuilder pcupdMessageBuilder = new PcupdMessageBuilder(MESSAGE_HEADER);
173         final SrpIdNumber srpIdNumber = createUpdateMessageSync(pcupdMessageBuilder);
174         final Message msg = new PcupdBuilder().setPcupdMessage(pcupdMessageBuilder.build()).build();
175         return sendMessage(msg, srpIdNumber, null);
176     }
177
178     private SrpIdNumber createUpdateMessageSync(final PcupdMessageBuilder pcupdMessageBuilder) {
179         final UpdatesBuilder updBuilder = new UpdatesBuilder();
180         // LSP mandatory in Upd
181         final Lsp lsp = new LspBuilder().setPlspId(PLSPID_ZERO).setSync(Boolean.TRUE).build();
182         // SRP Mandatory in Upd
183         final SrpBuilder srpBuilder = new SrpBuilder();
184         // not sue whether use 0 instead of nextRequest() or do not insert srp == SRP-ID-number = 0
185         srpBuilder.setOperationId(nextRequest());
186         final Srp srp = srpBuilder.build();
187         //ERO Mandatory in Upd
188         final PathBuilder pb = new PathBuilder();
189         pb.setEro(new EroBuilder().build());
190
191         updBuilder.setPath(pb.build());
192         updBuilder.setLsp(lsp).setSrp(srp).setPath(pb.build());
193
194         pcupdMessageBuilder.setUpdates(List.of(updBuilder.build()));
195         return srp.getOperationId();
196     }
197
198     @Holding("this")
199     private void markAllLspAsStale() {
200         staleLsps.addAll(lsps.keySet());
201     }
202
203     private boolean handleErrorMessage(final PcerrMessage message) {
204         final var errMsg = message.getPcerrMessage();
205         if (errMsg.getErrorType() instanceof StatefulCase) {
206             final StatefulCase stat = (StatefulCase) errMsg.getErrorType();
207             for (final Srps srps : stat.getStateful().nonnullSrps()) {
208                 final SrpIdNumber id = srps.getSrp().getOperationId();
209                 if (!SRPID_ZERO.equals(id)) {
210                     final PCEPRequest req = removeRequest(id);
211                     if (req != null) {
212                         req.done(OperationResults.createFailed(errMsg.getErrors()));
213                     } else {
214                         LOG.warn("Request ID {} not found in outstanding DB", id);
215                     }
216                 }
217             }
218         } else {
219             LOG.warn("Unhandled PCErr message {}.", errMsg);
220             return true;
221         }
222         return false;
223     }
224
225     private boolean isSolicited(final Srp srp, final Lsp lsp, final MessageContext ctx, final ReportedLspBuilder rlb) {
226         if (srp == null) {
227             return false;
228         }
229         final SrpIdNumber id = srp.getOperationId();
230         if (SRPID_ZERO.equals(id)) {
231             return false;
232         }
233         switch (lsp.getOperational()) {
234             case Active:
235             case Down:
236             case Up:
237                 if (!isTriggeredSyncInProcess()) {
238                     final PCEPRequest req = removeRequest(id);
239                     if (req != null) {
240                         LOG.debug("Request {} resulted in LSP operational state {}", id, lsp.getOperational());
241                         rlb.setMetadata(req.getMetadata());
242                         ctx.resolveRequest(req);
243                     } else {
244                         LOG.warn("Request ID {} not found in outstanding DB", id);
245                     }
246                 }
247                 break;
248             case GoingDown:
249             case GoingUp:
250                 // These are transitive states, so we don't have to do anything, as they will be followed
251                 // up...
252                 break;
253             default:
254                 break;
255         }
256         return true;
257     }
258
259     @Holding("this")
260     private boolean manageNextReport(final Reports report, final MessageContext ctx) {
261         final Lsp lsp = report.getLsp();
262         final PlspId plspid = lsp.getPlspId();
263         final Srp srp = report.getSrp();
264
265         if (!lsp.getSync() && (plspid == null || PLSPID_ZERO.equals(plspid))) {
266             purgeStaleLsps(ctx);
267             if (isTriggeredSyncInProcess()) {
268                 if (srp == null) {
269                     return false;
270                 }
271                 final SrpIdNumber id = srp.getOperationId();
272                 if (SRPID_ZERO.equals(id)) {
273                     return false;
274                 }
275                 final PCEPRequest req = removeRequest(id);
276                 ctx.resolveRequest(req);
277             }
278             stateSynchronizationAchieved(ctx);
279             return true;
280         }
281         final ReportedLspBuilder rlb = new ReportedLspBuilder();
282         boolean solicited = false;
283         solicited = isSolicited(srp, lsp, ctx, rlb);
284
285         // if remove flag is set in SRP object, remove the tunnel immediately
286         if (solicited) {
287             final Srp1 initiatedSrp = srp.augmentation(Srp1.class);
288             if (initiatedSrp != null && initiatedSrp.getRemove()) {
289                 super.removeLsp(ctx, plspid);
290                 return false;
291             }
292         }
293         rlb.setPath(BindingMap.of(buildPath(report, srp, lsp)));
294
295         String name = lookupLspName(plspid);
296         if (lsp.getTlvs() != null && lsp.getTlvs().getSymbolicPathName() != null) {
297             name = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(lsp.getTlvs().getSymbolicPathName().getPathName()
298                     .getValue())).toString();
299         }
300         //get LspDB from LSP and write it to pcc's node
301         final LspDbVersion lspDbVersion = geLspDbVersionTlv(lsp);
302         if (lspDbVersion != null) {
303             updatePccNode(ctx, new PathComputationClientBuilder()
304                 .addAugmentation(new PathComputationClient1Builder().setLspDbVersion(lspDbVersion).build()).build());
305         }
306         updateLsp(ctx, plspid, name, rlb, solicited, lsp.getRemove());
307         unmarkStaleLsp(plspid);
308
309         LOG.debug("LSP {} updated", lsp);
310         return true;
311     }
312
313     private static Path buildPath(final Reports report, final Srp srp, final Lsp lsp) {
314         final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client
315                 .attributes.path.computation.client.reported.lsp.PathBuilder pb = new org.opendaylight.yang.gen.v1
316                 .urn.opendaylight.params.xml.ns.yang.topology.pcep.rev200120.pcep.client.attributes.path.computation
317                 .client.reported.lsp.PathBuilder();
318         if (report.getPath() != null) {
319             pb.fieldsFrom(report.getPath());
320         }
321         // LSP is mandatory (if there is none, parser will throw an exception)
322         // this is to ensure a path will be created at any rate
323         final Path1Builder p1Builder = new Path1Builder();
324         p1Builder.setLsp(report.getLsp());
325         final PathSetupType pst;
326         if (srp != null && srp.getTlvs() != null && srp.getTlvs().getPathSetupType() != null) {
327             pst = srp.getTlvs().getPathSetupType();
328             p1Builder.setPathSetupType(pst);
329         } else {
330             pst = null;
331         }
332         pb.addAugmentation(p1Builder.build());
333         final var tlvs = report.getLsp().getTlvs();
334         if (tlvs != null) {
335             if (tlvs.getLspIdentifiers() != null) {
336                 pb.setLspId(tlvs.getLspIdentifiers().getLspId());
337             } else if (!PSTUtil.isDefaultPST(pst)) {
338                 pb.setLspId(new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820
339                         .LspId(lsp.getPlspId().getValue()));
340             }
341         }
342         return pb.build();
343     }
344
345     private boolean handlePcreqMessage(final PcreqMessage message) {
346
347         LOG.info("Start PcRequest Message handler");
348         Message rep = null;
349
350         /* Get a Path Computation to compute the Path from the Request */
351         // TODO: Adjust Junit Test to avoid this test
352         if (pceServerProvider == null) {
353             rep = createErrorMsg(PCEPErrors.RESOURCE_LIMIT_EXCEEDED, Uint32.ZERO);
354             sendMessage(rep, new SrpIdNumber(Uint32.ZERO), null);
355             return false;
356         }
357         PathComputation pathComputation = pceServerProvider.getPathComputation();
358         /* Reply with Error Message if no valid Path Computation is available */
359         if (pathComputation == null) {
360             rep = createErrorMsg(PCEPErrors.RESOURCE_LIMIT_EXCEEDED, Uint32.ZERO);
361             sendMessage(rep, new SrpIdNumber(Uint32.ZERO), null);
362             return false;
363         }
364         for (var req : message.nonnullRequests()) {
365             LOG.debug("Process request {}", req);
366             rep = pathComputation.computePath(req);
367             SrpIdNumber repId = null;
368             if (req.getRp() != null) {
369                 repId = new SrpIdNumber(req.getRp().getRequestId().getValue());
370             } else {
371                 repId = new SrpIdNumber(Uint32.ZERO);
372             }
373             sendMessage(rep, repId, null);
374         }
375         return false;
376     }
377
378     @Override
379     protected synchronized boolean onMessage(final MessageContext ctx, final Message message) {
380         if (message instanceof PcerrMessage) {
381             return handleErrorMessage((PcerrMessage) message);
382         }
383         if (message instanceof Pcreq) {
384             LOG.info("PcReq detected. Start Request Message handler");
385             return handlePcreqMessage(((Pcreq) message).getPcreqMessage());
386         }
387         if (!(message instanceof PcrptMessage)) {
388             return true;
389         }
390         listenerState.updateLastReceivedRptMsg();
391         final var rpt = ((PcrptMessage) message).getPcrptMessage();
392         for (final Reports report : rpt.nonnullReports()) {
393             if (!manageNextReport(report, ctx)) {
394                 return false;
395             }
396         }
397         return false;
398     }
399
400     private SrpIdNumber nextRequest() {
401         return new SrpIdNumber(Uint32.valueOf(requestId.getAndIncrement()));
402     }
403
404     @Override
405     @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH", justification = "SB does not grok TYPE_USE")
406     public synchronized ListenableFuture<OperationResult> addLsp(final AddLspArgs input) {
407         checkArgument(input != null && input.getName() != null && input.getNode() != null
408                 && input.getArguments() != null, MISSING_XML_TAG);
409         LOG.trace("AddLspArgs {}", input);
410         // Make sure there is no such LSP
411         final InstanceIdentifier<ReportedLsp> lsp = lspIdentifier(input.getName());
412         final ListenableFuture<Optional<ReportedLsp>> f = readOperationalData(lsp);
413         return f == null ? OperationResults.createUnsent(PCEPErrors.LSP_INTERNAL_ERROR).future()
414                 : Futures.transformAsync(f, new AddFunction(input, lsp), MoreExecutors.directExecutor());
415     }
416
417     @Override
418     @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH", justification = "SB does not grok TYPE_USE")
419     public synchronized ListenableFuture<OperationResult> removeLsp(final RemoveLspArgs input) {
420         checkArgument(input != null && input.getName() != null && input.getNode() != null, MISSING_XML_TAG);
421         LOG.trace("RemoveLspArgs {}", input);
422         // Make sure the LSP exists, we need it for PLSP-ID
423         final InstanceIdentifier<ReportedLsp> lsp = lspIdentifier(input.getName());
424         final ListenableFuture<Optional<ReportedLsp>> f = readOperationalData(lsp);
425         return f == null ? OperationResults.createUnsent(PCEPErrors.LSP_INTERNAL_ERROR).future()
426                 : Futures.transformAsync(f, rep -> {
427                     final Lsp reportedLsp = validateReportedLsp(rep, input);
428                     if (reportedLsp == null) {
429                         return OperationResults.createUnsent(PCEPErrors.UNKNOWN_PLSP_ID).future();
430                     }
431                     final PcinitiateMessageBuilder ib = new PcinitiateMessageBuilder(MESSAGE_HEADER);
432                     final Requests rb = buildRequest(rep, reportedLsp);
433                     ib.setRequests(List.of(rb));
434                     return sendMessage(new PcinitiateBuilder().setPcinitiateMessage(ib.build()).build(),
435                         rb.getSrp().getOperationId(), null);
436                 }, MoreExecutors.directExecutor());
437     }
438
439     private Requests buildRequest(final Optional<ReportedLsp> rep, final Lsp reportedLsp) {
440         // Build the request and send it
441         final RequestsBuilder rb = new RequestsBuilder();
442         final SrpBuilder srpBuilder = new SrpBuilder().addAugmentation(new Srp1Builder()
443                 .setRemove(Boolean.TRUE).build()).setOperationId(nextRequest()).setProcessingRule(Boolean.TRUE);
444         final Optional<PathSetupType> maybePST = getPST(rep);
445         if (maybePST.isPresent()) {
446             srpBuilder.setTlvs(new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful
447                     .rev200720.srp.object.srp.TlvsBuilder().setPathSetupType(maybePST.get()).build());
448         }
449         rb.setSrp(srpBuilder.build());
450         rb.setLsp(new LspBuilder().setRemove(Boolean.FALSE).setPlspId(reportedLsp.getPlspId())
451                 .setDelegate(reportedLsp.getDelegate()).build());
452         return rb.build();
453     }
454
455     private ListenableFuture<OperationResult> redelegate(final Lsp reportedLsp, final Srp srp, final Lsp lsp,
456             final UpdateLspArgs input) {
457         // the D bit that was reported decides the type of PCE message sent
458         final boolean isDelegate = requireNonNull(reportedLsp.getDelegate());
459         final Message msg;
460         if (isDelegate) {
461             // we already have delegation, send update
462             final UpdatesBuilder rb = new UpdatesBuilder();
463             rb.setSrp(srp);
464             rb.setLsp(lsp);
465             final PathBuilder pb = new PathBuilder();
466             pb.fieldsFrom(input.getArguments());
467             rb.setPath(pb.build());
468             final PcupdMessageBuilder ub = new PcupdMessageBuilder(MESSAGE_HEADER);
469             ub.setUpdates(List.of(rb.build()));
470             msg = new PcupdBuilder().setPcupdMessage(ub.build()).build();
471         } else {
472             final Lsp1 lspCreateFlag = reportedLsp.augmentation(Lsp1.class);
473             // we only retake delegation for PCE initiated tunnels
474             if (lspCreateFlag != null && !lspCreateFlag.getCreate()) {
475                 LOG.warn("Unable to retake delegation of PCC-initiated tunnel: {}", reportedLsp);
476                 return OperationResults.createUnsent(PCEPErrors.UPDATE_REQ_FOR_NON_LSP).future();
477             }
478             // we want to revoke delegation, different type of message
479             // is sent because of specification by Siva
480             // this message is also sent, when input delegate bit is set to 0
481             // generating an error in PCC
482             final List<Requests> reqs = new ArrayList<>();
483             reqs.add(new RequestsBuilder().setSrp(srp).setLsp(lsp).build());
484             final PcinitiateMessageBuilder ib = new PcinitiateMessageBuilder();
485             ib.setRequests(reqs);
486             msg = new PcinitiateBuilder().setPcinitiateMessage(ib.build()).build();
487         }
488         return sendMessage(msg, srp.getOperationId(), input.getArguments().getMetadata());
489     }
490
491     @Override
492     @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH", justification = "SB does not grok TYPE_USE")
493     public synchronized ListenableFuture<OperationResult> updateLsp(final UpdateLspArgs input) {
494         checkArgument(input != null && input.getName() != null && input.getNode() != null
495                 && input.getArguments() != null, MISSING_XML_TAG);
496         LOG.trace("UpdateLspArgs {}", input);
497         // Make sure the LSP exists
498         final InstanceIdentifier<ReportedLsp> lsp = lspIdentifier(input.getName());
499         final ListenableFuture<Optional<ReportedLsp>> f = readOperationalData(lsp);
500         return f == null ? OperationResults.createUnsent(PCEPErrors.LSP_INTERNAL_ERROR).future()
501                 : Futures.transformAsync(f, new UpdateFunction(input), MoreExecutors.directExecutor());
502     }
503
504     @Override
505     public synchronized ListenableFuture<OperationResult> ensureLspOperational(final EnsureLspOperationalInput input) {
506         checkArgument(input != null && input.getName() != null && input.getNode() != null, MISSING_XML_TAG);
507         final Arguments args = input.getArguments();
508         checkArgument(args != null, MISSING_XML_TAG);
509
510         final OperationalStatus op;
511         final Arguments1 aa = args.augmentation(Arguments1.class);
512         if (aa != null) {
513             op = aa.getOperational();
514         } else {
515             op = null;
516         }
517
518         // Make sure the LSP exists
519         final InstanceIdentifier<ReportedLsp> lsp = lspIdentifier(input.getName());
520         LOG.debug("Checking if LSP {} has operational state {}", lsp, op);
521         final ListenableFuture<Optional<ReportedLsp>> f = readOperationalData(lsp);
522         return f == null ? OperationResults.createUnsent(PCEPErrors.LSP_INTERNAL_ERROR).future()
523                 : listenableFuture(f, input, op);
524     }
525
526     private static ListenableFuture<OperationResult> listenableFuture(
527             final ListenableFuture<Optional<ReportedLsp>> future, final EnsureLspOperationalInput input,
528             final OperationalStatus op) {
529         return Futures.transform(future, rep -> {
530             if (!rep.isPresent()) {
531                 LOG.debug("Node {} does not contain LSP {}", input.getNode(), input.getName());
532                 return OperationResults.UNSENT;
533             }
534             // check if at least one of the paths has the same status as requested
535             for (final Path p : rep.get().nonnullPath().values()) {
536                 final Path1 p1 = p.augmentation(Path1.class);
537                 if (p1 == null) {
538                     LOG.warn("Node {} LSP {} does not contain data", input.getNode(), input.getName());
539                     return OperationResults.UNSENT;
540                 }
541                 if (op.equals(p1.getLsp().getOperational())) {
542                     return OperationResults.SUCCESS;
543                 }
544             }
545             return OperationResults.UNSENT;
546         }, MoreExecutors.directExecutor());
547     }
548
549     @Override
550     protected Lsp validateReportedLsp(final Optional<ReportedLsp> rep, final LspId input) {
551         if (!rep.isPresent()) {
552             LOG.debug("Node {} does not contain LSP {}", input.getNode(), input.getName());
553             return null;
554         }
555         // it doesn't matter how many lsps there are in the path list, we only need data that is the same in each path
556         final Path1 ra = rep.get().getPath().values().iterator().next().augmentation(Path1.class);
557         checkState(ra != null, "Reported LSP reported null from data-store.");
558         final Lsp reportedLsp = ra.getLsp();
559         checkState(reportedLsp != null, "Reported LSP does not contain LSP object.");
560         return reportedLsp;
561     }
562
563     private static Optional<PathSetupType> getPST(final Optional<ReportedLsp> rep) {
564         if (rep.isPresent()) {
565             final Path1 path1 = rep.get().getPath().values().iterator().next().augmentation(Path1.class);
566             if (path1 != null) {
567                 final PathSetupType pst = path1.getPathSetupType();
568                 if (!PSTUtil.isDefaultPST(pst)) {
569                     return Optional.of(pst);
570                 }
571             }
572         }
573         return Optional.empty();
574     }
575
576     /**
577      * Recover lspData and mark any LSPs in the LSP database that were previously reported by the PCC as stale.
578      */
579     @Override
580     protected synchronized void loadLspData(final Node node, final Map<String, ReportedLsp> lspData,
581             final Map<PlspId, String> lsps, final boolean incrementalSynchro) {
582         //load node's lsps from DS
583         final PathComputationClient pcc = node.augmentation(Node1.class).getPathComputationClient();
584         for (final ReportedLsp reportedLsp : pcc.nonnullReportedLsp().values()) {
585             final String lspName = reportedLsp.getName();
586             lspData.put(lspName, reportedLsp);
587             if (!reportedLsp.getPath().isEmpty()) {
588                 final Path1 path1 = reportedLsp.getPath().values().iterator().next().augmentation(Path1.class);
589                 if (path1 != null) {
590                     final PlspId plspId = path1.getLsp().getPlspId();
591                     if (!incrementalSynchro) {
592                         staleLsps.add(plspId);
593                     }
594                     lsps.put(plspId, lspName);
595                 }
596             }
597         }
598     }
599
600     /**
601      * When the PCC reports an LSP during state synchronization, if the LSP already
602      * exists in the LSP database, the PCE MUST update the LSP database and
603      * clear the stale marker from the LSP.
604      *
605      * @param plspId id
606      */
607     private synchronized void unmarkStaleLsp(final PlspId plspId) {
608         staleLsps.remove(plspId);
609     }
610
611     /**
612      * Purge any LSPs from the LSP database that are still marked as stale.
613      *
614      * @param ctx message context
615      */
616     private synchronized void purgeStaleLsps(final MessageContext ctx) {
617         for (final PlspId plspId : staleLsps) {
618             removeLsp(ctx, plspId);
619         }
620         staleLsps.clear();
621     }
622
623     private class ResyncLspFunction implements AsyncFunction<Optional<ReportedLsp>, OperationResult> {
624         private final TriggerSyncArgs input;
625
626         ResyncLspFunction(final TriggerSyncArgs input) {
627             this.input = input;
628         }
629
630         @Override
631         public ListenableFuture<OperationResult> apply(final Optional<ReportedLsp> rep) {
632             final Lsp reportedLsp = validateReportedLsp(rep, input);
633             if (reportedLsp == null || !rep.isPresent()) {
634                 return OperationResults.createUnsent(PCEPErrors.UNKNOWN_PLSP_ID).future();
635             }
636             // mark lsp as stale
637             final ReportedLsp staleLsp = rep.get();
638             if (!staleLsp.getPath().isEmpty()) {
639                 final Path1 path1 = staleLsp.getPath().values().iterator().next().augmentation(Path1.class);
640                 if (path1 != null) {
641                     staleLsps.add(path1.getLsp().getPlspId());
642                 }
643             }
644             updatePccState(PccSyncState.PcepTriggeredResync);
645             // create PCUpd with mandatory objects and LSP object set to 1
646             final SrpBuilder srpBuilder = new SrpBuilder();
647             srpBuilder.setOperationId(nextRequest());
648             srpBuilder.setProcessingRule(Boolean.TRUE);
649
650             final Optional<PathSetupType> maybePST = getPST(rep);
651             if (maybePST.isPresent()) {
652                 srpBuilder.setTlvs(
653                         new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful
654                                 .rev200720.srp.object.srp.TlvsBuilder()
655                                 .setPathSetupType(maybePST.get()).build());
656             }
657
658             final Srp srp = srpBuilder.build();
659             final Lsp lsp = new LspBuilder().setPlspId(reportedLsp.getPlspId()).setSync(Boolean.TRUE).build();
660
661             final Message msg = createPcepUpd(srp, lsp);
662             return sendMessage(msg, srp.getOperationId(), null);
663         }
664
665         private Message createPcepUpd(final Srp srp, final Lsp lsp) {
666             return new PcupdBuilder()
667                 .setPcupdMessage(new PcupdMessageBuilder(MESSAGE_HEADER)
668                     .setUpdates(List.of(new UpdatesBuilder()
669                         .setSrp(srp)
670                         .setLsp(lsp)
671                         .setPath(new PathBuilder().build())
672                         .build()))
673                     .build())
674                 .build();
675         }
676     }
677
678     private class AddFunction implements AsyncFunction<Optional<ReportedLsp>, OperationResult> {
679
680         private final AddLspArgs input;
681         private final InstanceIdentifier<ReportedLsp> lsp;
682
683         AddFunction(final AddLspArgs input, final InstanceIdentifier<ReportedLsp> lsp) {
684             this.input = input;
685             this.lsp = lsp;
686         }
687
688         @Override
689         public ListenableFuture<OperationResult> apply(final Optional<ReportedLsp> rep) {
690             if (rep.isPresent()) {
691                 LOG.debug("Node {} already contains lsp {} at {}", input.getNode(), input.getName(), lsp);
692                 return OperationResults.createUnsent(PCEPErrors.USED_SYMBOLIC_PATH_NAME).future();
693             }
694             if (!isInitiationCapability()) {
695                 return OperationResults.createUnsent(PCEPErrors.CAPABILITY_NOT_SUPPORTED).future();
696             }
697
698             // Build the request
699             final RequestsBuilder rb = new RequestsBuilder();
700             final var args = input.getArguments();
701             final Arguments2 args2 = args.augmentation(Arguments2.class);
702             final Lsp inputLsp = args2 != null ? args2.getLsp() : null;
703             if (inputLsp == null) {
704                 return OperationResults.createUnsent(PCEPErrors.LSP_MISSING).future();
705             }
706
707             rb.fieldsFrom(input.getArguments());
708
709             boolean segmentRouting = !PSTUtil.isDefaultPST(args2.getPathSetupType());
710
711             /* Call Path Computation if an ERO was not provided */
712             if (rb.getEro() == null || rb.getEro().nonnullSubobject().isEmpty()) {
713                 /* Get a Path Computation to compute the Path from the Arguments */
714                 // TODO: Adjust Junit Test to avoid this test
715                 if (pceServerProvider == null) {
716                     return OperationResults.createUnsent(PCEPErrors.ERO_MISSING).future();
717                 }
718                 PathComputation pathComputation = pceServerProvider.getPathComputation();
719                 if (pathComputation == null) {
720                     return OperationResults.createUnsent(PCEPErrors.ERO_MISSING).future();
721                 }
722                 rb.setEro(pathComputation.computeEro(args.getEndpointsObj(), args.getBandwidth(),
723                         args.getClassType(), args.getMetrics(), args.getXro(), args.getIro(), segmentRouting));
724             }
725
726             final TlvsBuilder tlvsBuilder;
727             if (inputLsp.getTlvs() != null) {
728                 tlvsBuilder = new TlvsBuilder(inputLsp.getTlvs());
729             } else {
730                 tlvsBuilder = new TlvsBuilder();
731             }
732             tlvsBuilder.setSymbolicPathName(
733                     new SymbolicPathNameBuilder().setPathName(new SymbolicPathName(input.getName()
734                             .getBytes(StandardCharsets.UTF_8))).build());
735
736             final SrpBuilder srpBuilder = new SrpBuilder()
737                     .setOperationId(nextRequest())
738                     .setProcessingRule(Boolean.TRUE);
739             if (segmentRouting) {
740                 srpBuilder.setTlvs(
741                         new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf
742                                 .stateful.rev200720.srp.object.srp.TlvsBuilder()
743                                 .setPathSetupType(args2.getPathSetupType()).build());
744             }
745             rb.setSrp(srpBuilder.build());
746
747             rb.setLsp(new LspBuilder()
748                 .setAdministrative(inputLsp.getAdministrative())
749                 .setDelegate(inputLsp.getDelegate())
750                 .setPlspId(PLSPID_ZERO)
751                 .setTlvs(tlvsBuilder.build())
752                 .build());
753
754             // Send the message
755             return sendMessage(new PcinitiateBuilder()
756                 .setPcinitiateMessage(new PcinitiateMessageBuilder(MESSAGE_HEADER)
757                     .setRequests(List.of(rb.build()))
758                     .build())
759                 .build(),
760                 rb.getSrp().getOperationId(), input.getArguments().getMetadata());
761         }
762     }
763
764     private class UpdateFunction implements AsyncFunction<Optional<ReportedLsp>, OperationResult> {
765
766         private final UpdateLspArgs input;
767
768         UpdateFunction(final UpdateLspArgs input) {
769             this.input = input;
770         }
771
772         @Override
773         public ListenableFuture<OperationResult> apply(final Optional<ReportedLsp> rep) {
774             final Lsp reportedLsp = validateReportedLsp(rep, input);
775             if (reportedLsp == null) {
776                 return OperationResults.createUnsent(PCEPErrors.UNKNOWN_PLSP_ID).future();
777             }
778             // create mandatory objects
779             final Arguments3 args = input.getArguments().augmentation(Arguments3.class);
780             final SrpBuilder srpBuilder = new SrpBuilder();
781             srpBuilder.setOperationId(nextRequest());
782             srpBuilder.setProcessingRule(Boolean.TRUE);
783             if (args != null && args.getPathSetupType() != null) {
784                 if (!PSTUtil.isDefaultPST(args.getPathSetupType())) {
785                     srpBuilder.setTlvs(
786                             new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful
787                                     .rev200720.srp.object.srp.TlvsBuilder()
788                                     .setPathSetupType(args.getPathSetupType()).build());
789                 }
790             } else {
791                 final Optional<PathSetupType> maybePST = getPST(rep);
792                 if (maybePST.isPresent()) {
793                     srpBuilder.setTlvs(
794                             new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful
795                                     .rev200720.srp.object.srp.TlvsBuilder()
796                                     .setPathSetupType(maybePST.get()).build());
797                 }
798             }
799             final Srp srp = srpBuilder.build();
800             final Lsp inputLsp = args != null ? args.getLsp() : null;
801             final LspBuilder lspBuilder = new LspBuilder().setPlspId(reportedLsp.getPlspId());
802             if (inputLsp != null) {
803                 lspBuilder.setDelegate(Boolean.TRUE.equals(inputLsp.getDelegate()))
804                         .setTlvs(inputLsp.getTlvs())
805                         .setAdministrative(Boolean.TRUE.equals(inputLsp.getAdministrative()));
806             }
807             return redelegate(reportedLsp, srp, lspBuilder.build(), input);
808         }
809     }
810
811     private static Pcerr createErrorMsg(@NonNull final PCEPErrors pcepErrors, final Uint32 reqID) {
812         return new PcerrBuilder()
813             .setPcerrMessage(new PcerrMessageBuilder()
814                 .setErrorType(new RequestCaseBuilder()
815                     .setRequest(new RequestBuilder()
816                         .setRps(List.of(new RpsBuilder()
817                             .setRp(new RpBuilder()
818                                 .setProcessingRule(false)
819                                 .setIgnore(false)
820                                 .setRequestId(new RequestId(reqID))
821                                 .build())
822                             .build()))
823                         .build())
824                     .build())
825                 .setErrors(List.of(new ErrorsBuilder()
826                     .setErrorObject(new ErrorObjectBuilder()
827                         .setType(pcepErrors.getErrorType())
828                         .setValue(pcepErrors.getErrorValue())
829                         .build())
830                     .build()))
831                 .build())
832             .build();
833     }
834 }