blob: a87560899d8b89084ab8a346bcc654a320223162 [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;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070020import org.onosproject.net.resource.ContinuousResource;
21import org.onosproject.net.resource.ContinuousResourceId;
22import org.onosproject.net.resource.DiscreteResourceId;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070023import org.onosproject.net.resource.ResourceAllocation;
Naoki Shiotabd1974c2016-04-29 18:44:17 -070024import org.onosproject.net.resource.ResourceConsumerId;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070025import org.onosproject.store.service.TransactionContext;
26import org.onosproject.store.service.TransactionalMap;
27import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
29
30import java.util.LinkedHashSet;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070031import java.util.Optional;
32import java.util.Set;
33import java.util.stream.Collectors;
34
Sho SHIMIZUa81141b2016-05-11 08:05:45 -070035import static com.google.common.base.Preconditions.checkArgument;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070036import static org.onosproject.store.resource.impl.ConsistentResourceStore.SERIALIZER;
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070037
Sho SHIMIZU7ecf5ea2016-05-13 15:28:59 -070038class TransactionalContinuousResourceSubStore {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070039 private final Logger log = LoggerFactory.getLogger(getClass());
40 private final TransactionalMap<DiscreteResourceId, Set<ContinuousResource>> childMap;
41 private final TransactionalMap<ContinuousResourceId, ContinuousResourceAllocation> consumers;
42
Sho SHIMIZU7ecf5ea2016-05-13 15:28:59 -070043 TransactionalContinuousResourceSubStore(TransactionContext tx) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070044 this.childMap = tx.getTransactionalMap(MapNames.CONTINUOUS_CHILD_MAP, SERIALIZER);
45 this.consumers = tx.getTransactionalMap(MapNames.CONTINUOUS_CONSUMER_MAP, SERIALIZER);
46 }
47
48 // iterate over the values in the set: O(n) operation
Sho SHIMIZUa81141b2016-05-11 08:05:45 -070049 Optional<ContinuousResource> lookup(ContinuousResourceId id) {
50 // continuous resource always has its parent
51 checkArgument(id.parent().isPresent());
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070052
53 Set<ContinuousResource> values = childMap.get(id.parent().get());
54 if (values == null) {
55 return Optional.empty();
56 }
57
58 return values.stream()
59 .filter(x -> x.id().equals(id))
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070060 .findFirst();
61 }
62
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -070063 boolean register(DiscreteResourceId parent, Set<ContinuousResource> resources) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -070064 // short-circuit: receiving empty resource is regarded as success
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -070065 if (resources.isEmpty()) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -070066 return true;
67 }
68
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -070069 Set<ContinuousResource> oldValues = childMap.putIfAbsent(parent, resources);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070070 if (oldValues == null) {
71 return true;
72 }
73
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -070074 Set<ContinuousResource> addedValues = Sets.difference(resources, oldValues);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070075 // no new value, then no-op
76 if (addedValues.isEmpty()) {
77 // don't write to map because all values are already stored
78 return true;
79 }
80
81 Set<ContinuousResourceId> addedIds = addedValues.stream()
82 .map(ContinuousResource::id)
83 .collect(Collectors.toSet());
84 // if the value is not found but the same ID is found
85 // (this happens only when being continuous resource)
86 if (oldValues.stream().anyMatch(x -> addedIds.contains(x.id()))) {
87 // no-op, but indicating failure (reject the request)
88 return false;
89 }
90 Set<ContinuousResource> newValues = new LinkedHashSet<>(oldValues);
91 newValues.addAll(addedValues);
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -070092 return childMap.replace(parent, oldValues, newValues);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070093 }
94
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -070095 boolean unregister(DiscreteResourceId parent, Set<ContinuousResource> resources) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -070096 // short-circuit: receiving empty resource is regarded as success
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -070097 if (resources.isEmpty()) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -070098 return true;
99 }
100
Sho SHIMIZUc7ffdfe2016-05-06 18:38:41 -0700101 // even if one of the resources is allocated to a consumer,
102 // all unregistrations are regarded as failure
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700103 boolean allocated = resources.stream().anyMatch(x -> isAllocated(x.id()));
Sho SHIMIZUc7ffdfe2016-05-06 18:38:41 -0700104 if (allocated) {
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700105 log.warn("Failed to unregister {}: allocation exists", parent);
Sho SHIMIZUc7ffdfe2016-05-06 18:38:41 -0700106 return false;
107 }
108
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700109 Set<ContinuousResource> oldValues = childMap.putIfAbsent(parent, new LinkedHashSet<>());
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700110 if (oldValues == null) {
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700111 log.trace("No-Op removing values. key {} did not exist", parent);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700112 return true;
113 }
114
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700115 if (resources.stream().allMatch(x -> !oldValues.contains(x))) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700116 // don't write map because none of the values are stored
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700117 log.trace("No-Op removing values. key {} did not contain {}", parent, resources);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700118 return true;
119 }
120
121 LinkedHashSet<ContinuousResource> newValues = new LinkedHashSet<>(oldValues);
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700122 newValues.removeAll(resources);
123 return childMap.replace(parent, oldValues, newValues);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700124 }
125
Sho SHIMIZUd82dc5b2016-05-13 13:50:29 -0700126 private boolean isAllocated(ContinuousResourceId id) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700127 ContinuousResourceAllocation allocations = consumers.get(id);
128 return allocations != null && !allocations.allocations().isEmpty();
129 }
130
Naoki Shiotabd1974c2016-04-29 18:44:17 -0700131 boolean allocate(ResourceConsumerId consumerId, ContinuousResource request) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700132 // if the resource is not registered, then abort
Sho SHIMIZUa81141b2016-05-11 08:05:45 -0700133 Optional<ContinuousResource> lookedUp = lookup(request.id());
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700134 if (!lookedUp.isPresent()) {
135 return false;
136 }
137 // Down cast: this must be safe as ContinuousResource is associated with ContinuousResourceId
Sho SHIMIZUa81141b2016-05-11 08:05:45 -0700138 ContinuousResource original = lookedUp.get();
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700139 ContinuousResourceAllocation allocations = consumers.get(request.id());
Sho SHIMIZUdffe3b82016-05-13 15:44:57 -0700140 if (!Optional.ofNullable(allocations)
141 .orElse(ContinuousResourceAllocation.empty(original))
Sho SHIMIZU38bcfcf2016-05-13 15:48:54 -0700142 .hasEnoughResource(request)) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700143 return false;
144 }
145
Naoki Shiotabd1974c2016-04-29 18:44:17 -0700146 return appendValue(original, new ResourceAllocation(request, consumerId));
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700147 }
148
149 // Appends the specified ResourceAllocation to the existing values stored in the map
150 // computational complexity: O(n) where n is the number of the elements in the associated allocation
151 private boolean appendValue(ContinuousResource original, ResourceAllocation value) {
152 ContinuousResourceAllocation oldValue = consumers.putIfAbsent(original.id(),
153 new ContinuousResourceAllocation(original, ImmutableList.of(value)));
154 if (oldValue == null) {
155 return true;
156 }
157
Sho SHIMIZU3120d822016-05-17 09:28:10 -0700158 ContinuousResourceAllocation newValue = oldValue.allocate(value);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700159 return consumers.replace(original.id(), oldValue, newValue);
160 }
161
Naoki Shiotabd1974c2016-04-29 18:44:17 -0700162 boolean release(ContinuousResource resource, ResourceConsumerId consumerId) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700163 ContinuousResourceAllocation oldAllocation = consumers.get(resource.id());
Sho SHIMIZUb2183962016-05-13 15:03:50 -0700164 ContinuousResourceAllocation newAllocation = oldAllocation.release(resource, consumerId);
Satish Kb05d70a2016-05-13 17:04:49 +0530165
Sho SHIMIZUb2183962016-05-13 15:03:50 -0700166 return consumers.replace(resource.id(), oldAllocation, newAllocation);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700167 }
168}