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