blob: d035c2dc6b07d855246858e018a84beaf8dfdc02 [file] [log] [blame]
Brian O'Connor41718fc2014-10-30 16:57:21 -07001/*
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 */
16package org.onlab.onos.store.resource.impl;
17
Ray Milkeye97ede92014-11-20 10:43:12 -080018import java.util.ArrayList;
19import java.util.Collection;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.List;
23import java.util.Map;
24import java.util.Set;
25
Brian O'Connor41718fc2014-10-30 16:57:21 -070026import org.apache.felix.scr.annotations.Activate;
27import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Deactivate;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Brian O'Connor41718fc2014-10-30 16:57:21 -070031import org.apache.felix.scr.annotations.Service;
Yuta HIGUCHIbf366d52014-12-02 12:57:22 -080032import org.onlab.onos.net.AnnotationKeys;
Brian O'Connor41718fc2014-10-30 16:57:21 -070033import org.onlab.onos.net.Link;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080034import org.onlab.onos.net.LinkKey;
Brian O'Connor41718fc2014-10-30 16:57:21 -070035import org.onlab.onos.net.intent.IntentId;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080036import org.onlab.onos.net.link.LinkService;
Brian O'Connor41718fc2014-10-30 16:57:21 -070037import org.onlab.onos.net.resource.Bandwidth;
38import org.onlab.onos.net.resource.BandwidthResourceAllocation;
39import org.onlab.onos.net.resource.Lambda;
40import org.onlab.onos.net.resource.LambdaResourceAllocation;
41import org.onlab.onos.net.resource.LinkResourceAllocations;
Ray Milkeye97ede92014-11-20 10:43:12 -080042import org.onlab.onos.net.resource.LinkResourceEvent;
Brian O'Connor41718fc2014-10-30 16:57:21 -070043import org.onlab.onos.net.resource.LinkResourceStore;
44import org.onlab.onos.net.resource.ResourceAllocation;
45import org.onlab.onos.net.resource.ResourceType;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080046import org.onlab.onos.store.serializers.KryoSerializer;
47import org.onlab.onos.store.serializers.StoreSerializer;
48import org.onlab.onos.store.service.BatchWriteRequest;
49import org.onlab.onos.store.service.BatchWriteRequest.Builder;
50import org.onlab.onos.store.service.BatchWriteResult;
51import org.onlab.onos.store.service.DatabaseAdminService;
Yuta HIGUCHI2fe63342014-11-17 21:33:38 -080052import org.onlab.onos.store.service.DatabaseException;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080053import org.onlab.onos.store.service.DatabaseService;
54import org.onlab.onos.store.service.VersionedValue;
55import org.onlab.onos.store.service.WriteRequest;
56import org.onlab.onos.store.service.WriteResult;
Brian O'Connor41718fc2014-10-30 16:57:21 -070057import org.slf4j.Logger;
58
Yuta HIGUCHI35242292014-11-12 18:53:15 -080059import com.google.common.base.Function;
60import com.google.common.collect.FluentIterable;
Ray Milkeye97ede92014-11-20 10:43:12 -080061import com.google.common.collect.ImmutableList;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080062import com.google.common.collect.ImmutableSet;
63import com.google.common.collect.Sets;
64
Yuta HIGUCHI35242292014-11-12 18:53:15 -080065import static com.google.common.base.Preconditions.checkArgument;
Brian O'Connor41718fc2014-10-30 16:57:21 -070066import static com.google.common.base.Preconditions.checkNotNull;
67import static com.google.common.base.Preconditions.checkState;
Yuta HIGUCHI35242292014-11-12 18:53:15 -080068import static com.google.common.base.Predicates.notNull;
69import static org.onlab.util.HexString.toHexString;
Brian O'Connor41718fc2014-10-30 16:57:21 -070070import static org.slf4j.LoggerFactory.getLogger;
71
72/**
Yuta HIGUCHI35242292014-11-12 18:53:15 -080073 * Manages link resources using database service.
Brian O'Connor41718fc2014-10-30 16:57:21 -070074 */
Yuta HIGUCHI9b108b32014-12-01 11:10:26 -080075@Component(immediate = true, enabled = false)
Brian O'Connor41718fc2014-10-30 16:57:21 -070076@Service
77public class DistributedLinkResourceStore implements LinkResourceStore {
Yuta HIGUCHI35242292014-11-12 18:53:15 -080078
Brian O'Connor41718fc2014-10-30 16:57:21 -070079 private final Logger log = getLogger(getClass());
Yuta HIGUCHI35242292014-11-12 18:53:15 -080080
81 // FIXME: what is the Bandwidth unit?
82 private static final Bandwidth DEFAULT_BANDWIDTH = Bandwidth.valueOf(1_000);
83
84 // table to store current allocations
85 /** LinkKey -> List<LinkResourceAllocations>. */
86 private static final String LINK_RESOURCE_ALLOCATIONS = "LinkResourceAllocations";
87
88 /** IntentId -> LinkResourceAllocations. */
89 private static final String INTENT_ALLOCATIONS = "IntentAllocations";
90
91 private static final Bandwidth EMPTY_BW = Bandwidth.valueOf(0);
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected DatabaseAdminService databaseAdminService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected DatabaseService databaseService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected LinkService linkService;
101
102 // Link annotation key name to use as bandwidth
Yuta HIGUCHIbf366d52014-12-02 12:57:22 -0800103 private String bandwidthAnnotation = AnnotationKeys.BANDWIDTH;
Ray Milkeye97ede92014-11-20 10:43:12 -0800104 // Link annotation key name to use as max lambda
Yuta HIGUCHIbf366d52014-12-02 12:57:22 -0800105 private String wavesAnnotation = AnnotationKeys.OPTICAL_WAVES;
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800106
107 private StoreSerializer serializer;
108
Brian O'Connor41718fc2014-10-30 16:57:21 -0700109
Yuta HIGUCHI45e5cd12014-11-24 14:57:13 -0800110 void createTable(String tableName) {
111 boolean tableReady = false;
112 do {
113 try {
114 if (!databaseAdminService.listTables().contains(tableName)) {
115 databaseAdminService.createTable(tableName);
116 }
117 tableReady = true;
118 } catch (DatabaseException e) {
119 log.debug("Failed creating table, retrying", e);
120 try {
121 Thread.sleep(200);
122 } catch (InterruptedException e1) {
123 throw new DatabaseException(e1);
124 }
125 }
126 } while (!tableReady);
127 }
128
Brian O'Connor41718fc2014-10-30 16:57:21 -0700129 @Activate
130 public void activate() {
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800131
132 serializer = new KryoSerializer();
133
Yuta HIGUCHI45e5cd12014-11-24 14:57:13 -0800134 createTable(LINK_RESOURCE_ALLOCATIONS);
135 createTable(INTENT_ALLOCATIONS);
Yuta HIGUCHI2fe63342014-11-17 21:33:38 -0800136
Brian O'Connor41718fc2014-10-30 16:57:21 -0700137 log.info("Started");
138 }
139
140 @Deactivate
141 public void deactivate() {
142 log.info("Stopped");
143 }
144
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800145 private Set<? extends ResourceAllocation> getResourceCapacity(ResourceType type, Link link) {
146 // TODO: plugin/provider mechanism to add resource type in the future?
147 if (type == ResourceType.BANDWIDTH) {
148 return ImmutableSet.of(getBandwidthResourceCapacity(link));
Brian O'Connor41718fc2014-10-30 16:57:21 -0700149 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800150 if (type == ResourceType.LAMBDA) {
151 return getLambdaResourceCapacity(link);
152 }
153 return null;
154 }
155
156 private Set<LambdaResourceAllocation> getLambdaResourceCapacity(Link link) {
157 // FIXME enumerate all the possible link/port lambdas
158 Set<LambdaResourceAllocation> allocations = new HashSet<>();
159 try {
160 final int waves = Integer.parseInt(link.annotations().value(wavesAnnotation));
161 for (int i = 1; i <= waves; i++) {
162 allocations.add(new LambdaResourceAllocation(Lambda.valueOf(i)));
163 }
164 } catch (NumberFormatException e) {
165 log.debug("No {} annotation on link %s", wavesAnnotation, link);
166 }
Brian O'Connor41718fc2014-10-30 16:57:21 -0700167 return allocations;
168 }
169
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800170 private BandwidthResourceAllocation getBandwidthResourceCapacity(Link link) {
171
172 // if Link annotation exist, use them
173 // if all fails, use DEFAULT_BANDWIDTH
174
175 Bandwidth bandwidth = null;
176 String strBw = link.annotations().value(bandwidthAnnotation);
177 if (strBw != null) {
178 try {
179 bandwidth = Bandwidth.valueOf(Double.parseDouble(strBw));
180 } catch (NumberFormatException e) {
181 // do nothings
182 bandwidth = null;
Brian O'Connor41718fc2014-10-30 16:57:21 -0700183 }
184 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800185
186 if (bandwidth == null) {
187 // fall back, use fixed default
188 bandwidth = DEFAULT_BANDWIDTH;
189 }
190 return new BandwidthResourceAllocation(bandwidth);
Brian O'Connor41718fc2014-10-30 16:57:21 -0700191 }
192
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800193 private Map<ResourceType, Set<? extends ResourceAllocation>> getResourceCapacity(Link link) {
194 Map<ResourceType, Set<? extends ResourceAllocation>> caps = new HashMap<>();
195 for (ResourceType type : ResourceType.values()) {
196 Set<? extends ResourceAllocation> cap = getResourceCapacity(type, link);
197 if (cap != null) {
198 caps.put(type, cap);
199 }
200 }
201 return caps;
202 }
203
204 @Override
205 public Set<ResourceAllocation> getFreeResources(Link link) {
206 Map<ResourceType, Set<? extends ResourceAllocation>> freeResources = getFreeResourcesEx(link);
207 Set<ResourceAllocation> allFree = new HashSet<>();
208 for (Set<? extends ResourceAllocation> r:freeResources.values()) {
209 allFree.addAll(r);
210 }
211 return allFree;
212 }
213
214 private Map<ResourceType, Set<? extends ResourceAllocation>> getFreeResourcesEx(Link link) {
215 // returns capacity - allocated
216
Brian O'Connor41718fc2014-10-30 16:57:21 -0700217 checkNotNull(link);
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800218 Map<ResourceType, Set<? extends ResourceAllocation>> free = new HashMap<>();
219 final Map<ResourceType, Set<? extends ResourceAllocation>> caps = getResourceCapacity(link);
220 final Iterable<LinkResourceAllocations> allocations = getAllocations(link);
221
222 for (ResourceType type : ResourceType.values()) {
223 // there should be class/category of resources
224 switch (type) {
Brian O'Connor41718fc2014-10-30 16:57:21 -0700225 case BANDWIDTH:
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800226 {
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
246 free.put(type, Sets.newHashSet(new BandwidthResourceAllocation(Bandwidth.valueOf(freeBw))));
Brian O'Connor41718fc2014-10-30 16:57:21 -0700247 break;
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800248 }
249
Brian O'Connor41718fc2014-10-30 16:57:21 -0700250 case LAMBDA:
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800251 {
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);
Brian O'Connor41718fc2014-10-30 16:57:21 -0700275 break;
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800276 }
277
Brian O'Connor41718fc2014-10-30 16:57:21 -0700278 default:
279 break;
280 }
281 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800282 return free;
Brian O'Connor41718fc2014-10-30 16:57:21 -0700283 }
284
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800285 private LinkResourceAllocations getIntentAllocations(IntentId id) {
286 VersionedValue vv
287 = databaseService.get(INTENT_ALLOCATIONS, toIntentDbKey(checkNotNull(id)));
288 if (vv == null || vv.value() == null) {
289 return null;
290 }
291 return decodeIntentAllocations(vv.value());
292 }
293
294 private Builder putIntentAllocations(Builder ctx,
295 IntentId id,
296 LinkResourceAllocations alloc) {
297 return ctx.put(INTENT_ALLOCATIONS,
298 toIntentDbKey(id),
299 encodeIntentAllocations(alloc));
300 }
301
302
303 @Override
304 public void allocateResources(LinkResourceAllocations allocations) {
305 checkNotNull(allocations);
306
307 Builder tx = BatchWriteRequest.newBuilder();
308
309 // TODO: Should IntentId -> Allocation be updated conditionally?
310 putIntentAllocations(tx, allocations.intendId(), allocations);
311
312 for (Link link : allocations.links()) {
313 allocateLinkResource(tx, link, allocations);
314 }
315
316 BatchWriteRequest batch = tx.build();
317// log.info("Intent: {}", databaseService.getAll(INTENT_ALLOCATIONS));
318// log.info("Link: {}", databaseService.getAll(LINK_RESOURCE_ALLOCATIONS));
319
320 BatchWriteResult result = databaseService.batchWrite(batch);
321 if (!result.isSuccessful()) {
322 log.error("Allocation Failed.");
323 if (log.isDebugEnabled()) {
324 logFailureDetail(batch, result);
325 }
326 // FIXME throw appropriate exception, with what failed.
327 checkState(result.isSuccessful(), "Allocation failed");
328 }
329 }
330
331 private void logFailureDetail(BatchWriteRequest batch,
332 BatchWriteResult result) {
333 for (int i = 0; i < batch.batchSize(); ++i) {
334 final WriteRequest req = batch.getAsList().get(i);
335 final WriteResult fail = result.getAsList().get(i);
336 switch (fail.status()) {
337 case ABORTED:
338 log.debug("ABORTED: {}@{}", req.key(), req.tableName());
Brian O'Connor41718fc2014-10-30 16:57:21 -0700339 break;
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800340 case PRECONDITION_VIOLATION:
341 switch (req.type()) {
342 case PUT_IF_ABSENT:
343 log.debug("{}: {}@{} : {}", req.type(),
344 req.key(), req.tableName(), fail.previousValue());
345 break;
346 case PUT_IF_VALUE:
347 case REMOVE_IF_VALUE:
348 log.debug("{}: {}@{} : was {}, expected {}", req.type(),
349 req.key(), req.tableName(),
350 fail.previousValue(),
351 toHexString(req.oldValue()));
352 break;
353 case PUT_IF_VERSION:
354 case REMOVE_IF_VERSION:
355 log.debug("{}: {}@{} : was {}, expected {}", req.type(),
356 req.key(), req.tableName(),
357 fail.previousValue().version(),
358 req.previousVersion());
359 break;
360 default:
361 log.error("Should never reach here.");
362 break;
363 }
Brian O'Connor41718fc2014-10-30 16:57:21 -0700364 break;
365 default:
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800366 log.error("Should never reach here.");
Brian O'Connor41718fc2014-10-30 16:57:21 -0700367 break;
368 }
369 }
Brian O'Connor41718fc2014-10-30 16:57:21 -0700370 }
371
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800372 private Builder allocateLinkResource(Builder builder, Link link,
373 LinkResourceAllocations allocations) {
Brian O'Connor41718fc2014-10-30 16:57:21 -0700374
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800375 // requested resources
376 Set<ResourceAllocation> reqs = allocations.getResourceAllocation(link);
Brian O'Connor41718fc2014-10-30 16:57:21 -0700377
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800378 Map<ResourceType, Set<? extends ResourceAllocation>> available = getFreeResourcesEx(link);
379 for (ResourceAllocation req : reqs) {
380 Set<? extends ResourceAllocation> avail = available.get(req.type());
381 if (req instanceof BandwidthResourceAllocation) {
382 // check if allocation should be accepted
383 if (avail.isEmpty()) {
384 checkState(!avail.isEmpty(),
385 "There's no Bandwidth resource on %s?",
386 link);
387 }
388 BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
389 double bwLeft = bw.bandwidth().toDouble();
390 bwLeft -= ((BandwidthResourceAllocation) req).bandwidth().toDouble();
391 if (bwLeft < 0) {
392 // FIXME throw appropriate Exception
393 checkState(bwLeft >= 0,
394 "There's no Bandwidth left on %s. %s",
395 link, bwLeft);
396 }
397 } else if (req instanceof LambdaResourceAllocation) {
398
399 // check if allocation should be accepted
400 if (!avail.contains(req)) {
401 // requested lambda was not available
402 // FIXME throw appropriate exception
403 checkState(avail.contains(req),
404 "Allocating %s on %s failed",
405 req, link);
406 }
Brian O'Connor41718fc2014-10-30 16:57:21 -0700407 }
Brian O'Connor41718fc2014-10-30 16:57:21 -0700408 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800409 // all requests allocatable => add allocation
410 final List<LinkResourceAllocations> before = getAllocations(link);
411 List<LinkResourceAllocations> after = new ArrayList<>(before.size());
412 after.addAll(before);
413 after.add(allocations);
414 replaceLinkAllocations(builder, LinkKey.linkKey(link), before, after);
415 return builder;
416 }
417
418 private Builder replaceLinkAllocations(Builder builder, LinkKey linkKey,
419 List<LinkResourceAllocations> before,
420 List<LinkResourceAllocations> after) {
421
422 byte[] oldValue = encodeLinkAllocations(before);
423 byte[] newValue = encodeLinkAllocations(after);
424 builder.putIfValueMatches(LINK_RESOURCE_ALLOCATIONS, toLinkDbKey(linkKey), oldValue, newValue);
425 return builder;
Brian O'Connor41718fc2014-10-30 16:57:21 -0700426 }
427
428 @Override
Ray Milkeye97ede92014-11-20 10:43:12 -0800429 public LinkResourceEvent releaseResources(LinkResourceAllocations allocations) {
Brian O'Connor41718fc2014-10-30 16:57:21 -0700430 checkNotNull(allocations);
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800431
432 final IntentId intendId = allocations.intendId();
433 final String dbIntentId = toIntentDbKey(intendId);
434 final Collection<Link> links = allocations.links();
435
436 // TODO: does release must happen in a batch?
437 boolean success;
438 do {
439 Builder tx = BatchWriteRequest.newBuilder();
440
441 // TODO: Should IntentId -> Allocation be updated conditionally?
442 tx.remove(INTENT_ALLOCATIONS, dbIntentId);
443
444 for (Link link : links) {
445 final LinkKey linkId = LinkKey.linkKey(link);
446 final String dbLinkId = toLinkDbKey(linkId);
447 VersionedValue vv = databaseService.get(LINK_RESOURCE_ALLOCATIONS, dbLinkId);
448 if (vv == null || vv.value() == null) {
449 // something is wrong, but it is already freed
450 log.warn("There was no resource left to release on {}", linkId);
451 continue;
452 }
453 List<LinkResourceAllocations> before = decodeLinkAllocations(vv.value());
454 List<LinkResourceAllocations> after = new ArrayList<>(before);
455 after.remove(allocations);
456 byte[] oldValue = encodeLinkAllocations(before);
457 byte[] newValue = encodeLinkAllocations(after);
458 tx.putIfValueMatches(LINK_RESOURCE_ALLOCATIONS, dbLinkId, oldValue, newValue);
Brian O'Connor41718fc2014-10-30 16:57:21 -0700459 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800460
461 BatchWriteResult batchWrite = databaseService.batchWrite(tx.build());
462 success = batchWrite.isSuccessful();
463 } while (!success);
Ray Milkeye97ede92014-11-20 10:43:12 -0800464
465 // Issue events to force recompilation of intents.
466
467 final List<LinkResourceAllocations> releasedResources =
468 ImmutableList.of(allocations);
469 return new LinkResourceEvent(
470 LinkResourceEvent.Type.ADDITIONAL_RESOURCES_AVAILABLE,
471 releasedResources);
Brian O'Connor41718fc2014-10-30 16:57:21 -0700472 }
473
474 @Override
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800475 public LinkResourceAllocations getAllocations(IntentId intentId) {
Brian O'Connor41718fc2014-10-30 16:57:21 -0700476 checkNotNull(intentId);
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800477 VersionedValue vv = databaseService.get(INTENT_ALLOCATIONS, toIntentDbKey(intentId));
478 if (vv == null) {
479 // FIXME: should we return null or LinkResourceAllocations with nothing allocated?
480 return null;
Brian O'Connor41718fc2014-10-30 16:57:21 -0700481 }
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800482 LinkResourceAllocations allocations = decodeIntentAllocations(vv.value());
483 return allocations;
484 }
485
486 private String toLinkDbKey(LinkKey linkid) {
487 // introduce cache if necessary
488 return linkid.toString();
489 // TODO: Above is irreversible, if we need reverse conversion
490 // we may need something like below, due to String only limitation
491// byte[] bytes = serializer.encode(linkid);
492// StringBuilder builder = new StringBuilder(bytes.length * 4);
493// boolean isFirst = true;
494// for (byte b : bytes) {
495// if (!isFirst) {
496// builder.append(',');
497// }
498// builder.append(b);
499// isFirst = false;
500// }
501// return builder.toString();
502 }
503
504// private LinkKey toLinkKey(String linkKey) {
505// String[] bytes = linkKey.split(",");
506// ByteBuffer buf = ByteBuffer.allocate(bytes.length);
507// for (String bs : bytes) {
508// buf.put(Byte.parseByte(bs));
509// }
510// buf.flip();
511// return serializer.decode(buf);
512// }
513
514 private String toIntentDbKey(IntentId intentid) {
515 return intentid.toString();
516 }
517
518 private IntentId toIntentId(String intentid) {
519 checkArgument(intentid.startsWith("0x"));
520 return IntentId.valueOf(Long.parseLong(intentid.substring(2)));
521 }
522
523 private LinkResourceAllocations decodeIntentAllocations(byte[] bytes) {
524 return serializer.decode(bytes);
525 }
526
527 private byte[] encodeIntentAllocations(LinkResourceAllocations alloc) {
528 return serializer.encode(checkNotNull(alloc));
529 }
530
531 private List<LinkResourceAllocations> decodeLinkAllocations(byte[] bytes) {
532 return serializer.decode(bytes);
533 }
534
535 private byte[] encodeLinkAllocations(List<LinkResourceAllocations> alloc) {
536 return serializer.encode(checkNotNull(alloc));
Brian O'Connor41718fc2014-10-30 16:57:21 -0700537 }
538
539 @Override
Yuta HIGUCHI35242292014-11-12 18:53:15 -0800540 public List<LinkResourceAllocations> getAllocations(Link link) {
541 checkNotNull(link);
542 final LinkKey key = LinkKey.linkKey(link);
543 final String dbKey = toLinkDbKey(key);
544 VersionedValue vv = databaseService.get(LINK_RESOURCE_ALLOCATIONS, dbKey);
545 if (vv == null) {
546 // write empty so that all other update can be replace operation
547 byte[] emptyList = encodeLinkAllocations(new ArrayList<>());
548 boolean written = databaseService.putIfAbsent(LINK_RESOURCE_ALLOCATIONS, dbKey, emptyList);
549 log.trace("Empty allocation write success? {}", written);
550 vv = databaseService.get(LINK_RESOURCE_ALLOCATIONS, dbKey);
551 if (vv == null) {
552 log.error("Failed to re-read allocation for {}", dbKey);
553 // note: cannot be Collections.emptyList();
554 return new ArrayList<>();
555 }
556 }
557 List<LinkResourceAllocations> allocations = decodeLinkAllocations(vv.value());
558 return allocations;
559 }
560
561 @Override
562 public Iterable<LinkResourceAllocations> getAllocations() {
563 //IntentId -> LinkResourceAllocations
564 Map<String, VersionedValue> all = databaseService.getAll(INTENT_ALLOCATIONS);
565
566 return FluentIterable.from(all.values())
567 .transform(new Function<VersionedValue, LinkResourceAllocations>() {
568
569 @Override
570 public LinkResourceAllocations apply(VersionedValue input) {
571 if (input == null || input.value() == null) {
572 return null;
573 }
574 return decodeIntentAllocations(input.value());
575 }
576 })
577 .filter(notNull());
Brian O'Connor41718fc2014-10-30 16:57:21 -0700578 }
Brian O'Connor41718fc2014-10-30 16:57:21 -0700579}