blob: bd2edf59bcf0ab4f1384578fd726b483ad0eeafe [file] [log] [blame]
Avantika-Huawei73862d42016-05-12 18:58:06 +05301/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.pce.pceservice;
17
18import static com.google.common.base.Preconditions.checkNotNull;
Avantika-Huawei73862d42016-05-12 18:58:06 +053019
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053020import java.util.Collection;
Priyanka Bb6963582016-05-20 20:21:20 +053021import java.util.Collections;
Avantika-Huawei73862d42016-05-12 18:58:06 +053022import java.util.Iterator;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053023import java.util.LinkedList;
Avantika-Huawei73862d42016-05-12 18:58:06 +053024import java.util.List;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053025import java.util.Map;
26import java.util.Optional;
27import java.util.Map.Entry;
Priyanka Bb6963582016-05-20 20:21:20 +053028import java.util.Set;
Priyanka B9fa4ed32016-05-27 11:59:24 +053029import java.util.concurrent.Executors;
30import java.util.concurrent.ScheduledExecutorService;
31import java.util.concurrent.TimeUnit;
Avantika-Huawei73862d42016-05-12 18:58:06 +053032
33import org.apache.felix.scr.annotations.Activate;
34import org.apache.felix.scr.annotations.Component;
35import org.apache.felix.scr.annotations.Deactivate;
36import org.apache.felix.scr.annotations.Reference;
37import org.apache.felix.scr.annotations.ReferenceCardinality;
38import org.apache.felix.scr.annotations.Service;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053039import org.onlab.packet.IpAddress;
40import org.onlab.packet.IpPrefix;
41import org.onlab.util.Bandwidth;
Avantika-Huawei73862d42016-05-12 18:58:06 +053042import org.onosproject.core.ApplicationId;
43import org.onosproject.core.CoreService;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053044import org.onosproject.incubator.net.resource.label.LabelResourceAdminService;
45import org.onosproject.incubator.net.resource.label.LabelResourceId;
46import org.onosproject.incubator.net.resource.label.LabelResourceService;
Avantika-Huawei73862d42016-05-12 18:58:06 +053047import org.onosproject.core.IdGenerator;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053048import org.onosproject.incubator.net.tunnel.DefaultTunnel;
49import org.onosproject.incubator.net.tunnel.IpTunnelEndPoint;
50import org.onosproject.incubator.net.tunnel.LabelStack;
Avantika-Huawei73862d42016-05-12 18:58:06 +053051import org.onosproject.incubator.net.tunnel.Tunnel;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053052import org.onosproject.incubator.net.tunnel.TunnelEndPoint;
53import org.onosproject.incubator.net.tunnel.TunnelEvent;
Avantika-Huawei73862d42016-05-12 18:58:06 +053054import org.onosproject.incubator.net.tunnel.TunnelId;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053055import org.onosproject.incubator.net.tunnel.TunnelListener;
56import org.onosproject.incubator.net.tunnel.TunnelName;
Avantika-Huawei73862d42016-05-12 18:58:06 +053057import org.onosproject.incubator.net.tunnel.TunnelService;
Priyanka B3f92c5a2016-05-27 10:14:16 +053058import org.onosproject.mastership.MastershipService;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053059import org.onosproject.net.DefaultAnnotations;
60import org.onosproject.net.DefaultAnnotations.Builder;
61import org.onosproject.net.Device;
Avantika-Huawei73862d42016-05-12 18:58:06 +053062import org.onosproject.net.DeviceId;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053063import org.onosproject.net.Link;
Priyanka Bb6963582016-05-20 20:21:20 +053064import org.onosproject.net.Path;
Priyanka Bb6963582016-05-20 20:21:20 +053065import org.onosproject.net.device.DeviceService;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053066import org.onosproject.net.flowobjective.FlowObjectiveService;
67import org.onosproject.net.flowobjective.Objective;
68import org.onosproject.net.intent.Constraint;
69import org.onosproject.net.intent.constraint.BandwidthConstraint;
Priyanka B3f92c5a2016-05-27 10:14:16 +053070import org.onosproject.net.link.LinkEvent;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053071import org.onosproject.pce.pceservice.constraint.CapabilityConstraint;
72import org.onosproject.pce.pceservice.constraint.CapabilityConstraint.CapabilityType;
73import org.onosproject.pce.pceservice.constraint.CostConstraint;
74import org.onosproject.pce.pceservice.constraint.SharedBandwidthConstraint;
75import org.onosproject.net.resource.Resource;
76import org.onosproject.net.resource.ResourceAllocation;
77import org.onosproject.net.resource.ResourceConsumer;
78import org.onosproject.net.resource.ResourceQueryService;
Priyanka Bb6963582016-05-20 20:21:20 +053079import org.onosproject.net.resource.ResourceService;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053080import org.onosproject.net.resource.Resources;
Priyanka Bb6963582016-05-20 20:21:20 +053081import org.onosproject.net.topology.LinkWeight;
82import org.onosproject.net.topology.PathService;
83import org.onosproject.net.topology.TopologyEdge;
Priyanka B3f92c5a2016-05-27 10:14:16 +053084import org.onosproject.net.topology.TopologyEvent;
85import org.onosproject.net.topology.TopologyListener;
86import org.onosproject.net.topology.TopologyService;
Avantika-Huawei73862d42016-05-12 18:58:06 +053087import org.onosproject.pce.pceservice.api.PceService;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053088import org.onosproject.pce.pcestore.PcePathInfo;
89import org.onosproject.pce.pcestore.PceccTunnelInfo;
90import org.onosproject.pce.pcestore.api.PceStore;
Avantika-Huawei73862d42016-05-12 18:58:06 +053091import org.onosproject.store.serializers.KryoNamespaces;
92import org.onosproject.store.service.DistributedSet;
93import org.onosproject.store.service.Serializer;
94import org.onosproject.store.service.StorageService;
95import org.slf4j.Logger;
96import org.slf4j.LoggerFactory;
97
Priyanka Bb6963582016-05-20 20:21:20 +053098import com.google.common.collect.ImmutableList;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053099import com.google.common.collect.ImmutableSet;
100
101import static org.onosproject.incubator.net.tunnel.Tunnel.Type.MPLS;
102import static org.onosproject.incubator.net.tunnel.Tunnel.State.INIT;
103import static org.onosproject.incubator.net.tunnel.Tunnel.State.ESTABLISHED;
104import static org.onosproject.incubator.net.tunnel.Tunnel.State.UNSTABLE;
105import static org.onosproject.pce.pceservice.LspType.WITH_SIGNALLING;
106import static org.onosproject.pce.pceservice.LspType.SR_WITHOUT_SIGNALLING;
107import static org.onosproject.pce.pceservice.LspType.WITHOUT_SIGNALLING_AND_WITHOUT_SR;
108
109import static org.onosproject.pce.pceservice.PcepAnnotationKeys.BANDWIDTH;
110import static org.onosproject.pce.pceservice.PcepAnnotationKeys.LOCAL_LSP_ID;
111import static org.onosproject.pce.pceservice.PcepAnnotationKeys.LSP_SIG_TYPE;
112import static org.onosproject.pce.pceservice.PcepAnnotationKeys.PCE_INIT;
113import static org.onosproject.pce.pceservice.PcepAnnotationKeys.PLSP_ID;
114import static org.onosproject.pce.pceservice.PcepAnnotationKeys.PCC_TUNNEL_ID;
115import static org.onosproject.pce.pceservice.PcepAnnotationKeys.DELEGATE;
116import static org.onosproject.pce.pceservice.PcepAnnotationKeys.COST_TYPE;
117
118import org.onosproject.net.packet.InboundPacket;
119import org.onosproject.net.packet.PacketContext;
120import org.onosproject.net.packet.PacketProcessor;
121import org.onosproject.net.packet.PacketService;
Priyanka Bb6963582016-05-20 20:21:20 +0530122
Avantika-Huawei73862d42016-05-12 18:58:06 +0530123/**
124 * Implementation of PCE service.
125 */
126@Component(immediate = true)
127@Service
128public class PceManager implements PceService {
129 private static final Logger log = LoggerFactory.getLogger(PceManager.class);
130
131 public static final String PCE_SERVICE_APP = "org.onosproject.pce";
Avantika-Huawei73862d42016-05-12 18:58:06 +0530132 private static final String LOCAL_LSP_ID_GEN_TOPIC = "pcep-local-lsp-id";
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530133 private static final int PREFIX_LENGTH = 32;
134
135 private static final String TUNNEL_CONSUMER_ID_GEN_TOPIC = "pcep-tunnel-consumer-id";
136 private IdGenerator tunnelConsumerIdGen;
137
138 private static final String LSRID = "lsrId";
139 private static final String TRUE = "true";
140 private static final String FALSE = "false";
141 private static final String END_OF_SYNC_IP_PREFIX = "0.0.0.0/32";
142
Avantika-Huawei73862d42016-05-12 18:58:06 +0530143 private IdGenerator localLspIdIdGen;
144 protected DistributedSet<Short> localLspIdFreeList;
145
146 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
147 protected CoreService coreService;
148
149 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530150 protected ResourceService resourceService;
151
152 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
153 protected ResourceQueryService resourceQueryService;
154
155 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
156 protected PathService pathService;
157
158 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
159 protected PceStore pceStore;
160
161 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Avantika-Huawei73862d42016-05-12 18:58:06 +0530162 protected TunnelService tunnelService;
163
164 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
165 protected StorageService storageService;
166
Priyanka Bb6963582016-05-20 20:21:20 +0530167 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530168 protected PacketService packetService;
Priyanka Bb6963582016-05-20 20:21:20 +0530169
170 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
171 protected DeviceService deviceService;
172
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530173 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
174 protected LabelResourceAdminService labelRsrcAdminService;
175
176 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
177 protected LabelResourceService labelRsrcService;
178
179 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
180 protected FlowObjectiveService flowObjectiveService;
181
Priyanka B3f92c5a2016-05-27 10:14:16 +0530182 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
183 protected MastershipService mastershipService;
184
185 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
186 protected TopologyService topologyService;
187
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530188 private TunnelListener listener = new InnerTunnelListener();
189 private BasicPceccHandler crHandler;
190 private PceccSrTeBeHandler srTeHandler;
Avantika-Huawei73862d42016-05-12 18:58:06 +0530191 private ApplicationId appId;
192
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530193 private final PcepPacketProcessor processor = new PcepPacketProcessor();
Priyanka B3f92c5a2016-05-27 10:14:16 +0530194 private final TopologyListener topologyListener = new InternalTopologyListener();
Priyanka B9fa4ed32016-05-27 11:59:24 +0530195 private ScheduledExecutorService executor;
196
197 public static final int INITIAL_DELAY = 30;
198 public static final int PERIODIC_DELAY = 30;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530199
Avantika-Huawei73862d42016-05-12 18:58:06 +0530200 /**
201 * Creates new instance of PceManager.
202 */
203 public PceManager() {
204 }
205
206 @Activate
207 protected void activate() {
208 appId = coreService.registerApplication(PCE_SERVICE_APP);
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530209 crHandler = BasicPceccHandler.getInstance();
210 crHandler.initialize(labelRsrcService, flowObjectiveService, appId, pceStore);
Avantika-Huawei73862d42016-05-12 18:58:06 +0530211
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530212 srTeHandler = PceccSrTeBeHandler.getInstance();
213 srTeHandler.initialize(labelRsrcAdminService, labelRsrcService, flowObjectiveService, appId, pceStore);
214
215 tunnelService.addListener(listener);
216
217 tunnelConsumerIdGen = coreService.getIdGenerator(TUNNEL_CONSUMER_ID_GEN_TOPIC);
Avantika-Huawei73862d42016-05-12 18:58:06 +0530218 localLspIdIdGen = coreService.getIdGenerator(LOCAL_LSP_ID_GEN_TOPIC);
219 localLspIdFreeList = storageService.<Short>setBuilder()
220 .withName("pcepLocalLspIdDeletedList")
221 .withSerializer(Serializer.using(KryoNamespaces.API))
222 .build()
223 .asDistributedSet();
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530224
225 packetService.addProcessor(processor, PacketProcessor.director(4));
Priyanka B3f92c5a2016-05-27 10:14:16 +0530226 topologyService.addListener(topologyListener);
Priyanka B9fa4ed32016-05-27 11:59:24 +0530227 executor = Executors.newSingleThreadScheduledExecutor();
228 //Start a timer when the component is up, with initial delay of 30min and periodic delays at 30min
229 executor.scheduleAtFixedRate(new GlobalOptimizationTimer(), INITIAL_DELAY, PERIODIC_DELAY, TimeUnit.MINUTES);
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530230 log.info("Started");
Avantika-Huawei73862d42016-05-12 18:58:06 +0530231 }
232
233 @Deactivate
234 protected void deactivate() {
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530235 tunnelService.removeListener(listener);
236 packetService.removeProcessor(processor);
Priyanka B3f92c5a2016-05-27 10:14:16 +0530237 topologyService.removeListener(topologyListener);
Priyanka B9fa4ed32016-05-27 11:59:24 +0530238 //Shutdown the thread when component is deactivated
239 executor.shutdown();
Avantika-Huawei73862d42016-05-12 18:58:06 +0530240 log.info("Stopped");
241 }
242
Priyanka Bb6963582016-05-20 20:21:20 +0530243 /**
244 * Returns an edge-weight capable of evaluating links on the basis of the
245 * specified constraints.
246 *
247 * @param constraints path constraints
248 * @return edge-weight function
249 */
250 private LinkWeight weight(List<Constraint> constraints) {
251 return new TeConstraintBasedLinkWeight(constraints);
252 }
253
254 /**
255 * Computes a path between two devices.
256 *
257 * @param src ingress device
258 * @param dst egress device
259 * @param constraints path constraints
260 * @return computed path based on constraints
261 */
262 protected Set<Path> computePath(DeviceId src, DeviceId dst, List<Constraint> constraints) {
263 if (pathService == null) {
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530264 return ImmutableSet.of();
Priyanka Bb6963582016-05-20 20:21:20 +0530265 }
266 Set<Path> paths = pathService.getPaths(src, dst, weight(constraints));
267 if (!paths.isEmpty()) {
268 return paths;
269 }
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530270 return ImmutableSet.of();
Priyanka Bb6963582016-05-20 20:21:20 +0530271 }
272
Avantika-Huawei73862d42016-05-12 18:58:06 +0530273 //[TODO:] handle requests in queue
274 @Override
275 public boolean setupPath(DeviceId src, DeviceId dst, String tunnelName, List<Constraint> constraints,
276 LspType lspType) {
277 checkNotNull(src);
278 checkNotNull(dst);
279 checkNotNull(tunnelName);
Avantika-Huawei73862d42016-05-12 18:58:06 +0530280 checkNotNull(lspType);
281
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530282 // Convert from DeviceId to TunnelEndPoint
283 Device srcDevice = deviceService.getDevice(src);
284 Device dstDevice = deviceService.getDevice(dst);
Avantika-Huawei73862d42016-05-12 18:58:06 +0530285
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530286 if (srcDevice == null || dstDevice == null) {
287 // Device is not known.
288 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
289 return false;
290 }
291
292 // In future projections instead of annotations will be used to fetch LSR ID.
293 String srcLsrId = srcDevice.annotations().value(LSRID);
294 String dstLsrId = dstDevice.annotations().value(LSRID);
295
296 if (srcLsrId == null || dstLsrId == null) {
297 // LSR id is not known.
298 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
299 return false;
300 }
301
302 TunnelEndPoint srcEndPoint = IpTunnelEndPoint.ipTunnelPoint(IpAddress.valueOf(srcLsrId));
303 TunnelEndPoint dstEndPoint = IpTunnelEndPoint.ipTunnelPoint(IpAddress.valueOf(dstLsrId));
304
305 double bwConstraintValue = 0;
306 CostConstraint costConstraint = null;
307 if (constraints != null) {
308 constraints.add(CapabilityConstraint.of(CapabilityType.valueOf(lspType.name())));
309 Iterator<Constraint> iterator = constraints.iterator();
310
311 while (iterator.hasNext()) {
312 Constraint constraint = iterator.next();
313 if (constraint instanceof BandwidthConstraint) {
314 bwConstraintValue = ((BandwidthConstraint) constraint).bandwidth().bps();
315 } else if (constraint instanceof CostConstraint) {
316 costConstraint = (CostConstraint) constraint;
317 }
318 }
319
320 /*
321 * Add cost at the end of the list of constraints. The path computation algorithm also computes cumulative
322 * cost. The function which checks the limiting/capability constraints also returns per link cost. This
323 * function can either return the result of limiting/capability constraint validation or the value of link
324 * cost, depending upon what is the last constraint in the loop.
325 */
326 if (costConstraint != null) {
327 constraints.remove(costConstraint);
328 constraints.add(costConstraint);
329 }
330 } else {
331 constraints = new LinkedList<>();
332 constraints.add(CapabilityConstraint.of(CapabilityType.valueOf(lspType.name())));
333 }
334
335 Set<Path> computedPathSet = computePath(src, dst, constraints);
336
337 // NO-PATH
338 if (computedPathSet.isEmpty()) {
339 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
340 return false;
341 }
342
343 Builder annotationBuilder = DefaultAnnotations.builder();
344 if (bwConstraintValue != 0) {
345 annotationBuilder.set(BANDWIDTH, String.valueOf(bwConstraintValue));
346 }
347 if (costConstraint != null) {
348 annotationBuilder.set(COST_TYPE, String.valueOf(costConstraint.type()));
349 }
350 annotationBuilder.set(LSP_SIG_TYPE, lspType.name());
351 annotationBuilder.set(PCE_INIT, TRUE);
352 annotationBuilder.set(DELEGATE, TRUE);
353
354 Path computedPath = computedPathSet.iterator().next();
355 LabelStack labelStack = null;
356
357 if (lspType == SR_WITHOUT_SIGNALLING) {
358 labelStack = srTeHandler.computeLabelStack(computedPath);
359 // Failed to form a label stack.
360 if (labelStack == null) {
361 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
362 return false;
363 }
364 }
365
366 if (lspType != WITH_SIGNALLING) {
367 /*
368 * Local LSP id which is assigned by RSVP for RSVP signalled LSPs, will be assigned by
369 * PCE for non-RSVP signalled LSPs.
370 */
371 annotationBuilder.set(LOCAL_LSP_ID, String.valueOf(getNextLocalLspId()));
372 }
373
374 // For SR-TE tunnels, call SR manager for label stack and put it inside tunnel.
375 Tunnel tunnel = new DefaultTunnel(null, srcEndPoint, dstEndPoint, MPLS, INIT, null, null,
376 TunnelName.tunnelName(tunnelName), computedPath,
377 labelStack, annotationBuilder.build());
378
379 // Allocate bandwidth.
380 TunnelConsumerId consumerId = null;
381 if (bwConstraintValue != 0) {
382 consumerId = reserveBandwidth(computedPath, bwConstraintValue, null);
383 if (consumerId == null) {
384 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
385 return false;
386 }
387 }
388
389 TunnelId tunnelId = tunnelService.setupTunnel(appId, src, tunnel, computedPath);
390 if (tunnelId == null) {
391 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
392 if (consumerId != null) {
393 resourceService.release(consumerId);
394 }
395 return false;
396 }
397
398 if (consumerId != null) {
399 // Store tunnel consumer id in LSP-Label store.
400 PceccTunnelInfo pceccTunnelInfo = new PceccTunnelInfo(null, consumerId);
401 pceStore.addTunnelInfo(tunnelId, pceccTunnelInfo);
402 }
403 return true;
404 }
Avantika-Huawei73862d42016-05-12 18:58:06 +0530405
406 @Override
407 public boolean updatePath(TunnelId tunnelId, List<Constraint> constraints) {
408 checkNotNull(tunnelId);
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530409 Set<Path> computedPathSet = null;
410 Tunnel tunnel = tunnelService.queryTunnel(tunnelId);
Avantika-Huawei73862d42016-05-12 18:58:06 +0530411
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530412 if (tunnel == null) {
413 return false;
414 }
Avantika-Huawei73862d42016-05-12 18:58:06 +0530415
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530416 if (tunnel.type() != MPLS || FALSE.equalsIgnoreCase(tunnel.annotations().value(DELEGATE))) {
417 // Only delegated LSPs can be updated.
418 return false;
419 }
420
421 List<Link> links = tunnel.path().links();
422 String lspSigType = tunnel.annotations().value(LSP_SIG_TYPE);
423 double bwConstraintValue = 0;
Priyanka B3f92c5a2016-05-27 10:14:16 +0530424 String costType = null;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530425 SharedBandwidthConstraint shBwConstraint = null;
426 BandwidthConstraint bwConstraint = null;
427 CostConstraint costConstraint = null;
428
429 if (constraints != null) {
430 // Call path computation in shared bandwidth mode.
431 Iterator<Constraint> iterator = constraints.iterator();
432 while (iterator.hasNext()) {
433 Constraint constraint = iterator.next();
434 if (constraint instanceof BandwidthConstraint) {
435 bwConstraint = (BandwidthConstraint) constraint;
436 bwConstraintValue = bwConstraint.bandwidth().bps();
437 } else if (constraint instanceof CostConstraint) {
438 costConstraint = (CostConstraint) constraint;
Priyanka B3f92c5a2016-05-27 10:14:16 +0530439 costType = costConstraint.type().name();
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530440 }
441 }
442
443 // Remove and keep the cost constraint at the end of the list of constraints.
444 if (costConstraint != null) {
445 constraints.remove(costConstraint);
446 }
447
448 Bandwidth existingBwValue = null;
449 String existingBwAnnotation = tunnel.annotations().value(BANDWIDTH);
450 if (existingBwAnnotation != null) {
451 existingBwValue = Bandwidth.bps(Double.parseDouble(existingBwAnnotation));
452
453 /*
454 * The computation is a shared bandwidth constraint based, so need to remove bandwidth constraint which
455 * has been utilized to create shared bandwidth constraint.
456 */
457 if (bwConstraint != null) {
458 constraints.remove(bwConstraint);
459 }
460 }
461
462 if (existingBwValue != null) {
463 shBwConstraint = new SharedBandwidthConstraint(links, existingBwValue, bwConstraint.bandwidth());
464 constraints.add(shBwConstraint);
465 }
466 } else {
467 constraints = new LinkedList<>();
468 }
469
470 constraints.add(CapabilityConstraint.of(CapabilityType.valueOf(lspSigType)));
471 if (costConstraint != null) {
472 constraints.add(costConstraint);
473 }
474
475 computedPathSet = computePath(links.get(0).src().deviceId(), links.get(links.size() - 1).dst().deviceId(),
476 constraints);
477
478 // NO-PATH
479 if (computedPathSet.isEmpty()) {
480 return false;
481 }
482
483 Builder annotationBuilder = DefaultAnnotations.builder();
484 annotationBuilder.set(BANDWIDTH, String.valueOf(bwConstraintValue));
Priyanka B3f92c5a2016-05-27 10:14:16 +0530485 if (costType != null) {
486 annotationBuilder.set(COST_TYPE, costType);
487 }
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530488 annotationBuilder.set(LSP_SIG_TYPE, lspSigType);
489 annotationBuilder.set(PCE_INIT, TRUE);
490 annotationBuilder.set(DELEGATE, TRUE);
491 annotationBuilder.set(PLSP_ID, tunnel.annotations().value(PLSP_ID));
492 annotationBuilder.set(PCC_TUNNEL_ID, tunnel.annotations().value(PCC_TUNNEL_ID));
493
494 Path computedPath = computedPathSet.iterator().next();
495 LabelStack labelStack = null;
496 TunnelConsumerId consumerId = null;
497 LspType lspType = LspType.valueOf(lspSigType);
498 long localLspId = 0;
499 if (lspType != WITH_SIGNALLING) {
500 /*
501 * Local LSP id which is assigned by RSVP for RSVP signalled LSPs, will be assigned by
502 * PCE for non-RSVP signalled LSPs.
503 */
504 localLspId = getNextLocalLspId();
505 annotationBuilder.set(LOCAL_LSP_ID, String.valueOf(localLspId));
506
507 if (lspType == SR_WITHOUT_SIGNALLING) {
508 labelStack = srTeHandler.computeLabelStack(computedPath);
509 // Failed to form a label stack.
510 if (labelStack == null) {
511 return false;
512 }
513 }
514 }
515
516 Tunnel updatedTunnel = new DefaultTunnel(null, tunnel.src(), tunnel.dst(), MPLS, INIT, null, null,
517 tunnel.tunnelName(), computedPath,
518 labelStack, annotationBuilder.build());
519
520 // Allocate shared bandwidth.
521 if (bwConstraintValue != 0) {
522 consumerId = reserveBandwidth(computedPath, bwConstraintValue, shBwConstraint);
523 if (consumerId == null) {
524 return false;
525 }
526 }
527
528 TunnelId updatedTunnelId = tunnelService.setupTunnel(appId, links.get(0).src().deviceId(), updatedTunnel,
529 computedPath);
530
531 if (updatedTunnelId == null) {
532 if (consumerId != null) {
533 resourceService.release(consumerId);
534 }
535 return false;
536 }
537
538 if (consumerId != null) {
539 // Store tunnel consumer id in LSP-Label store.
540 PceccTunnelInfo pceccTunnelInfo = new PceccTunnelInfo(null, consumerId);
541 pceStore.addTunnelInfo(updatedTunnelId, pceccTunnelInfo);
542 }
Avantika-Huawei73862d42016-05-12 18:58:06 +0530543 return true;
544 }
545
546 @Override
547 public boolean releasePath(TunnelId tunnelId) {
548 checkNotNull(tunnelId);
549 // 1. Query Tunnel from Tunnel manager.
550 Tunnel tunnel = tunnelService.queryTunnel(tunnelId);
551
552 if (tunnel == null) {
553 return false;
554 }
555
556 // 2. Call tunnel service.
557 return tunnelService.downTunnel(appId, tunnel.tunnelId());
558 }
559
560 @Override
561 public Iterable<Tunnel> queryAllPath() {
562 return tunnelService.queryTunnel(MPLS);
563 }
564
565 @Override
566 public Tunnel queryPath(TunnelId tunnelId) {
567 return tunnelService.queryTunnel(tunnelId);
568 }
569
570 /**
571 * Returns the next local LSP identifier to be used either by getting from
572 * freed list if available otherwise generating a new one.
573 *
574 * @return value of local LSP identifier
575 */
576 private short getNextLocalLspId() {
577 // If there is any free id use it. Otherwise generate new id.
578 if (localLspIdFreeList.isEmpty()) {
579 return (short) localLspIdIdGen.getNewId();
580 }
581 Iterator<Short> it = localLspIdFreeList.iterator();
582 Short value = it.next();
583 localLspIdFreeList.remove(value);
584 return value;
585 }
586
Priyanka Bb6963582016-05-20 20:21:20 +0530587 protected class TeConstraintBasedLinkWeight implements LinkWeight {
588
589 private final List<Constraint> constraints;
590
591 /**
592 * Creates a new edge-weight function capable of evaluating links
593 * on the basis of the specified constraints.
594 *
595 * @param constraints path constraints
596 */
597 public TeConstraintBasedLinkWeight(List<Constraint> constraints) {
598 if (constraints == null) {
599 this.constraints = Collections.emptyList();
600 } else {
601 this.constraints = ImmutableList.copyOf(constraints);
602 }
603 }
604
605 @Override
606 public double weight(TopologyEdge edge) {
607 if (!constraints.iterator().hasNext()) {
608 //Takes default cost/hopcount as 1 if no constraints specified
609 return 1.0;
610 }
611
612 Iterator<Constraint> it = constraints.iterator();
613 double cost = 1;
614
615 //If any constraint fails return -1 also value of cost returned from cost constraint can't be negative
616 while (it.hasNext() && cost > 0) {
617 Constraint constraint = it.next();
618 if (constraint instanceof CapabilityConstraint) {
619 cost = ((CapabilityConstraint) constraint).isValidLink(edge.link(), deviceService) ? 1 : -1;
620 } else {
621 cost = constraint.cost(edge.link(), resourceService::isAvailable);
622 }
623 }
624 return cost;
625 }
626 }
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530627
Priyanka B3f92c5a2016-05-27 10:14:16 +0530628 //TODO: annotations used for temporarily later projection/network config will be used
629 private class InternalTopologyListener implements TopologyListener {
630 @Override
631 public void event(TopologyEvent event) {
632 event.reasons().forEach(e -> {
633 //If event type is link removed, get the impacted tunnel
634 if (e instanceof LinkEvent) {
635 LinkEvent linkEvent = (LinkEvent) e;
636 if (linkEvent.type() == LinkEvent.Type.LINK_REMOVED) {
637 tunnelService.queryTunnel(MPLS).forEach(t -> {
638 if (t.path().links().contains(((Link) e.subject()))) {
639 // Check whether this ONOS instance is master for ingress device if yes,
640 // recompute and send update
641 checkForMasterAndUpdateTunnel(t.path().src().deviceId(), t);
642 }
643 });
644 }
645 }
646 });
647 }
648 }
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530649
Priyanka B3f92c5a2016-05-27 10:14:16 +0530650 private boolean checkForMasterAndUpdateTunnel(DeviceId src, Tunnel tunnel) {
651 /**
652 * Master of ingress node will recompute and also delegation flag must be set.
653 */
654 if (mastershipService.isLocalMaster(src)
655 && Boolean.valueOf(tunnel.annotations().value(DELEGATE)) != null) {
656 LinkedList<Constraint> constraintList = new LinkedList<>();
657
658 if (tunnel.annotations().value(BANDWIDTH) != null) {
659 //Requested bandwidth will be same as previous allocated bandwidth for the tunnel
660 BandwidthConstraint localConst = new BandwidthConstraint(Bandwidth.bps(Double.parseDouble(tunnel
661 .annotations().value(BANDWIDTH))));
662 constraintList.add(localConst);
663 }
664 if (tunnel.annotations().value(COST_TYPE) != null) {
665 constraintList.add(CostConstraint.of(CostConstraint.Type.valueOf(tunnel.annotations().value(
666 COST_TYPE))));
667 }
Priyanka B9fa4ed32016-05-27 11:59:24 +0530668
669 /*
670 * If tunnel was UP after recomputation failed then store failed path in PCE store send PCIntiate(remove)
671 * and If tunnel is failed and computation fails nothing to do because tunnel status will be same[Failed]
672 */
673 if (!updatePath(tunnel.tunnelId(), constraintList) && !tunnel.state().equals(Tunnel.State.FAILED)) {
Priyanka B3f92c5a2016-05-27 10:14:16 +0530674 // If updation fails store in PCE store as failed path
675 // then PCInitiate (Remove)
676 pceStore.addFailedPathInfo(new PcePathInfo(tunnel.path().src().deviceId(), tunnel
677 .path().dst().deviceId(), tunnel.tunnelName().value(), constraintList,
678 LspType.valueOf(tunnel.annotations().value(LSP_SIG_TYPE))));
679 //Release that tunnel calling PCInitiate
680 releasePath(tunnel.tunnelId());
681 }
682 }
683
684 return false;
685 }
686
687 // Allocates the bandwidth locally for PCECC tunnels.
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530688 private TunnelConsumerId reserveBandwidth(Path computedPath, double bandwidthConstraint,
689 SharedBandwidthConstraint shBwConstraint) {
690 checkNotNull(computedPath);
691 checkNotNull(bandwidthConstraint);
692 Resource resource = null;
693 double bwToAllocate = 0;
694
695 TunnelConsumerId consumer = TunnelConsumerId.valueOf(tunnelConsumerIdGen.getNewId());
696
697 /**
698 * Shared bandwidth sub-case : Lesser bandwidth required than original -
699 * No reservation required.
700 */
701 Double additionalBwValue = null;
702 if (shBwConstraint != null) {
703 additionalBwValue = ((bandwidthConstraint - shBwConstraint.sharedBwValue().bps()) <= 0) ? null
704 : (bandwidthConstraint - shBwConstraint.sharedBwValue().bps());
705 }
706
707 Optional<ResourceAllocation> resAlloc = null;
708 for (Link link : computedPath.links()) {
709 bwToAllocate = 0;
710 if ((shBwConstraint != null) && (shBwConstraint.links().contains(link))) {
711 if (additionalBwValue != null) {
712 bwToAllocate = bandwidthConstraint - additionalBwValue;
713 }
714 } else {
715 bwToAllocate = bandwidthConstraint;
716 }
717
718 /**
719 * In shared bandwidth cases, where new BW is lesser than old BW, it
720 * is not required to allocate anything.
721 */
722 if (bwToAllocate != 0) {
723 resource = Resources.continuous(link.src().deviceId(), link.src().port(), Bandwidth.class)
724 .resource(bwToAllocate);
725 resAlloc = resourceService.allocate(consumer, resource);
726
727 // If allocation for any link fails, then release the partially allocated bandwidth.
728 if (!resAlloc.isPresent()) {
729 resourceService.release(consumer);
730 return null;
731 }
732 }
733 }
734
735 /*
736 * Note: Storing of tunnel consumer id is done by caller of bandwidth reservation function. So deleting tunnel
737 * consumer id should be done by caller of bandwidth releasing function. This will prevent ambiguities related
738 * to who is supposed to store/delete.
739 */
740 return consumer;
741 }
742
743 /*
744 * Deallocates the bandwidth which is reserved locally for PCECC tunnels.
745 */
746 private void releaseBandwidth(Tunnel tunnel) {
747 // Between same source and destination, search the tunnel with same symbolic path name.
748 Collection<Tunnel> tunnelQueryResult = tunnelService.queryTunnel(tunnel.src(), tunnel.dst());
749 Tunnel newTunnel = null;
750 for (Tunnel tunnelObj : tunnelQueryResult) {
751 if (tunnel.tunnelName().value().equals(tunnelObj.tunnelName().value())) {
752 newTunnel = tunnelObj;
753 break;
754 }
755 }
756
757 // Even if one link is shared, the bandwidth release should happen based on shared mechanism.
758 boolean isLinkShared = false;
759 if (newTunnel != null) {
760 for (Link link : tunnel.path().links()) {
761 if (newTunnel.path().links().contains(link)) {
762 isLinkShared = true;
763 break;
764 }
765 }
766 }
767
768 if (isLinkShared) {
769 releaseSharedBandwidth(newTunnel, tunnel);
770 return;
771 }
772
773 resourceService.release(pceStore.getTunnelInfo(tunnel.tunnelId()).tunnelConsumerId());
774 return;
775
776 /*
777 * Note: Storing of tunnel consumer id is done by caller of bandwidth reservation function. So deleting tunnel
778 * consumer id should be done by caller of bandwidth releasing function. This will prevent ambiguities related
779 * to who is supposed to store/delete.
780 */
781 }
782
783 /**
784 * Re-allocates the bandwidth for the tunnel for which the bandwidth was
785 * allocated in shared mode initially.
786 */
787 private synchronized void releaseSharedBandwidth(Tunnel newTunnel, Tunnel oldTunnel) {
788 // 1. Release old tunnel's bandwidth.
789 resourceService.release(pceStore.getTunnelInfo(oldTunnel.tunnelId()).tunnelConsumerId());
790
791 // 2. Release new tunnel's bandwidth
792 ResourceConsumer consumer = pceStore.getTunnelInfo(newTunnel.tunnelId()).tunnelConsumerId();
793 resourceService.release(consumer);
794
795 // 3. Allocate new tunnel's complete bandwidth.
796 double bandwidth = Double.parseDouble(newTunnel.annotations().value(BANDWIDTH));
797 Resource resource;
798
799 for (Link link : newTunnel.path().links()) {
800 resource = Resources.continuous(link.src().deviceId(), link.src().port(), Bandwidth.class)
801 .resource(bandwidth);
802 resourceService.allocate(consumer, resource); // Reusing new tunnel's TunnelConsumerId intentionally.
803 }
804 }
805
806 // Listens on tunnel events.
807 private class InnerTunnelListener implements TunnelListener {
808 @Override
809 public void event(TunnelEvent event) {
810 // Event gets generated with old tunnel object.
811 Tunnel tunnel = event.subject();
812 if (tunnel.type() != MPLS) {
813 return;
814 }
815
816 LspType lspType = LspType.valueOf(tunnel.annotations().value(LSP_SIG_TYPE));
817 String tunnelBandwidth = tunnel.annotations().value(BANDWIDTH);
818 double bwConstraintValue = 0;
819 if (tunnelBandwidth != null) {
820 bwConstraintValue = Double.parseDouble(tunnelBandwidth);
821 }
822
823 switch (event.type()) {
824 case TUNNEL_ADDED:
825 // Allocate bandwidth for non-initiated, delegated LSPs with non-zero bandwidth (learned LSPs).
826 String pceInit = tunnel.annotations().value(PCE_INIT);
827 if (FALSE.equalsIgnoreCase(pceInit)
828 && bwConstraintValue != 0) {
829 reserveBandwidth(tunnel.path(), bwConstraintValue, null);
830 }
831 break;
832
833 case TUNNEL_UPDATED:
834 // Allocate/send labels for basic PCECC tunnels.
835 if ((tunnel.state() == ESTABLISHED) && (lspType == WITHOUT_SIGNALLING_AND_WITHOUT_SR)) {
836 crHandler.allocateLabel(tunnel);
837 }
838
839 if (tunnel.state() == UNSTABLE) {
840 /*
841 * During LSP DB sync if PCC doesn't report LSP which was PCE initiated, it's state is turned into
842 * unstable so that it can be setup again. Add into failed path store so that it can be recomputed
843 * and setup while global reoptimization.
844 */
845
846 List<Constraint> constraints = new LinkedList<>();
847 String bandwidth = tunnel.annotations().value(BANDWIDTH);
848 if (bandwidth != null) {
849 constraints.add(new BandwidthConstraint(Bandwidth
850 .bps(Double.parseDouble(bandwidth))));
851 }
852
853 String costType = tunnel.annotations().value(COST_TYPE);
854 if (costType != null) {
855 CostConstraint costConstraint = new CostConstraint(CostConstraint.Type.valueOf(costType));
856 constraints.add(costConstraint);
857 }
858
859 constraints.add(CapabilityConstraint
860 .of(CapabilityType.valueOf(tunnel.annotations().value(LSP_SIG_TYPE))));
861
862 List<Link> links = tunnel.path().links();
863 pceStore.addFailedPathInfo(new PcePathInfo(links.get(0).src().deviceId(),
864 links.get(links.size() - 1).dst().deviceId(),
865 tunnel.tunnelName().value(), constraints, lspType));
866 }
867 break;
868
869 case TUNNEL_REMOVED:
870 if (lspType != WITH_SIGNALLING) {
871 localLspIdFreeList.add(Short.valueOf(tunnel.annotations().value(LOCAL_LSP_ID)));
872 }
873
874 // If not zero bandwidth, and delegated (initiated LSPs will also be delegated).
875 if (bwConstraintValue != 0) {
876 releaseBandwidth(event.subject());
877
878 // Release basic PCECC labels.
879 if (lspType == WITHOUT_SIGNALLING_AND_WITHOUT_SR) {
880 // Delete stored tunnel consumer id from PCE store (while still retaining label list.)
881 PceccTunnelInfo pceccTunnelInfo = pceStore.getTunnelInfo(tunnel.tunnelId());
882 pceccTunnelInfo.tunnelConsumerId(null);
883 crHandler.releaseLabel(tunnel);
884 } else {
885 pceStore.removeTunnelInfo(tunnel.tunnelId());
886 }
887 }
888 break;
889
890 default:
891 break;
892
893 }
894 return;
895 }
896 }
897
898 private boolean syncLabelDb(DeviceId deviceId) {
899 checkNotNull(deviceId);
900 Map<DeviceId, LabelResourceId> globalNodeLabelMap = pceStore.getGlobalNodeLabels();
901
902 for (Entry<DeviceId, LabelResourceId> entry : globalNodeLabelMap.entrySet()) {
903
904 // Convert from DeviceId to TunnelEndPoint
905 Device srcDevice = deviceService.getDevice(entry.getKey());
906
907 /*
908 * If there is a slight difference in timing such that if device subsystem has removed the device but PCE
909 * store still has it, just ignore such devices.
910 */
911 if (srcDevice == null) {
912 continue;
913 }
914
915 String srcLsrId = srcDevice.annotations().value(LSRID);
916 if (srcLsrId == null) {
917 continue;
918 }
919
920 srTeHandler.advertiseNodeLabelRule(deviceId,
921 entry.getValue(),
922 IpPrefix.valueOf(IpAddress.valueOf(srcLsrId), PREFIX_LENGTH),
923 Objective.Operation.ADD, false);
924 }
925
926 Map<Link, LabelResourceId> adjLabelMap = pceStore.getAdjLabels();
927 for (Entry<Link, LabelResourceId> entry : adjLabelMap.entrySet()) {
928 if (entry.getKey().src().deviceId().equals(deviceId)) {
929 srTeHandler.installAdjLabelRule(deviceId,
930 entry.getValue(),
931 entry.getKey().src().port(),
932 entry.getKey().dst().port(),
933 Objective.Operation.ADD);
934 }
935 }
936
937 srTeHandler.advertiseNodeLabelRule(deviceId,
938 LabelResourceId.labelResourceId(0),
939 IpPrefix.valueOf(END_OF_SYNC_IP_PREFIX),
940 Objective.Operation.ADD, true);
941
942 return true;
943 }
944
945 // Process the packet received.
946 private class PcepPacketProcessor implements PacketProcessor {
947 // Process the packet received and in our case initiates the label DB sync.
948 @Override
949 public void process(PacketContext context) {
950 // Stop processing if the packet has been handled, since we
951 // can't do any more to it.
952
953 if (context.isHandled()) {
954 return;
955 }
956
957 InboundPacket pkt = context.inPacket();
958 syncLabelDb(pkt.receivedFrom().deviceId());
959 }
960 }
961
Priyanka B9fa4ed32016-05-27 11:59:24 +0530962 //Computes path from tunnel store and also path failed to setup.
963 private void callForOptimization() {
964 //Recompute the LSPs which it was delegated [LSPs stored in PCE store (failed paths)]
965 for (PcePathInfo failedPathInfo : pceStore.getFailedPathInfos()) {
966 checkForMasterAndSetupPath(failedPathInfo);
967 }
968
969 //Recompute the LSPs for which it was delegated [LSPs stored in tunnel store]
970 tunnelService.queryTunnel(MPLS).forEach(t -> {
971 checkForMasterAndUpdateTunnel(t.path().src().deviceId(), t);
972 });
973 }
974
975 private boolean checkForMasterAndSetupPath(PcePathInfo failedPathInfo) {
976 /**
977 * Master of ingress node will setup the path failed stored in PCE store.
978 */
979 if (mastershipService.isLocalMaster(failedPathInfo.src())) {
980 if (setupPath(failedPathInfo.src(), failedPathInfo.dst(), failedPathInfo.name(),
981 failedPathInfo.constraints(), failedPathInfo.lspType())) {
982 // If computation is success remove that path
983 pceStore.removeFailedPathInfo(failedPathInfo);
984 return true;
985 }
986 }
987
988 return false;
989 }
990
991 //Timer to call global optimization
992 private class GlobalOptimizationTimer implements Runnable {
993
994 public GlobalOptimizationTimer() {
995 }
996
997 @Override
998 public void run() {
999 callForOptimization();
1000 }
1001 }
Avantika-Huawei73862d42016-05-12 18:58:06 +05301002}