@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()));
 }
Example #17
0
  @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));
 }
Example #22
0
/** 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));
 }