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
9 package org.opendaylight.protocol.pcep.pcc.mock;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createLsp;
13 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createLspTlvs;
14 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createLspTlvsEndofSync;
15 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createPath;
16 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createPcRtpMessage;
17 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.createSrp;
18 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.reqToRptPath;
19 import static org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil.updToRptPath;
21 import com.google.common.base.Optional;
22 import com.google.common.base.Preconditions;
23 import com.google.common.net.InetAddresses;
24 import io.netty.util.Timeout;
25 import io.netty.util.Timer;
26 import java.math.BigInteger;
27 import java.net.InetAddress;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
32 import java.util.Map.Entry;
33 import java.util.concurrent.TimeUnit;
34 import java.util.concurrent.atomic.AtomicLong;
35 import javax.annotation.Nonnull;
36 import javax.annotation.concurrent.GuardedBy;
37 import org.opendaylight.protocol.pcep.pcc.mock.api.LspType;
38 import org.opendaylight.protocol.pcep.pcc.mock.api.PCCSession;
39 import org.opendaylight.protocol.pcep.pcc.mock.api.PCCTunnelManager;
40 import org.opendaylight.protocol.pcep.pcc.mock.spi.MsgBuilderUtil;
41 import org.opendaylight.protocol.pcep.spi.PCEPErrors;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.crabbe.initiated.rev131126.Lsp1;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.crabbe.initiated.rev131126.Lsp1Builder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.crabbe.initiated.rev131126.Srp1;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.crabbe.initiated.rev131126.Srp1Builder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.crabbe.initiated.rev131126.pcinitiate.message.pcinitiate.message.Requests;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.OperationalStatus;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.Pcrpt;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.PlspId;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.SrpIdNumber;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.object.Lsp;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.object.LspBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.lsp.object.lsp.Tlvs;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.pcrpt.message.pcrpt.message.reports.Path;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.pcrpt.message.pcrpt.message.reports.PathBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.pcupd.message.pcupd.message.Updates;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.srp.object.Srp;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev131222.srp.object.SrpBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.explicit.route.object.ero.Subobject;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.rsvp.rev150820.basic.explicit.route.subobjects.subobject.type.IpPrefixCase;
62 public final class PCCTunnelManagerImpl implements PCCTunnelManager {
64 private static final Optional<Srp> NO_SRP = Optional.absent();
66 private final Map<Integer, PCCSession> sessions = new HashMap<>();
67 private final AtomicLong plspIDsCounter;
68 private final String address;
69 private final Timer timer;
70 private final int redelegationTimeout;
71 private final int stateTimeout;
72 private final int lspsCount;
73 private final Optional<TimerHandler> timerHandler;
75 private final Map<PlspId, PCCTunnel> tunnels = new HashMap<>();
76 private PCCSyncOptimization syncOptimization;
78 public PCCTunnelManagerImpl(final int lspsCount, final InetAddress address, final int redelegationTimeout,
79 final int stateTimeout, final Timer timer, final Optional<TimerHandler> timerHandler) {
80 Preconditions.checkArgument(lspsCount >= 0);
81 this.redelegationTimeout = redelegationTimeout;
82 this.stateTimeout = stateTimeout;
83 this.plspIDsCounter = new AtomicLong(lspsCount);
84 this.address = InetAddresses.toAddrString(requireNonNull(address));
85 this.timer = requireNonNull(timer);
86 this.timerHandler = timerHandler;
87 this.lspsCount = lspsCount;
90 protected void reportToAll(final Updates update, final PCCSession session) {
91 final PlspId plspId = update.getLsp().getPlspId();
92 final PCCTunnel tunnel = this.tunnels.get(plspId);
93 final long srpId = update.getSrp().getOperationId().getValue();
95 if (hasDelegation(tunnel, session)) {
96 final Srp srp = createSrp(update.getSrp().getOperationId().getValue());
97 final Path path = updToRptPath(update.getPath());
98 final List<Subobject> subobjects = update.getPath().getEro().getSubobject();
99 final Lsp lsp = update.getLsp();
100 sendToAll(tunnel, plspId, subobjects, srp, path, lsp);
101 //update tunnel state
102 tunnel.setLspState(path);
104 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UPDATE_REQ_FOR_NON_LSP, srpId));
107 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UNKNOWN_PLSP_ID, srpId));
111 private void returnDelegation(final Updates update, final PCCSession session) {
112 final PlspId plspId = update.getLsp().getPlspId();
113 final PCCTunnel tunnel = this.tunnels.get(plspId);
114 final long srpId = update.getSrp().getOperationId().getValue();
115 if (tunnel != null) {
116 //check if session really has a delegation
117 if (hasDelegation(tunnel, session)) {
119 final Tlvs tlvs = buildTlvs(tunnel, plspId.getValue(), Optional.absent());
120 final Pcrpt pcrtp = createPcRtpMessage(new LspBuilder(update.getLsp()).setSync(true).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 long 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) || (tunnel.getDelegationHolder() == session.getId()))) {
148 tunnel.cancelTimeouts();
149 setDelegation(plspId, session);
151 final Tlvs tlvs = buildTlvs(tunnel, plspId.getValue(), Optional.absent());
152 session.sendReport(createPcRtpMessage(
153 new LspBuilder(request.getLsp()).setSync(true).setOperational(OperationalStatus.Up).setDelegate(true).setTlvs(tlvs).build(),
154 Optional.of(createSrp(srpId)), tunnel.getLspState()));
156 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.LSP_NOT_PCE_INITIATED, srpId));
159 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UNKNOWN_PLSP_ID, srpId));
164 public synchronized void onSessionUp(final PCCSession session) {
165 this.syncOptimization = new PCCSyncOptimization(session);
166 lazyTunnelInicialization();
168 //first session - delegate all PCC's LSPs only when reporting at startup
169 if (!this.sessions.containsKey(session.getId()) && (session.getId() == 0)) {
170 for (final PlspId plspId : this.tunnels.keySet()) {
171 setDelegation(plspId, session);
174 this.sessions.put(session.getId(), session);
176 if (!this.syncOptimization.isTriggeredInitSyncEnabled()) {
182 public synchronized void onSessionDown(final PCCSession session) {
183 for (final Entry<PlspId, PCCTunnel> entry : this.tunnels.entrySet()) {
184 final PCCTunnel tunnel = entry.getValue();
185 final PlspId plspId = entry.getKey();
186 //deal with delegations
187 if (hasDelegation(tunnel, session)) {
188 startStateTimeout(tunnel, entry.getKey());
189 startRedelegationTimer(tunnel, plspId, session);
194 protected void addTunnel(final Requests request, final PCCSession session) {
195 final PlspId plspId = new PlspId(this.plspIDsCounter.incrementAndGet());
196 final PCCTunnel tunnel = new PCCTunnel(request.getLsp().getTlvs().getSymbolicPathName().getPathName().getValue(),
197 session.getId(), LspType.PCE_LSP, reqToRptPath(request));
198 sendToAll(tunnel, plspId, request.getEro().getSubobject(), createSrp(request.getSrp().getOperationId().getValue()),
199 tunnel.getLspState(), new LspBuilder(request.getLsp()).addAugmentation(Lsp1.class, new Lsp1Builder().setCreate(true).build()).build());
200 this.tunnels.put(plspId, tunnel);
203 protected void removeTunnel(final Requests request, final PCCSession session) {
204 final PlspId plspId = request.getLsp().getPlspId();
205 final PCCTunnel tunnel = this.tunnels.get(plspId);
206 final long srpId = request.getSrp().getOperationId().getValue();
207 if (tunnel != null) {
208 if (tunnel.getType() == LspType.PCE_LSP) {
209 if (hasDelegation(tunnel, session)) {
210 this.tunnels.remove(plspId);
211 sendToAll(tunnel, plspId, tunnel.getLspState().getEro().getSubobject(),
212 new SrpBuilder(request.getSrp()).addAugmentation(Srp1.class, new Srp1Builder().setRemove(true).build()).build(),
213 reqToRptPath(request), request.getLsp());
215 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UPDATE_REQ_FOR_NON_LSP, srpId));
218 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.LSP_NOT_PCE_INITIATED, srpId));
221 session.sendError(MsgBuilderUtil.createErrorMsg(PCEPErrors.UNKNOWN_PLSP_ID, srpId));
226 public void onMessagePcupd(@Nonnull final Updates update, @Nonnull final PCCSession session) {
227 final Lsp lsp = update.getLsp();
228 if (isInitialSyncTriggered(lsp)) {
230 if (this.timerHandler.isPresent()) {
231 this.timerHandler.get().createDisconnectTask();
233 } else if (isReSyncTriggered(lsp)) {
234 handledDbTriggeredResync(update, session);
235 } else if ((lsp.isDelegate() != null) && lsp.isDelegate()) {
237 reportToAll(update, session);
239 //returning LSP delegation
240 returnDelegation(update, session);
245 public void onMessagePcInitiate(@Nonnull final Requests request, @Nonnull final PCCSession session) {
246 if ((request.getSrp().getAugmentation(Srp1.class) != null) && request.getSrp().getAugmentation(Srp1.class).isRemove()) {
248 removeTunnel(request, session);
249 } else if ((request.getLsp().isDelegate() != null) && request.getLsp().isDelegate() && (request.getEndpointsObj() == null)) {
250 //take LSP delegation
251 takeDelegation(request, session);
254 addTunnel(request, session);
258 private Tlvs buildTlvs(final PCCTunnel tunnel, final Long plspId, final Optional<List<Subobject>> subobjectsList) {
259 final List<Subobject> subObject = subobjectsList.isPresent() ? subobjectsList.get() : tunnel.getLspState().getEro().getSubobject();
260 final String destinationAddress = getDestinationAddress(subObject, this.address);
262 return createLspTlvs(plspId, true, destinationAddress, this.address, this.address, Optional.of(tunnel.getPathName()),
263 this.syncOptimization.incrementLspDBVersion());
266 private void lazyTunnelInicialization() {
267 if (this.tunnels.isEmpty()) {
268 final BigInteger dbV = this.syncOptimization.getLocalLspDbVersionValue();
269 if (this.syncOptimization.isSyncAvoidanceEnabled() && !((dbV != null) && dbV.equals(BigInteger.ONE))) {
270 this.tunnels.putAll(PCCTunnelBuilder.createTunnels(this.address, dbV.intValue()));
272 this.tunnels.putAll(PCCTunnelBuilder.createTunnels(this.address, this.lspsCount));
277 private boolean isReSyncTriggered(final Lsp lsp) {
278 return this.syncOptimization.isTriggeredReSyncEnabled() && lsp.isSync();
281 private boolean isInitialSyncTriggered(final Lsp lsp) {
282 return (lsp.getPlspId().getValue() == 0) && lsp.isSync() && this.syncOptimization.isTriggeredInitSyncEnabled();
285 private void handledDbTriggeredResync(final Updates update, final PCCSession session) {
286 this.syncOptimization.setResynchronizingState(Boolean.TRUE);
287 final SrpIdNumber operationId = update.getSrp().getOperationId();
288 if (update.getLsp().getPlspId().getValue() == 0) {
289 reportAllKnownLsp(Optional.of(operationId), session);
291 reportLsp(update.getLsp().getPlspId(), operationId, session);
293 sendEndOfSynchronization(session, Optional.of(operationId));
294 this.syncOptimization.setResynchronizingState(Boolean.FALSE);
297 private void lspReport(final PCCSession session) {
298 if (!this.tunnels.isEmpty()) {
299 if (!this.syncOptimization.isSyncAvoidanceEnabled()) {
300 reportAllKnownLsp(session);
301 sendEndOfSynchronization(session);
302 } else if (!this.syncOptimization.doesLspDbMatch()) {
303 if (this.syncOptimization.isDeltaSyncEnabled()) {
304 reportMissedLsp(session);
305 sendEndOfSynchronization(session);
307 reportAllKnownLsp(session);
308 sendEndOfSynchronization(session);
315 * Reports Missed Lsp when DbVersion doesnt match
319 private void reportMissedLsp(final PCCSession session) {
320 for (long missedLsp = this.syncOptimization.getRemoteLspDbVersionValue().longValue() + 1;
321 missedLsp <= this.syncOptimization.getLocalLspDbVersionValue().longValue(); missedLsp++) {
322 final PlspId plspId = new PlspId(missedLsp);
323 final PCCTunnel tunnel = this.tunnels.get(plspId);
324 createLspAndSendReport(missedLsp, tunnel, session, Optional.absent(), NO_SRP);
328 private void createLspAndSendReport(final long plspId, final PCCTunnel tunnel, final PCCSession session, final Optional<Boolean> isSync, final Optional<Srp> srp) {
329 final boolean delegation = hasDelegation(tunnel, session);
331 tunnel.cancelTimeouts();
333 final String destinationAddress = getDestinationAddress(tunnel.getLspState().getEro().getSubobject(), this.address);
334 final Tlvs tlvs = createLspTlvs(plspId, true, destinationAddress, this.address, this.address, Optional.of(tunnel.getPathName()),
335 this.syncOptimization.incrementLspDBVersion());
337 final boolean sync = isSync.isPresent() ? isSync.get() : this.syncOptimization.isSyncNeedIt();
338 final Lsp lsp = createLsp(plspId, sync, Optional.fromNullable(tlvs), delegation, false);
339 final Pcrpt pcrtp = createPcRtpMessage(lsp, srp, tunnel.getLspState());
340 session.sendReport(pcrtp);
343 private void sendEndOfSynchronization(final PCCSession session) {
344 sendEndOfSynchronization(session, Optional.absent());
347 private void sendEndOfSynchronization(final PCCSession session, final Optional<SrpIdNumber> operationId) {
349 if (operationId.isPresent()) {
350 srp = new SrpBuilder().setOperationId(operationId.get()).build();
352 Optional<Tlvs> tlv = Optional.absent();
353 if (this.syncOptimization.isSyncAvoidanceEnabled()) {
354 tlv = createLspTlvsEndofSync(this.syncOptimization.incrementLspDBVersion().get());
356 final Pcrpt pcrtp = createPcRtpMessage(createLsp(0, false, tlv, true, false), Optional.fromNullable(srp), createPath(Collections
358 session.sendReport(pcrtp);
361 private void reportAllKnownLsp(final PCCSession session) {
362 reportAllKnownLsp(Optional.absent(), session);
365 private void reportAllKnownLsp(final Optional<SrpIdNumber> operationId, final PCCSession session) {
367 if (operationId.isPresent()) {
368 srp = new SrpBuilder().setOperationId(operationId.get()).build();
371 for (final Entry<PlspId, PCCTunnel> entry : this.tunnels.entrySet()) {
372 final PCCTunnel tunnel = entry.getValue();
373 final long plspId = entry.getKey().getValue();
374 createLspAndSendReport(plspId, tunnel, session, Optional.absent(), Optional.fromNullable(srp));
378 private void reportLsp(final PlspId plspId, final SrpIdNumber operationId, final PCCSession session) {
379 final PCCTunnel tunnel = this.tunnels.get(plspId);
380 if (tunnel == null) {
383 final Srp srp = new SrpBuilder().setOperationId(operationId).build();
384 createLspAndSendReport(plspId.getValue(), tunnel, session, Optional.of(Boolean.TRUE), Optional.of(srp));
387 private void sendToAll(final PCCTunnel tunnel, final PlspId plspId, final List<Subobject> subobjects, final Srp srp, final Path path, final Lsp lsp) {
388 for (final PCCSession session : this.sessions.values()) {
389 final boolean isDelegated = hasDelegation(tunnel, session);
390 final Tlvs tlvs = buildTlvs(tunnel, plspId.getValue(), Optional.of(subobjects));
392 final Pcrpt pcRpt = createPcRtpMessage(
395 .setOperational(OperationalStatus.Up)
396 .setDelegate(isDelegated)
398 .addAugmentation(Lsp1.class, new Lsp1Builder().setCreate(tunnel.getType() == LspType.PCE_LSP).build())
399 .setTlvs(tlvs).build(),
400 Optional.fromNullable(srp), path);
401 session.sendReport(pcRpt);
405 private void startStateTimeout(final PCCTunnel tunnel, final PlspId plspId) {
406 if (this.stateTimeout > -1) {
407 final Timeout newStateTimeout = this.timer.newTimeout(timeout -> {
408 if (tunnel.getType() == LspType.PCE_LSP) {
409 PCCTunnelManagerImpl.this.tunnels.remove(plspId);
410 //report tunnel removal to all
411 sendToAll(tunnel, plspId, Collections.emptyList(), createSrp(0), new PathBuilder().build(),
412 createLsp(plspId.getValue(), false, Optional.absent(), false, true));
414 }, this.stateTimeout, TimeUnit.SECONDS);
415 tunnel.setStateTimeout(newStateTimeout);
419 private void startRedelegationTimer(final PCCTunnel tunnel, final PlspId plspId, final PCCSession session) {
420 final Timeout newRedelegationTimeout = this.timer.newTimeout(timeout -> {
422 PCCTunnelManagerImpl.this.setDelegation(plspId, null);
423 //delegate to another PCE
424 int index = session.getId();
425 for (int i = 1; i < PCCTunnelManagerImpl.this.sessions.size(); i++) {
427 if (index == PCCTunnelManagerImpl.this.sessions.size()) {
430 final PCCSession nextSession = PCCTunnelManagerImpl.this.sessions.get(index);
431 if (nextSession != null) {
432 tunnel.cancelTimeouts();
433 final Tlvs tlvs = buildTlvs(tunnel, plspId.getValue(), Optional.absent());
435 nextSession.sendReport(createPcRtpMessage(
436 createLsp(plspId.getValue(), true, Optional.fromNullable(tlvs), true, false), NO_SRP,
437 tunnel.getLspState()));
438 tunnel.setDelegationHolder(nextSession.getId());
442 }, this.redelegationTimeout, TimeUnit.SECONDS);
443 tunnel.setRedelegationTimeout(newRedelegationTimeout);
446 private void setDelegation(final PlspId plspId, final PCCSession session) {
447 final PCCTunnel tunnel = this.tunnels.get(plspId);
449 if (session != null) {
450 sessionId = session.getId();
452 sessionId = PCCTunnelBuilder.PCC_DELEGATION;
454 tunnel.setDelegationHolder(sessionId);
457 private static boolean hasDelegation(final PCCTunnel tunnel, final PCCSession session) {
458 final int sessionId = session.getId();
459 final int delegationHolder = tunnel.getDelegationHolder();
460 return delegationHolder == sessionId;
463 private static String getDestinationAddress(final List<Subobject> subobjects, final String defaultAddress) {
464 if ((subobjects != null) && !subobjects.isEmpty()) {
465 final String prefix = ((IpPrefixCase) subobjects.get(subobjects.size() - 1).getSubobjectType())
466 .getIpPrefix().getIpPrefix().getIpv4Prefix().getValue();
467 return prefix.substring(0, prefix.indexOf('/'));
469 return defaultAddress;