blob: 897b487d2c1e471ec183d992f711c1479e65b50d [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;
Satish Kb05d70a2016-05-13 17:04:49 +053036import java.util.stream.Stream;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070037
Sho SHIMIZUa81141b2016-05-11 08:05:45 -070038import static com.google.common.base.Preconditions.checkArgument;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070039import static org.onosproject.store.resource.impl.ConsistentResourceStore.SERIALIZER;
40import static org.onosproject.store.resource.impl.ResourceStoreUtil.hasEnoughResource;
41
42class TransactionalContinuousResourceStore {
43 private final Logger log = LoggerFactory.getLogger(getClass());
44 private final TransactionalMap<DiscreteResourceId, Set<ContinuousResource>> childMap;
45 private final TransactionalMap<ContinuousResourceId, ContinuousResourceAllocation> consumers;
46
47 TransactionalContinuousResourceStore(TransactionContext tx) {
48 this.childMap = tx.getTransactionalMap(MapNames.CONTINUOUS_CHILD_MAP, SERIALIZER);
49 this.consumers = tx.getTransactionalMap(MapNames.CONTINUOUS_CONSUMER_MAP, SERIALIZER);
50 }
51
52 // iterate over the values in the set: O(n) operation
Sho SHIMIZUa81141b2016-05-11 08:05:45 -070053 Optional<ContinuousResource> lookup(ContinuousResourceId id) {
54 // continuous resource always has its parent
55 checkArgument(id.parent().isPresent());
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070056
57 Set<ContinuousResource> values = childMap.get(id.parent().get());
58 if (values == null) {
59 return Optional.empty();
60 }
61
62 return values.stream()
63 .filter(x -> x.id().equals(id))
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070064 .findFirst();
65 }
66
Sho SHIMIZUa2d99eb2016-05-06 14:52:55 -070067 boolean register(DiscreteResourceId key, List<ContinuousResource> values) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -070068 // short-circuit: receiving empty resource is regarded as success
69 if (values.isEmpty()) {
70 return true;
71 }
72
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070073 Set<ContinuousResource> requested = new LinkedHashSet<>(values);
74 Set<ContinuousResource> oldValues = childMap.putIfAbsent(key, requested);
75 if (oldValues == null) {
76 return true;
77 }
78
79 Set<ContinuousResource> addedValues = Sets.difference(requested, oldValues);
80 // no new value, then no-op
81 if (addedValues.isEmpty()) {
82 // don't write to map because all values are already stored
83 return true;
84 }
85
86 Set<ContinuousResourceId> addedIds = addedValues.stream()
87 .map(ContinuousResource::id)
88 .collect(Collectors.toSet());
89 // if the value is not found but the same ID is found
90 // (this happens only when being continuous resource)
91 if (oldValues.stream().anyMatch(x -> addedIds.contains(x.id()))) {
92 // no-op, but indicating failure (reject the request)
93 return false;
94 }
95 Set<ContinuousResource> newValues = new LinkedHashSet<>(oldValues);
96 newValues.addAll(addedValues);
97 return childMap.replace(key, oldValues, newValues);
98 }
99
Sho SHIMIZUa2d99eb2016-05-06 14:52:55 -0700100 boolean unregister(DiscreteResourceId key, List<ContinuousResource> values) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -0700101 // short-circuit: receiving empty resource is regarded as success
102 if (values.isEmpty()) {
103 return true;
104 }
105
Sho SHIMIZUc7ffdfe2016-05-06 18:38:41 -0700106 // even if one of the resources is allocated to a consumer,
107 // all unregistrations are regarded as failure
108 boolean allocated = values.stream().anyMatch(x -> isAllocated(x.id()));
109 if (allocated) {
110 log.warn("Failed to unregister {}: allocation exists", key);
111 return false;
112 }
113
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700114 Set<ContinuousResource> oldValues = childMap.putIfAbsent(key, new LinkedHashSet<>());
115 if (oldValues == null) {
116 log.trace("No-Op removing values. key {} did not exist", key);
117 return true;
118 }
119
120 if (values.stream().allMatch(x -> !oldValues.contains(x))) {
121 // don't write map because none of the values are stored
122 log.trace("No-Op removing values. key {} did not contain {}", key, values);
123 return true;
124 }
125
126 LinkedHashSet<ContinuousResource> newValues = new LinkedHashSet<>(oldValues);
127 newValues.removeAll(values);
128 return childMap.replace(key, oldValues, newValues);
129 }
130
131 boolean isAllocated(ContinuousResourceId id) {
132 ContinuousResourceAllocation allocations = consumers.get(id);
133 return allocations != null && !allocations.allocations().isEmpty();
134 }
135
136 boolean allocate(ResourceConsumer consumer, ContinuousResource request) {
137 // if the resource is not registered, then abort
Sho SHIMIZUa81141b2016-05-11 08:05:45 -0700138 Optional<ContinuousResource> lookedUp = lookup(request.id());
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700139 if (!lookedUp.isPresent()) {
140 return false;
141 }
142 // Down cast: this must be safe as ContinuousResource is associated with ContinuousResourceId
Sho SHIMIZUa81141b2016-05-11 08:05:45 -0700143 ContinuousResource original = lookedUp.get();
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700144 ContinuousResourceAllocation allocations = consumers.get(request.id());
145 if (!hasEnoughResource(original, request, allocations)) {
146 return false;
147 }
148
149 boolean success = appendValue(original, new ResourceAllocation(request, consumer));
150 if (!success) {
151 return false;
152 }
153
154 return true;
155 }
156
157 // Appends the specified ResourceAllocation to the existing values stored in the map
158 // computational complexity: O(n) where n is the number of the elements in the associated allocation
159 private boolean appendValue(ContinuousResource original, ResourceAllocation value) {
160 ContinuousResourceAllocation oldValue = consumers.putIfAbsent(original.id(),
161 new ContinuousResourceAllocation(original, ImmutableList.of(value)));
162 if (oldValue == null) {
163 return true;
164 }
165
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700166 ContinuousResourceAllocation newValue = new ContinuousResourceAllocation(original,
167 ImmutableList.<ResourceAllocation>builder()
168 .addAll(oldValue.allocations())
169 .add(value)
170 .build());
171 return consumers.replace(original.id(), oldValue, newValue);
172 }
173
174 boolean release(ContinuousResource resource, ResourceConsumer consumer) {
175 ContinuousResourceAllocation oldAllocation = consumers.get(resource.id());
Satish Kb05d70a2016-05-13 17:04:49 +0530176
177 List<ResourceAllocation> nonMatchResources = oldAllocation.allocations().stream()
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700178 .filter(x -> !(x.consumer().equals(consumer) &&
179 ((ContinuousResource) x.resource()).value() == resource.value()))
Satish Kb05d70a2016-05-13 17:04:49 +0530180 .collect(Collectors.toList());
181
182 List<ResourceAllocation> matchResources = oldAllocation.allocations().stream()
183 .filter(x -> (x.consumer().equals(consumer) &&
184 ((ContinuousResource) x.resource()).value() == resource.value()))
185 .collect(Collectors.toList());
186
187 if (matchResources.size() > 1) {
188 matchResources.remove(0);
189 }
190
191 ImmutableList<ResourceAllocation> finalAllocations = Stream.concat(nonMatchResources.stream(),
192 matchResources.stream()).collect(GuavaCollectors.toImmutableList());
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700193
194 if (!consumers.replace(resource.id(), oldAllocation,
Satish Kb05d70a2016-05-13 17:04:49 +0530195 new ContinuousResourceAllocation(oldAllocation.original(), finalAllocations))) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700196 return false;
197 }
198
199 return true;
200 }
201}