blob: 6b22b75c1a4b57322904d3fc75e15c79b00a3fc1 [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;
Ayaka Koshibee114f042015-05-01 11:43:00 -070020import org.slf4j.Logger;
21import org.onlab.util.KryoNamespace;
22import org.onlab.util.PositionalParameterStringFormatter;
23import org.onosproject.net.Link;
24import org.onosproject.net.LinkKey;
25import org.onosproject.net.intent.IntentId;
26import org.onosproject.net.link.LinkService;
Sho SHIMIZU63feca72015-05-07 10:44:25 -070027import org.onosproject.net.resource.BandwidthResource;
Ayaka Koshibee114f042015-05-01 11:43:00 -070028import org.onosproject.net.resource.BandwidthResourceAllocation;
Sho SHIMIZU94b7ff42015-05-06 17:51:49 -070029import org.onosproject.net.resource.LambdaResource;
Ayaka Koshibee114f042015-05-01 11:43:00 -070030import org.onosproject.net.resource.LambdaResourceAllocation;
31import org.onosproject.net.resource.LinkResourceAllocations;
32import org.onosproject.net.resource.LinkResourceEvent;
33import org.onosproject.net.resource.LinkResourceStore;
34import org.onosproject.net.resource.LinkResourceStoreDelegate;
35import org.onosproject.net.resource.MplsLabel;
36import org.onosproject.net.resource.MplsLabelResourceAllocation;
37import org.onosproject.net.resource.ResourceAllocation;
38import org.onosproject.net.resource.ResourceAllocationException;
39import org.onosproject.net.resource.ResourceType;
40import org.onosproject.store.AbstractStore;
41import org.onosproject.store.serializers.KryoNamespaces;
42import org.onosproject.store.service.ConsistentMap;
43import org.onosproject.store.service.Serializer;
44import org.onosproject.store.service.StorageService;
45import org.onosproject.store.service.TransactionContext;
46import org.onosproject.store.service.TransactionException;
47import org.onosproject.store.service.TransactionalMap;
48import org.onosproject.store.service.Versioned;
49
50import com.google.common.collect.ImmutableList;
51import com.google.common.collect.ImmutableSet;
52import com.google.common.collect.Sets;
53
54import static com.google.common.base.Preconditions.checkNotNull;
55import static com.google.common.base.Preconditions.checkState;
56import static org.slf4j.LoggerFactory.getLogger;
57import static org.onosproject.net.AnnotationKeys.BANDWIDTH;
58import static org.onosproject.net.AnnotationKeys.OPTICAL_WAVES;
59
60/**
61 * Store that manages link resources using Copycat-backed TransactionalMaps.
62 */
Ayaka Koshibe474ef5f2015-05-01 15:24:51 -070063@Component(immediate = true, enabled = true)
Ayaka Koshibee114f042015-05-01 11:43:00 -070064@Service
65public class ConsistentLinkResourceStore extends
66 AbstractStore<LinkResourceEvent, LinkResourceStoreDelegate> implements
67 LinkResourceStore {
68
69 private final Logger log = getLogger(getClass());
70
Sho SHIMIZU6d01d3d2015-05-08 14:08:36 -070071 private static final BandwidthResource DEFAULT_BANDWIDTH = new BandwidthResource(Bandwidth.mbps(1_000));
72 private static final BandwidthResource EMPTY_BW = new BandwidthResource(Bandwidth.bps(0));
Ayaka Koshibee114f042015-05-01 11:43:00 -070073
74 // Smallest non-reserved MPLS label
75 private static final int MIN_UNRESERVED_LABEL = 0x10;
76 // Max non-reserved MPLS label = 239
77 private static final int MAX_UNRESERVED_LABEL = 0xEF;
78
79 // table to store current allocations
80 /** LinkKey -> List<LinkResourceAllocations>. */
81 private static final String LINK_RESOURCE_ALLOCATIONS = "LinkResourceAllocations";
82
83 /** IntentId -> LinkResourceAllocations. */
84 private static final String INTENT_ALLOCATIONS = "IntentAllocations";
85
86 private static final Serializer SERIALIZER = Serializer.using(
87 new KryoNamespace.Builder().register(KryoNamespaces.API).build());
88
89 // for reading committed values.
90 private ConsistentMap<IntentId, LinkResourceAllocations> intentAllocMap;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected StorageService storageService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected LinkService linkService;
97
98 @Activate
99 public void activate() {
100 intentAllocMap = storageService.<IntentId, LinkResourceAllocations>consistentMapBuilder()
101 .withName(INTENT_ALLOCATIONS)
102 .withSerializer(SERIALIZER)
103 .build();
104 log.info("Started");
105 }
106
107 @Deactivate
108 public void deactivate() {
109 log.info("Stopped");
110 }
111
112 private TransactionalMap<IntentId, LinkResourceAllocations> getIntentAllocs(TransactionContext tx) {
113 return tx.getTransactionalMap(INTENT_ALLOCATIONS, SERIALIZER);
114 }
115
116 private TransactionalMap<LinkKey, List<LinkResourceAllocations>> getLinkAllocs(TransactionContext tx) {
117 return tx.getTransactionalMap(LINK_RESOURCE_ALLOCATIONS, SERIALIZER);
118 }
119
120 private TransactionContext getTxContext() {
121 return storageService.transactionContextBuilder().build();
122 }
123
124 private Set<? extends ResourceAllocation> getResourceCapacity(ResourceType type, Link link) {
125 if (type == ResourceType.BANDWIDTH) {
126 return ImmutableSet.of(getBandwidthResourceCapacity(link));
127 }
128 if (type == ResourceType.LAMBDA) {
129 return getLambdaResourceCapacity(link);
130 }
131 if (type == ResourceType.MPLS_LABEL) {
132 return getMplsResourceCapacity();
133 }
134 return ImmutableSet.of();
135 }
136
137 private Set<LambdaResourceAllocation> getLambdaResourceCapacity(Link link) {
138 Set<LambdaResourceAllocation> allocations = new HashSet<>();
139 try {
140 final int waves = Integer.parseInt(link.annotations().value(OPTICAL_WAVES));
141 for (int i = 1; i <= waves; i++) {
Sho SHIMIZU94b7ff42015-05-06 17:51:49 -0700142 allocations.add(new LambdaResourceAllocation(LambdaResource.valueOf(i)));
Ayaka Koshibee114f042015-05-01 11:43:00 -0700143 }
144 } catch (NumberFormatException e) {
145 log.debug("No {} annotation on link {}", OPTICAL_WAVES, link);
146 }
147 return allocations;
148 }
149
150 private BandwidthResourceAllocation getBandwidthResourceCapacity(Link link) {
151
152 // if Link annotation exist, use them
153 // if all fails, use DEFAULT_BANDWIDTH
Sho SHIMIZU63feca72015-05-07 10:44:25 -0700154 BandwidthResource bandwidth = null;
Ayaka Koshibee114f042015-05-01 11:43:00 -0700155 String strBw = link.annotations().value(BANDWIDTH);
156 if (strBw != null) {
157 try {
Sho SHIMIZU6d01d3d2015-05-08 14:08:36 -0700158 bandwidth = new BandwidthResource(Bandwidth.mbps(Double.parseDouble(strBw)));
Ayaka Koshibee114f042015-05-01 11:43:00 -0700159 } catch (NumberFormatException e) {
160 // do nothings
161 bandwidth = null;
162 }
163 }
164
165 if (bandwidth == null) {
166 // fall back, use fixed default
167 bandwidth = DEFAULT_BANDWIDTH;
168 }
169 return new BandwidthResourceAllocation(bandwidth);
170 }
171
172 private Set<MplsLabelResourceAllocation> getMplsResourceCapacity() {
173 Set<MplsLabelResourceAllocation> allocations = new HashSet<>();
174 //Ignoring reserved labels of 0 through 15
175 for (int i = MIN_UNRESERVED_LABEL; i <= MAX_UNRESERVED_LABEL; i++) {
176 allocations.add(new MplsLabelResourceAllocation(MplsLabel
177 .valueOf(i)));
178
179 }
180 return allocations;
181 }
182
183 private Map<ResourceType, Set<? extends ResourceAllocation>> getResourceCapacity(Link link) {
184 Map<ResourceType, Set<? extends ResourceAllocation>> caps = new HashMap<>();
185 for (ResourceType type : ResourceType.values()) {
186 Set<? extends ResourceAllocation> cap = getResourceCapacity(type, link);
187 if (cap != null) {
188 caps.put(type, cap);
189 }
190 }
191 return caps;
192 }
193
194 @Override
195 public Set<ResourceAllocation> getFreeResources(Link link) {
196 TransactionContext tx = getTxContext();
197
198 tx.begin();
199 try {
200 Map<ResourceType, Set<? extends ResourceAllocation>> freeResources = getFreeResourcesEx(tx, link);
201 Set<ResourceAllocation> allFree = new HashSet<>();
202 freeResources.values().forEach(allFree::addAll);
203 return allFree;
204 } finally {
205 tx.abort();
206 }
207 }
208
209 private Map<ResourceType, Set<? extends ResourceAllocation>> getFreeResourcesEx(TransactionContext tx, Link link) {
210 checkNotNull(tx);
211 checkNotNull(link);
212
213 Map<ResourceType, Set<? extends ResourceAllocation>> free = new HashMap<>();
214 final Map<ResourceType, Set<? extends ResourceAllocation>> caps = getResourceCapacity(link);
215 final Iterable<LinkResourceAllocations> allocations = getAllocations(tx, link);
216
217 for (ResourceType type : ResourceType.values()) {
218 // there should be class/category of resources
219
220 switch (type) {
221 case BANDWIDTH:
222 Set<? extends ResourceAllocation> bw = caps.get(type);
223 if (bw == null || bw.isEmpty()) {
224 bw = Sets.newHashSet(new BandwidthResourceAllocation(EMPTY_BW));
225 }
226
227 BandwidthResourceAllocation cap = (BandwidthResourceAllocation) bw.iterator().next();
228 double freeBw = cap.bandwidth().toDouble();
229
230 // enumerate current allocations, subtracting resources
231 for (LinkResourceAllocations alloc : allocations) {
232 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
233 for (ResourceAllocation a : types) {
234 if (a instanceof BandwidthResourceAllocation) {
235 BandwidthResourceAllocation bwA = (BandwidthResourceAllocation) a;
236 freeBw -= bwA.bandwidth().toDouble();
237 }
238 }
239 }
240
Sho SHIMIZU6d01d3d2015-05-08 14:08:36 -0700241 free.put(type, Sets.newHashSet(
242 new BandwidthResourceAllocation(new BandwidthResource(Bandwidth.bps(freeBw)))));
Ayaka Koshibee114f042015-05-01 11:43:00 -0700243 break;
244 case LAMBDA:
245 Set<? extends ResourceAllocation> lmd = caps.get(type);
246 if (lmd == null || lmd.isEmpty()) {
247 // nothing left
248 break;
249 }
250 Set<LambdaResourceAllocation> freeL = new HashSet<>();
251 for (ResourceAllocation r : lmd) {
252 if (r instanceof LambdaResourceAllocation) {
253 freeL.add((LambdaResourceAllocation) r);
254 }
255 }
256
257 // enumerate current allocations, removing resources
258 for (LinkResourceAllocations alloc : allocations) {
259 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
260 for (ResourceAllocation a : types) {
261 if (a instanceof LambdaResourceAllocation) {
262 freeL.remove(a);
263 }
264 }
265 }
266
267 free.put(type, freeL);
268 break;
269 case MPLS_LABEL:
270 Set<? extends ResourceAllocation> mpls = caps.get(type);
271 if (mpls == null || mpls.isEmpty()) {
272 // nothing left
273 break;
274 }
275 Set<MplsLabelResourceAllocation> freeLabel = new HashSet<>();
276 for (ResourceAllocation r : mpls) {
277 if (r instanceof MplsLabelResourceAllocation) {
278 freeLabel.add((MplsLabelResourceAllocation) r);
279 }
280 }
281
282 // enumerate current allocations, removing resources
283 for (LinkResourceAllocations alloc : allocations) {
284 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
285 for (ResourceAllocation a : types) {
286 if (a instanceof MplsLabelResourceAllocation) {
287 freeLabel.remove(a);
288 }
289 }
290 }
291
292 free.put(type, freeLabel);
293 break;
294 default:
295 log.debug("unsupported ResourceType {}", type);
296 break;
297 }
298 }
299 return free;
300 }
301
302 @Override
303 public void allocateResources(LinkResourceAllocations allocations) {
304 checkNotNull(allocations);
305 TransactionContext tx = getTxContext();
306
307 tx.begin();
308 try {
309 TransactionalMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
310 intentAllocs.put(allocations.intentId(), allocations);
311 allocations.links().forEach(link -> allocateLinkResource(tx, link, allocations));
312 tx.commit();
313 } catch (Exception e) {
314 log.error("Exception thrown, rolling back", e);
315 tx.abort();
316 throw e;
317 }
318 }
319
320 private void allocateLinkResource(TransactionContext tx, Link link,
321 LinkResourceAllocations allocations) {
322 // requested resources
323 Set<ResourceAllocation> reqs = allocations.getResourceAllocation(link);
324 Map<ResourceType, Set<? extends ResourceAllocation>> available = getFreeResourcesEx(tx, link);
325 for (ResourceAllocation req : reqs) {
326 Set<? extends ResourceAllocation> avail = available.get(req.type());
327 if (req instanceof BandwidthResourceAllocation) {
328 // check if allocation should be accepted
329 if (avail.isEmpty()) {
330 checkState(!avail.isEmpty(),
331 "There's no Bandwidth resource on %s?",
332 link);
333 }
334 BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
335 double bwLeft = bw.bandwidth().toDouble();
336 BandwidthResourceAllocation bwReq = ((BandwidthResourceAllocation) req);
337 bwLeft -= bwReq.bandwidth().toDouble();
338 if (bwLeft < 0) {
339 throw new ResourceAllocationException(
340 PositionalParameterStringFormatter.format(
341 "Unable to allocate bandwidth for link {} "
342 + " requested amount is {} current allocation is {}",
343 link,
344 bwReq.bandwidth().toDouble(),
345 bw));
346 }
347 } else if (req instanceof LambdaResourceAllocation) {
348 LambdaResourceAllocation lambdaAllocation = (LambdaResourceAllocation) req;
349 // check if allocation should be accepted
350 if (!avail.contains(req)) {
351 // requested lambda was not available
352 throw new ResourceAllocationException(
353 PositionalParameterStringFormatter.format(
354 "Unable to allocate lambda for link {} lambda is {}",
355 link,
356 lambdaAllocation.lambda().toInt()));
357 }
358 } else if (req instanceof MplsLabelResourceAllocation) {
359 MplsLabelResourceAllocation mplsAllocation = (MplsLabelResourceAllocation) req;
360 if (!avail.contains(req)) {
361 throw new ResourceAllocationException(
362 PositionalParameterStringFormatter
363 .format("Unable to allocate MPLS label for link "
364 + "{} MPLS label is {}",
365 link,
366 mplsAllocation
367 .mplsLabel()
368 .toString()));
369 }
370 }
371 }
372 // all requests allocatable => add allocation
373 final LinkKey linkKey = LinkKey.linkKey(link);
374 TransactionalMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
375 List<LinkResourceAllocations> before = linkAllocs.get(linkKey);
376 if (before == null) {
377 List<LinkResourceAllocations> after = new ArrayList<>();
378 after.add(allocations);
379 before = linkAllocs.putIfAbsent(linkKey, after);
380 if (before != null) {
381 // concurrent allocation detected, retry transaction : is this needed?
382 log.warn("Concurrent Allocation, retrying");
383 throw new TransactionException();
384 }
385 } else {
386 List<LinkResourceAllocations> after = new ArrayList<>(before.size() + 1);
387 after.addAll(before);
388 after.add(allocations);
389 linkAllocs.replace(linkKey, before, after);
390 }
391 }
392
393 @Override
394 public LinkResourceEvent releaseResources(LinkResourceAllocations allocations) {
395 checkNotNull(allocations);
396
397 final IntentId intentId = allocations.intentId();
398 final Collection<Link> links = allocations.links();
399 boolean success = false;
400 do {
401 TransactionContext tx = getTxContext();
402 tx.begin();
403 try {
404 TransactionalMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
405 intentAllocs.remove(intentId);
406
407 TransactionalMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
408 links.forEach(link -> {
409 final LinkKey linkId = LinkKey.linkKey(link);
410
411 List<LinkResourceAllocations> before = linkAllocs.get(linkId);
412 if (before == null || before.isEmpty()) {
413 // something is wrong, but it is already freed
414 log.warn("There was no resource left to release on {}", linkId);
415 return;
416 }
417 List<LinkResourceAllocations> after = new ArrayList<>(before);
418 after.remove(allocations);
419 linkAllocs.replace(linkId, before, after);
420 });
421 tx.commit();
422 success = true;
423 } catch (TransactionException e) {
424 log.debug("Transaction failed, retrying", e);
425 tx.abort();
426 } catch (Exception e) {
427 log.error("Exception thrown during releaseResource {}", allocations, e);
428 tx.abort();
429 throw e;
430 }
431 } while (!success);
432
433 // Issue events to force recompilation of intents.
434 final List<LinkResourceAllocations> releasedResources = ImmutableList.of(allocations);
435 return new LinkResourceEvent(
436 LinkResourceEvent.Type.ADDITIONAL_RESOURCES_AVAILABLE,
437 releasedResources);
438
439 }
440
441 @Override
442 public LinkResourceAllocations getAllocations(IntentId intentId) {
443 checkNotNull(intentId);
444 Versioned<LinkResourceAllocations> alloc = null;
445 try {
446 alloc = intentAllocMap.get(intentId);
447 } catch (Exception e) {
448 log.warn("Could not read resource allocation information", e);
449 }
450 return alloc == null ? null : alloc.value();
451 }
452
453 @Override
454 public Iterable<LinkResourceAllocations> getAllocations(Link link) {
455 checkNotNull(link);
456 TransactionContext tx = getTxContext();
457 Iterable<LinkResourceAllocations> res = null;
458 tx.begin();
459 try {
460 res = getAllocations(tx, link);
461 } finally {
462 tx.abort();
463 }
464 return res == null ? Collections.emptyList() : res;
465 }
466
467 @Override
468 public Iterable<LinkResourceAllocations> getAllocations() {
469 try {
470 Set<LinkResourceAllocations> allocs =
471 intentAllocMap.values().stream().map(Versioned::value).collect(Collectors.toSet());
472 return ImmutableSet.copyOf(allocs);
473 } catch (Exception e) {
474 log.warn("Could not read resource allocation information", e);
475 }
476 return ImmutableSet.of();
477 }
478
479 private Iterable<LinkResourceAllocations> getAllocations(TransactionContext tx, Link link) {
480 checkNotNull(tx);
481 checkNotNull(link);
482 final LinkKey key = LinkKey.linkKey(link);
483 TransactionalMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
484 List<LinkResourceAllocations> res = null;
485
486 res = linkAllocs.get(key);
487 if (res == null) {
488 res = linkAllocs.putIfAbsent(key, new ArrayList<>());
489
490 if (res == null) {
491 return Collections.emptyList();
492 } else {
493 return res;
494 }
495 }
496 return res;
497 }
498
499}