blob: 82dfe32f7b265a012aaf3ed85a57a4500722db2c [file] [log] [blame]
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -07001/*
2 * Copyright 2015 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.onosproject.store.newresource.impl;
17
18import com.google.common.annotations.Beta;
Sho SHIMIZUe7db6142015-11-04 11:24:22 -080019import com.google.common.collect.ImmutableList;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070025import org.onosproject.net.newresource.ResourceConsumer;
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080026import org.onosproject.net.newresource.ResourceEvent;
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -070027import org.onosproject.net.newresource.ResourcePath;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070028import org.onosproject.net.newresource.ResourceStore;
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080029import org.onosproject.net.newresource.ResourceStoreDelegate;
30import org.onosproject.store.AbstractStore;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070031import org.onosproject.store.serializers.KryoNamespaces;
32import org.onosproject.store.service.ConsistentMap;
33import org.onosproject.store.service.Serializer;
34import org.onosproject.store.service.StorageService;
35import org.onosproject.store.service.TransactionContext;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070036import org.onosproject.store.service.TransactionalMap;
37import org.onosproject.store.service.Versioned;
38import org.slf4j.Logger;
39import org.slf4j.LoggerFactory;
40
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070041import java.util.ArrayList;
42import java.util.Arrays;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070043import java.util.Collection;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070044import java.util.Collections;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070045import java.util.Iterator;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070046import java.util.LinkedHashSet;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070047import java.util.List;
48import java.util.Map;
49import java.util.Optional;
50import java.util.stream.Collectors;
51
52import static com.google.common.base.Preconditions.checkArgument;
53import static com.google.common.base.Preconditions.checkNotNull;
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080054import static org.onosproject.net.newresource.ResourceEvent.Type.*;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070055
56/**
57 * Implementation of ResourceStore using TransactionalMap.
58 */
Sho SHIMIZU9a2b8292015-10-28 13:00:16 -070059@Component(immediate = true)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070060@Service
61@Beta
Sho SHIMIZUfa62b472015-11-02 17:35:46 -080062public class ConsistentResourceStore extends AbstractStore<ResourceEvent, ResourceStoreDelegate>
63 implements ResourceStore {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070064 private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class);
65
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070066 private static final String CONSUMER_MAP = "onos-resource-consumers";
67 private static final String CHILD_MAP = "onos-resource-children";
68 private static final Serializer SERIALIZER = Serializer.using(
69 Arrays.asList(KryoNamespaces.BASIC, KryoNamespaces.API));
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070070
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected StorageService service;
73
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070074 private ConsistentMap<ResourcePath, ResourceConsumer> consumerMap;
75 private ConsistentMap<ResourcePath, List<ResourcePath>> childMap;
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070076
77 @Activate
78 public void activate() {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070079 consumerMap = service.<ResourcePath, ResourceConsumer>consistentMapBuilder()
80 .withName(CONSUMER_MAP)
81 .withSerializer(SERIALIZER)
82 .build();
83 childMap = service.<ResourcePath, List<ResourcePath>>consistentMapBuilder()
84 .withName(CHILD_MAP)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070085 .withSerializer(SERIALIZER)
86 .build();
Sho SHIMIZUe7db6142015-11-04 11:24:22 -080087
88 childMap.put(ResourcePath.ROOT, ImmutableList.of());
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070089 }
90
91 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -070092 public Optional<ResourceConsumer> getConsumer(ResourcePath resource) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070093 checkNotNull(resource);
94
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070095 Versioned<ResourceConsumer> consumer = consumerMap.get(resource);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070096 if (consumer == null) {
97 return Optional.empty();
98 }
99
100 return Optional.of(consumer.value());
101 }
102
103 @Override
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700104 public boolean register(List<ResourcePath> resources) {
105 checkNotNull(resources);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700106
107 TransactionContext tx = service.transactionContextBuilder().build();
108 tx.begin();
109
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800110 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
111 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700112
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800113 Map<ResourcePath, List<ResourcePath>> resourceMap = resources.stream()
114 .filter(x -> x.parent().isPresent())
115 .collect(Collectors.groupingBy(x -> x.parent().get()));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700116
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800117 for (Map.Entry<ResourcePath, List<ResourcePath>> entry: resourceMap.entrySet()) {
118 if (!isRegistered(childTxMap, entry.getKey())) {
119 return abortTransaction(tx);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700120 }
121
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800122 if (!appendValues(childTxMap, entry.getKey(), entry.getValue())) {
123 return abortTransaction(tx);
124 }
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700125 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800126
Sho SHIMIZUfa62b472015-11-02 17:35:46 -0800127 boolean success = tx.commit();
128 if (success) {
129 List<ResourceEvent> events = resources.stream()
130 .filter(x -> x.parent().isPresent())
131 .map(x -> new ResourceEvent(RESOURCE_ADDED, x))
132 .collect(Collectors.toList());
133 notifyDelegate(events);
134 }
135 return success;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700136 }
137
138 @Override
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700139 public boolean unregister(List<ResourcePath> resources) {
140 checkNotNull(resources);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700141
142 TransactionContext tx = service.transactionContextBuilder().build();
143 tx.begin();
144
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800145 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
146 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
147 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
148 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700149
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800150 Map<ResourcePath, List<ResourcePath>> resourceMap = resources.stream()
151 .filter(x -> x.parent().isPresent())
152 .collect(Collectors.groupingBy(x -> x.parent().get()));
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700153
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800154 // even if one of the resources is allocated to a consumer,
155 // all unregistrations are regarded as failure
156 for (Map.Entry<ResourcePath, List<ResourcePath>> entry: resourceMap.entrySet()) {
157 if (entry.getValue().stream().anyMatch(x -> consumerTxMap.get(x) != null)) {
158 return abortTransaction(tx);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700159 }
160
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800161 if (!removeValues(childTxMap, entry.getKey(), entry.getValue())) {
162 return abortTransaction(tx);
163 }
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700164 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800165
Sho SHIMIZUfa62b472015-11-02 17:35:46 -0800166 boolean success = tx.commit();
167 if (success) {
168 List<ResourceEvent> events = resources.stream()
169 .filter(x -> x.parent().isPresent())
170 .map(x -> new ResourceEvent(RESOURCE_REMOVED, x))
171 .collect(Collectors.toList());
172 notifyDelegate(events);
173 }
174 return success;
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700175 }
176
177 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700178 public boolean allocate(List<ResourcePath> resources, ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700179 checkNotNull(resources);
180 checkNotNull(consumer);
181
182 TransactionContext tx = service.transactionContextBuilder().build();
183 tx.begin();
184
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800185 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
186 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
187 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
188 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700189
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800190 for (ResourcePath resource: resources) {
191 if (!isRegistered(childTxMap, resource)) {
192 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700193 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700194
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800195 ResourceConsumer oldValue = consumerTxMap.put(resource, consumer);
196 if (oldValue != null) {
197 return abortTransaction(tx);
198 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700199 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800200
201 return tx.commit();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700202 }
203
204 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700205 public boolean release(List<ResourcePath> resources, List<ResourceConsumer> consumers) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700206 checkNotNull(resources);
207 checkNotNull(consumers);
208 checkArgument(resources.size() == consumers.size());
209
210 TransactionContext tx = service.transactionContextBuilder().build();
211 tx.begin();
212
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800213 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
214 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
215 Iterator<ResourcePath> resourceIte = resources.iterator();
216 Iterator<ResourceConsumer> consumerIte = consumers.iterator();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700217
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800218 while (resourceIte.hasNext() && consumerIte.hasNext()) {
219 ResourcePath resource = resourceIte.next();
220 ResourceConsumer consumer = consumerIte.next();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700221
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800222 // if this single release fails (because the resource is allocated to another consumer,
223 // the whole release fails
224 if (!consumerTxMap.remove(resource, consumer)) {
225 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700226 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700227 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800228
229 return tx.commit();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700230 }
231
232 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700233 public Collection<ResourcePath> getResources(ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700234 checkNotNull(consumer);
235
236 // NOTE: getting all entries may become performance bottleneck
237 // TODO: revisit for better backend data structure
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700238 return consumerMap.entrySet().stream()
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700239 .filter(x -> x.getValue().value().equals(consumer))
240 .map(Map.Entry::getKey)
241 .collect(Collectors.toList());
242 }
243
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700244 @Override
Sho SHIMIZUe7f4f3f2015-10-13 16:27:25 -0700245 public Collection<ResourcePath> getChildResources(ResourcePath parent) {
246 checkNotNull(parent);
247
248 Versioned<List<ResourcePath>> children = childMap.get(parent);
249 if (children == null) {
250 return Collections.emptyList();
251 }
252
253 return children.value();
254 }
255
256 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700257 public <T> Collection<ResourcePath> getAllocatedResources(ResourcePath parent, Class<T> cls) {
258 checkNotNull(parent);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700259 checkNotNull(cls);
260
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700261 Versioned<List<ResourcePath>> children = childMap.get(parent);
262 if (children == null) {
263 return Collections.emptyList();
264 }
265
266 return children.value().stream()
267 .filter(x -> x.lastComponent().getClass().equals(cls))
268 .filter(consumerMap::containsKey)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700269 .collect(Collectors.toList());
270 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700271
272 /**
273 * Abort the transaction.
274 *
275 * @param tx transaction context
276 * @return always false
277 */
278 private boolean abortTransaction(TransactionContext tx) {
279 tx.abort();
280 return false;
281 }
282
283 /**
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700284 * Appends the values to the existing values associated with the specified key.
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700285 * If the map already has all the given values, appending will not happen.
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700286 *
287 * @param map map holding multiple values for a key
288 * @param key key specifying values
289 * @param values values to be appended
290 * @param <K> type of the key
291 * @param <V> type of the element of the list
292 * @return true if the operation succeeds, false otherwise.
293 */
Sho SHIMIZU2a1b2332015-08-18 22:40:12 -0700294 private <K, V> boolean appendValues(TransactionalMap<K, List<V>> map, K key, List<V> values) {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700295 List<V> oldValues = map.get(key);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700296 if (oldValues == null) {
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700297 return map.replace(key, oldValues, new ArrayList<>(values));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700298 }
299
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700300 LinkedHashSet<V> oldSet = new LinkedHashSet<>(oldValues);
301 if (oldSet.containsAll(values)) {
302 // don't write to map because all values are already stored
303 return true;
304 }
305
306 oldSet.addAll(values);
307 return map.replace(key, oldValues, new ArrayList<>(oldSet));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700308 }
309
310 /**
Sho SHIMIZUba1f83b2015-10-14 08:11:20 -0700311 * Removes the values from the existing values associated with the specified key.
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700312 * If the map doesn't contain the given values, removal will not happen.
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700313 *
314 * @param map map holding multiple values for a key
315 * @param key key specifying values
316 * @param values values to be removed
317 * @param <K> type of the key
318 * @param <V> type of the element of the list
319 * @return true if the operation succeeds, false otherwise
320 */
321 private <K, V> boolean removeValues(TransactionalMap<K, List<V>> map, K key, List<V> values) {
322 List<V> oldValues = map.get(key);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700323 if (oldValues == null) {
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700324 return map.replace(key, oldValues, new ArrayList<>());
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700325 }
326
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700327 LinkedHashSet<V> oldSet = new LinkedHashSet<>(oldValues);
328 if (values.stream().allMatch(x -> !oldSet.contains(x))) {
329 // don't write map because none of the values are stored
330 return true;
331 }
332
333 oldSet.removeAll(values);
334 return map.replace(key, oldValues, new ArrayList<>(oldSet));
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700335 }
336
337 /**
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700338 * Checks if the specified resource is registered as a child of a resource in the map.
339 *
340 * @param map map storing parent - child relationship of resources
341 * @param resource resource to be checked
342 * @return true if the resource is registered, false otherwise.
343 */
344 private boolean isRegistered(TransactionalMap<ResourcePath, List<ResourcePath>> map, ResourcePath resource) {
345 // root is always regarded to be registered
Sho SHIMIZU01120782015-08-21 15:48:43 -0700346 if (resource.isRoot()) {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700347 return true;
348 }
349
350 List<ResourcePath> value = map.get(resource.parent().get());
351 return value != null && value.contains(resource);
352 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700353}