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