Eliminate network-pcep-topology-config
[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.rev220730.AddLspArgs;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.EnsureLspOperationalInput;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.LspId;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.Node1;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.OperationResult;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.PccSyncState;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.RemoveLspArgs;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.TriggerSyncArgs;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.UpdateLspArgs;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.ensure.lsp.operational.args.Arguments;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.pcep.client.attributes.PathComputationClient;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.pcep.client.attributes.PathComputationClientBuilder;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.pcep.client.attributes.path.computation.client.ReportedLsp;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.pcep.client.attributes.path.computation.client.ReportedLspBuilder;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.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.rev220730.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.rev220730.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         Message rep = null;
362
363         /* Get a Path Computation to compute the Path from the Request */
364         // TODO: Adjust Junit Test to avoid this test
365         if (pceServerProvider == null) {
366             rep = createErrorMsg(PCEPErrors.RESOURCE_LIMIT_EXCEEDED, Uint32.ZERO);
367             sendMessage(rep, new SrpIdNumber(Uint32.ZERO), null);
368             return false;
369         }
370         PathComputation pathComputation = pceServerProvider.getPathComputation();
371         /* Reply with Error Message if no valid Path Computation is available */
372         if (pathComputation == null) {
373             rep = createErrorMsg(PCEPErrors.RESOURCE_LIMIT_EXCEEDED, Uint32.ZERO);
374             sendMessage(rep, new SrpIdNumber(Uint32.ZERO), null);
375             return false;
376         }
377         for (var req : message.nonnullRequests()) {
378             LOG.debug("Process request {}", req);
379             rep = pathComputation.computePath(req);
380             SrpIdNumber repId = null;
381             if (req.getRp() != null) {
382                 repId = new SrpIdNumber(req.getRp().getRequestId().getValue());
383             } else {
384                 repId = new SrpIdNumber(Uint32.ZERO);
385             }
386             sendMessage(rep, repId, null);
387         }
388         return false;
389     }
390
391     @Override
392     protected synchronized boolean onMessage(final MessageContext ctx, final Message message) {
393         if (message instanceof PcerrMessage) {
394             return handleErrorMessage((PcerrMessage) message);
395         }
396         if (message instanceof Pcreq) {
397             LOG.info("PcReq detected. Start Request Message handler");
398             return handlePcreqMessage(((Pcreq) message).getPcreqMessage());
399         }
400         if (!(message instanceof PcrptMessage)) {
401             return true;
402         }
403         listenerState.updateLastReceivedRptMsg();
404         final var rpt = ((PcrptMessage) message).getPcrptMessage();
405         for (final Reports report : rpt.nonnullReports()) {
406             if (!manageNextReport(report, ctx)) {
407                 return false;
408             }
409         }
410         return false;
411     }
412
413     private SrpIdNumber nextRequest() {
414         return new SrpIdNumber(Uint32.valueOf(requestId.getAndIncrement()));
415     }
416
417     @Override
418     @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH", justification = "SB does not grok TYPE_USE")
419     public synchronized ListenableFuture<OperationResult> addLsp(final AddLspArgs input) {
420         checkArgument(input != null && input.getName() != null && input.getNode() != null
421                 && input.getArguments() != null, MISSING_XML_TAG);
422         LOG.trace("AddLspArgs {}", input);
423         // Make sure there is no such LSP
424         final InstanceIdentifier<ReportedLsp> lsp = lspIdentifier(input.getName());
425         final ListenableFuture<Optional<ReportedLsp>> f = readOperationalData(lsp);
426         return f == null ? OperationResults.createUnsent(PCEPErrors.LSP_INTERNAL_ERROR).future()
427                 : Futures.transformAsync(f, new AddFunction(input, lsp), MoreExecutors.directExecutor());
428     }
429
430     @Override
431     @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH", justification = "SB does not grok TYPE_USE")
432     public synchronized ListenableFuture<OperationResult> removeLsp(final RemoveLspArgs input) {
433         checkArgument(input != null && input.getName() != null && input.getNode() != null, MISSING_XML_TAG);
434         LOG.trace("RemoveLspArgs {}", input);
435         // Make sure the LSP exists, we need it for PLSP-ID
436         final InstanceIdentifier<ReportedLsp> lsp = lspIdentifier(input.getName());
437         final ListenableFuture<Optional<ReportedLsp>> f = readOperationalData(lsp);
438         return f == null ? OperationResults.createUnsent(PCEPErrors.LSP_INTERNAL_ERROR).future()
439                 : Futures.transformAsync(f, rep -> {
440                     final Lsp reportedLsp = validateReportedLsp(rep, input);
441                     if (reportedLsp == null) {
442                         return OperationResults.createUnsent(PCEPErrors.UNKNOWN_PLSP_ID).future();
443                     }
444                     final PcinitiateMessageBuilder ib = new PcinitiateMessageBuilder(MESSAGE_HEADER);
445                     final Requests rb = buildRequest(rep, reportedLsp);
446                     ib.setRequests(List.of(rb));
447                     return sendMessage(new PcinitiateBuilder().setPcinitiateMessage(ib.build()).build(),
448                         rb.getSrp().getOperationId(), null);
449                 }, MoreExecutors.directExecutor());
450     }
451
452     private Requests buildRequest(final Optional<ReportedLsp> rep, final Lsp reportedLsp) {
453         // Build the request and send it
454         final RequestsBuilder rb = new RequestsBuilder();
455         final SrpBuilder srpBuilder = new SrpBuilder().addAugmentation(new Srp1Builder()
456                 .setRemove(Boolean.TRUE).build()).setOperationId(nextRequest()).setProcessingRule(Boolean.TRUE);
457         final Optional<PathSetupType> maybePST = getPST(rep);
458         if (maybePST.isPresent()) {
459             srpBuilder.setTlvs(new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful
460                     .rev200720.srp.object.srp.TlvsBuilder().setPathSetupType(maybePST.get()).build());
461         }
462         rb.setSrp(srpBuilder.build());
463         rb.setLsp(new LspBuilder().setRemove(Boolean.FALSE).setPlspId(reportedLsp.getPlspId())
464                 .setDelegate(reportedLsp.getDelegate()).build());
465         return rb.build();
466     }
467
468     private ListenableFuture<OperationResult> redelegate(final Lsp reportedLsp, final Srp srp, final Lsp lsp,
469             final UpdateLspArgs input) {
470         // the D bit that was reported decides the type of PCE message sent
471         final boolean isDelegate = requireNonNull(reportedLsp.getDelegate());
472         final Message msg;
473         if (isDelegate) {
474             // we already have delegation, send update
475             final UpdatesBuilder rb = new UpdatesBuilder();
476             rb.setSrp(srp);
477             rb.setLsp(lsp);
478             final PathBuilder pb = new PathBuilder();
479             pb.fieldsFrom(input.getArguments());
480             rb.setPath(pb.build());
481             final PcupdMessageBuilder ub = new PcupdMessageBuilder(MESSAGE_HEADER);
482             ub.setUpdates(List.of(rb.build()));
483             msg = new PcupdBuilder().setPcupdMessage(ub.build()).build();
484         } else {
485             final Lsp1 lspCreateFlag = reportedLsp.augmentation(Lsp1.class);
486             // we only retake delegation for PCE initiated tunnels
487             if (lspCreateFlag != null && !lspCreateFlag.getCreate()) {
488                 LOG.warn("Unable to retake delegation of PCC-initiated tunnel: {}", reportedLsp);
489                 return OperationResults.createUnsent(PCEPErrors.UPDATE_REQ_FOR_NON_LSP).future();
490             }
491             // we want to revoke delegation, different type of message
492             // is sent because of specification by Siva
493             // this message is also sent, when input delegate bit is set to 0
494             // generating an error in PCC
495             final List<Requests> reqs = new ArrayList<>();
496             reqs.add(new RequestsBuilder().setSrp(srp).setLsp(lsp).build());
497             final PcinitiateMessageBuilder ib = new PcinitiateMessageBuilder();
498             ib.setRequests(reqs);
499             msg = new PcinitiateBuilder().setPcinitiateMessage(ib.build()).build();
500         }
501         return sendMessage(msg, srp.getOperationId(), input.getArguments().getMetadata());
502     }
503
504     @Override
505     @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH", justification = "SB does not grok TYPE_USE")
506     public synchronized ListenableFuture<OperationResult> updateLsp(final UpdateLspArgs input) {
507         checkArgument(input != null && input.getName() != null && input.getNode() != null
508                 && input.getArguments() != null, MISSING_XML_TAG);
509         LOG.trace("UpdateLspArgs {}", input);
510         // Make sure the LSP exists
511         final InstanceIdentifier<ReportedLsp> lsp = lspIdentifier(input.getName());
512         final ListenableFuture<Optional<ReportedLsp>> f = readOperationalData(lsp);
513         return f == null ? OperationResults.createUnsent(PCEPErrors.LSP_INTERNAL_ERROR).future()
514                 : Futures.transformAsync(f, new UpdateFunction(input), MoreExecutors.directExecutor());
515     }
516
517     @Override
518     public synchronized ListenableFuture<OperationResult> ensureLspOperational(final EnsureLspOperationalInput input) {
519         checkArgument(input != null && input.getName() != null && input.getNode() != null, MISSING_XML_TAG);
520         final Arguments args = input.getArguments();
521         checkArgument(args != null, MISSING_XML_TAG);
522
523         final OperationalStatus op;
524         final Arguments1 aa = args.augmentation(Arguments1.class);
525         if (aa != null) {
526             op = aa.getOperational();
527         } else {
528             op = null;
529         }
530
531         // Make sure the LSP exists
532         final InstanceIdentifier<ReportedLsp> lsp = lspIdentifier(input.getName());
533         LOG.debug("Checking if LSP {} has operational state {}", lsp, op);
534         final ListenableFuture<Optional<ReportedLsp>> f = readOperationalData(lsp);
535         return f == null ? OperationResults.createUnsent(PCEPErrors.LSP_INTERNAL_ERROR).future()
536                 : listenableFuture(f, input, op);
537     }
538
539     private static ListenableFuture<OperationResult> listenableFuture(
540             final ListenableFuture<Optional<ReportedLsp>> future, final EnsureLspOperationalInput input,
541             final OperationalStatus op) {
542         return Futures.transform(future, rep -> {
543             if (!rep.isPresent()) {
544                 LOG.debug("Node {} does not contain LSP {}", input.getNode(), input.getName());
545                 return OperationResults.UNSENT;
546             }
547             // check if at least one of the paths has the same status as requested
548             for (final Path p : rep.get().nonnullPath().values()) {
549                 final Path1 p1 = p.augmentation(Path1.class);
550                 if (p1 == null) {
551                     LOG.warn("Node {} LSP {} does not contain data", input.getNode(), input.getName());
552                     return OperationResults.UNSENT;
553                 }
554                 if (op.equals(p1.getLsp().getOperational())) {
555                     return OperationResults.SUCCESS;
556                 }
557             }
558             return OperationResults.UNSENT;
559         }, MoreExecutors.directExecutor());
560     }
561
562     @Override
563     protected Lsp validateReportedLsp(final Optional<ReportedLsp> rep, final LspId input) {
564         if (!rep.isPresent()) {
565             LOG.debug("Node {} does not contain LSP {}", input.getNode(), input.getName());
566             return null;
567         }
568         // it doesn't matter how many lsps there are in the path list, we only need data that is the same in each path
569         final Path1 ra = rep.get().getPath().values().iterator().next().augmentation(Path1.class);
570         checkState(ra != null, "Reported LSP reported null from data-store.");
571         final Lsp reportedLsp = ra.getLsp();
572         checkState(reportedLsp != null, "Reported LSP does not contain LSP object.");
573         return reportedLsp;
574     }
575
576     private static Optional<PathSetupType> getPST(final Optional<ReportedLsp> rep) {
577         if (rep.isPresent()) {
578             final Path1 path1 = rep.get().getPath().values().iterator().next().augmentation(Path1.class);
579             if (path1 != null) {
580                 final PathSetupType pst = path1.getPathSetupType();
581                 if (!PSTUtil.isDefaultPST(pst)) {
582                     return Optional.of(pst);
583                 }
584             }
585         }
586         return Optional.empty();
587     }
588
589     /**
590      * Recover lspData and mark any LSPs in the LSP database that were previously reported by the PCC as stale.
591      */
592     @Override
593     protected synchronized void loadLspData(final Node node, final Map<String, ReportedLsp> lspData,
594             final Map<PlspId, String> lsps, final boolean incrementalSynchro) {
595         //load node's lsps from DS
596         final PathComputationClient pcc = node.augmentation(Node1.class).getPathComputationClient();
597         for (final ReportedLsp reportedLsp : pcc.nonnullReportedLsp().values()) {
598             final String lspName = reportedLsp.getName();
599             lspData.put(lspName, reportedLsp);
600             if (!reportedLsp.getPath().isEmpty()) {
601                 final Path1 path1 = reportedLsp.getPath().values().iterator().next().augmentation(Path1.class);
602                 if (path1 != null) {
603                     final PlspId plspId = path1.getLsp().getPlspId();
604                     if (!incrementalSynchro) {
605                         staleLsps.add(plspId);
606                     }
607                     lsps.put(plspId, lspName);
608                 }
609             }
610         }
611     }
612
613     /**
614      * When the PCC reports an LSP during state synchronization, if the LSP already
615      * exists in the LSP database, the PCE MUST update the LSP database and
616      * clear the stale marker from the LSP.
617      *
618      * @param plspId id
619      */
620     private synchronized void unmarkStaleLsp(final PlspId plspId) {
621         staleLsps.remove(plspId);
622     }
623
624     /**
625      * Purge any LSPs from the LSP database that are still marked as stale.
626      *
627      * @param ctx message context
628      */
629     private synchronized void purgeStaleLsps(final MessageContext ctx) {
630         for (final PlspId plspId : staleLsps) {
631             removeLsp(ctx, plspId);
632         }
633         staleLsps.clear();
634     }
635
636     private class ResyncLspFunction implements AsyncFunction<Optional<ReportedLsp>, OperationResult> {
637         private final TriggerSyncArgs input;
638
639         ResyncLspFunction(final TriggerSyncArgs input) {
640             this.input = input;
641         }
642
643         @Override
644         public ListenableFuture<OperationResult> apply(final Optional<ReportedLsp> rep) {
645             final Lsp reportedLsp = validateReportedLsp(rep, input);
646             if (reportedLsp == null || !rep.isPresent()) {
647                 return OperationResults.createUnsent(PCEPErrors.UNKNOWN_PLSP_ID).future();
648             }
649             // mark lsp as stale
650             final ReportedLsp staleLsp = rep.get();
651             if (!staleLsp.getPath().isEmpty()) {
652                 final Path1 path1 = staleLsp.getPath().values().iterator().next().augmentation(Path1.class);
653                 if (path1 != null) {
654                     staleLsps.add(path1.getLsp().getPlspId());
655                 }
656             }
657             updatePccState(PccSyncState.PcepTriggeredResync);
658             // create PCUpd with mandatory objects and LSP object set to 1
659             final SrpBuilder srpBuilder = new SrpBuilder();
660             srpBuilder.setOperationId(nextRequest());
661             srpBuilder.setProcessingRule(Boolean.TRUE);
662
663             final Optional<PathSetupType> maybePST = getPST(rep);
664             if (maybePST.isPresent()) {
665                 srpBuilder.setTlvs(
666                         new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful
667                                 .rev200720.srp.object.srp.TlvsBuilder()
668                                 .setPathSetupType(maybePST.get()).build());
669             }
670
671             final Srp srp = srpBuilder.build();
672             final Lsp lsp = new LspBuilder().setPlspId(reportedLsp.getPlspId()).setSync(Boolean.TRUE).build();
673
674             final Message msg = createPcepUpd(srp, lsp);
675             return sendMessage(msg, srp.getOperationId(), null);
676         }
677
678         private Message createPcepUpd(final Srp srp, final Lsp lsp) {
679             return new PcupdBuilder()
680                 .setPcupdMessage(new PcupdMessageBuilder(MESSAGE_HEADER)
681                     .setUpdates(List.of(new UpdatesBuilder()
682                         .setSrp(srp)
683                         .setLsp(lsp)
684                         .setPath(new PathBuilder().build())
685                         .build()))
686                     .build())
687                 .build();
688         }
689     }
690
691     private class AddFunction implements AsyncFunction<Optional<ReportedLsp>, OperationResult> {
692
693         private final AddLspArgs input;
694         private final InstanceIdentifier<ReportedLsp> lsp;
695
696         AddFunction(final AddLspArgs input, final InstanceIdentifier<ReportedLsp> lsp) {
697             this.input = input;
698             this.lsp = lsp;
699         }
700
701         @Override
702         public ListenableFuture<OperationResult> apply(final Optional<ReportedLsp> rep) {
703             if (rep.isPresent()) {
704                 LOG.debug("Node {} already contains lsp {} at {}", input.getNode(), input.getName(), lsp);
705                 return OperationResults.createUnsent(PCEPErrors.USED_SYMBOLIC_PATH_NAME).future();
706             }
707             if (!isInitiationCapability()) {
708                 return OperationResults.createUnsent(PCEPErrors.CAPABILITY_NOT_SUPPORTED).future();
709             }
710
711             // Build the request
712             final RequestsBuilder rb = new RequestsBuilder();
713             final var args = input.getArguments();
714             final Arguments2 args2 = args.augmentation(Arguments2.class);
715             final Lsp inputLsp = args2 != null ? args2.getLsp() : null;
716             if (inputLsp == null) {
717                 return OperationResults.createUnsent(PCEPErrors.LSP_MISSING).future();
718             }
719
720             rb.fieldsFrom(input.getArguments());
721
722             boolean segmentRouting = !PSTUtil.isDefaultPST(args2.getPathSetupType());
723
724             /* Call Path Computation if an ERO was not provided */
725             if (rb.getEro() == null || rb.getEro().nonnullSubobject().isEmpty()) {
726                 /* Get a Path Computation to compute the Path from the Arguments */
727                 // TODO: Adjust Junit Test to avoid this test
728                 if (pceServerProvider == null) {
729                     return OperationResults.createUnsent(PCEPErrors.ERO_MISSING).future();
730                 }
731                 PathComputation pathComputation = pceServerProvider.getPathComputation();
732                 if (pathComputation == null) {
733                     return OperationResults.createUnsent(PCEPErrors.ERO_MISSING).future();
734                 }
735                 rb.setEro(pathComputation.computeEro(args.getEndpointsObj(), args.getBandwidth(),
736                         args.getClassType(), args.getMetrics(), args.getXro(), args.getIro(), segmentRouting));
737             }
738
739             final TlvsBuilder tlvsBuilder;
740             if (inputLsp.getTlvs() != null) {
741                 tlvsBuilder = new TlvsBuilder(inputLsp.getTlvs());
742             } else {
743                 tlvsBuilder = new TlvsBuilder();
744             }
745             tlvsBuilder.setSymbolicPathName(
746                     new SymbolicPathNameBuilder().setPathName(new SymbolicPathName(input.getName()
747                             .getBytes(StandardCharsets.UTF_8))).build());
748
749             final SrpBuilder srpBuilder = new SrpBuilder()
750                     .setOperationId(nextRequest())
751                     .setProcessingRule(Boolean.TRUE);
752             if (segmentRouting) {
753                 srpBuilder.setTlvs(
754                         new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf
755                                 .stateful.rev200720.srp.object.srp.TlvsBuilder()
756                                 .setPathSetupType(args2.getPathSetupType()).build());
757             }
758             rb.setSrp(srpBuilder.build());
759
760             rb.setLsp(new LspBuilder()
761                 .setAdministrative(inputLsp.getAdministrative())
762                 .setDelegate(inputLsp.getDelegate())
763                 .setPlspId(PLSPID_ZERO)
764                 .setTlvs(tlvsBuilder.build())
765                 .build());
766
767             // Send the message
768             return sendMessage(new PcinitiateBuilder()
769                 .setPcinitiateMessage(new PcinitiateMessageBuilder(MESSAGE_HEADER)
770                     .setRequests(List.of(rb.build()))
771                     .build())
772                 .build(),
773                 rb.getSrp().getOperationId(), input.getArguments().getMetadata());
774         }
775     }
776
777     private class UpdateFunction implements AsyncFunction<Optional<ReportedLsp>, OperationResult> {
778
779         private final UpdateLspArgs input;
780
781         UpdateFunction(final UpdateLspArgs input) {
782             this.input = input;
783         }
784
785         @Override
786         public ListenableFuture<OperationResult> apply(final Optional<ReportedLsp> rep) {
787             final Lsp reportedLsp = validateReportedLsp(rep, input);
788             if (reportedLsp == null) {
789                 return OperationResults.createUnsent(PCEPErrors.UNKNOWN_PLSP_ID).future();
790             }
791             // create mandatory objects
792             final Arguments3 args = input.getArguments().augmentation(Arguments3.class);
793             final SrpBuilder srpBuilder = new SrpBuilder();
794             srpBuilder.setOperationId(nextRequest());
795             srpBuilder.setProcessingRule(Boolean.TRUE);
796             if (args != null && args.getPathSetupType() != null) {
797                 if (!PSTUtil.isDefaultPST(args.getPathSetupType())) {
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(args.getPathSetupType()).build());
802                 }
803             } else {
804                 final Optional<PathSetupType> maybePST = getPST(rep);
805                 if (maybePST.isPresent()) {
806                     srpBuilder.setTlvs(
807                             new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful
808                                     .rev200720.srp.object.srp.TlvsBuilder()
809                                     .setPathSetupType(maybePST.get()).build());
810                 }
811             }
812             final Srp srp = srpBuilder.build();
813             final Lsp inputLsp = args != null ? args.getLsp() : null;
814             final LspBuilder lspBuilder = new LspBuilder().setPlspId(reportedLsp.getPlspId());
815             if (inputLsp != null) {
816                 lspBuilder.setDelegate(Boolean.TRUE.equals(inputLsp.getDelegate()))
817                         .setTlvs(inputLsp.getTlvs())
818                         .setAdministrative(Boolean.TRUE.equals(inputLsp.getAdministrative()));
819             }
820             return redelegate(reportedLsp, srp, lspBuilder.build(), input);
821         }
822     }
823
824     private static Pcerr createErrorMsg(@NonNull final PCEPErrors pcepErrors, final Uint32 reqID) {
825         return new PcerrBuilder()
826             .setPcerrMessage(new PcerrMessageBuilder()
827                 .setErrorType(new RequestCaseBuilder()
828                     .setRequest(new RequestBuilder()
829                         .setRps(List.of(new RpsBuilder()
830                             .setRp(new RpBuilder()
831                                 .setProcessingRule(false)
832                                 .setIgnore(false)
833                                 .setRequestId(new RequestId(reqID))
834                                 .build())
835                             .build()))
836                         .build())
837                     .build())
838                 .setErrors(List.of(new ErrorsBuilder()
839                     .setErrorObject(new ErrorObjectBuilder()
840                         .setType(pcepErrors.getErrorType())
841                         .setValue(pcepErrors.getErrorValue())
842                         .build())
843                     .build()))
844                 .build())
845             .build();
846     }
847 }