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