1 package org.onosproject.store.resource.impl;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.HashSet;
11 import java.util.stream.Collectors;
13 import org.apache.felix.scr.annotations.Component;
14 import org.apache.felix.scr.annotations.Reference;
15 import org.apache.felix.scr.annotations.ReferenceCardinality;
16 import org.apache.felix.scr.annotations.Service;
17 import org.apache.felix.scr.annotations.Activate;
18 import org.apache.felix.scr.annotations.Deactivate;
19 import org.onlab.util.Bandwidth;
20 import org.onosproject.net.OmsPort;
21 import org.onosproject.net.device.DeviceService;
22 import org.slf4j.Logger;
23 import org.onlab.util.PositionalParameterStringFormatter;
24 import org.onosproject.net.Link;
25 import org.onosproject.net.LinkKey;
26 import org.onosproject.net.Port;
27 import org.onosproject.net.intent.IntentId;
28 import org.onosproject.net.link.LinkService;
29 import org.onosproject.net.resource.link.BandwidthResource;
30 import org.onosproject.net.resource.link.BandwidthResourceAllocation;
31 import org.onosproject.net.resource.link.LambdaResource;
32 import org.onosproject.net.resource.link.LambdaResourceAllocation;
33 import org.onosproject.net.resource.link.LinkResourceAllocations;
34 import org.onosproject.net.resource.link.LinkResourceEvent;
35 import org.onosproject.net.resource.link.LinkResourceStore;
36 import org.onosproject.net.resource.link.LinkResourceStoreDelegate;
37 import org.onosproject.net.resource.link.MplsLabel;
38 import org.onosproject.net.resource.link.MplsLabelResourceAllocation;
39 import org.onosproject.net.resource.ResourceAllocation;
40 import org.onosproject.net.resource.ResourceAllocationException;
41 import org.onosproject.net.resource.ResourceType;
42 import org.onosproject.store.AbstractStore;
43 import org.onosproject.store.serializers.KryoNamespaces;
44 import org.onosproject.store.service.ConsistentMap;
45 import org.onosproject.store.service.Serializer;
46 import org.onosproject.store.service.StorageService;
47 import org.onosproject.store.service.TransactionContext;
48 import org.onosproject.store.service.TransactionException;
49 import org.onosproject.store.service.TransactionalMap;
50 import org.onosproject.store.service.Versioned;
52 import com.google.common.collect.ImmutableList;
53 import com.google.common.collect.ImmutableSet;
54 import com.google.common.collect.Sets;
56 import static com.google.common.base.Preconditions.checkNotNull;
57 import static com.google.common.base.Preconditions.checkState;
58 import static org.slf4j.LoggerFactory.getLogger;
59 import static org.onosproject.net.AnnotationKeys.BANDWIDTH;
62 * Store that manages link resources using Copycat-backed TransactionalMaps.
64 @Component(immediate = true, enabled = true)
66 public class ConsistentLinkResourceStore extends
67 AbstractStore<LinkResourceEvent, LinkResourceStoreDelegate> implements
70 private final Logger log = getLogger(getClass());
72 private static final BandwidthResource DEFAULT_BANDWIDTH = new BandwidthResource(Bandwidth.mbps(1_000));
73 private static final BandwidthResource EMPTY_BW = new BandwidthResource(Bandwidth.bps(0));
75 // Smallest non-reserved MPLS label
76 private static final int MIN_UNRESERVED_LABEL = 0x10;
77 // Max non-reserved MPLS label = 239
78 private static final int MAX_UNRESERVED_LABEL = 0xEF;
80 // table to store current allocations
81 /** LinkKey -> List<LinkResourceAllocations>. */
82 private static final String LINK_RESOURCE_ALLOCATIONS = "LinkAllocations";
84 /** IntentId -> LinkResourceAllocations. */
85 private static final String INTENT_ALLOCATIONS = "LinkIntentAllocations";
87 private static final Serializer SERIALIZER = Serializer.using(KryoNamespaces.API);
89 // for reading committed values.
90 private ConsistentMap<IntentId, LinkResourceAllocations> intentAllocMap;
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected StorageService storageService;
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected LinkService linkService;
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected DeviceService deviceService;
102 public void activate() {
103 intentAllocMap = storageService.<IntentId, LinkResourceAllocations>consistentMapBuilder()
104 .withName(INTENT_ALLOCATIONS)
105 .withSerializer(SERIALIZER)
111 public void deactivate() {
115 private TransactionalMap<IntentId, LinkResourceAllocations> getIntentAllocs(TransactionContext tx) {
116 return tx.getTransactionalMap(INTENT_ALLOCATIONS, SERIALIZER);
119 private TransactionalMap<LinkKey, List<LinkResourceAllocations>> getLinkAllocs(TransactionContext tx) {
120 return tx.getTransactionalMap(LINK_RESOURCE_ALLOCATIONS, SERIALIZER);
123 private TransactionContext getTxContext() {
124 return storageService.transactionContextBuilder().build();
127 private Set<? extends ResourceAllocation> getResourceCapacity(ResourceType type, Link link) {
128 if (type == ResourceType.BANDWIDTH) {
129 return ImmutableSet.of(getBandwidthResourceCapacity(link));
131 if (type == ResourceType.LAMBDA) {
132 return getLambdaResourceCapacity(link);
134 if (type == ResourceType.MPLS_LABEL) {
135 return getMplsResourceCapacity();
137 return ImmutableSet.of();
140 private Set<LambdaResourceAllocation> getLambdaResourceCapacity(Link link) {
141 Set<LambdaResourceAllocation> allocations = new HashSet<>();
142 Port port = deviceService.getPort(link.src().deviceId(), link.src().port());
143 if (port instanceof OmsPort) {
144 OmsPort omsPort = (OmsPort) port;
146 // Assume fixed grid for now
147 for (int i = 0; i < omsPort.totalChannels(); i++) {
148 allocations.add(new LambdaResourceAllocation(LambdaResource.valueOf(i)));
154 private BandwidthResourceAllocation getBandwidthResourceCapacity(Link link) {
156 // if Link annotation exist, use them
157 // if all fails, use DEFAULT_BANDWIDTH
158 BandwidthResource bandwidth = null;
159 String strBw = link.annotations().value(BANDWIDTH);
162 bandwidth = new BandwidthResource(Bandwidth.mbps(Double.parseDouble(strBw)));
163 } catch (NumberFormatException e) {
169 if (bandwidth == null) {
170 // fall back, use fixed default
171 bandwidth = DEFAULT_BANDWIDTH;
173 return new BandwidthResourceAllocation(bandwidth);
176 private Set<MplsLabelResourceAllocation> getMplsResourceCapacity() {
177 Set<MplsLabelResourceAllocation> allocations = new HashSet<>();
178 //Ignoring reserved labels of 0 through 15
179 for (int i = MIN_UNRESERVED_LABEL; i <= MAX_UNRESERVED_LABEL; i++) {
180 allocations.add(new MplsLabelResourceAllocation(MplsLabel
187 private Map<ResourceType, Set<? extends ResourceAllocation>> getResourceCapacity(Link link) {
188 Map<ResourceType, Set<? extends ResourceAllocation>> caps = new HashMap<>();
189 for (ResourceType type : ResourceType.values()) {
190 Set<? extends ResourceAllocation> cap = getResourceCapacity(type, link);
199 public Set<ResourceAllocation> getFreeResources(Link link) {
200 TransactionContext tx = getTxContext();
204 Map<ResourceType, Set<? extends ResourceAllocation>> freeResources = getFreeResourcesEx(tx, link);
205 Set<ResourceAllocation> allFree = new HashSet<>();
206 freeResources.values().forEach(allFree::addAll);
213 private Map<ResourceType, Set<? extends ResourceAllocation>> getFreeResourcesEx(TransactionContext tx, Link link) {
217 Map<ResourceType, Set<? extends ResourceAllocation>> free = new HashMap<>();
218 final Map<ResourceType, Set<? extends ResourceAllocation>> caps = getResourceCapacity(link);
219 final Iterable<LinkResourceAllocations> allocations = getAllocations(tx, link);
221 for (ResourceType type : ResourceType.values()) {
222 // there should be class/category of resources
226 Set<? extends ResourceAllocation> bw = caps.get(type);
227 if (bw == null || bw.isEmpty()) {
228 bw = Sets.newHashSet(new BandwidthResourceAllocation(EMPTY_BW));
231 BandwidthResourceAllocation cap = (BandwidthResourceAllocation) bw.iterator().next();
232 double freeBw = cap.bandwidth().toDouble();
234 // enumerate current allocations, subtracting resources
235 for (LinkResourceAllocations alloc : allocations) {
236 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
237 for (ResourceAllocation a : types) {
238 if (a instanceof BandwidthResourceAllocation) {
239 BandwidthResourceAllocation bwA = (BandwidthResourceAllocation) a;
240 freeBw -= bwA.bandwidth().toDouble();
245 free.put(type, Sets.newHashSet(
246 new BandwidthResourceAllocation(new BandwidthResource(Bandwidth.bps(freeBw)))));
249 Set<? extends ResourceAllocation> lmd = caps.get(type);
250 if (lmd == null || lmd.isEmpty()) {
254 Set<LambdaResourceAllocation> freeL = new HashSet<>();
255 for (ResourceAllocation r : lmd) {
256 if (r instanceof LambdaResourceAllocation) {
257 freeL.add((LambdaResourceAllocation) r);
261 // enumerate current allocations, removing resources
262 for (LinkResourceAllocations alloc : allocations) {
263 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
264 for (ResourceAllocation a : types) {
265 if (a instanceof LambdaResourceAllocation) {
271 free.put(type, freeL);
274 Set<? extends ResourceAllocation> mpls = caps.get(type);
275 if (mpls == null || mpls.isEmpty()) {
279 Set<MplsLabelResourceAllocation> freeLabel = new HashSet<>();
280 for (ResourceAllocation r : mpls) {
281 if (r instanceof MplsLabelResourceAllocation) {
282 freeLabel.add((MplsLabelResourceAllocation) r);
286 // enumerate current allocations, removing resources
287 for (LinkResourceAllocations alloc : allocations) {
288 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
289 for (ResourceAllocation a : types) {
290 if (a instanceof MplsLabelResourceAllocation) {
296 free.put(type, freeLabel);
299 log.debug("unsupported ResourceType {}", type);
307 public void allocateResources(LinkResourceAllocations allocations) {
308 checkNotNull(allocations);
309 TransactionContext tx = getTxContext();
313 TransactionalMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
314 intentAllocs.put(allocations.intentId(), allocations);
315 allocations.links().forEach(link -> allocateLinkResource(tx, link, allocations));
317 } catch (Exception e) {
318 log.error("Exception thrown, rolling back", e);
324 private void allocateLinkResource(TransactionContext tx, Link link,
325 LinkResourceAllocations allocations) {
326 // requested resources
327 Set<ResourceAllocation> reqs = allocations.getResourceAllocation(link);
328 Map<ResourceType, Set<? extends ResourceAllocation>> available = getFreeResourcesEx(tx, link);
329 for (ResourceAllocation req : reqs) {
330 Set<? extends ResourceAllocation> avail = available.get(req.type());
331 if (req instanceof BandwidthResourceAllocation) {
332 // check if allocation should be accepted
333 if (avail.isEmpty()) {
334 checkState(!avail.isEmpty(),
335 "There's no Bandwidth resource on %s?",
338 BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
339 double bwLeft = bw.bandwidth().toDouble();
340 BandwidthResourceAllocation bwReq = ((BandwidthResourceAllocation) req);
341 bwLeft -= bwReq.bandwidth().toDouble();
343 throw new ResourceAllocationException(
344 PositionalParameterStringFormatter.format(
345 "Unable to allocate bandwidth for link {} "
346 + " requested amount is {} current allocation is {}",
348 bwReq.bandwidth().toDouble(),
351 } else if (req instanceof LambdaResourceAllocation) {
352 LambdaResourceAllocation lambdaAllocation = (LambdaResourceAllocation) req;
353 // check if allocation should be accepted
354 if (!avail.contains(req)) {
355 // requested lambda was not available
356 throw new ResourceAllocationException(
357 PositionalParameterStringFormatter.format(
358 "Unable to allocate lambda for link {} lambda is {}",
360 lambdaAllocation.lambda().toInt()));
362 } else if (req instanceof MplsLabelResourceAllocation) {
363 MplsLabelResourceAllocation mplsAllocation = (MplsLabelResourceAllocation) req;
364 if (!avail.contains(req)) {
365 throw new ResourceAllocationException(
366 PositionalParameterStringFormatter
367 .format("Unable to allocate MPLS label for link "
368 + "{} MPLS label is {}",
376 // all requests allocatable => add allocation
377 final LinkKey linkKey = LinkKey.linkKey(link);
378 TransactionalMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
379 List<LinkResourceAllocations> before = linkAllocs.get(linkKey);
380 if (before == null) {
381 List<LinkResourceAllocations> after = new ArrayList<>();
382 after.add(allocations);
383 before = linkAllocs.putIfAbsent(linkKey, after);
384 if (before != null) {
385 // concurrent allocation detected, retry transaction : is this needed?
386 log.warn("Concurrent Allocation, retrying");
387 throw new TransactionException();
390 List<LinkResourceAllocations> after = new ArrayList<>(before.size() + 1);
391 after.addAll(before);
392 after.add(allocations);
393 linkAllocs.replace(linkKey, before, after);
398 public LinkResourceEvent releaseResources(LinkResourceAllocations allocations) {
399 checkNotNull(allocations);
401 final IntentId intentId = allocations.intentId();
402 final Collection<Link> links = allocations.links();
403 boolean success = false;
405 TransactionContext tx = getTxContext();
408 TransactionalMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
409 intentAllocs.remove(intentId);
411 TransactionalMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
412 links.forEach(link -> {
413 final LinkKey linkId = LinkKey.linkKey(link);
415 List<LinkResourceAllocations> before = linkAllocs.get(linkId);
416 if (before == null || before.isEmpty()) {
417 // something is wrong, but it is already freed
418 log.warn("There was no resource left to release on {}", linkId);
421 List<LinkResourceAllocations> after = new ArrayList<>(before);
422 after.remove(allocations);
423 linkAllocs.replace(linkId, before, after);
427 } catch (TransactionException e) {
428 log.debug("Transaction failed, retrying", e);
430 } catch (Exception e) {
431 log.error("Exception thrown during releaseResource {}", allocations, e);
437 // Issue events to force recompilation of intents.
438 final List<LinkResourceAllocations> releasedResources = ImmutableList.of(allocations);
439 return new LinkResourceEvent(
440 LinkResourceEvent.Type.ADDITIONAL_RESOURCES_AVAILABLE,
446 public LinkResourceAllocations getAllocations(IntentId intentId) {
447 checkNotNull(intentId);
448 Versioned<LinkResourceAllocations> alloc = null;
450 alloc = intentAllocMap.get(intentId);
451 } catch (Exception e) {
452 log.warn("Could not read resource allocation information", e);
454 return alloc == null ? null : alloc.value();
458 public Iterable<LinkResourceAllocations> getAllocations(Link link) {
460 TransactionContext tx = getTxContext();
461 Iterable<LinkResourceAllocations> res = null;
464 res = getAllocations(tx, link);
468 return res == null ? Collections.emptyList() : res;
472 public Iterable<LinkResourceAllocations> getAllocations() {
474 Set<LinkResourceAllocations> allocs =
475 intentAllocMap.values().stream().map(Versioned::value).collect(Collectors.toSet());
476 return ImmutableSet.copyOf(allocs);
477 } catch (Exception e) {
478 log.warn("Could not read resource allocation information", e);
480 return ImmutableSet.of();
483 private Iterable<LinkResourceAllocations> getAllocations(TransactionContext tx, Link link) {
486 final LinkKey key = LinkKey.linkKey(link);
487 TransactionalMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
488 List<LinkResourceAllocations> res = null;
490 res = linkAllocs.get(key);
492 res = linkAllocs.putIfAbsent(key, new ArrayList<>());
495 return Collections.emptyList();