blob: f15bc86a1658a0888d25b6001dbe5b5de71d1865 [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());
Madan Jampanic7f49f92015-12-10 11:35:06 -080089 log.info("Started");
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070090 }
91
92 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -070093 public Optional<ResourceConsumer> getConsumer(ResourcePath resource) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070094 checkNotNull(resource);
95
Sho SHIMIZUba41fc12015-08-12 15:43:22 -070096 Versioned<ResourceConsumer> consumer = consumerMap.get(resource);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -070097 if (consumer == null) {
98 return Optional.empty();
99 }
100
101 return Optional.of(consumer.value());
102 }
103
104 @Override
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700105 public boolean register(List<ResourcePath> resources) {
106 checkNotNull(resources);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700107
108 TransactionContext tx = service.transactionContextBuilder().build();
109 tx.begin();
110
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800111 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
112 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700113
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800114 Map<ResourcePath, List<ResourcePath>> resourceMap = resources.stream()
115 .filter(x -> x.parent().isPresent())
116 .collect(Collectors.groupingBy(x -> x.parent().get()));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700117
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800118 for (Map.Entry<ResourcePath, List<ResourcePath>> entry: resourceMap.entrySet()) {
119 if (!isRegistered(childTxMap, entry.getKey())) {
120 return abortTransaction(tx);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700121 }
122
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800123 if (!appendValues(childTxMap, entry.getKey(), entry.getValue())) {
124 return abortTransaction(tx);
125 }
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700126 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800127
Sho SHIMIZUfa62b472015-11-02 17:35:46 -0800128 boolean success = tx.commit();
129 if (success) {
130 List<ResourceEvent> events = resources.stream()
131 .filter(x -> x.parent().isPresent())
132 .map(x -> new ResourceEvent(RESOURCE_ADDED, x))
133 .collect(Collectors.toList());
134 notifyDelegate(events);
135 }
136 return success;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700137 }
138
139 @Override
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700140 public boolean unregister(List<ResourcePath> resources) {
141 checkNotNull(resources);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700142
143 TransactionContext tx = service.transactionContextBuilder().build();
144 tx.begin();
145
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800146 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
147 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
148 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
149 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700150
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800151 Map<ResourcePath, List<ResourcePath>> resourceMap = resources.stream()
152 .filter(x -> x.parent().isPresent())
153 .collect(Collectors.groupingBy(x -> x.parent().get()));
Sho SHIMIZU83e17a02015-08-20 14:07:05 -0700154
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800155 // even if one of the resources is allocated to a consumer,
156 // all unregistrations are regarded as failure
157 for (Map.Entry<ResourcePath, List<ResourcePath>> entry: resourceMap.entrySet()) {
158 if (entry.getValue().stream().anyMatch(x -> consumerTxMap.get(x) != null)) {
159 return abortTransaction(tx);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700160 }
161
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800162 if (!removeValues(childTxMap, entry.getKey(), entry.getValue())) {
163 return abortTransaction(tx);
164 }
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700165 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800166
Sho SHIMIZUfa62b472015-11-02 17:35:46 -0800167 boolean success = tx.commit();
168 if (success) {
169 List<ResourceEvent> events = resources.stream()
170 .filter(x -> x.parent().isPresent())
171 .map(x -> new ResourceEvent(RESOURCE_REMOVED, x))
172 .collect(Collectors.toList());
173 notifyDelegate(events);
174 }
175 return success;
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700176 }
177
178 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700179 public boolean allocate(List<ResourcePath> resources, ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700180 checkNotNull(resources);
181 checkNotNull(consumer);
182
183 TransactionContext tx = service.transactionContextBuilder().build();
184 tx.begin();
185
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800186 TransactionalMap<ResourcePath, List<ResourcePath>> childTxMap =
187 tx.getTransactionalMap(CHILD_MAP, SERIALIZER);
188 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
189 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700190
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800191 for (ResourcePath resource: resources) {
192 if (!isRegistered(childTxMap, resource)) {
193 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700194 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700195
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800196 ResourceConsumer oldValue = consumerTxMap.put(resource, consumer);
197 if (oldValue != null) {
198 return abortTransaction(tx);
199 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700200 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800201
202 return tx.commit();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700203 }
204
205 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700206 public boolean release(List<ResourcePath> resources, List<ResourceConsumer> consumers) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700207 checkNotNull(resources);
208 checkNotNull(consumers);
209 checkArgument(resources.size() == consumers.size());
210
211 TransactionContext tx = service.transactionContextBuilder().build();
212 tx.begin();
213
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800214 TransactionalMap<ResourcePath, ResourceConsumer> consumerTxMap =
215 tx.getTransactionalMap(CONSUMER_MAP, SERIALIZER);
216 Iterator<ResourcePath> resourceIte = resources.iterator();
217 Iterator<ResourceConsumer> consumerIte = consumers.iterator();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700218
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800219 while (resourceIte.hasNext() && consumerIte.hasNext()) {
220 ResourcePath resource = resourceIte.next();
221 ResourceConsumer consumer = consumerIte.next();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700222
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800223 // if this single release fails (because the resource is allocated to another consumer,
224 // the whole release fails
225 if (!consumerTxMap.remove(resource, consumer)) {
226 return abortTransaction(tx);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700227 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700228 }
Sho SHIMIZU1e0a34c2015-11-02 16:52:29 -0800229
230 return tx.commit();
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700231 }
232
233 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700234 public Collection<ResourcePath> getResources(ResourceConsumer consumer) {
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700235 checkNotNull(consumer);
236
237 // NOTE: getting all entries may become performance bottleneck
238 // TODO: revisit for better backend data structure
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700239 return consumerMap.entrySet().stream()
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700240 .filter(x -> x.getValue().value().equals(consumer))
241 .map(Map.Entry::getKey)
242 .collect(Collectors.toList());
243 }
244
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700245 @Override
Sho SHIMIZUe7f4f3f2015-10-13 16:27:25 -0700246 public Collection<ResourcePath> getChildResources(ResourcePath parent) {
247 checkNotNull(parent);
248
249 Versioned<List<ResourcePath>> children = childMap.get(parent);
250 if (children == null) {
251 return Collections.emptyList();
252 }
253
254 return children.value();
255 }
256
257 @Override
Sho SHIMIZU1f5e5912015-08-10 17:00:00 -0700258 public <T> Collection<ResourcePath> getAllocatedResources(ResourcePath parent, Class<T> cls) {
259 checkNotNull(parent);
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700260 checkNotNull(cls);
261
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700262 Versioned<List<ResourcePath>> children = childMap.get(parent);
263 if (children == null) {
264 return Collections.emptyList();
265 }
266
267 return children.value().stream()
Sho SHIMIZUc9546a32015-11-10 11:22:28 -0800268 .filter(x -> x.last().getClass().equals(cls))
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700269 .filter(consumerMap::containsKey)
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700270 .collect(Collectors.toList());
271 }
Sho SHIMIZUd29847f2015-08-13 09:10:59 -0700272
273 /**
274 * Abort the transaction.
275 *
276 * @param tx transaction context
277 * @return always false
278 */
279 private boolean abortTransaction(TransactionContext tx) {
280 tx.abort();
281 return false;
282 }
283
284 /**
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700285 * Appends the values to the existing values associated with the specified key.
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700286 * If the map already has all the given values, appending will not happen.
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700287 *
288 * @param map map holding multiple values for a key
289 * @param key key specifying values
290 * @param values values to be appended
291 * @param <K> type of the key
292 * @param <V> type of the element of the list
293 * @return true if the operation succeeds, false otherwise.
294 */
Sho SHIMIZU2a1b2332015-08-18 22:40:12 -0700295 private <K, V> boolean appendValues(TransactionalMap<K, List<V>> map, K key, List<V> values) {
Sho SHIMIZU73130f72015-11-09 17:10:08 -0800296 List<V> oldValues = map.putIfAbsent(key, new ArrayList<>(values));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700297 if (oldValues == null) {
Sho SHIMIZU93a74b32015-11-09 11:48:23 -0800298 return true;
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700299 }
300
Sho SHIMIZU4568c412015-08-21 16:39:07 -0700301 LinkedHashSet<V> oldSet = new LinkedHashSet<>(oldValues);
302 if (oldSet.containsAll(values)) {
303 // don't write to map because all values are already stored
304 return true;
305 }
306
307 oldSet.addAll(values);
308 return map.replace(key, oldValues, new ArrayList<>(oldSet));
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700309 }
310
311 /**
Sho SHIMIZUba1f83b2015-10-14 08:11:20 -0700312 * Removes the values from the existing values associated with the specified key.
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700313 * If the map doesn't contain the given values, removal will not happen.
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700314 *
315 * @param map map holding multiple values for a key
316 * @param key key specifying values
317 * @param values values to be removed
318 * @param <K> type of the key
319 * @param <V> type of the element of the list
320 * @return true if the operation succeeds, false otherwise
321 */
322 private <K, V> boolean removeValues(TransactionalMap<K, List<V>> map, K key, List<V> values) {
323 List<V> oldValues = map.get(key);
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700324 if (oldValues == null) {
Sho SHIMIZU93a74b32015-11-09 11:48:23 -0800325 map.put(key, new ArrayList<>());
326 return true;
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700327 }
328
Sho SHIMIZU5618ee52015-08-21 17:19:44 -0700329 LinkedHashSet<V> oldSet = new LinkedHashSet<>(oldValues);
330 if (values.stream().allMatch(x -> !oldSet.contains(x))) {
331 // don't write map because none of the values are stored
332 return true;
333 }
334
335 oldSet.removeAll(values);
336 return map.replace(key, oldValues, new ArrayList<>(oldSet));
Sho SHIMIZU2d8a13a2015-08-18 22:37:41 -0700337 }
338
339 /**
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700340 * Checks if the specified resource is registered as a child of a resource in the map.
341 *
342 * @param map map storing parent - child relationship of resources
343 * @param resource resource to be checked
344 * @return true if the resource is registered, false otherwise.
345 */
346 private boolean isRegistered(TransactionalMap<ResourcePath, List<ResourcePath>> map, ResourcePath resource) {
347 // root is always regarded to be registered
Sho SHIMIZUc9546a32015-11-10 11:22:28 -0800348 if (!resource.parent().isPresent()) {
Sho SHIMIZUba41fc12015-08-12 15:43:22 -0700349 return true;
350 }
351
352 List<ResourcePath> value = map.get(resource.parent().get());
353 return value != null && value.contains(resource);
354 }
Sho SHIMIZU78ee25c2015-07-16 15:54:14 -0700355}