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