blob: 734486267baf6aa3ef86cada3cd1b552380525fd [file] [log] [blame]
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -08001/*
Ray Milkey9a39eca2015-01-05 09:41:01 -08002 * Copyright 2014-2015 Open Networking Laboratory
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.store.resource.impl;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -080017
18import java.util.ArrayList;
19import java.util.Collection;
20import java.util.Collections;
21import java.util.HashMap;
22import java.util.HashSet;
23import java.util.List;
24import java.util.Map;
25import java.util.Set;
26
27import org.apache.felix.scr.annotations.Activate;
28import org.apache.felix.scr.annotations.Component;
29import org.apache.felix.scr.annotations.Deactivate;
30import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
32import org.apache.felix.scr.annotations.Service;
Ray Milkey9a39eca2015-01-05 09:41:01 -080033import org.onlab.util.PositionalParameterStringFormatter;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.net.AnnotationKeys;
35import org.onosproject.net.Link;
36import org.onosproject.net.LinkKey;
37import org.onosproject.net.intent.IntentId;
38import org.onosproject.net.link.LinkService;
39import org.onosproject.net.resource.Bandwidth;
40import org.onosproject.net.resource.BandwidthResourceAllocation;
41import org.onosproject.net.resource.Lambda;
42import org.onosproject.net.resource.LambdaResourceAllocation;
43import org.onosproject.net.resource.LinkResourceAllocations;
44import org.onosproject.net.resource.LinkResourceEvent;
45import org.onosproject.net.resource.LinkResourceStore;
46import org.onosproject.net.resource.ResourceAllocation;
Ray Milkey9a39eca2015-01-05 09:41:01 -080047import org.onosproject.net.resource.ResourceAllocationException;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.resource.ResourceType;
49import org.onosproject.store.StoreDelegate;
50import org.onosproject.store.hz.AbstractHazelcastStore;
51import org.onosproject.store.hz.STxMap;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -080052import org.slf4j.Logger;
53
54import com.google.common.collect.ImmutableList;
55import com.google.common.collect.ImmutableSet;
56import com.google.common.collect.Sets;
Yuta HIGUCHId36a58e2014-12-02 12:46:33 -080057import com.hazelcast.config.Config;
58import com.hazelcast.config.MapConfig;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -080059import com.hazelcast.core.TransactionalMap;
60import com.hazelcast.transaction.TransactionContext;
61import com.hazelcast.transaction.TransactionException;
62import com.hazelcast.transaction.TransactionOptions;
63import com.hazelcast.transaction.TransactionOptions.TransactionType;
64
65import static com.google.common.base.Preconditions.checkNotNull;
66import static com.google.common.base.Preconditions.checkState;
67import static org.slf4j.LoggerFactory.getLogger;
68
69/**
70 * Manages link resources using Hazelcast.
71 */
Yuta HIGUCHI9b108b32014-12-01 11:10:26 -080072@Component(immediate = true, enabled = true)
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -080073@Service
74public class HazelcastLinkResourceStore
75 extends AbstractHazelcastStore<LinkResourceEvent, StoreDelegate<LinkResourceEvent>>
76 implements LinkResourceStore {
77
78
79 private final Logger log = getLogger(getClass());
80
Sho SHIMIZU0ce220a2015-01-23 15:54:47 -080081 private static final Bandwidth DEFAULT_BANDWIDTH = Bandwidth.mbps(1_000);
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -080082
Sho SHIMIZU0ce220a2015-01-23 15:54:47 -080083 private static final Bandwidth EMPTY_BW = Bandwidth.bps(0);
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -080084
85 // table to store current allocations
86 /** LinkKey -> List<LinkResourceAllocations>. */
87 private static final String LINK_RESOURCE_ALLOCATIONS = "LinkResourceAllocations";
88
89 /** IntentId -> LinkResourceAllocations. */
90 private static final String INTENT_ALLOCATIONS = "IntentAllocations";
91
92
93 // TODO make this configurable
94 // number of retries to attempt on allocation failure, due to
95 // concurrent update
96 private static int maxAllocateRetries = 5;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected LinkService linkService;
100
101 // Link annotation key name to use as bandwidth
Yuta HIGUCHIbf366d52014-12-02 12:57:22 -0800102 private String bandwidthAnnotation = AnnotationKeys.BANDWIDTH;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800103 // Link annotation key name to use as max lambda
Yuta HIGUCHIbf366d52014-12-02 12:57:22 -0800104 private String wavesAnnotation = AnnotationKeys.OPTICAL_WAVES;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800105
106 @Override
107 @Activate
108 public void activate() {
109 super.activate();
Yuta HIGUCHId36a58e2014-12-02 12:46:33 -0800110
111 final Config config = theInstance.getConfig();
112
113 MapConfig linkCfg = config.getMapConfig(LINK_RESOURCE_ALLOCATIONS);
114 linkCfg.setAsyncBackupCount(MapConfig.MAX_BACKUP_COUNT - linkCfg.getBackupCount());
115
116 MapConfig intentCfg = config.getMapConfig(INTENT_ALLOCATIONS);
117 intentCfg.setAsyncBackupCount(MapConfig.MAX_BACKUP_COUNT - intentCfg.getBackupCount());
118
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800119 log.info("Started");
120 }
121
122 @Deactivate
123 public void deactivate() {
124 log.info("Stopped");
125 }
126
127 private STxMap<IntentId, LinkResourceAllocations> getIntentAllocs(TransactionContext tx) {
128 TransactionalMap<byte[], byte[]> raw = tx.getMap(INTENT_ALLOCATIONS);
129 return new STxMap<>(raw, serializer);
130 }
131
132 private STxMap<LinkKey, List<LinkResourceAllocations>> getLinkAllocs(TransactionContext tx) {
133 TransactionalMap<byte[], byte[]> raw = tx.getMap(LINK_RESOURCE_ALLOCATIONS);
134 return new STxMap<>(raw, serializer);
135 }
136
137 private Set<? extends ResourceAllocation> getResourceCapacity(ResourceType type, Link link) {
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800138 if (type == ResourceType.BANDWIDTH) {
139 return ImmutableSet.of(getBandwidthResourceCapacity(link));
140 }
141 if (type == ResourceType.LAMBDA) {
142 return getLambdaResourceCapacity(link);
143 }
144 return null;
145 }
146
147 private Set<LambdaResourceAllocation> getLambdaResourceCapacity(Link link) {
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800148 Set<LambdaResourceAllocation> allocations = new HashSet<>();
149 try {
150 final int waves = Integer.parseInt(link.annotations().value(wavesAnnotation));
151 for (int i = 1; i <= waves; i++) {
152 allocations.add(new LambdaResourceAllocation(Lambda.valueOf(i)));
153 }
154 } catch (NumberFormatException e) {
155 log.debug("No {} annotation on link %s", wavesAnnotation, link);
156 }
157 return allocations;
158 }
159
160 private BandwidthResourceAllocation getBandwidthResourceCapacity(Link link) {
161
162 // if Link annotation exist, use them
163 // if all fails, use DEFAULT_BANDWIDTH
164
165 Bandwidth bandwidth = null;
166 String strBw = link.annotations().value(bandwidthAnnotation);
167 if (strBw != null) {
168 try {
Sho SHIMIZU0ce220a2015-01-23 15:54:47 -0800169 bandwidth = Bandwidth.mbps(Double.parseDouble(strBw));
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800170 } catch (NumberFormatException e) {
171 // do nothings
172 bandwidth = null;
173 }
174 }
175
176 if (bandwidth == null) {
177 // fall back, use fixed default
178 bandwidth = DEFAULT_BANDWIDTH;
179 }
180 return new BandwidthResourceAllocation(bandwidth);
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) {
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800196 TransactionOptions opt = new TransactionOptions();
197 // read-only and will never be commited, thus does not need durability
198 opt.setTransactionType(TransactionType.LOCAL);
199 TransactionContext tx = theInstance.newTransactionContext(opt);
200 tx.beginTransaction();
201 try {
202 Map<ResourceType, Set<? extends ResourceAllocation>> freeResources = getFreeResourcesEx(tx, link);
203 Set<ResourceAllocation> allFree = new HashSet<>();
204 for (Set<? extends ResourceAllocation> r : freeResources.values()) {
205 allFree.addAll(r);
206 }
207 return allFree;
208 } finally {
209 tx.rollbackTransaction();
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800210 }
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800211
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800212 }
213
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800214 private Map<ResourceType, Set<? extends ResourceAllocation>> getFreeResourcesEx(TransactionContext tx, Link link) {
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800215 // returns capacity - allocated
216
217 checkNotNull(link);
218 Map<ResourceType, Set<? extends ResourceAllocation>> free = new HashMap<>();
219 final Map<ResourceType, Set<? extends ResourceAllocation>> caps = getResourceCapacity(link);
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800220 final Iterable<LinkResourceAllocations> allocations = getAllocations(tx, link);
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800221
222 for (ResourceType type : ResourceType.values()) {
223 // there should be class/category of resources
224 switch (type) {
225 case BANDWIDTH:
226 {
227 Set<? extends ResourceAllocation> bw = caps.get(ResourceType.BANDWIDTH);
228 if (bw == null || bw.isEmpty()) {
229 bw = Sets.newHashSet(new BandwidthResourceAllocation(EMPTY_BW));
230 }
231
232 BandwidthResourceAllocation cap = (BandwidthResourceAllocation) bw.iterator().next();
233 double freeBw = cap.bandwidth().toDouble();
234
235 // enumerate current allocations, subtracting resources
236 for (LinkResourceAllocations alloc : allocations) {
237 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
238 for (ResourceAllocation a : types) {
239 if (a instanceof BandwidthResourceAllocation) {
240 BandwidthResourceAllocation bwA = (BandwidthResourceAllocation) a;
241 freeBw -= bwA.bandwidth().toDouble();
242 }
243 }
244 }
245
Sho SHIMIZU0ce220a2015-01-23 15:54:47 -0800246 free.put(type, Sets.newHashSet(new BandwidthResourceAllocation(Bandwidth.bps(freeBw))));
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800247 break;
248 }
249
250 case LAMBDA:
251 {
252 Set<? extends ResourceAllocation> lmd = caps.get(type);
253 if (lmd == null || lmd.isEmpty()) {
254 // nothing left
255 break;
256 }
257 Set<LambdaResourceAllocation> freeL = new HashSet<>();
258 for (ResourceAllocation r : lmd) {
259 if (r instanceof LambdaResourceAllocation) {
260 freeL.add((LambdaResourceAllocation) r);
261 }
262 }
263
264 // enumerate current allocations, removing resources
265 for (LinkResourceAllocations alloc : allocations) {
266 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
267 for (ResourceAllocation a : types) {
268 if (a instanceof LambdaResourceAllocation) {
269 freeL.remove(a);
270 }
271 }
272 }
273
274 free.put(type, freeL);
275 break;
276 }
277
278 default:
279 break;
280 }
281 }
282 return free;
283 }
284
285 @Override
286 public void allocateResources(LinkResourceAllocations allocations) {
287 checkNotNull(allocations);
288
289 for (int i = 0; i < maxAllocateRetries; ++i) {
290 TransactionContext tx = theInstance.newTransactionContext();
291 tx.beginTransaction();
292 try {
293
294 STxMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
295 // should this be conditional write?
296 intentAllocs.put(allocations.intendId(), allocations);
297
298 for (Link link : allocations.links()) {
299 allocateLinkResource(tx, link, allocations);
300 }
301
302 tx.commitTransaction();
303 return;
304 } catch (TransactionException e) {
305 log.debug("Failed to commit allocations for {}. [retry={}]",
306 allocations.intendId(), i);
307 log.trace(" details {} ", allocations, e);
308 continue;
309 } catch (Exception e) {
310 log.error("Exception thrown, rolling back", e);
311 tx.rollbackTransaction();
312 throw e;
313 }
314 }
315 }
316
317 private void allocateLinkResource(TransactionContext tx, Link link,
318 LinkResourceAllocations allocations) {
319
320 // requested resources
321 Set<ResourceAllocation> reqs = allocations.getResourceAllocation(link);
322
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800323 Map<ResourceType, Set<? extends ResourceAllocation>> available = getFreeResourcesEx(tx, link);
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800324 for (ResourceAllocation req : reqs) {
325 Set<? extends ResourceAllocation> avail = available.get(req.type());
326 if (req instanceof BandwidthResourceAllocation) {
327 // check if allocation should be accepted
328 if (avail.isEmpty()) {
329 checkState(!avail.isEmpty(),
330 "There's no Bandwidth resource on %s?",
331 link);
332 }
333 BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
334 double bwLeft = bw.bandwidth().toDouble();
Ray Milkey9a39eca2015-01-05 09:41:01 -0800335 BandwidthResourceAllocation bwReq = ((BandwidthResourceAllocation) req);
336 bwLeft -= bwReq.bandwidth().toDouble();
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800337 if (bwLeft < 0) {
Ray Milkey9a39eca2015-01-05 09:41:01 -0800338 throw new ResourceAllocationException(
339 PositionalParameterStringFormatter.format(
340 "Unable to allocate bandwidth for link {} "
341 + " requested amount is {} current allocation is {}",
342 link,
343 bwReq.bandwidth().toDouble(),
344 bw));
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800345 }
346 } else if (req instanceof LambdaResourceAllocation) {
Ray Milkey9a39eca2015-01-05 09:41:01 -0800347 LambdaResourceAllocation lambdaAllocation = (LambdaResourceAllocation) req;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800348 // check if allocation should be accepted
349 if (!avail.contains(req)) {
350 // requested lambda was not available
Ray Milkey9a39eca2015-01-05 09:41:01 -0800351 throw new ResourceAllocationException(
352 PositionalParameterStringFormatter.format(
Sho SHIMIZU2b2e6812015-01-21 10:50:36 -0800353 "Unable to allocate lambda for link {} lambda is {}",
Ray Milkey9a39eca2015-01-05 09:41:01 -0800354 link,
355 lambdaAllocation.lambda().toInt()));
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800356 }
357 }
358 }
359 // all requests allocatable => add allocation
360 final LinkKey linkKey = LinkKey.linkKey(link);
361 STxMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
Yuta HIGUCHIacdec312014-12-02 21:01:16 -0800362 List<LinkResourceAllocations> before = linkAllocs.get(linkKey);
363 if (before == null) {
364 List<LinkResourceAllocations> after = new ArrayList<>();
365 after.add(allocations);
366 before = linkAllocs.putIfAbsent(linkKey, after);
367 if (before != null) {
368 // concurrent allocation detected, retry transaction
369 throw new TransactionException("Concurrent Allocation, retry");
370 }
Yuta HIGUCHI427a2142014-12-03 11:03:18 -0800371 } else {
372 List<LinkResourceAllocations> after = new ArrayList<>(before.size() + 1);
373 after.addAll(before);
374 after.add(allocations);
375 linkAllocs.replace(linkKey, before, after);
Yuta HIGUCHIacdec312014-12-02 21:01:16 -0800376 }
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800377 }
378
379 @Override
380 public LinkResourceEvent releaseResources(LinkResourceAllocations allocations) {
381 checkNotNull(allocations);
382
383 final IntentId intendId = allocations.intendId();
384 final Collection<Link> links = allocations.links();
385
386 boolean success = false;
387 do {
Yuta HIGUCHI65934892014-12-04 17:47:44 -0800388 // Note: might want to break it down into smaller tx unit
389 // to lower the chance of collisions.
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800390 TransactionContext tx = theInstance.newTransactionContext();
391 tx.beginTransaction();
392 try {
393 STxMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
394 intentAllocs.remove(intendId);
395
396 STxMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
397
398 for (Link link : links) {
399 final LinkKey linkId = LinkKey.linkKey(link);
400
401 List<LinkResourceAllocations> before = linkAllocs.get(linkId);
402 if (before == null || before.isEmpty()) {
403 // something is wrong, but it is already freed
404 log.warn("There was no resource left to release on {}", linkId);
405 continue;
406 }
407 List<LinkResourceAllocations> after = new ArrayList<>(before);
408 after.remove(allocations);
409 linkAllocs.replace(linkId, before, after);
410 }
411
412 tx.commitTransaction();
413 success = true;
414 } catch (TransactionException e) {
415 log.debug("Transaction failed, retrying");
416 } catch (Exception e) {
417 log.error("Exception thrown during releaseResource {}",
418 allocations, e);
419 tx.rollbackTransaction();
420 throw e;
421 }
422 } while (!success);
423
424 // Issue events to force recompilation of intents.
425 final List<LinkResourceAllocations> releasedResources =
426 ImmutableList.of(allocations);
427 return new LinkResourceEvent(
428 LinkResourceEvent.Type.ADDITIONAL_RESOURCES_AVAILABLE,
429 releasedResources);
430 }
431
432 @Override
433 public LinkResourceAllocations getAllocations(IntentId intentId) {
434 checkNotNull(intentId);
435 TransactionOptions opt = new TransactionOptions();
436 // read-only and will never be commited, thus does not need durability
437 opt.setTransactionType(TransactionType.LOCAL);
438 TransactionContext tx = theInstance.newTransactionContext(opt);
439 tx.beginTransaction();
440 try {
441 STxMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
442 return intentAllocs.get(intentId);
443 } finally {
444 tx.rollbackTransaction();
445 }
446 }
447
448 @Override
449 public List<LinkResourceAllocations> getAllocations(Link link) {
450 checkNotNull(link);
451 final LinkKey key = LinkKey.linkKey(link);
452
453 TransactionOptions opt = new TransactionOptions();
454 // read-only and will never be commited, thus does not need durability
455 opt.setTransactionType(TransactionType.LOCAL);
456 TransactionContext tx = theInstance.newTransactionContext(opt);
457 tx.beginTransaction();
458 List<LinkResourceAllocations> res = null;
459 try {
460 STxMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
461 res = linkAllocs.get(key);
462 } finally {
463 tx.rollbackTransaction();
464 }
465
466 if (res == null) {
467 // try to add empty list
468 TransactionContext tx2 = theInstance.newTransactionContext();
469 tx2.beginTransaction();
470 try {
471 res = getLinkAllocs(tx2).putIfAbsent(key, new ArrayList<>());
472 tx2.commitTransaction();
473 if (res == null) {
474 return Collections.emptyList();
475 } else {
476 return res;
477 }
478 } catch (TransactionException e) {
479 // concurrently added?
480 return getAllocations(link);
481 } catch (Exception e) {
482 tx.rollbackTransaction();
483 }
484 }
485 return res;
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800486 }
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800487
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800488 private Iterable<LinkResourceAllocations> getAllocations(TransactionContext tx,
489 Link link) {
490 checkNotNull(tx);
491 checkNotNull(link);
492 final LinkKey key = LinkKey.linkKey(link);
493
494 STxMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
495 List<LinkResourceAllocations> res = null;
496 res = linkAllocs.get(key);
497 if (res == null) {
Yuta HIGUCHIacdec312014-12-02 21:01:16 -0800498 res = linkAllocs.putIfAbsent(key, new ArrayList<>());
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800499 if (res == null) {
500 return Collections.emptyList();
501 } else {
502 return res;
503 }
504 }
Yuta HIGUCHI81419e22014-12-03 16:54:16 -0800505 return res;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800506 }
507
508 @Override
509 public Iterable<LinkResourceAllocations> getAllocations() {
510 TransactionContext tx = theInstance.newTransactionContext();
511 tx.beginTransaction();
512 try {
513 STxMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
514 return intentAllocs.values();
515 } finally {
516 tx.rollbackTransaction();
517 }
518 }
519}