blob: 84f7bb24e30f3b028da85b73ac7950e3d7877151 [file] [log] [blame]
Ayaka Koshibee114f042015-05-01 11:43:00 -07001package org.onosproject.store.resource.impl;
2
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.Collections;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.List;
9import java.util.Map;
10import java.util.Set;
11import java.util.stream.Collectors;
12
13import org.apache.felix.scr.annotations.Component;
14import org.apache.felix.scr.annotations.Reference;
15import org.apache.felix.scr.annotations.ReferenceCardinality;
16import org.apache.felix.scr.annotations.Service;
17import org.apache.felix.scr.annotations.Activate;
18import org.apache.felix.scr.annotations.Deactivate;
Sho SHIMIZU6d01d3d2015-05-08 14:08:36 -070019import org.onlab.util.Bandwidth;
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070020import org.onosproject.net.OmsPort;
21import org.onosproject.net.device.DeviceService;
Ayaka Koshibee114f042015-05-01 11:43:00 -070022import org.slf4j.Logger;
23import org.onlab.util.KryoNamespace;
24import org.onlab.util.PositionalParameterStringFormatter;
25import org.onosproject.net.Link;
26import org.onosproject.net.LinkKey;
27import org.onosproject.net.intent.IntentId;
28import org.onosproject.net.link.LinkService;
Brian O'Connor6de2e202015-05-21 14:30:41 -070029import org.onosproject.net.resource.link.BandwidthResource;
30import org.onosproject.net.resource.link.BandwidthResourceAllocation;
31import org.onosproject.net.resource.link.LambdaResource;
32import org.onosproject.net.resource.link.LambdaResourceAllocation;
33import org.onosproject.net.resource.link.LinkResourceAllocations;
34import org.onosproject.net.resource.link.LinkResourceEvent;
35import org.onosproject.net.resource.link.LinkResourceStore;
36import org.onosproject.net.resource.link.LinkResourceStoreDelegate;
37import org.onosproject.net.resource.link.MplsLabel;
38import org.onosproject.net.resource.link.MplsLabelResourceAllocation;
Ayaka Koshibee114f042015-05-01 11:43:00 -070039import org.onosproject.net.resource.ResourceAllocation;
40import org.onosproject.net.resource.ResourceAllocationException;
41import org.onosproject.net.resource.ResourceType;
42import org.onosproject.store.AbstractStore;
43import org.onosproject.store.serializers.KryoNamespaces;
44import org.onosproject.store.service.ConsistentMap;
45import org.onosproject.store.service.Serializer;
46import org.onosproject.store.service.StorageService;
47import org.onosproject.store.service.TransactionContext;
48import org.onosproject.store.service.TransactionException;
49import org.onosproject.store.service.TransactionalMap;
50import org.onosproject.store.service.Versioned;
51
52import com.google.common.collect.ImmutableList;
53import com.google.common.collect.ImmutableSet;
54import com.google.common.collect.Sets;
55
56import static com.google.common.base.Preconditions.checkNotNull;
57import static com.google.common.base.Preconditions.checkState;
58import static org.slf4j.LoggerFactory.getLogger;
59import static org.onosproject.net.AnnotationKeys.BANDWIDTH;
Ayaka Koshibee114f042015-05-01 11:43:00 -070060
61/**
62 * Store that manages link resources using Copycat-backed TransactionalMaps.
63 */
Ayaka Koshibe474ef5f2015-05-01 15:24:51 -070064@Component(immediate = true, enabled = true)
Ayaka Koshibee114f042015-05-01 11:43:00 -070065@Service
66public class ConsistentLinkResourceStore extends
67 AbstractStore<LinkResourceEvent, LinkResourceStoreDelegate> implements
68 LinkResourceStore {
69
70 private final Logger log = getLogger(getClass());
71
Sho SHIMIZU6d01d3d2015-05-08 14:08:36 -070072 private static final BandwidthResource DEFAULT_BANDWIDTH = new BandwidthResource(Bandwidth.mbps(1_000));
73 private static final BandwidthResource EMPTY_BW = new BandwidthResource(Bandwidth.bps(0));
Ayaka Koshibee114f042015-05-01 11:43:00 -070074
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;
79
80 // table to store current allocations
81 /** LinkKey -> List<LinkResourceAllocations>. */
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070082 private static final String LINK_RESOURCE_ALLOCATIONS = "o";
Ayaka Koshibee114f042015-05-01 11:43:00 -070083
84 /** IntentId -> LinkResourceAllocations. */
85 private static final String INTENT_ALLOCATIONS = "IntentAllocations";
86
87 private static final Serializer SERIALIZER = Serializer.using(
88 new KryoNamespace.Builder().register(KryoNamespaces.API).build());
89
90 // for reading committed values.
91 private ConsistentMap<IntentId, LinkResourceAllocations> intentAllocMap;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected StorageService storageService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected LinkService linkService;
98
Marc De Leenheer1afa2a02015-05-13 09:18:07 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected DeviceService deviceService;
101
Ayaka Koshibee114f042015-05-01 11:43:00 -0700102 @Activate
103 public void activate() {
104 intentAllocMap = storageService.<IntentId, LinkResourceAllocations>consistentMapBuilder()
105 .withName(INTENT_ALLOCATIONS)
106 .withSerializer(SERIALIZER)
107 .build();
108 log.info("Started");
109 }
110
111 @Deactivate
112 public void deactivate() {
113 log.info("Stopped");
114 }
115
116 private TransactionalMap<IntentId, LinkResourceAllocations> getIntentAllocs(TransactionContext tx) {
117 return tx.getTransactionalMap(INTENT_ALLOCATIONS, SERIALIZER);
118 }
119
120 private TransactionalMap<LinkKey, List<LinkResourceAllocations>> getLinkAllocs(TransactionContext tx) {
121 return tx.getTransactionalMap(LINK_RESOURCE_ALLOCATIONS, SERIALIZER);
122 }
123
124 private TransactionContext getTxContext() {
125 return storageService.transactionContextBuilder().build();
126 }
127
128 private Set<? extends ResourceAllocation> getResourceCapacity(ResourceType type, Link link) {
129 if (type == ResourceType.BANDWIDTH) {
130 return ImmutableSet.of(getBandwidthResourceCapacity(link));
131 }
132 if (type == ResourceType.LAMBDA) {
133 return getLambdaResourceCapacity(link);
134 }
135 if (type == ResourceType.MPLS_LABEL) {
136 return getMplsResourceCapacity();
137 }
138 return ImmutableSet.of();
139 }
140
141 private Set<LambdaResourceAllocation> getLambdaResourceCapacity(Link link) {
142 Set<LambdaResourceAllocation> allocations = new HashSet<>();
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700143
144 OmsPort port = (OmsPort) deviceService.getPort(link.src().deviceId(), link.src().port());
145
146 // Assume fixed grid for now
147 for (int i = 0; i < port.totalChannels(); i++) {
148 allocations.add(new LambdaResourceAllocation(LambdaResource.valueOf(i)));
Ayaka Koshibee114f042015-05-01 11:43:00 -0700149 }
Marc De Leenheer1afa2a02015-05-13 09:18:07 -0700150
Ayaka Koshibee114f042015-05-01 11:43:00 -0700151 return allocations;
152 }
153
154 private BandwidthResourceAllocation getBandwidthResourceCapacity(Link link) {
155
156 // if Link annotation exist, use them
157 // if all fails, use DEFAULT_BANDWIDTH
Sho SHIMIZU63feca72015-05-07 10:44:25 -0700158 BandwidthResource bandwidth = null;
Ayaka Koshibee114f042015-05-01 11:43:00 -0700159 String strBw = link.annotations().value(BANDWIDTH);
160 if (strBw != null) {
161 try {
Sho SHIMIZU6d01d3d2015-05-08 14:08:36 -0700162 bandwidth = new BandwidthResource(Bandwidth.mbps(Double.parseDouble(strBw)));
Ayaka Koshibee114f042015-05-01 11:43:00 -0700163 } catch (NumberFormatException e) {
164 // do nothings
165 bandwidth = null;
166 }
167 }
168
169 if (bandwidth == null) {
170 // fall back, use fixed default
171 bandwidth = DEFAULT_BANDWIDTH;
172 }
173 return new BandwidthResourceAllocation(bandwidth);
174 }
175
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
181 .valueOf(i)));
182
183 }
184 return allocations;
185 }
186
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);
191 if (cap != null) {
192 caps.put(type, cap);
193 }
194 }
195 return caps;
196 }
197
198 @Override
199 public Set<ResourceAllocation> getFreeResources(Link link) {
200 TransactionContext tx = getTxContext();
201
202 tx.begin();
203 try {
204 Map<ResourceType, Set<? extends ResourceAllocation>> freeResources = getFreeResourcesEx(tx, link);
205 Set<ResourceAllocation> allFree = new HashSet<>();
206 freeResources.values().forEach(allFree::addAll);
207 return allFree;
208 } finally {
209 tx.abort();
210 }
211 }
212
213 private Map<ResourceType, Set<? extends ResourceAllocation>> getFreeResourcesEx(TransactionContext tx, Link link) {
214 checkNotNull(tx);
215 checkNotNull(link);
216
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);
220
221 for (ResourceType type : ResourceType.values()) {
222 // there should be class/category of resources
223
224 switch (type) {
225 case BANDWIDTH:
226 Set<? extends ResourceAllocation> bw = caps.get(type);
227 if (bw == null || bw.isEmpty()) {
228 bw = Sets.newHashSet(new BandwidthResourceAllocation(EMPTY_BW));
229 }
230
231 BandwidthResourceAllocation cap = (BandwidthResourceAllocation) bw.iterator().next();
232 double freeBw = cap.bandwidth().toDouble();
233
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();
241 }
242 }
243 }
244
Sho SHIMIZU6d01d3d2015-05-08 14:08:36 -0700245 free.put(type, Sets.newHashSet(
246 new BandwidthResourceAllocation(new BandwidthResource(Bandwidth.bps(freeBw)))));
Ayaka Koshibee114f042015-05-01 11:43:00 -0700247 break;
248 case LAMBDA:
249 Set<? extends ResourceAllocation> lmd = caps.get(type);
250 if (lmd == null || lmd.isEmpty()) {
251 // nothing left
252 break;
253 }
254 Set<LambdaResourceAllocation> freeL = new HashSet<>();
255 for (ResourceAllocation r : lmd) {
256 if (r instanceof LambdaResourceAllocation) {
257 freeL.add((LambdaResourceAllocation) r);
258 }
259 }
260
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) {
266 freeL.remove(a);
267 }
268 }
269 }
270
271 free.put(type, freeL);
272 break;
273 case MPLS_LABEL:
274 Set<? extends ResourceAllocation> mpls = caps.get(type);
275 if (mpls == null || mpls.isEmpty()) {
276 // nothing left
277 break;
278 }
279 Set<MplsLabelResourceAllocation> freeLabel = new HashSet<>();
280 for (ResourceAllocation r : mpls) {
281 if (r instanceof MplsLabelResourceAllocation) {
282 freeLabel.add((MplsLabelResourceAllocation) r);
283 }
284 }
285
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) {
291 freeLabel.remove(a);
292 }
293 }
294 }
295
296 free.put(type, freeLabel);
297 break;
298 default:
299 log.debug("unsupported ResourceType {}", type);
300 break;
301 }
302 }
303 return free;
304 }
305
306 @Override
307 public void allocateResources(LinkResourceAllocations allocations) {
308 checkNotNull(allocations);
309 TransactionContext tx = getTxContext();
310
311 tx.begin();
312 try {
313 TransactionalMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
314 intentAllocs.put(allocations.intentId(), allocations);
315 allocations.links().forEach(link -> allocateLinkResource(tx, link, allocations));
316 tx.commit();
317 } catch (Exception e) {
318 log.error("Exception thrown, rolling back", e);
319 tx.abort();
320 throw e;
321 }
322 }
323
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?",
336 link);
337 }
338 BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
339 double bwLeft = bw.bandwidth().toDouble();
340 BandwidthResourceAllocation bwReq = ((BandwidthResourceAllocation) req);
341 bwLeft -= bwReq.bandwidth().toDouble();
342 if (bwLeft < 0) {
343 throw new ResourceAllocationException(
344 PositionalParameterStringFormatter.format(
345 "Unable to allocate bandwidth for link {} "
346 + " requested amount is {} current allocation is {}",
347 link,
348 bwReq.bandwidth().toDouble(),
349 bw));
350 }
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 {}",
359 link,
360 lambdaAllocation.lambda().toInt()));
361 }
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 {}",
369 link,
370 mplsAllocation
371 .mplsLabel()
372 .toString()));
373 }
374 }
375 }
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();
388 }
389 } else {
390 List<LinkResourceAllocations> after = new ArrayList<>(before.size() + 1);
391 after.addAll(before);
392 after.add(allocations);
393 linkAllocs.replace(linkKey, before, after);
394 }
395 }
396
397 @Override
398 public LinkResourceEvent releaseResources(LinkResourceAllocations allocations) {
399 checkNotNull(allocations);
400
401 final IntentId intentId = allocations.intentId();
402 final Collection<Link> links = allocations.links();
403 boolean success = false;
404 do {
405 TransactionContext tx = getTxContext();
406 tx.begin();
407 try {
408 TransactionalMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
409 intentAllocs.remove(intentId);
410
411 TransactionalMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
412 links.forEach(link -> {
413 final LinkKey linkId = LinkKey.linkKey(link);
414
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);
419 return;
420 }
421 List<LinkResourceAllocations> after = new ArrayList<>(before);
422 after.remove(allocations);
423 linkAllocs.replace(linkId, before, after);
424 });
425 tx.commit();
426 success = true;
427 } catch (TransactionException e) {
428 log.debug("Transaction failed, retrying", e);
429 tx.abort();
430 } catch (Exception e) {
431 log.error("Exception thrown during releaseResource {}", allocations, e);
432 tx.abort();
433 throw e;
434 }
435 } while (!success);
436
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,
441 releasedResources);
442
443 }
444
445 @Override
446 public LinkResourceAllocations getAllocations(IntentId intentId) {
447 checkNotNull(intentId);
448 Versioned<LinkResourceAllocations> alloc = null;
449 try {
450 alloc = intentAllocMap.get(intentId);
451 } catch (Exception e) {
452 log.warn("Could not read resource allocation information", e);
453 }
454 return alloc == null ? null : alloc.value();
455 }
456
457 @Override
458 public Iterable<LinkResourceAllocations> getAllocations(Link link) {
459 checkNotNull(link);
460 TransactionContext tx = getTxContext();
461 Iterable<LinkResourceAllocations> res = null;
462 tx.begin();
463 try {
464 res = getAllocations(tx, link);
465 } finally {
466 tx.abort();
467 }
468 return res == null ? Collections.emptyList() : res;
469 }
470
471 @Override
472 public Iterable<LinkResourceAllocations> getAllocations() {
473 try {
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);
479 }
480 return ImmutableSet.of();
481 }
482
483 private Iterable<LinkResourceAllocations> getAllocations(TransactionContext tx, Link link) {
484 checkNotNull(tx);
485 checkNotNull(link);
486 final LinkKey key = LinkKey.linkKey(link);
487 TransactionalMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
488 List<LinkResourceAllocations> res = null;
489
490 res = linkAllocs.get(key);
491 if (res == null) {
492 res = linkAllocs.putIfAbsent(key, new ArrayList<>());
493
494 if (res == null) {
495 return Collections.emptyList();
496 } else {
497 return res;
498 }
499 }
500 return res;
501 }
502
503}