blob: c7a7d6e49f62e08c5f6a217a86dacbb7cd9522ac [file] [log] [blame]
Sho SHIMIZU22fb2832016-05-06 11:44:03 -07001/*
2 * Copyright 2016-present 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.resource.impl;
17
18import com.google.common.collect.ImmutableList;
19import com.google.common.collect.Sets;
20import org.onlab.util.GuavaCollectors;
21import org.onosproject.net.resource.ContinuousResource;
22import org.onosproject.net.resource.ContinuousResourceId;
23import org.onosproject.net.resource.DiscreteResourceId;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070024import org.onosproject.net.resource.ResourceAllocation;
25import org.onosproject.net.resource.ResourceConsumer;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070026import org.onosproject.store.service.TransactionContext;
27import org.onosproject.store.service.TransactionalMap;
28import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30
31import java.util.LinkedHashSet;
32import java.util.List;
33import java.util.Optional;
34import java.util.Set;
35import java.util.stream.Collectors;
36
Sho SHIMIZUa81141b2016-05-11 08:05:45 -070037import static com.google.common.base.Preconditions.checkArgument;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070038import static org.onosproject.store.resource.impl.ConsistentResourceStore.SERIALIZER;
39import static org.onosproject.store.resource.impl.ResourceStoreUtil.hasEnoughResource;
40
41class TransactionalContinuousResourceStore {
42 private final Logger log = LoggerFactory.getLogger(getClass());
43 private final TransactionalMap<DiscreteResourceId, Set<ContinuousResource>> childMap;
44 private final TransactionalMap<ContinuousResourceId, ContinuousResourceAllocation> consumers;
45
46 TransactionalContinuousResourceStore(TransactionContext tx) {
47 this.childMap = tx.getTransactionalMap(MapNames.CONTINUOUS_CHILD_MAP, SERIALIZER);
48 this.consumers = tx.getTransactionalMap(MapNames.CONTINUOUS_CONSUMER_MAP, SERIALIZER);
49 }
50
51 // iterate over the values in the set: O(n) operation
Sho SHIMIZUa81141b2016-05-11 08:05:45 -070052 Optional<ContinuousResource> lookup(ContinuousResourceId id) {
53 // continuous resource always has its parent
54 checkArgument(id.parent().isPresent());
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070055
56 Set<ContinuousResource> values = childMap.get(id.parent().get());
57 if (values == null) {
58 return Optional.empty();
59 }
60
61 return values.stream()
62 .filter(x -> x.id().equals(id))
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070063 .findFirst();
64 }
65
Sho SHIMIZUa2d99eb2016-05-06 14:52:55 -070066 boolean register(DiscreteResourceId key, List<ContinuousResource> values) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -070067 // short-circuit: receiving empty resource is regarded as success
68 if (values.isEmpty()) {
69 return true;
70 }
71
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070072 Set<ContinuousResource> requested = new LinkedHashSet<>(values);
73 Set<ContinuousResource> oldValues = childMap.putIfAbsent(key, requested);
74 if (oldValues == null) {
75 return true;
76 }
77
78 Set<ContinuousResource> addedValues = Sets.difference(requested, oldValues);
79 // no new value, then no-op
80 if (addedValues.isEmpty()) {
81 // don't write to map because all values are already stored
82 return true;
83 }
84
85 Set<ContinuousResourceId> addedIds = addedValues.stream()
86 .map(ContinuousResource::id)
87 .collect(Collectors.toSet());
88 // if the value is not found but the same ID is found
89 // (this happens only when being continuous resource)
90 if (oldValues.stream().anyMatch(x -> addedIds.contains(x.id()))) {
91 // no-op, but indicating failure (reject the request)
92 return false;
93 }
94 Set<ContinuousResource> newValues = new LinkedHashSet<>(oldValues);
95 newValues.addAll(addedValues);
96 return childMap.replace(key, oldValues, newValues);
97 }
98
Sho SHIMIZUa2d99eb2016-05-06 14:52:55 -070099 boolean unregister(DiscreteResourceId key, List<ContinuousResource> values) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -0700100 // short-circuit: receiving empty resource is regarded as success
101 if (values.isEmpty()) {
102 return true;
103 }
104
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700105 Set<ContinuousResource> oldValues = childMap.putIfAbsent(key, new LinkedHashSet<>());
106 if (oldValues == null) {
107 log.trace("No-Op removing values. key {} did not exist", key);
108 return true;
109 }
110
111 if (values.stream().allMatch(x -> !oldValues.contains(x))) {
112 // don't write map because none of the values are stored
113 log.trace("No-Op removing values. key {} did not contain {}", key, values);
114 return true;
115 }
116
117 LinkedHashSet<ContinuousResource> newValues = new LinkedHashSet<>(oldValues);
118 newValues.removeAll(values);
119 return childMap.replace(key, oldValues, newValues);
120 }
121
122 boolean isAllocated(ContinuousResourceId id) {
123 ContinuousResourceAllocation allocations = consumers.get(id);
124 return allocations != null && !allocations.allocations().isEmpty();
125 }
126
127 boolean allocate(ResourceConsumer consumer, ContinuousResource request) {
128 // if the resource is not registered, then abort
Sho SHIMIZUa81141b2016-05-11 08:05:45 -0700129 Optional<ContinuousResource> lookedUp = lookup(request.id());
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700130 if (!lookedUp.isPresent()) {
131 return false;
132 }
133 // Down cast: this must be safe as ContinuousResource is associated with ContinuousResourceId
Sho SHIMIZUa81141b2016-05-11 08:05:45 -0700134 ContinuousResource original = lookedUp.get();
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700135 ContinuousResourceAllocation allocations = consumers.get(request.id());
136 if (!hasEnoughResource(original, request, allocations)) {
137 return false;
138 }
139
140 boolean success = appendValue(original, new ResourceAllocation(request, consumer));
141 if (!success) {
142 return false;
143 }
144
145 return true;
146 }
147
148 // Appends the specified ResourceAllocation to the existing values stored in the map
149 // computational complexity: O(n) where n is the number of the elements in the associated allocation
150 private boolean appendValue(ContinuousResource original, ResourceAllocation value) {
151 ContinuousResourceAllocation oldValue = consumers.putIfAbsent(original.id(),
152 new ContinuousResourceAllocation(original, ImmutableList.of(value)));
153 if (oldValue == null) {
154 return true;
155 }
156
157 if (oldValue.allocations().contains(value)) {
158 // don't write to map because all values are already stored
159 return true;
160 }
161
162 ContinuousResourceAllocation newValue = new ContinuousResourceAllocation(original,
163 ImmutableList.<ResourceAllocation>builder()
164 .addAll(oldValue.allocations())
165 .add(value)
166 .build());
167 return consumers.replace(original.id(), oldValue, newValue);
168 }
169
170 boolean release(ContinuousResource resource, ResourceConsumer consumer) {
171 ContinuousResourceAllocation oldAllocation = consumers.get(resource.id());
172 ImmutableList<ResourceAllocation> newAllocations = oldAllocation.allocations().stream()
173 .filter(x -> !(x.consumer().equals(consumer) &&
174 ((ContinuousResource) x.resource()).value() == resource.value()))
175 .collect(GuavaCollectors.toImmutableList());
176
177 if (!consumers.replace(resource.id(), oldAllocation,
178 new ContinuousResourceAllocation(oldAllocation.original(), newAllocations))) {
179 return false;
180 }
181
182 return true;
183 }
184}