blob: 7a91f06ecae9f58d1af8dd13bc5dda73e4a19ac3 [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;
24import org.onosproject.net.resource.Resource;
25import org.onosproject.net.resource.ResourceAllocation;
26import org.onosproject.net.resource.ResourceConsumer;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070027import org.onosproject.store.service.TransactionContext;
28import org.onosproject.store.service.TransactionalMap;
29import org.slf4j.Logger;
30import org.slf4j.LoggerFactory;
31
32import java.util.LinkedHashSet;
33import java.util.List;
34import java.util.Optional;
35import java.util.Set;
36import java.util.stream.Collectors;
37
38import 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
52 Optional<Resource> lookup(ContinuousResourceId id) {
53 if (!id.parent().isPresent()) {
54 return Optional.of(Resource.ROOT);
55 }
56
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))
64 .map(x -> (Resource) x)
65 .findFirst();
66 }
67
Sho SHIMIZUa2d99eb2016-05-06 14:52:55 -070068 boolean register(DiscreteResourceId key, List<ContinuousResource> values) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -070069 // short-circuit: receiving empty resource is regarded as success
70 if (values.isEmpty()) {
71 return true;
72 }
73
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070074 Set<ContinuousResource> requested = new LinkedHashSet<>(values);
75 Set<ContinuousResource> oldValues = childMap.putIfAbsent(key, requested);
76 if (oldValues == null) {
77 return true;
78 }
79
80 Set<ContinuousResource> addedValues = Sets.difference(requested, oldValues);
81 // no new value, then no-op
82 if (addedValues.isEmpty()) {
83 // don't write to map because all values are already stored
84 return true;
85 }
86
87 Set<ContinuousResourceId> addedIds = addedValues.stream()
88 .map(ContinuousResource::id)
89 .collect(Collectors.toSet());
90 // if the value is not found but the same ID is found
91 // (this happens only when being continuous resource)
92 if (oldValues.stream().anyMatch(x -> addedIds.contains(x.id()))) {
93 // no-op, but indicating failure (reject the request)
94 return false;
95 }
96 Set<ContinuousResource> newValues = new LinkedHashSet<>(oldValues);
97 newValues.addAll(addedValues);
98 return childMap.replace(key, oldValues, newValues);
99 }
100
Sho SHIMIZUa2d99eb2016-05-06 14:52:55 -0700101 boolean unregister(DiscreteResourceId key, List<ContinuousResource> values) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -0700102 // short-circuit: receiving empty resource is regarded as success
103 if (values.isEmpty()) {
104 return true;
105 }
106
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700107 Set<ContinuousResource> oldValues = childMap.putIfAbsent(key, new LinkedHashSet<>());
108 if (oldValues == null) {
109 log.trace("No-Op removing values. key {} did not exist", key);
110 return true;
111 }
112
113 if (values.stream().allMatch(x -> !oldValues.contains(x))) {
114 // don't write map because none of the values are stored
115 log.trace("No-Op removing values. key {} did not contain {}", key, values);
116 return true;
117 }
118
119 LinkedHashSet<ContinuousResource> newValues = new LinkedHashSet<>(oldValues);
120 newValues.removeAll(values);
121 return childMap.replace(key, oldValues, newValues);
122 }
123
124 boolean isAllocated(ContinuousResourceId id) {
125 ContinuousResourceAllocation allocations = consumers.get(id);
126 return allocations != null && !allocations.allocations().isEmpty();
127 }
128
129 boolean allocate(ResourceConsumer consumer, ContinuousResource request) {
130 // if the resource is not registered, then abort
131 Optional<Resource> lookedUp = lookup(request.id());
132 if (!lookedUp.isPresent()) {
133 return false;
134 }
135 // Down cast: this must be safe as ContinuousResource is associated with ContinuousResourceId
136 ContinuousResource original = (ContinuousResource) lookedUp.get();
137 ContinuousResourceAllocation allocations = consumers.get(request.id());
138 if (!hasEnoughResource(original, request, allocations)) {
139 return false;
140 }
141
142 boolean success = appendValue(original, new ResourceAllocation(request, consumer));
143 if (!success) {
144 return false;
145 }
146
147 return true;
148 }
149
150 // Appends the specified ResourceAllocation to the existing values stored in the map
151 // computational complexity: O(n) where n is the number of the elements in the associated allocation
152 private boolean appendValue(ContinuousResource original, ResourceAllocation value) {
153 ContinuousResourceAllocation oldValue = consumers.putIfAbsent(original.id(),
154 new ContinuousResourceAllocation(original, ImmutableList.of(value)));
155 if (oldValue == null) {
156 return true;
157 }
158
159 if (oldValue.allocations().contains(value)) {
160 // don't write to map because all values are already stored
161 return true;
162 }
163
164 ContinuousResourceAllocation newValue = new ContinuousResourceAllocation(original,
165 ImmutableList.<ResourceAllocation>builder()
166 .addAll(oldValue.allocations())
167 .add(value)
168 .build());
169 return consumers.replace(original.id(), oldValue, newValue);
170 }
171
172 boolean release(ContinuousResource resource, ResourceConsumer consumer) {
173 ContinuousResourceAllocation oldAllocation = consumers.get(resource.id());
174 ImmutableList<ResourceAllocation> newAllocations = oldAllocation.allocations().stream()
175 .filter(x -> !(x.consumer().equals(consumer) &&
176 ((ContinuousResource) x.resource()).value() == resource.value()))
177 .collect(GuavaCollectors.toImmutableList());
178
179 if (!consumers.replace(resource.id(), oldAllocation,
180 new ContinuousResourceAllocation(oldAllocation.original(), newAllocations))) {
181 return false;
182 }
183
184 return true;
185 }
186}