ONOS-2446: Implement API to declare resource hierarchy
Remove API to define resource boundary
(ResourceAdminService.defineResourceBoundary) to integrate with API for
resource hierarchy
Change-Id: Iffa28dec16320122fe41f4f455000596fa266acb
diff --git a/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java b/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java
index 9c88c54..a20c2b7 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java
@@ -35,8 +35,12 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -54,18 +58,25 @@
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);
+ private static final String CONSUMER_MAP = "onos-resource-consumers";
+ private static final String CHILD_MAP = "onos-resource-children";
+ private static final Serializer SERIALIZER = Serializer.using(
+ Arrays.asList(KryoNamespaces.BASIC, KryoNamespaces.API));
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService service;
- private ConsistentMap<ResourcePath, ResourceConsumer> consumers;
+ private ConsistentMap<ResourcePath, ResourceConsumer> consumerMap;
+ private ConsistentMap<ResourcePath, List<ResourcePath>> childMap;
@Activate
public void activate() {
- consumers = service.<ResourcePath, ResourceConsumer>consistentMapBuilder()
- .withName(MAP_NAME)
+ consumerMap = service.<ResourcePath, ResourceConsumer>consistentMapBuilder()
+ .withName(CONSUMER_MAP)
+ .withSerializer(SERIALIZER)
+ .build();
+ childMap = service.<ResourcePath, List<ResourcePath>>consistentMapBuilder()
+ .withName(CHILD_MAP)
.withSerializer(SERIALIZER)
.build();
}
@@ -74,7 +85,7 @@
public Optional<ResourceConsumer> getConsumer(ResourcePath resource) {
checkNotNull(resource);
- Versioned<ResourceConsumer> consumer = consumers.get(resource);
+ Versioned<ResourceConsumer> consumer = consumerMap.get(resource);
if (consumer == null) {
return Optional.empty();
}
@@ -83,6 +94,33 @@
}
@Override
+ public boolean register(ResourcePath resource, List<ResourcePath> children) {
+ checkNotNull(resource);
+ checkNotNull(children);
+
+ TransactionContext tx = service.transactionContextBuilder().build();
+ tx.begin();
+
+ try {
+ TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
+ tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
+
+ if (!isRegistered(childTxMap, resource)) {
+ return abortTransaction(tx);
+ }
+
+ if (!appendValue(childTxMap, resource, children)) {
+ return abortTransaction(tx);
+ }
+
+ return commitTransaction(tx);
+ } catch (TransactionException e) {
+ log.error("Exception thrown, abort the transaction", e);
+ return abortTransaction(tx);
+ }
+ }
+
+ @Override
public boolean allocate(List<ResourcePath> resources, ResourceConsumer consumer) {
checkNotNull(resources);
checkNotNull(consumer);
@@ -91,11 +129,18 @@
tx.begin();
try {
- TransactionalMap<ResourcePath, ResourceConsumer> txMap = tx.getTransactionalMap(MAP_NAME, SERIALIZER);
+ TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
+ tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
+ TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
+ tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
+
for (ResourcePath resource: resources) {
- ResourceConsumer existing = txMap.putIfAbsent(resource, consumer);
- // if the resource is already allocated to another consumer, the whole allocation fails
- if (existing != null) {
+ if (!isRegistered(childTxMap, resource)) {
+ return abortTransaction(tx);
+ }
+
+ ResourceConsumer oldValue = consumerTxMap.put(resource, consumer);
+ if (oldValue != null) {
return abortTransaction(tx);
}
}
@@ -117,7 +162,8 @@
tx.begin();
try {
- TransactionalMap<ResourcePath, ResourceConsumer> txMap = tx.getTransactionalMap(MAP_NAME, SERIALIZER);
+ TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
+ tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
Iterator<ResourcePath> resourceIte = resources.iterator();
Iterator<ResourceConsumer> consumerIte = consumers.iterator();
@@ -127,7 +173,7 @@
// if this single release fails (because the resource is allocated to another consumer,
// the whole release fails
- if (!txMap.remove(resource, consumer)) {
+ if (!consumerTxMap.remove(resource, consumer)) {
return abortTransaction(tx);
}
}
@@ -145,7 +191,7 @@
// NOTE: getting all entries may become performance bottleneck
// TODO: revisit for better backend data structure
- return consumers.entrySet().stream()
+ return consumerMap.entrySet().stream()
.filter(x -> x.getValue().value().equals(consumer))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
@@ -156,12 +202,14 @@
checkNotNull(parent);
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().parent().isPresent() && x.getKey().parent().get().equals(parent))
- .filter(x -> x.getKey().lastComponent().getClass() == cls)
- .map(Map.Entry::getKey)
+ Versioned<List<ResourcePath>> children = childMap.get(parent);
+ if (children == null) {
+ return Collections.emptyList();
+ }
+
+ return children.value().stream()
+ .filter(x -> x.lastComponent().getClass().equals(cls))
+ .filter(consumerMap::containsKey)
.collect(Collectors.toList());
}
@@ -186,4 +234,45 @@
tx.commit();
return true;
}
+
+ /**
+ * Appends the values to the existing values associated with the specified key.
+ *
+ * @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 appendValue(TransactionalMap<K, List<V>> map, K key, List<V> values) {
+ List<V> oldValues = map.get(key);
+ List<V> newValues;
+ if (oldValues == null) {
+ newValues = new ArrayList<>(values);
+ } else {
+ LinkedHashSet<V> newSet = new LinkedHashSet<>(oldValues);
+ newSet.addAll(values);
+ newValues = new ArrayList<>(newSet);
+ }
+
+ return map.replace(key, oldValues, newValues);
+ }
+
+ /**
+ * Checks if the specified resource is registered as a child of a resource in the map.
+ *
+ * @param map map storing parent - child relationship of resources
+ * @param resource resource to be checked
+ * @return true if the resource is registered, false otherwise.
+ */
+ private boolean isRegistered(TransactionalMap<ResourcePath, List<ResourcePath>> map, ResourcePath resource) {
+ // root is always regarded to be registered
+ if (!resource.parent().isPresent()) {
+ return true;
+ }
+
+ List<ResourcePath> value = map.get(resource.parent().get());
+ return value != null && value.contains(resource);
+ }
}