@Override public CompletableFuture<Boolean> compareAndSet(V expect, V update) { final MeteringAgent.Context newTimer = monitor.startTimer(COMPARE_AND_SET); return backingMap .replace(name, serializer.encode(expect), serializer.encode(update)) .whenComplete((r, e) -> newTimer.stop(e)); }
@Override public CompletableFuture<Boolean> replace(K key, V oldValue, V newValue) { checkNotNull(key, ERROR_NULL_KEY); checkNotNull(newValue, ERROR_NULL_VALUE); checkIfUnmodifiable(); byte[] existing = oldValue != null ? serializer.encode(oldValue) : null; return database .replace(name, keyCache.getUnchecked(key), existing, serializer.encode(newValue)) .thenApply(this::unwrapResult); }
@Override public CompletableFuture<Versioned<V>> put(K key, V value) { checkNotNull(key, ERROR_NULL_KEY); checkNotNull(value, ERROR_NULL_VALUE); checkIfUnmodifiable(); return database .put(name, keyCache.getUnchecked(key), serializer.encode(value)) .thenApply(this::unwrapResult) .thenApply( v -> v != null ? new Versioned<>(serializer.decode(v.value()), v.version(), v.creationTime()) : null); }
@Activate protected void activate() { Serializer serializer = Serializer.using( Arrays.asList(KryoNamespaces.API), Identifier.class, RegionId.class, Region.class, DefaultRegion.class, Region.Type.class); regionsRepo = storageService .<RegionId, Region>consistentMapBuilder() .withSerializer(serializer) .withName("onos-regions") .withRelaxedReadConsistency() .build(); regionsRepo.addListener(listener); regionsById = regionsRepo.asJavaMap(); membershipRepo = storageService .<RegionId, Set<DeviceId>>consistentMapBuilder() .withSerializer(serializer) .withName("onos-region-devices") .withRelaxedReadConsistency() .build(); membershipRepo.addListener(membershipListener); regionDevices = membershipRepo.asJavaMap(); log.info("Started"); }
@Override public CompletableFuture<V> getAndSet(V value) { final MeteringAgent.Context newTimer = monitor.startTimer(GET_AND_SET); if (value == null) { return backingMap .remove(name) .thenApply(Versioned::valueOrNull) .thenApply(v -> v == null ? null : serializer.<V>decode(v)) .whenComplete((r, e) -> newTimer.stop(e)); } return backingMap .put(name, serializer.encode(value)) .thenApply(Versioned::valueOrNull) .thenApply(v -> v == null ? null : serializer.<V>decode(v)) .whenComplete((r, e) -> newTimer.stop(e)); }
@Override public CompletableFuture<Versioned<V>> putAndGet(K key, V value) { checkNotNull(key, ERROR_NULL_KEY); checkNotNull(value, ERROR_NULL_VALUE); checkIfUnmodifiable(); return database .putAndGet(name, keyCache.getUnchecked(key), serializer.encode(value)) .thenApply(this::unwrapResult) .thenApply( v -> { Versioned<byte[]> rawNewValue = v.newValue(); return new Versioned<>( serializer.decode(rawNewValue.value()), rawNewValue.version(), rawNewValue.creationTime()); }); }
private Map.Entry<K, Versioned<V>> fromRawEntry(Map.Entry<String, Versioned<byte[]>> e) { return Pair.of( dK(e.getKey()), new Versioned<>( serializer.decode(e.getValue().value()), e.getValue().version(), e.getValue().creationTime())); }
@Override public CompletableFuture<Boolean> remove(K key, V value) { checkNotNull(key, ERROR_NULL_KEY); checkNotNull(value, ERROR_NULL_VALUE); checkIfUnmodifiable(); return database .remove(name, keyCache.getUnchecked(key), serializer.encode(value)) .thenApply(this::unwrapResult); }
private PacketRequestTracker() { requests = storageService .<TrafficSelector, Set<PacketRequest>>consistentMapBuilder() .withName("onos-packet-requests") .withPartitionsDisabled() .withSerializer(Serializer.using(KryoNamespaces.API)) .build(); }
@Override public CompletableFuture<V> get() { final MeteringAgent.Context newTimer = monitor.startTimer(GET); return backingMap .get(name) .thenApply(Versioned::valueOrNull) .thenApply(v -> v == null ? null : serializer.<V>decode(v)) .whenComplete((r, e) -> newTimer.stop(e)); }
@Override public CompletableFuture<Void> set(V value) { final MeteringAgent.Context newTimer = monitor.startTimer(SET); if (value == null) { return backingMap.remove(name).whenComplete((r, e) -> newTimer.stop(e)).thenApply(v -> null); } return backingMap .put(name, serializer.encode(value)) .whenComplete((r, e) -> newTimer.stop(e)) .thenApply(v -> null); }
@Override public CompletableFuture<Versioned<V>> get(K key) { checkNotNull(key, ERROR_NULL_KEY); return database .get(name, keyCache.getUnchecked(key)) .thenApply( v -> v != null ? new Versioned<>(serializer.decode(v.value()), v.version(), v.creationTime()) : null); }
@Override public CompletableFuture<Collection<Versioned<V>>> values() { return database .values(name) .thenApply( c -> c.stream() .map( v -> new Versioned<V>( serializer.decode(v.value()), v.version(), v.creationTime())) .collect(Collectors.toList())); }
@Override public CompletableFuture<Optional<Versioned<V>>> replaceAndGet( K key, long oldVersion, V newValue) { checkNotNull(key, ERROR_NULL_KEY); checkNotNull(newValue, ERROR_NULL_VALUE); checkIfUnmodifiable(); return database .replaceAndGet(name, keyCache.getUnchecked(key), oldVersion, serializer.encode(newValue)) .thenApply(this::unwrapResult) .thenApply( v -> { if (v.updated()) { Versioned<byte[]> rawNewValue = v.newValue(); return Optional.of( new Versioned<>( serializer.decode(rawNewValue.value()), rawNewValue.version(), rawNewValue.creationTime())); } else { return Optional.empty(); } }); }
@Override public <K, V> AsyncConsistentMap<K, V> newAsyncConsistentMap(String name, Serializer serializer) { checkNotNull(name); checkNotNull(serializer); Map<PartitionId, AsyncConsistentMap<K, V>> maps = Maps.transformValues( members, partition -> partition.newAsyncConsistentMap(name, serializer)); Hasher<K> hasher = key -> { long hashCode = HashCode.fromBytes(Bytes.ensureCapacity(serializer.encode(key), 8, 0)).asLong(); return sortedMemberPartitionIds.get(Hashing.consistentHash(hashCode, members.size())); }; return new PartitionedAsyncConsistentMap<>(name, maps, hasher); }
@Override public CompletableFuture<Versioned<V>> putIfAbsent(K key, V value) { checkNotNull(key, ERROR_NULL_KEY); checkNotNull(value, ERROR_NULL_VALUE); checkIfUnmodifiable(); AtomicReference<MapEvent<K, V>> event = new AtomicReference<>(); return database .putIfAbsentAndGet(name, keyCache.getUnchecked(key), serializer.encode(value)) .thenApply(this::unwrapResult) .whenComplete( (r, e) -> { if (r != null && r.updated()) { event.set( new MapEvent<K, V>( name, MapEvent.Type.INSERT, key, r.newValue().<V>map(serializer::decode))); } }) .thenApply(v -> v.updated() ? null : v.oldValue().<V>map(serializer::decode)) .whenComplete((r, e) -> notifyListeners(event.get())); }
@Activate public void activate() { mcastRib = storageService .<McastRoute, MulticastData>consistentMapBuilder() .withName(MCASTRIB) .withSerializer( Serializer.using( KryoNamespace.newBuilder() .register(KryoNamespaces.API) .register( AtomicReference.class, MulticastData.class, McastRoute.class, McastRoute.Type.class) .build())) // .withRelaxedReadConsistency() .build(); mcastRoutes = mcastRib.asJavaMap(); log.info("Started"); }
@Activate protected void activate() { tunnelInfoMap = storageService .<TunnelId, ResourceConsumer>consistentMapBuilder() .withName("onos-pce-tunnelinfomap") .withSerializer( Serializer.using( new KryoNamespace.Builder() .register(KryoNamespaces.API) .register(TunnelId.class, TunnelConsumerId.class) .build())) .build(); failedPathSet = storageService .<PcePathInfo>setBuilder() .withName("failed-path-info") .withSerializer(SERIALIZER) .build() .asDistributedSet(); log.info("Started"); }
/** Implementation of ResourceStore using TransactionalMap. */ @Component(immediate = true) @Service @Beta public class ConsistentResourceStore extends AbstractStore<ResourceEvent, ResourceStoreDelegate> implements ResourceStore { private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class); private static final String DISCRETE_CONSUMER_MAP = "onos-discrete-consumers"; private static final String CONTINUOUS_CONSUMER_MAP = "onos-continuous-consumers"; private static final String CHILD_MAP = "onos-resource-children"; private static final Serializer SERIALIZER = Serializer.using( Arrays.asList(KryoNamespaces.BASIC, KryoNamespaces.API), ContinuousResourceAllocation.class); // TODO: We should provide centralized values for this private static final int MAX_RETRIES = 5; private static final int RETRY_DELAY = 1_000; // millis @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected StorageService service; private ConsistentMap<DiscreteResource, ResourceConsumer> discreteConsumers; private ConsistentMap<ResourceId, ContinuousResourceAllocation> continuousConsumers; private ConsistentMap<DiscreteResource, Set<Resource>> childMap; @Activate public void activate() { discreteConsumers = service .<DiscreteResource, ResourceConsumer>consistentMapBuilder() .withName(DISCRETE_CONSUMER_MAP) .withSerializer(SERIALIZER) .build(); continuousConsumers = service .<ResourceId, ContinuousResourceAllocation>consistentMapBuilder() .withName(CONTINUOUS_CONSUMER_MAP) .withSerializer(SERIALIZER) .build(); childMap = service .<DiscreteResource, Set<Resource>>consistentMapBuilder() .withName(CHILD_MAP) .withSerializer(SERIALIZER) .build(); Tools.retryable( () -> childMap.put(Resource.ROOT, new LinkedHashSet<>()), ConsistentMapException.class, MAX_RETRIES, RETRY_DELAY); log.info("Started"); } @Override public List<ResourceConsumer> getConsumers(Resource resource) { checkNotNull(resource); checkArgument(resource instanceof DiscreteResource || resource instanceof ContinuousResource); if (resource instanceof DiscreteResource) { return getConsumers((DiscreteResource) resource); } else { return getConsumers((ContinuousResource) resource); } } private List<ResourceConsumer> getConsumers(DiscreteResource resource) { Versioned<ResourceConsumer> consumer = discreteConsumers.get(resource); if (consumer == null) { return ImmutableList.of(); } return ImmutableList.of(consumer.value()); } private List<ResourceConsumer> getConsumers(ContinuousResource resource) { Versioned<ContinuousResourceAllocation> allocations = continuousConsumers.get(resource.id()); if (allocations == null) { return ImmutableList.of(); } return allocations .value() .allocations() .stream() .filter(x -> x.resource().equals(resource)) .map(ResourceAllocation::consumer) .collect(GuavaCollectors.toImmutableList()); } @Override public boolean register(List<Resource> resources) { checkNotNull(resources); if (log.isTraceEnabled()) { resources.forEach(r -> log.trace("registering {}", r)); } TransactionContext tx = service.transactionContextBuilder().build(); tx.begin(); TransactionalMap<DiscreteResource, Set<Resource>> childTxMap = tx.getTransactionalMap(CHILD_MAP, SERIALIZER); Map<DiscreteResource, List<Resource>> resourceMap = resources .stream() .filter(x -> x.parent().isPresent()) .collect(Collectors.groupingBy(x -> x.parent().get())); for (Map.Entry<DiscreteResource, List<Resource>> entry : resourceMap.entrySet()) { Optional<DiscreteResource> child = lookup(childTxMap, entry.getKey()); if (!child.isPresent()) { return abortTransaction(tx); } if (!appendValues(childTxMap, entry.getKey(), entry.getValue())) { return abortTransaction(tx); } } boolean success = tx.commit(); if (success) { List<ResourceEvent> events = resources .stream() .filter(x -> x.parent().isPresent()) .map(x -> new ResourceEvent(RESOURCE_ADDED, x)) .collect(Collectors.toList()); notifyDelegate(events); } return success; } @Override public boolean unregister(List<Resource> resources) { checkNotNull(resources); TransactionContext tx = service.transactionContextBuilder().build(); tx.begin(); TransactionalMap<DiscreteResource, Set<Resource>> childTxMap = tx.getTransactionalMap(CHILD_MAP, SERIALIZER); TransactionalMap<DiscreteResource, ResourceConsumer> discreteConsumerTxMap = tx.getTransactionalMap(DISCRETE_CONSUMER_MAP, SERIALIZER); TransactionalMap<ResourceId, ContinuousResourceAllocation> continuousConsumerTxMap = tx.getTransactionalMap(CONTINUOUS_CONSUMER_MAP, SERIALIZER); // Extract Discrete instances from resources Map<DiscreteResource, List<Resource>> resourceMap = resources .stream() .filter(x -> x.parent().isPresent()) .collect(Collectors.groupingBy(x -> x.parent().get())); // even if one of the resources is allocated to a consumer, // all unregistrations are regarded as failure for (Map.Entry<DiscreteResource, List<Resource>> entry : resourceMap.entrySet()) { boolean allocated = entry .getValue() .stream() .anyMatch( x -> { if (x instanceof DiscreteResource) { return discreteConsumerTxMap.get((DiscreteResource) x) != null; } else if (x instanceof ContinuousResource) { ContinuousResourceAllocation allocations = continuousConsumerTxMap.get(x.id()); return allocations != null && !allocations.allocations().isEmpty(); } else { return false; } }); if (allocated) { return abortTransaction(tx); } if (!removeValues(childTxMap, entry.getKey(), entry.getValue())) { return abortTransaction(tx); } } boolean success = tx.commit(); if (success) { List<ResourceEvent> events = resources .stream() .filter(x -> x.parent().isPresent()) .map(x -> new ResourceEvent(RESOURCE_REMOVED, x)) .collect(Collectors.toList()); notifyDelegate(events); } return success; } @Override public boolean allocate(List<Resource> resources, ResourceConsumer consumer) { checkNotNull(resources); checkNotNull(consumer); TransactionContext tx = service.transactionContextBuilder().build(); tx.begin(); TransactionalMap<DiscreteResource, Set<Resource>> childTxMap = tx.getTransactionalMap(CHILD_MAP, SERIALIZER); TransactionalMap<DiscreteResource, ResourceConsumer> discreteConsumerTxMap = tx.getTransactionalMap(DISCRETE_CONSUMER_MAP, SERIALIZER); TransactionalMap<ResourceId, ContinuousResourceAllocation> continuousConsumerTxMap = tx.getTransactionalMap(CONTINUOUS_CONSUMER_MAP, SERIALIZER); for (Resource resource : resources) { if (resource instanceof DiscreteResource) { if (!lookup(childTxMap, resource).isPresent()) { return abortTransaction(tx); } ResourceConsumer oldValue = discreteConsumerTxMap.put((DiscreteResource) resource, consumer); if (oldValue != null) { return abortTransaction(tx); } } else if (resource instanceof ContinuousResource) { Optional<ContinuousResource> continuous = lookup(childTxMap, (ContinuousResource) resource); if (!continuous.isPresent()) { return abortTransaction(tx); } ContinuousResourceAllocation allocations = continuousConsumerTxMap.get(continuous.get().id()); if (!hasEnoughResource(continuous.get(), (ContinuousResource) resource, allocations)) { return abortTransaction(tx); } boolean success = appendValue( continuousConsumerTxMap, continuous.get(), new ResourceAllocation(continuous.get(), consumer)); if (!success) { return abortTransaction(tx); } } } return tx.commit(); } @Override public boolean release(List<Resource> resources, List<ResourceConsumer> consumers) { checkNotNull(resources); checkNotNull(consumers); checkArgument(resources.size() == consumers.size()); TransactionContext tx = service.transactionContextBuilder().build(); tx.begin(); TransactionalMap<DiscreteResource, ResourceConsumer> discreteConsumerTxMap = tx.getTransactionalMap(DISCRETE_CONSUMER_MAP, SERIALIZER); TransactionalMap<ResourceId, ContinuousResourceAllocation> continuousConsumerTxMap = tx.getTransactionalMap(CONTINUOUS_CONSUMER_MAP, SERIALIZER); Iterator<Resource> resourceIte = resources.iterator(); Iterator<ResourceConsumer> consumerIte = consumers.iterator(); while (resourceIte.hasNext() && consumerIte.hasNext()) { Resource resource = resourceIte.next(); ResourceConsumer consumer = consumerIte.next(); if (resource instanceof DiscreteResource) { // if this single release fails (because the resource is allocated to another consumer, // the whole release fails if (!discreteConsumerTxMap.remove((DiscreteResource) resource, consumer)) { return abortTransaction(tx); } } else if (resource instanceof ContinuousResource) { ContinuousResource continuous = (ContinuousResource) resource; ContinuousResourceAllocation allocation = continuousConsumerTxMap.get(continuous.id()); ImmutableList<ResourceAllocation> newAllocations = allocation .allocations() .stream() .filter( x -> !(x.consumer().equals(consumer) && ((ContinuousResource) x.resource()).value() == continuous.value())) .collect(GuavaCollectors.toImmutableList()); if (!continuousConsumerTxMap.replace( continuous.id(), allocation, new ContinuousResourceAllocation(allocation.original(), newAllocations))) { return abortTransaction(tx); } } } return tx.commit(); } @Override public boolean isAvailable(Resource resource) { checkNotNull(resource); checkArgument(resource instanceof DiscreteResource || resource instanceof ContinuousResource); // check if it's registered or not. Versioned<Set<Resource>> v = childMap.get(resource.parent().get()); if (v == null || !v.value().contains(resource)) { return false; } if (resource instanceof DiscreteResource) { // check if already consumed return getConsumers((DiscreteResource) resource).isEmpty(); } else { ContinuousResource requested = (ContinuousResource) resource; ContinuousResource registered = v.value() .stream() .filter(c -> c.id().equals(resource.id())) .findFirst() .map(c -> (ContinuousResource) c) .get(); if (registered.value() < requested.value()) { // Capacity < requested, can never satisfy return false; } // check if there's enough left return isAvailable(requested); } } private boolean isAvailable(ContinuousResource resource) { Versioned<ContinuousResourceAllocation> allocation = continuousConsumers.get(resource.id()); if (allocation == null) { // no allocation (=no consumer) full registered resources available return true; } return hasEnoughResource(allocation.value().original(), resource, allocation.value()); } @Override public Collection<Resource> getResources(ResourceConsumer consumer) { checkNotNull(consumer); // NOTE: getting all entries may become performance bottleneck // TODO: revisit for better backend data structure Stream<DiscreteResource> discreteStream = discreteConsumers .entrySet() .stream() .filter(x -> x.getValue().value().equals(consumer)) .map(Map.Entry::getKey); Stream<ContinuousResource> continuousStream = continuousConsumers .values() .stream() .flatMap( x -> x.value() .allocations() .stream() .map(y -> Maps.immutableEntry(x.value().original(), y))) .filter(x -> x.getValue().consumer().equals(consumer)) .map(x -> x.getKey()); return Stream.concat(discreteStream, continuousStream).collect(Collectors.toList()); } @Override public Collection<Resource> getChildResources(Resource parent) { checkNotNull(parent); if (!(parent instanceof DiscreteResource)) { // only Discrete resource can have child resource return ImmutableList.of(); } Versioned<Set<Resource>> children = childMap.get((DiscreteResource) parent); if (children == null) { return ImmutableList.of(); } return children.value(); } @Override public <T> Collection<Resource> getAllocatedResources(Resource parent, Class<T> cls) { checkNotNull(parent); checkNotNull(cls); checkArgument(parent instanceof DiscreteResource); Versioned<Set<Resource>> children = childMap.get((DiscreteResource) parent); if (children == null) { return ImmutableList.of(); } Stream<DiscreteResource> discrete = children .value() .stream() .filter(x -> x.last().getClass().equals(cls)) .filter(x -> x instanceof DiscreteResource) .map(x -> (DiscreteResource) x) .filter(discreteConsumers::containsKey); Stream<ContinuousResource> continuous = children .value() .stream() .filter(x -> x.id().equals(parent.id().child(cls))) .filter(x -> x instanceof ContinuousResource) .map(x -> (ContinuousResource) x) .filter(x -> continuousConsumers.containsKey(x.id())) .filter(x -> continuousConsumers.get(x.id()) != null) .filter(x -> !continuousConsumers.get(x.id()).value().allocations().isEmpty()); return Stream.concat(discrete, continuous).collect(Collectors.toList()); } /** * Abort the transaction. * * @param tx transaction context * @return always false */ private boolean abortTransaction(TransactionContext tx) { tx.abort(); return false; } // Appends the specified ResourceAllocation to the existing values stored in the map private boolean appendValue( TransactionalMap<ResourceId, ContinuousResourceAllocation> map, ContinuousResource original, ResourceAllocation value) { ContinuousResourceAllocation oldValue = map.putIfAbsent( original.id(), new ContinuousResourceAllocation(original, ImmutableList.of(value))); if (oldValue == null) { return true; } if (oldValue.allocations().contains(value)) { // don't write to map because all values are already stored return true; } ContinuousResourceAllocation newValue = new ContinuousResourceAllocation( original, ImmutableList.<ResourceAllocation>builder() .addAll(oldValue.allocations()) .add(value) .build()); return map.replace(original.id(), oldValue, newValue); } /** * Appends the values to the existing values associated with the specified key. If the map already * has all the given values, appending will not happen. * * @param map map holding multiple values for a key * @param key key specifying values * @param values values to be appended * @param <K> type of the key * @param <V> type of the element of the list * @return true if the operation succeeds, false otherwise. */ private <K, V> boolean appendValues(TransactionalMap<K, Set<V>> map, K key, List<V> values) { Set<V> oldValues = map.putIfAbsent(key, new LinkedHashSet<>(values)); if (oldValues == null) { return true; } if (oldValues.containsAll(values)) { // don't write to map because all values are already stored return true; } LinkedHashSet<V> newValues = new LinkedHashSet<>(oldValues); newValues.addAll(values); return map.replace(key, oldValues, newValues); } /** * Removes the values from the existing values associated with the specified key. If the map * doesn't contain the given values, removal will not happen. * * @param map map holding multiple values for a key * @param key key specifying values * @param values values to be removed * @param <K> type of the key * @param <V> type of the element of the list * @return true if the operation succeeds, false otherwise */ private <K, V> boolean removeValues( TransactionalMap<K, Set<V>> map, K key, List<? extends V> values) { Set<V> oldValues = map.putIfAbsent(key, new LinkedHashSet<>()); if (oldValues == null) { return true; } if (values.stream().allMatch(x -> !oldValues.contains(x))) { // don't write map because none of the values are stored return true; } LinkedHashSet<V> newValues = new LinkedHashSet<>(oldValues); newValues.removeAll(values); return map.replace(key, oldValues, newValues); } /** * Returns the resource which has the same key as the key of the specified resource in the list as * a value of the map. * * @param map map storing parent - child relationship of resources * @param resource resource to be checked for its key * @return the resource which is regarded as the same as the specified resource */ // Naive implementation, which traverses all elements in the list private <T extends Resource> Optional<T> lookup( TransactionalMap<DiscreteResource, Set<Resource>> map, T resource) { // if it is root, always returns itself if (!resource.parent().isPresent()) { return Optional.of(resource); } Set<Resource> values = map.get(resource.parent().get()); if (values == null) { return Optional.empty(); } @SuppressWarnings("unchecked") Optional<T> result = values.stream().filter(x -> x.id().equals(resource.id())).map(x -> (T) x).findFirst(); return result; } /** * Checks if there is enough resource volume to allocated the requested resource against the * specified resource. * * @param original original resource * @param request requested resource * @param allocation current allocation of the resource * @return true if there is enough resource volume. Otherwise, false. */ private boolean hasEnoughResource( ContinuousResource original, ContinuousResource request, ContinuousResourceAllocation allocation) { if (allocation == null) { return request.value() <= original.value(); } double allocated = allocation .allocations() .stream() .filter(x -> x.resource() instanceof ContinuousResource) .map(x -> (ContinuousResource) x.resource()) .mapToDouble(ContinuousResource::value) .sum(); double left = original.value() - allocated; return request.value() <= left; } // internal use only private static final class ContinuousResourceAllocation { private final ContinuousResource original; private final ImmutableList<ResourceAllocation> allocations; private ContinuousResourceAllocation( ContinuousResource original, ImmutableList<ResourceAllocation> allocations) { this.original = original; this.allocations = allocations; } private ContinuousResource original() { return original; } private ImmutableList<ResourceAllocation> allocations() { return allocations; } } }
/** Manages the pool of available labels to devices, links and tunnels. */ @Component(immediate = true) @Service public class DistributedPceStore implements PceStore { private static final String PATH_INFO_NULL = "Path Info cannot be null"; private static final String PCECC_TUNNEL_INFO_NULL = "PCECC Tunnel Info cannot be null"; private static final String TUNNEL_ID_NULL = "Tunnel Id cannot be null"; private final Logger log = LoggerFactory.getLogger(getClass()); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected StorageService storageService; // Mapping tunnel with device local info with tunnel consumer id private ConsistentMap<TunnelId, ResourceConsumer> tunnelInfoMap; // List of Failed path info private DistributedSet<PcePathInfo> failedPathSet; private static final Serializer SERIALIZER = Serializer.using( new KryoNamespace.Builder() .register(KryoNamespaces.API) .register(PcePathInfo.class) .register(CostConstraint.class) .register(CostConstraint.Type.class) .register(BandwidthConstraint.class) .register(SharedBandwidthConstraint.class) .register(CapabilityConstraint.class) .register(CapabilityConstraint.CapabilityType.class) .register(LspType.class) .build()); @Activate protected void activate() { tunnelInfoMap = storageService .<TunnelId, ResourceConsumer>consistentMapBuilder() .withName("onos-pce-tunnelinfomap") .withSerializer( Serializer.using( new KryoNamespace.Builder() .register(KryoNamespaces.API) .register(TunnelId.class, TunnelConsumerId.class) .build())) .build(); failedPathSet = storageService .<PcePathInfo>setBuilder() .withName("failed-path-info") .withSerializer(SERIALIZER) .build() .asDistributedSet(); log.info("Started"); } @Deactivate protected void deactivate() { log.info("Stopped"); } @Override public boolean existsTunnelInfo(TunnelId tunnelId) { checkNotNull(tunnelId, TUNNEL_ID_NULL); return tunnelInfoMap.containsKey(tunnelId); } @Override public boolean existsFailedPathInfo(PcePathInfo failedPathInfo) { checkNotNull(failedPathInfo, PATH_INFO_NULL); return failedPathSet.contains(failedPathInfo); } @Override public int getTunnelInfoCount() { return tunnelInfoMap.size(); } @Override public int getFailedPathInfoCount() { return failedPathSet.size(); } @Override public Map<TunnelId, ResourceConsumer> getTunnelInfos() { return tunnelInfoMap .entrySet() .stream() .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().value())); } @Override public Iterable<PcePathInfo> getFailedPathInfos() { return ImmutableSet.copyOf(failedPathSet); } @Override public ResourceConsumer getTunnelInfo(TunnelId tunnelId) { checkNotNull(tunnelId, TUNNEL_ID_NULL); return tunnelInfoMap.get(tunnelId) == null ? null : tunnelInfoMap.get(tunnelId).value(); } @Override public void addTunnelInfo(TunnelId tunnelId, ResourceConsumer tunnelConsumerId) { checkNotNull(tunnelId, TUNNEL_ID_NULL); checkNotNull(tunnelConsumerId, PCECC_TUNNEL_INFO_NULL); tunnelInfoMap.put(tunnelId, tunnelConsumerId); } @Override public void addFailedPathInfo(PcePathInfo failedPathInfo) { checkNotNull(failedPathInfo, PATH_INFO_NULL); failedPathSet.add(failedPathInfo); } @Override public boolean removeTunnelInfo(TunnelId tunnelId) { checkNotNull(tunnelId, TUNNEL_ID_NULL); if (tunnelInfoMap.remove(tunnelId) == null) { log.error("Tunnel info deletion for tunnel id {} has failed.", tunnelId.toString()); return false; } return true; } @Override public boolean removeFailedPathInfo(PcePathInfo failedPathInfo) { checkNotNull(failedPathInfo, PATH_INFO_NULL); if (!failedPathSet.remove(failedPathInfo)) { log.error("Failed path info {} deletion has failed.", failedPathInfo.toString()); return false; } return true; } }
@Override public CompletableFuture<Boolean> containsValue(V value) { checkNotNull(value, ERROR_NULL_VALUE); return database.containsValue(name, serializer.encode(value)); }
/** Implementation of ResourceStore using TransactionalMap. */ @Component(immediate = true, enabled = false) @Service @Beta public class ConsistentResourceStore implements ResourceStore { private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class); private static final String MAP_NAME = "onos-resource-consumers"; private static final Serializer SERIALIZER = Serializer.using(KryoNamespaces.API); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected StorageService service; private ConsistentMap<Resource<?, ?>, ResourceConsumer> consumers; @Activate public void activate() { consumers = service .<Resource<?, ?>, ResourceConsumer>consistentMapBuilder() .withName(MAP_NAME) .withSerializer(SERIALIZER) .build(); } @Override public <S, T> Optional<ResourceConsumer> getConsumer(Resource<S, T> resource) { checkNotNull(resource); Versioned<ResourceConsumer> consumer = consumers.get(resource); if (consumer == null) { return Optional.empty(); } return Optional.of(consumer.value()); } @Override public boolean allocate(List<? extends Resource<?, ?>> resources, ResourceConsumer consumer) { checkNotNull(resources); checkNotNull(consumer); TransactionContext tx = service.transactionContextBuilder().build(); tx.begin(); try { TransactionalMap<Resource<?, ?>, ResourceConsumer> txMap = tx.getTransactionalMap(MAP_NAME, SERIALIZER); for (Resource<?, ?> resource : resources) { ResourceConsumer existing = txMap.putIfAbsent(resource, consumer); // if the resource is already allocated to another consumer, the whole allocation fails if (existing != null) { tx.abort(); return false; } } tx.commit(); return true; } catch (Exception e) { log.error("Exception thrown, abort the transaction", e); tx.abort(); return false; } } @Override public boolean release( List<? extends Resource<?, ?>> resources, List<ResourceConsumer> consumers) { checkNotNull(resources); checkNotNull(consumers); checkArgument(resources.size() == consumers.size()); TransactionContext tx = service.transactionContextBuilder().build(); tx.begin(); try { TransactionalMap<Resource<?, ?>, ResourceConsumer> txMap = tx.getTransactionalMap(MAP_NAME, SERIALIZER); Iterator<? extends Resource<?, ?>> resourceIte = resources.iterator(); Iterator<ResourceConsumer> consumerIte = consumers.iterator(); while (resourceIte.hasNext() && consumerIte.hasNext()) { Resource<?, ?> resource = resourceIte.next(); ResourceConsumer consumer = consumerIte.next(); // if this single release fails (because the resource is allocated to another consumer, // the whole release fails if (!txMap.remove(resource, consumer)) { tx.abort(); return false; } } return true; } catch (TransactionException e) { log.error("Exception thrown, abort the transaction", e); tx.abort(); return false; } } @Override public Collection<Resource<?, ?>> getResources(ResourceConsumer consumer) { checkNotNull(consumer); // NOTE: getting all entries may become performance bottleneck // TODO: revisit for better backend data structure return consumers .entrySet() .stream() .filter(x -> x.getValue().value().equals(consumer)) .map(Map.Entry::getKey) .collect(Collectors.toList()); } @SuppressWarnings("unchecked") @Override public <S, T> Collection<Resource<S, T>> getAllocatedResources(S subject, Class<T> cls) { checkNotNull(subject); checkNotNull(cls); // NOTE: getting all entries may become performance bottleneck // TODO: revisit for better backend data structure return consumers .entrySet() .stream() .filter( x -> x.getKey().subject().equals(subject) && x.getKey().resource().getClass() == cls) // cast is ensured by the above filter method .map(x -> (Resource<S, T>) x.getKey()) .collect(Collectors.toList()); } }
protected K dK(String key) { return serializer.decode(HexString.fromHexString(key)); }
@Override public String load(K key) { return HexString.toHexString(serializer.encode(key)); }