2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.bgpcep.pcep.topology.provider;
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;
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;
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;
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);
117 public Class<MessageHeader> implementedInterface() {
118 return MessageHeader.class;
122 public ProtocolVersion getVersion() {
127 private final AtomicLong requestId = new AtomicLong(1L);
130 private final List<PlspId> staleLsps = new ArrayList<>();
132 private final PceServerProvider pceServerProvider;
135 * Creates a new stateful topology session listener for given server session manager.
137 PCEPTopologySessionListener(final ServerSessionManager serverSessionManager) {
138 super(serverSessionManager);
139 pceServerProvider = serverSessionManager.getPCEPTopologyProviderDependencies().getPceServerProvider();
142 private static LspDbVersion geLspDbVersionTlv(final Lsp lsp) {
143 final var tlvs = lsp.getTlvs();
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);
148 return tlvs1.getLspDbVersion();
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);
162 return OperationResults.UNSENT.future();
165 private ListenableFuture<OperationResult> triggerLspSyncronization(final TriggerSyncArgs input) {
166 LOG.trace("Trigger Lsp Resynchronization {}", input);
168 // Make sure the LSP exists
169 final InstanceIdentifier<ReportedLsp> lsp = lspIdentifier(input.getName());
170 final FluentFuture<Optional<ReportedLsp>> f = readOperationalData(lsp);
172 return OperationResults.createUnsent(PCEPErrors.LSP_INTERNAL_ERROR).future();
174 return Futures.transformAsync(f, new ResyncLspFunction(input), MoreExecutors.directExecutor());
177 private ListenableFuture<OperationResult> triggerResyncronization(final TriggerSyncArgs input) {
178 LOG.trace("Trigger Resynchronization {}", input);
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);
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);
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();
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())
212 private void markAllLspAsStale() {
213 staleLsps.addAll(lsps.keySet());
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);
225 req.finish(OperationResults.createFailed(errMsg.getErrors()));
227 LOG.warn("Request ID {} not found in outstanding DB", id);
232 LOG.warn("Unhandled PCErr message {}.", errMsg);
238 private boolean isSolicited(final Srp srp, final Lsp lsp, final MessageContext ctx, final ReportedLspBuilder rlb) {
242 final SrpIdNumber id = srp.getOperationId();
243 if (SRPID_ZERO.equals(id)) {
246 switch (lsp.getOperational()) {
250 if (!isTriggeredSyncInProcess()) {
251 final PCEPRequest req = removeRequest(id);
253 LOG.debug("Request {} resulted in LSP operational state {}", id, lsp.getOperational());
254 rlb.setMetadata(req.getMetadata());
255 ctx.resolveRequest(req);
257 LOG.warn("Request ID {} not found in outstanding DB", id);
263 // These are transitive states, so we don't have to do anything, as they will be followed
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();
278 if (!lsp.getSync() && (plspid == null || PLSPID_ZERO.equals(plspid))) {
280 if (isTriggeredSyncInProcess()) {
284 final SrpIdNumber id = srp.getOperationId();
285 if (SRPID_ZERO.equals(id)) {
288 final PCEPRequest req = removeRequest(id);
289 ctx.resolveRequest(req);
291 stateSynchronizationAchieved(ctx);
294 final ReportedLspBuilder rlb = new ReportedLspBuilder();
295 boolean solicited = false;
296 solicited = isSolicited(srp, lsp, ctx, rlb);
298 // if remove flag is set in SRP object, remove the tunnel immediately
300 final Srp1 initiatedSrp = srp.augmentation(Srp1.class);
301 if (initiatedSrp != null && initiatedSrp.getRemove()) {
302 super.removeLsp(ctx, plspid);
306 rlb.setPath(BindingMap.of(buildPath(report, srp, lsp)));
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();
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());
319 updateLsp(ctx, plspid, name, rlb, solicited, lsp.getRemove());
320 unmarkStaleLsp(plspid);
322 LOG.debug("LSP {} updated", lsp);
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());
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);
345 pb.addAugmentation(p1Builder.build());
346 final var tlvs = report.getLsp().getTlvs();
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()));
358 private boolean handlePcreqMessage(final PcreqMessage message) {
360 LOG.info("Start PcRequest Message handler");
362 /* Get a Path Computation to compute the Path from the Request */
363 PathComputation pathComputation = pceServerProvider.getPathComputation();
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);
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());
378 repId = new SrpIdNumber(Uint32.ZERO);
380 sendMessage(rep, repId, null);
386 protected synchronized boolean onMessage(final MessageContext ctx, final Message message) {
387 if (message instanceof PcerrMessage) {
388 return handleErrorMessage((PcerrMessage) message);
390 if (message instanceof Pcreq) {
391 LOG.info("PcReq detected. Start Request Message handler");
392 return handlePcreqMessage(((Pcreq) message).getPcreqMessage());
394 if (!(message instanceof PcrptMessage)) {
397 listenerState.updateLastReceivedRptMsg();
398 final var rpt = ((PcrptMessage) message).getPcrptMessage();
399 for (final Reports report : rpt.nonnullReports()) {
400 if (!manageNextReport(report, ctx)) {
407 private SrpIdNumber nextRequest() {
408 return new SrpIdNumber(Uint32.valueOf(requestId.getAndIncrement()));
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());
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();
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());
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());
456 rb.setSrp(srpBuilder.build());
457 rb.setLsp(new LspBuilder().setRemove(Boolean.FALSE).setPlspId(reportedLsp.getPlspId())
458 .setDelegate(reportedLsp.getDelegate()).build());
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());
470 // we already have delegation, send update
471 final UpdatesBuilder rb = new UpdatesBuilder();
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();
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();
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();
497 return sendMessage(msg, srp.getOperationId(), input.getArguments().getMetadata());
501 @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH", justification = "SB does not grok TYPE_USE")
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());
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);
520 final OperationalStatus op;
521 final Arguments1 aa = args.augmentation(Arguments1.class);
523 op = aa.getOperational();
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);
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;
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);
548 LOG.warn("Node {} LSP {} does not contain data", input.getNode(), input.getName());
549 return OperationResults.UNSENT;
551 if (op.equals(p1.getLsp().getOperational())) {
552 return OperationResults.SUCCESS;
555 return OperationResults.UNSENT;
556 }, MoreExecutors.directExecutor());
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());
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.");
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);
577 final PathSetupType pst = path1.getPathSetupType();
578 if (!PSTUtil.isDefaultPST(pst)) {
579 return Optional.of(pst);
583 return Optional.empty();
587 * Recover lspData and mark any LSPs in the LSP database that were previously reported by the PCC as stale.
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);
600 final PlspId plspId = path1.getLsp().getPlspId();
601 if (!incrementalSynchro) {
602 staleLsps.add(plspId);
604 lsps.put(plspId, lspName);
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.
617 private synchronized void unmarkStaleLsp(final PlspId plspId) {
618 staleLsps.remove(plspId);
622 * Purge any LSPs from the LSP database that are still marked as stale.
624 * @param ctx message context
626 private synchronized void purgeStaleLsps(final MessageContext ctx) {
627 for (final PlspId plspId : staleLsps) {
628 removeLsp(ctx, plspId);
633 private class ResyncLspFunction implements AsyncFunction<Optional<ReportedLsp>, OperationResult> {
634 private final TriggerSyncArgs input;
636 ResyncLspFunction(final TriggerSyncArgs input) {
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();
647 final ReportedLsp staleLsp = rep.get();
648 if (!staleLsp.getPath().isEmpty()) {
649 final Path1 path1 = staleLsp.getPath().values().iterator().next().augmentation(Path1.class);
651 staleLsps.add(path1.getLsp().getPlspId());
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);
660 final Optional<PathSetupType> maybePST = getPST(rep);
661 if (maybePST.isPresent()) {
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());
668 final Srp srp = srpBuilder.build();
669 final Lsp lsp = new LspBuilder().setPlspId(reportedLsp.getPlspId()).setSync(Boolean.TRUE).build();
671 final Message msg = createPcepUpd(srp, lsp);
672 return sendMessage(msg, srp.getOperationId(), null);
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()
681 .setPath(new PathBuilder().build())
688 private class AddFunction implements AsyncFunction<Optional<ReportedLsp>, OperationResult> {
690 private final AddLspArgs input;
691 private final InstanceIdentifier<ReportedLsp> lsp;
693 AddFunction(final AddLspArgs input, final InstanceIdentifier<ReportedLsp> lsp) {
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();
704 if (!isInitiationCapability()) {
705 return OperationResults.createUnsent(PCEPErrors.CAPABILITY_NOT_SUPPORTED).future();
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();
717 rb.fieldsFrom(input.getArguments());
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();
727 rb.setEro(pathComputation.computeEro(args.getEndpointsObj(), args.getBandwidth(), args.getClassType(),
728 args.getMetrics(), segmentRouting));
731 final TlvsBuilder tlvsBuilder;
732 if (inputLsp.getTlvs() != null) {
733 tlvsBuilder = new TlvsBuilder(inputLsp.getTlvs());
735 tlvsBuilder = new TlvsBuilder();
737 tlvsBuilder.setSymbolicPathName(
738 new SymbolicPathNameBuilder().setPathName(new SymbolicPathName(input.getName()
739 .getBytes(StandardCharsets.UTF_8))).build());
741 final SrpBuilder srpBuilder = new SrpBuilder()
742 .setOperationId(nextRequest())
743 .setProcessingRule(Boolean.TRUE);
744 if (segmentRouting) {
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());
750 rb.setSrp(srpBuilder.build());
752 rb.setLsp(new LspBuilder()
753 .setAdministrative(inputLsp.getAdministrative())
754 .setDelegate(inputLsp.getDelegate())
755 .setPlspId(PLSPID_ZERO)
756 .setTlvs(tlvsBuilder.build())
760 return sendMessage(new PcinitiateBuilder()
761 .setPcinitiateMessage(new PcinitiateMessageBuilder(MESSAGE_HEADER)
762 .setRequests(List.of(rb.build()))
765 rb.getSrp().getOperationId(), input.getArguments().getMetadata());
769 private class UpdateFunction implements AsyncFunction<Optional<ReportedLsp>, OperationResult> {
771 private final UpdateLspArgs input;
773 UpdateFunction(final UpdateLspArgs input) {
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();
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())) {
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());
796 final Optional<PathSetupType> maybePST = getPST(rep);
797 if (maybePST.isPresent()) {
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());
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()));
812 return redelegate(reportedLsp, srp, lspBuilder.build(), input);
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)
825 .setRequestId(new RequestId(reqID))
830 .setErrors(List.of(new ErrorsBuilder()
831 .setErrorObject(new ErrorObjectBuilder()
832 .setType(pcepErrors.getErrorType())
833 .setValue(pcepErrors.getErrorValue())