blob: 1294f0b91fd2c85a429580bc28e6893268b88f3a [file] [log] [blame]
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -08001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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;
Brian O'Connorabafb502014-12-02 22:26:20 -080033import org.onosproject.net.AnnotationKeys;
34import org.onosproject.net.Link;
35import org.onosproject.net.LinkKey;
36import org.onosproject.net.intent.IntentId;
37import org.onosproject.net.link.LinkService;
38import org.onosproject.net.resource.Bandwidth;
39import org.onosproject.net.resource.BandwidthResourceAllocation;
40import org.onosproject.net.resource.Lambda;
41import org.onosproject.net.resource.LambdaResourceAllocation;
42import org.onosproject.net.resource.LinkResourceAllocations;
43import org.onosproject.net.resource.LinkResourceEvent;
44import org.onosproject.net.resource.LinkResourceStore;
45import org.onosproject.net.resource.ResourceAllocation;
46import org.onosproject.net.resource.ResourceType;
47import org.onosproject.store.StoreDelegate;
48import org.onosproject.store.hz.AbstractHazelcastStore;
49import org.onosproject.store.hz.STxMap;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -080050import org.slf4j.Logger;
51
52import com.google.common.collect.ImmutableList;
53import com.google.common.collect.ImmutableSet;
54import com.google.common.collect.Sets;
Yuta HIGUCHId36a58e2014-12-02 12:46:33 -080055import com.hazelcast.config.Config;
56import com.hazelcast.config.MapConfig;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -080057import com.hazelcast.core.TransactionalMap;
58import com.hazelcast.transaction.TransactionContext;
59import com.hazelcast.transaction.TransactionException;
60import com.hazelcast.transaction.TransactionOptions;
61import com.hazelcast.transaction.TransactionOptions.TransactionType;
62
63import static com.google.common.base.Preconditions.checkNotNull;
64import static com.google.common.base.Preconditions.checkState;
65import static org.slf4j.LoggerFactory.getLogger;
66
67/**
68 * Manages link resources using Hazelcast.
69 */
Yuta HIGUCHI9b108b32014-12-01 11:10:26 -080070@Component(immediate = true, enabled = true)
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -080071@Service
72public class HazelcastLinkResourceStore
73 extends AbstractHazelcastStore<LinkResourceEvent, StoreDelegate<LinkResourceEvent>>
74 implements LinkResourceStore {
75
76
77 private final Logger log = getLogger(getClass());
78
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -080079 private static final Bandwidth DEFAULT_BANDWIDTH = Bandwidth.valueOf(1_000);
80
81 private static final Bandwidth EMPTY_BW = Bandwidth.valueOf(0);
82
83 // table to store current allocations
84 /** LinkKey -> List<LinkResourceAllocations>. */
85 private static final String LINK_RESOURCE_ALLOCATIONS = "LinkResourceAllocations";
86
87 /** IntentId -> LinkResourceAllocations. */
88 private static final String INTENT_ALLOCATIONS = "IntentAllocations";
89
90
91 // TODO make this configurable
92 // number of retries to attempt on allocation failure, due to
93 // concurrent update
94 private static int maxAllocateRetries = 5;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected LinkService linkService;
98
99 // Link annotation key name to use as bandwidth
Yuta HIGUCHIbf366d52014-12-02 12:57:22 -0800100 private String bandwidthAnnotation = AnnotationKeys.BANDWIDTH;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800101 // Link annotation key name to use as max lambda
Yuta HIGUCHIbf366d52014-12-02 12:57:22 -0800102 private String wavesAnnotation = AnnotationKeys.OPTICAL_WAVES;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800103
104 @Override
105 @Activate
106 public void activate() {
107 super.activate();
Yuta HIGUCHId36a58e2014-12-02 12:46:33 -0800108
109 final Config config = theInstance.getConfig();
110
111 MapConfig linkCfg = config.getMapConfig(LINK_RESOURCE_ALLOCATIONS);
112 linkCfg.setAsyncBackupCount(MapConfig.MAX_BACKUP_COUNT - linkCfg.getBackupCount());
113
114 MapConfig intentCfg = config.getMapConfig(INTENT_ALLOCATIONS);
115 intentCfg.setAsyncBackupCount(MapConfig.MAX_BACKUP_COUNT - intentCfg.getBackupCount());
116
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800117 log.info("Started");
118 }
119
120 @Deactivate
121 public void deactivate() {
122 log.info("Stopped");
123 }
124
125 private STxMap<IntentId, LinkResourceAllocations> getIntentAllocs(TransactionContext tx) {
126 TransactionalMap<byte[], byte[]> raw = tx.getMap(INTENT_ALLOCATIONS);
127 return new STxMap<>(raw, serializer);
128 }
129
130 private STxMap<LinkKey, List<LinkResourceAllocations>> getLinkAllocs(TransactionContext tx) {
131 TransactionalMap<byte[], byte[]> raw = tx.getMap(LINK_RESOURCE_ALLOCATIONS);
132 return new STxMap<>(raw, serializer);
133 }
134
135 private Set<? extends ResourceAllocation> getResourceCapacity(ResourceType type, Link link) {
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800136 if (type == ResourceType.BANDWIDTH) {
137 return ImmutableSet.of(getBandwidthResourceCapacity(link));
138 }
139 if (type == ResourceType.LAMBDA) {
140 return getLambdaResourceCapacity(link);
141 }
142 return null;
143 }
144
145 private Set<LambdaResourceAllocation> getLambdaResourceCapacity(Link link) {
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800146 Set<LambdaResourceAllocation> allocations = new HashSet<>();
147 try {
148 final int waves = Integer.parseInt(link.annotations().value(wavesAnnotation));
149 for (int i = 1; i <= waves; i++) {
150 allocations.add(new LambdaResourceAllocation(Lambda.valueOf(i)));
151 }
152 } catch (NumberFormatException e) {
153 log.debug("No {} annotation on link %s", wavesAnnotation, link);
154 }
155 return allocations;
156 }
157
158 private BandwidthResourceAllocation getBandwidthResourceCapacity(Link link) {
159
160 // if Link annotation exist, use them
161 // if all fails, use DEFAULT_BANDWIDTH
162
163 Bandwidth bandwidth = null;
164 String strBw = link.annotations().value(bandwidthAnnotation);
165 if (strBw != null) {
166 try {
167 bandwidth = Bandwidth.valueOf(Double.parseDouble(strBw));
168 } catch (NumberFormatException e) {
169 // do nothings
170 bandwidth = null;
171 }
172 }
173
174 if (bandwidth == null) {
175 // fall back, use fixed default
176 bandwidth = DEFAULT_BANDWIDTH;
177 }
178 return new BandwidthResourceAllocation(bandwidth);
179 }
180
181 private Map<ResourceType, Set<? extends ResourceAllocation>> getResourceCapacity(Link link) {
182 Map<ResourceType, Set<? extends ResourceAllocation>> caps = new HashMap<>();
183 for (ResourceType type : ResourceType.values()) {
184 Set<? extends ResourceAllocation> cap = getResourceCapacity(type, link);
185 if (cap != null) {
186 caps.put(type, cap);
187 }
188 }
189 return caps;
190 }
191
192 @Override
193 public Set<ResourceAllocation> getFreeResources(Link link) {
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800194 TransactionOptions opt = new TransactionOptions();
195 // read-only and will never be commited, thus does not need durability
196 opt.setTransactionType(TransactionType.LOCAL);
197 TransactionContext tx = theInstance.newTransactionContext(opt);
198 tx.beginTransaction();
199 try {
200 Map<ResourceType, Set<? extends ResourceAllocation>> freeResources = getFreeResourcesEx(tx, link);
201 Set<ResourceAllocation> allFree = new HashSet<>();
202 for (Set<? extends ResourceAllocation> r : freeResources.values()) {
203 allFree.addAll(r);
204 }
205 return allFree;
206 } finally {
207 tx.rollbackTransaction();
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800208 }
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800209
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800210 }
211
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800212 private Map<ResourceType, Set<? extends ResourceAllocation>> getFreeResourcesEx(TransactionContext tx, Link link) {
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800213 // returns capacity - allocated
214
215 checkNotNull(link);
216 Map<ResourceType, Set<? extends ResourceAllocation>> free = new HashMap<>();
217 final Map<ResourceType, Set<? extends ResourceAllocation>> caps = getResourceCapacity(link);
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800218 final Iterable<LinkResourceAllocations> allocations = getAllocations(tx, link);
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800219
220 for (ResourceType type : ResourceType.values()) {
221 // there should be class/category of resources
222 switch (type) {
223 case BANDWIDTH:
224 {
225 Set<? extends ResourceAllocation> bw = caps.get(ResourceType.BANDWIDTH);
226 if (bw == null || bw.isEmpty()) {
227 bw = Sets.newHashSet(new BandwidthResourceAllocation(EMPTY_BW));
228 }
229
230 BandwidthResourceAllocation cap = (BandwidthResourceAllocation) bw.iterator().next();
231 double freeBw = cap.bandwidth().toDouble();
232
233 // enumerate current allocations, subtracting resources
234 for (LinkResourceAllocations alloc : allocations) {
235 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
236 for (ResourceAllocation a : types) {
237 if (a instanceof BandwidthResourceAllocation) {
238 BandwidthResourceAllocation bwA = (BandwidthResourceAllocation) a;
239 freeBw -= bwA.bandwidth().toDouble();
240 }
241 }
242 }
243
244 free.put(type, Sets.newHashSet(new BandwidthResourceAllocation(Bandwidth.valueOf(freeBw))));
245 break;
246 }
247
248 case LAMBDA:
249 {
250 Set<? extends ResourceAllocation> lmd = caps.get(type);
251 if (lmd == null || lmd.isEmpty()) {
252 // nothing left
253 break;
254 }
255 Set<LambdaResourceAllocation> freeL = new HashSet<>();
256 for (ResourceAllocation r : lmd) {
257 if (r instanceof LambdaResourceAllocation) {
258 freeL.add((LambdaResourceAllocation) r);
259 }
260 }
261
262 // enumerate current allocations, removing resources
263 for (LinkResourceAllocations alloc : allocations) {
264 Set<ResourceAllocation> types = alloc.getResourceAllocation(link);
265 for (ResourceAllocation a : types) {
266 if (a instanceof LambdaResourceAllocation) {
267 freeL.remove(a);
268 }
269 }
270 }
271
272 free.put(type, freeL);
273 break;
274 }
275
276 default:
277 break;
278 }
279 }
280 return free;
281 }
282
283 @Override
284 public void allocateResources(LinkResourceAllocations allocations) {
285 checkNotNull(allocations);
286
287 for (int i = 0; i < maxAllocateRetries; ++i) {
288 TransactionContext tx = theInstance.newTransactionContext();
289 tx.beginTransaction();
290 try {
291
292 STxMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
293 // should this be conditional write?
294 intentAllocs.put(allocations.intendId(), allocations);
295
296 for (Link link : allocations.links()) {
297 allocateLinkResource(tx, link, allocations);
298 }
299
300 tx.commitTransaction();
301 return;
302 } catch (TransactionException e) {
303 log.debug("Failed to commit allocations for {}. [retry={}]",
304 allocations.intendId(), i);
305 log.trace(" details {} ", allocations, e);
306 continue;
307 } catch (Exception e) {
308 log.error("Exception thrown, rolling back", e);
309 tx.rollbackTransaction();
310 throw e;
311 }
312 }
313 }
314
315 private void allocateLinkResource(TransactionContext tx, Link link,
316 LinkResourceAllocations allocations) {
317
318 // requested resources
319 Set<ResourceAllocation> reqs = allocations.getResourceAllocation(link);
320
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800321 Map<ResourceType, Set<? extends ResourceAllocation>> available = getFreeResourcesEx(tx, link);
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800322 for (ResourceAllocation req : reqs) {
323 Set<? extends ResourceAllocation> avail = available.get(req.type());
324 if (req instanceof BandwidthResourceAllocation) {
325 // check if allocation should be accepted
326 if (avail.isEmpty()) {
327 checkState(!avail.isEmpty(),
328 "There's no Bandwidth resource on %s?",
329 link);
330 }
331 BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
332 double bwLeft = bw.bandwidth().toDouble();
333 bwLeft -= ((BandwidthResourceAllocation) req).bandwidth().toDouble();
334 if (bwLeft < 0) {
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800335 checkState(bwLeft >= 0,
336 "There's no Bandwidth left on %s. %s",
337 link, bwLeft);
338 }
339 } else if (req instanceof LambdaResourceAllocation) {
340
341 // check if allocation should be accepted
342 if (!avail.contains(req)) {
343 // requested lambda was not available
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800344 checkState(avail.contains(req),
345 "Allocating %s on %s failed",
346 req, link);
347 }
348 }
349 }
350 // all requests allocatable => add allocation
351 final LinkKey linkKey = LinkKey.linkKey(link);
352 STxMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
Yuta HIGUCHIacdec312014-12-02 21:01:16 -0800353 List<LinkResourceAllocations> before = linkAllocs.get(linkKey);
354 if (before == null) {
355 List<LinkResourceAllocations> after = new ArrayList<>();
356 after.add(allocations);
357 before = linkAllocs.putIfAbsent(linkKey, after);
358 if (before != null) {
359 // concurrent allocation detected, retry transaction
360 throw new TransactionException("Concurrent Allocation, retry");
361 }
Yuta HIGUCHI427a2142014-12-03 11:03:18 -0800362 } else {
363 List<LinkResourceAllocations> after = new ArrayList<>(before.size() + 1);
364 after.addAll(before);
365 after.add(allocations);
366 linkAllocs.replace(linkKey, before, after);
Yuta HIGUCHIacdec312014-12-02 21:01:16 -0800367 }
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800368 }
369
370 @Override
371 public LinkResourceEvent releaseResources(LinkResourceAllocations allocations) {
372 checkNotNull(allocations);
373
374 final IntentId intendId = allocations.intendId();
375 final Collection<Link> links = allocations.links();
376
377 boolean success = false;
378 do {
Yuta HIGUCHI65934892014-12-04 17:47:44 -0800379 // Note: might want to break it down into smaller tx unit
380 // to lower the chance of collisions.
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800381 TransactionContext tx = theInstance.newTransactionContext();
382 tx.beginTransaction();
383 try {
384 STxMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
385 intentAllocs.remove(intendId);
386
387 STxMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
388
389 for (Link link : links) {
390 final LinkKey linkId = LinkKey.linkKey(link);
391
392 List<LinkResourceAllocations> before = linkAllocs.get(linkId);
393 if (before == null || before.isEmpty()) {
394 // something is wrong, but it is already freed
395 log.warn("There was no resource left to release on {}", linkId);
396 continue;
397 }
398 List<LinkResourceAllocations> after = new ArrayList<>(before);
399 after.remove(allocations);
400 linkAllocs.replace(linkId, before, after);
401 }
402
403 tx.commitTransaction();
404 success = true;
405 } catch (TransactionException e) {
406 log.debug("Transaction failed, retrying");
407 } catch (Exception e) {
408 log.error("Exception thrown during releaseResource {}",
409 allocations, e);
410 tx.rollbackTransaction();
411 throw e;
412 }
413 } while (!success);
414
415 // Issue events to force recompilation of intents.
416 final List<LinkResourceAllocations> releasedResources =
417 ImmutableList.of(allocations);
418 return new LinkResourceEvent(
419 LinkResourceEvent.Type.ADDITIONAL_RESOURCES_AVAILABLE,
420 releasedResources);
421 }
422
423 @Override
424 public LinkResourceAllocations getAllocations(IntentId intentId) {
425 checkNotNull(intentId);
426 TransactionOptions opt = new TransactionOptions();
427 // read-only and will never be commited, thus does not need durability
428 opt.setTransactionType(TransactionType.LOCAL);
429 TransactionContext tx = theInstance.newTransactionContext(opt);
430 tx.beginTransaction();
431 try {
432 STxMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
433 return intentAllocs.get(intentId);
434 } finally {
435 tx.rollbackTransaction();
436 }
437 }
438
439 @Override
440 public List<LinkResourceAllocations> getAllocations(Link link) {
441 checkNotNull(link);
442 final LinkKey key = LinkKey.linkKey(link);
443
444 TransactionOptions opt = new TransactionOptions();
445 // read-only and will never be commited, thus does not need durability
446 opt.setTransactionType(TransactionType.LOCAL);
447 TransactionContext tx = theInstance.newTransactionContext(opt);
448 tx.beginTransaction();
449 List<LinkResourceAllocations> res = null;
450 try {
451 STxMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
452 res = linkAllocs.get(key);
453 } finally {
454 tx.rollbackTransaction();
455 }
456
457 if (res == null) {
458 // try to add empty list
459 TransactionContext tx2 = theInstance.newTransactionContext();
460 tx2.beginTransaction();
461 try {
462 res = getLinkAllocs(tx2).putIfAbsent(key, new ArrayList<>());
463 tx2.commitTransaction();
464 if (res == null) {
465 return Collections.emptyList();
466 } else {
467 return res;
468 }
469 } catch (TransactionException e) {
470 // concurrently added?
471 return getAllocations(link);
472 } catch (Exception e) {
473 tx.rollbackTransaction();
474 }
475 }
476 return res;
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800477 }
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800478
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800479 private Iterable<LinkResourceAllocations> getAllocations(TransactionContext tx,
480 Link link) {
481 checkNotNull(tx);
482 checkNotNull(link);
483 final LinkKey key = LinkKey.linkKey(link);
484
485 STxMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx);
486 List<LinkResourceAllocations> res = null;
487 res = linkAllocs.get(key);
488 if (res == null) {
Yuta HIGUCHIacdec312014-12-02 21:01:16 -0800489 res = linkAllocs.putIfAbsent(key, new ArrayList<>());
Yuta HIGUCHIbd1aee12014-12-01 20:05:47 -0800490 if (res == null) {
491 return Collections.emptyList();
492 } else {
493 return res;
494 }
495 }
Yuta HIGUCHI81419e22014-12-03 16:54:16 -0800496 return res;
Yuta HIGUCHI3cc4d9b2014-11-29 18:17:17 -0800497 }
498
499 @Override
500 public Iterable<LinkResourceAllocations> getAllocations() {
501 TransactionContext tx = theInstance.newTransactionContext();
502 tx.beginTransaction();
503 try {
504 STxMap<IntentId, LinkResourceAllocations> intentAllocs = getIntentAllocs(tx);
505 return intentAllocs.values();
506 } finally {
507 tx.rollbackTransaction();
508 }
509 }
510}