2 * Copyright (c) 2015 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.protocol.pcep.pcc.mock;
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createLsp;
12 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createLspTlvs;
13 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createLspTlvsEndofSync;
14 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createPath;
15 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createPcRtpMessage;
16 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createSrp;
17 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.reqToRptPath;
18 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.updToRptPath;
20 import com.google.common.base.Preconditions;
21 import com.google.common.net.InetAddresses;
22 import io.netty.util.Timeout;
23 import io.netty.util.Timer;
24 import java.net.InetAddress;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
29 import java.util.Map.Entry;
30 import java.util.Optional;
31 import java.util.concurrent.TimeUnit;
32 import java.util.concurrent.atomic.AtomicLong;
33 import org.checkerframework.checker.lock.qual.GuardedBy;
34 import org.opendaylight.protocol.pcep.pcc.mock.api.LspType;
35 import org.opendaylight.protocol.pcep.pcc.mock.api.PCCSession;
36 import org.opendaylight.protocol.pcep.pcc.mock.api.PCCTunnelManager;
37 import org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil;
38 import org.opendaylight.protocol.pcep.spi.PCEPErrors;
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.Lsp1Builder;
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.pcinitiate.message.pcinitiate.message.Requests;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.OperationalStatus;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.Pcrpt;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.PlspId;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.SrpIdNumber;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.lsp.object.Lsp;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.lsp.object.LspBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.lsp.object.lsp.Tlvs;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.pcrpt.message.pcrpt.message.reports.Path;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.pcrpt.message.pcrpt.message.reports.PathBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.pcupd.message.pcupd.message.Updates;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.srp.object.Srp;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev181109.srp.object.SrpBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.explicit.route.object.ero.Subobject;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820.basic.explicit.route.subobjects.subobject.type.IpPrefixCase;
58 import org.opendaylight.yangtools.yang.common.Uint32;
59 import org.opendaylight.yangtools.yang.common.Uint64;
61 public final class PCCTunnelManagerImpl implements PCCTunnelManager {
63 private static final Optional<Srp> NO_SRP = Optional.empty();
65 private final Map<Integer, PCCSession> sessions = new HashMap<>();
66 private final AtomicLong plspIDsCounter;
67 private final String address;
68 private final Timer timer;
69 private final int redelegationTimeout;
70 private final int stateTimeout;
71 private final int lspsCount;
72 private final Optional<TimerHandler> timerHandler;
74 private final Map<PlspId, PCCTunnel> tunnels = new HashMap<>();
75 private PCCSyncOptimization syncOptimization;
77 public PCCTunnelManagerImpl(final int lspsCount, final InetAddress address, final int redelegationTimeout,
78 final int stateTimeout, final Timer timer, final Optional<TimerHandler> timerHandler) {
79 Preconditions.checkArgument(lspsCount >= 0);
80 this.redelegationTimeout = redelegationTimeout;
81 this.stateTimeout = stateTimeout;
82 this.plspIDsCounter = new AtomicLong(lspsCount);
83 this.address = InetAddresses.toAddrString(requireNonNull(address));
84 this.timer = requireNonNull(timer);
85 this.timerHandler = timerHandler;
86 this.lspsCount = lspsCount;
89 protected void reportToAll(final Updates update, final PCCSession session) {
90 final PlspId plspId = update.getLsp().getPlspId();
91 final PCCTunnel tunnel = this.tunnels.get(plspId);
92 final Uint32 srpId = update.getSrp().getOperationId().getValue();
94 if (hasDelegation(tunnel, session)) {
95 final Srp srp = createSrp(srpId);
96 final Path path = updToRptPath(update.getPath());
97 final List<Subobject> subobjects = update.getPath().getEro().getSubobject();
98 final Lsp lsp = update.getLsp();
99 sendToAll(tunnel, plspId, subobjects, srp, path, lsp);
100 //update tunnel state
101 tunnel.setLspState(path);
103 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UPDATE_REQ_FOR_NON_LSP, srpId));
106 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UNKNOWN_PLSP_ID, srpId));
110 private void returnDelegation(final Updates update, final PCCSession session) {
111 final PlspId plspId = update.getLsp().getPlspId();
112 final PCCTunnel tunnel = this.tunnels.get(plspId);
113 final Uint32 srpId = update.getSrp().getOperationId().getValue();
114 if (tunnel != null) {
115 //check if session really has a delegation
116 if (hasDelegation(tunnel, session)) {
118 final Tlvs tlvs = buildTlvs(tunnel, plspId.getValue(), Optional.empty());
119 final Pcrpt pcrtp = createPcRtpMessage(new LspBuilder(update.getLsp()).setSync(true)
120 .setOperational(OperationalStatus.Up).setDelegate(false)
121 .setTlvs(tlvs).build(), Optional.of(createSrp(srpId)), tunnel.getLspState());
122 session.sendReport(pcrtp);
124 startStateTimeout(tunnel, plspId);
125 //if PCC's LSP, start re-delegation timer
126 if (tunnel.getType() == LspType.PCC_LSP) {
127 startRedelegationTimer(tunnel, plspId, session);
129 //if PCE-initiated LSP, revoke delegation instantly
130 setDelegation(plspId, null);
133 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UPDATE_REQ_FOR_NON_LSP, srpId));
136 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UNKNOWN_PLSP_ID, srpId));
140 protected void takeDelegation(final Requests request, final PCCSession session) {
141 final PlspId plspId = request.getLsp().getPlspId();
142 final PCCTunnel tunnel = this.tunnels.get(plspId);
143 final Uint32 srpId = request.getSrp().getOperationId().getValue();
144 if (tunnel != null) {
145 //check if tunnel has no delegation
146 if (tunnel.getType() == LspType.PCE_LSP && (tunnel.getDelegationHolder() == -1
147 || tunnel.getDelegationHolder() == session.getId())) {
149 tunnel.cancelTimeouts();
150 setDelegation(plspId, session);
152 final Tlvs tlvs = buildTlvs(tunnel, plspId.getValue(), Optional.empty());
153 session.sendReport(createPcRtpMessage(
154 new LspBuilder(request.getLsp()).setSync(true).setOperational(OperationalStatus.Up)
155 .setDelegate(true).setTlvs(tlvs).build(),
156 Optional.of(createSrp(srpId)), tunnel.getLspState()));
158 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.LSP_NOT_PCE_INITIATED, srpId));
161 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UNKNOWN_PLSP_ID, srpId));
166 public synchronized void onSessionUp(final PCCSession session) {
167 this.syncOptimization = new PCCSyncOptimization(session);
168 lazyTunnelInicialization();
170 //first session - delegate all PCC's LSPs only when reporting at startup
171 if (!this.sessions.containsKey(session.getId()) && session.getId() == 0) {
172 for (final PlspId plspId : this.tunnels.keySet()) {
173 setDelegation(plspId, session);
176 this.sessions.put(session.getId(), session);
178 if (!this.syncOptimization.isTriggeredInitSyncEnabled()) {
184 public synchronized void onSessionDown(final PCCSession session) {
185 for (final Entry<PlspId, PCCTunnel> entry : this.tunnels.entrySet()) {
186 final PCCTunnel tunnel = entry.getValue();
187 final PlspId plspId = entry.getKey();
188 //deal with delegations
189 if (hasDelegation(tunnel, session)) {
190 startStateTimeout(tunnel, entry.getKey());
191 startRedelegationTimer(tunnel, plspId, session);
196 protected void addTunnel(final Requests request, final PCCSession session) {
197 final PlspId plspId = new PlspId(Uint32.valueOf(this.plspIDsCounter.incrementAndGet()));
198 final PCCTunnel tunnel = new PCCTunnel(request.getLsp().getTlvs().getSymbolicPathName()
199 .getPathName().getValue(), session.getId(), LspType.PCE_LSP, reqToRptPath(request));
200 sendToAll(tunnel, plspId, request.getEro().getSubobject(),
201 createSrp(request.getSrp().getOperationId().getValue()), tunnel.getLspState(),
202 new LspBuilder(request.getLsp())
203 .addAugmentation(Lsp1.class, new Lsp1Builder().setCreate(true).build()).build());
204 this.tunnels.put(plspId, tunnel);
207 protected void removeTunnel(final Requests request, final PCCSession session) {
208 final PlspId plspId = request.getLsp().getPlspId();
209 final PCCTunnel tunnel = this.tunnels.get(plspId);
210 final Uint32 srpId = request.getSrp().getOperationId().getValue();
211 if (tunnel != null) {
212 if (tunnel.getType() == LspType.PCE_LSP) {
213 if (hasDelegation(tunnel, session)) {
214 this.tunnels.remove(plspId);
215 sendToAll(tunnel, plspId, tunnel.getLspState().getEro().getSubobject(),
216 new SrpBuilder(request.getSrp())
217 .addAugmentation(Srp1.class, new Srp1Builder().setRemove(true).build()).build(),
218 reqToRptPath(request), request.getLsp());
220 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UPDATE_REQ_FOR_NON_LSP, srpId));
223 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.LSP_NOT_PCE_INITIATED, srpId));
226 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UNKNOWN_PLSP_ID, srpId));
231 public void onMessagePcupd(final Updates update, final PCCSession session) {
232 final Lsp lsp = update.getLsp();
233 if (isInitialSyncTriggered(lsp)) {
235 if (this.timerHandler.isPresent()) {
236 this.timerHandler.get().createDisconnectTask();
238 } else if (isReSyncTriggered(lsp)) {
239 handledDbTriggeredResync(update, session);
240 } else if (lsp.isDelegate() != null && lsp.isDelegate()) {
242 reportToAll(update, session);
244 //returning LSP delegation
245 returnDelegation(update, session);
250 public void onMessagePcInitiate(final Requests request, final PCCSession session) {
251 if (request.getSrp().augmentation(Srp1.class) != null
252 && request.getSrp().augmentation(Srp1.class).isRemove()) {
254 removeTunnel(request, session);
255 } else if (request.getLsp().isDelegate() != null && request.getLsp().isDelegate()
256 && request.getEndpointsObj() == null) {
257 //take LSP delegation
258 takeDelegation(request, session);
261 addTunnel(request, session);
265 private Tlvs buildTlvs(final PCCTunnel tunnel, final Uint32 plspId,
266 final Optional<List<Subobject>> subobjectsList) {
267 final List<Subobject> subObject = subobjectsList.isPresent() ? subobjectsList.get() :
268 tunnel.getLspState().getEro().getSubobject();
269 final String destinationAddress = getDestinationAddress(subObject, this.address);
271 return createLspTlvs(plspId, true, destinationAddress, this.address, this.address,
272 Optional.of(tunnel.getPathName()), this.syncOptimization.incrementLspDBVersion());
275 private synchronized void lazyTunnelInicialization() {
276 if (this.tunnels.isEmpty()) {
277 final Uint64 dbV = this.syncOptimization.getLocalLspDbVersionValue();
278 if (dbV != null && this.syncOptimization.isSyncAvoidanceEnabled() && !dbV.equals(Uint64.ONE)) {
279 this.tunnels.putAll(PCCTunnelBuilder.createTunnels(this.address, dbV.intValue()));
281 this.tunnels.putAll(PCCTunnelBuilder.createTunnels(this.address, this.lspsCount));
286 private boolean isReSyncTriggered(final Lsp lsp) {
287 return this.syncOptimization.isTriggeredReSyncEnabled() && lsp.isSync();
290 private boolean isInitialSyncTriggered(final Lsp lsp) {
291 return lsp.getPlspId().getValue().toJava() == 0 && lsp.isSync()
292 && this.syncOptimization.isTriggeredInitSyncEnabled();
295 private void handledDbTriggeredResync(final Updates update, final PCCSession session) {
296 this.syncOptimization.setResynchronizingState(Boolean.TRUE);
297 final SrpIdNumber operationId = update.getSrp().getOperationId();
298 if (update.getLsp().getPlspId().getValue().toJava() == 0) {
299 reportAllKnownLsp(Optional.of(operationId), session);
301 reportLsp(update.getLsp().getPlspId(), operationId, session);
303 sendEndOfSynchronization(session, Optional.of(operationId));
304 this.syncOptimization.setResynchronizingState(Boolean.FALSE);
307 private void lspReport(final PCCSession session) {
308 if (!this.tunnels.isEmpty()) {
309 if (!this.syncOptimization.isSyncAvoidanceEnabled()) {
310 reportAllKnownLsp(session);
311 sendEndOfSynchronization(session);
312 } else if (!this.syncOptimization.doesLspDbMatch()) {
313 if (this.syncOptimization.isDeltaSyncEnabled()) {
314 reportMissedLsp(session);
315 sendEndOfSynchronization(session);
317 reportAllKnownLsp(session);
318 sendEndOfSynchronization(session);
325 * Reports Missed Lsp when DbVersion doesnt match.
327 private void reportMissedLsp(final PCCSession session) {
328 for (long missedLsp = this.syncOptimization.getRemoteLspDbVersionValue().longValue() + 1;
329 missedLsp <= this.syncOptimization.getLocalLspDbVersionValue().longValue(); missedLsp++) {
330 final Uint32 missed = Uint32.valueOf(missedLsp);
331 final PlspId plspId = new PlspId(missed);
332 final PCCTunnel tunnel = this.tunnels.get(plspId);
333 createLspAndSendReport(missed, tunnel, session, Optional.empty(), NO_SRP);
337 private void createLspAndSendReport(final Uint32 plspId, final PCCTunnel tunnel, final PCCSession session,
338 final Optional<Boolean> isSync, final Optional<Srp> srp) {
339 final boolean delegation = hasDelegation(tunnel, session);
341 tunnel.cancelTimeouts();
343 final String destinationAddress
344 = getDestinationAddress(tunnel.getLspState().getEro().getSubobject(), this.address);
345 final Tlvs tlvs = createLspTlvs(plspId, true, destinationAddress, this.address,
346 this.address, Optional.of(tunnel.getPathName()), this.syncOptimization.incrementLspDBVersion());
348 final boolean sync = isSync.isPresent() ? isSync.get() : this.syncOptimization.isSyncNeedIt();
349 final Lsp lsp = createLsp(plspId, sync, Optional.ofNullable(tlvs), delegation, false);
350 final Pcrpt pcrtp = createPcRtpMessage(lsp, srp, tunnel.getLspState());
351 session.sendReport(pcrtp);
354 private void sendEndOfSynchronization(final PCCSession session) {
355 sendEndOfSynchronization(session, Optional.empty());
358 private void sendEndOfSynchronization(final PCCSession session, final Optional<SrpIdNumber> operationId) {
360 if (operationId.isPresent()) {
361 srp = new SrpBuilder().setOperationId(operationId.get()).build();
363 Optional<Tlvs> tlv = Optional.empty();
364 if (this.syncOptimization.isSyncAvoidanceEnabled()) {
365 tlv = createLspTlvsEndofSync(this.syncOptimization.incrementLspDBVersion().get());
367 final Pcrpt pcrtp = createPcRtpMessage(createLsp(Uint32.ZERO, false, tlv, true, false),
368 Optional.ofNullable(srp), createPath(Collections.emptyList()));
369 session.sendReport(pcrtp);
372 private void reportAllKnownLsp(final PCCSession session) {
373 reportAllKnownLsp(Optional.empty(), session);
376 private void reportAllKnownLsp(final Optional<SrpIdNumber> operationId, final PCCSession session) {
378 if (operationId.isPresent()) {
379 srp = new SrpBuilder().setOperationId(operationId.get()).build();
382 for (final Entry<PlspId, PCCTunnel> entry : this.tunnels.entrySet()) {
383 final PCCTunnel tunnel = entry.getValue();
384 final Uint32 plspId = entry.getKey().getValue();
385 createLspAndSendReport(plspId, tunnel, session, Optional.empty(), Optional.ofNullable(srp));
389 private void reportLsp(final PlspId plspId, final SrpIdNumber operationId, final PCCSession session) {
390 final PCCTunnel tunnel = this.tunnels.get(plspId);
391 if (tunnel == null) {
394 final Srp srp = new SrpBuilder().setOperationId(operationId).build();
395 createLspAndSendReport(plspId.getValue(), tunnel, session, Optional.of(Boolean.TRUE), Optional.of(srp));
398 private void sendToAll(final PCCTunnel tunnel, final PlspId plspId, final List<Subobject> subobjects, final Srp srp,
399 final Path path, final Lsp lsp) {
400 for (final PCCSession session : this.sessions.values()) {
401 final boolean isDelegated = hasDelegation(tunnel, session);
402 final Tlvs tlvs = buildTlvs(tunnel, plspId.getValue(), Optional.of(subobjects));
404 final Pcrpt pcRpt = createPcRtpMessage(
407 .setOperational(OperationalStatus.Up)
408 .setDelegate(isDelegated)
410 .addAugmentation(Lsp1.class, new Lsp1Builder()
411 .setCreate(tunnel.getType() == LspType.PCE_LSP).build())
412 .setTlvs(tlvs).build(),
413 Optional.ofNullable(srp), path);
414 session.sendReport(pcRpt);
418 private void startStateTimeout(final PCCTunnel tunnel, final PlspId plspId) {
419 if (this.stateTimeout > -1) {
420 final Timeout newStateTimeout = this.timer.newTimeout(timeout -> {
421 if (tunnel.getType() == LspType.PCE_LSP) {
422 PCCTunnelManagerImpl.this.tunnels.remove(plspId);
423 //report tunnel removal to all
424 sendToAll(tunnel, plspId, Collections.emptyList(), createSrp(Uint32.ZERO),
425 new PathBuilder().build(), createLsp(plspId.getValue(), false, Optional.empty(), false, true));
427 }, this.stateTimeout, TimeUnit.SECONDS);
428 tunnel.setStateTimeout(newStateTimeout);
432 private void startRedelegationTimer(final PCCTunnel tunnel, final PlspId plspId, final PCCSession session) {
433 final Timeout newRedelegationTimeout = this.timer.newTimeout(timeout -> {
435 PCCTunnelManagerImpl.this.setDelegation(plspId, null);
436 //delegate to another PCE
437 int index = session.getId();
438 for (int i = 1; i < PCCTunnelManagerImpl.this.sessions.size(); i++) {
440 if (index == PCCTunnelManagerImpl.this.sessions.size()) {
443 final PCCSession nextSession = PCCTunnelManagerImpl.this.sessions.get(index);
444 if (nextSession != null) {
445 tunnel.cancelTimeouts();
446 final Tlvs tlvs = buildTlvs(tunnel, plspId.getValue(), Optional.empty());
448 nextSession.sendReport(createPcRtpMessage(
449 createLsp(plspId.getValue(), true, Optional.ofNullable(tlvs), true, false), NO_SRP,
450 tunnel.getLspState()));
451 tunnel.setDelegationHolder(nextSession.getId());
455 }, this.redelegationTimeout, TimeUnit.SECONDS);
456 tunnel.setRedelegationTimeout(newRedelegationTimeout);
459 private void setDelegation(final PlspId plspId, final PCCSession session) {
460 final PCCTunnel tunnel = this.tunnels.get(plspId);
462 if (session != null) {
463 sessionId = session.getId();
465 sessionId = PCCTunnelBuilder.PCC_DELEGATION;
467 tunnel.setDelegationHolder(sessionId);
470 private static boolean hasDelegation(final PCCTunnel tunnel, final PCCSession session) {
471 final int sessionId = session.getId();
472 final int delegationHolder = tunnel.getDelegationHolder();
473 return delegationHolder == sessionId;
476 private static String getDestinationAddress(final List<Subobject> subobjects, final String defaultAddress) {
477 if (subobjects != null && !subobjects.isEmpty()) {
478 final String prefix = ((IpPrefixCase) subobjects.get(subobjects.size() - 1).getSubobjectType())
479 .getIpPrefix().getIpPrefix().getIpv4Prefix().getValue();
480 return prefix.substring(0, prefix.indexOf('/'));
482 return defaultAddress;