blob: bf15461f6215d226f34c1526390dc081be83a75b [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
Jordan Halterman6359c832017-03-29 16:53:21 -070038/**
39 * Transactional substore for continuous resources.
40 */
41class TransactionalContinuousResourceSubStore
42 implements TransactionalResourceSubStore<ContinuousResourceId, ContinuousResource> {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070043 private final Logger log = LoggerFactory.getLogger(getClass());
44 private final TransactionalMap<DiscreteResourceId, Set<ContinuousResource>> childMap;
45 private final TransactionalMap<ContinuousResourceId, ContinuousResourceAllocation> consumers;
46
Sho SHIMIZU7ecf5ea2016-05-13 15:28:59 -070047 TransactionalContinuousResourceSubStore(TransactionContext tx) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070048 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
Jordan Halterman6359c832017-03-29 16:53:21 -070053 @Override
54 public Optional<ContinuousResource> lookup(ContinuousResourceId id) {
Sho SHIMIZUa81141b2016-05-11 08:05:45 -070055 // continuous resource always has its parent
56 checkArgument(id.parent().isPresent());
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070057
58 Set<ContinuousResource> values = childMap.get(id.parent().get());
59 if (values == null) {
60 return Optional.empty();
61 }
62
63 return values.stream()
64 .filter(x -> x.id().equals(id))
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070065 .findFirst();
66 }
67
Jordan Halterman6359c832017-03-29 16:53:21 -070068 @Override
69 public boolean register(DiscreteResourceId parent, Set<ContinuousResource> resources) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -070070 // short-circuit: receiving empty resource is regarded as success
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -070071 if (resources.isEmpty()) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -070072 return true;
73 }
74
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -070075 Set<ContinuousResource> oldValues = childMap.putIfAbsent(parent, resources);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070076 if (oldValues == null) {
77 return true;
78 }
79
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -070080 Set<ContinuousResource> addedValues = Sets.difference(resources, oldValues);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070081 // 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);
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -070098 return childMap.replace(parent, oldValues, newValues);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -070099 }
100
Jordan Halterman6359c832017-03-29 16:53:21 -0700101 @Override
102 public boolean unregister(DiscreteResourceId parent, Set<ContinuousResource> resources) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -0700103 // short-circuit: receiving empty resource is regarded as success
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700104 if (resources.isEmpty()) {
Sho SHIMIZU1bf46b12016-05-06 15:42:29 -0700105 return true;
106 }
107
Sho SHIMIZUc7ffdfe2016-05-06 18:38:41 -0700108 // even if one of the resources is allocated to a consumer,
109 // all unregistrations are regarded as failure
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700110 boolean allocated = resources.stream().anyMatch(x -> isAllocated(x.id()));
Sho SHIMIZUc7ffdfe2016-05-06 18:38:41 -0700111 if (allocated) {
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700112 log.warn("Failed to unregister {}: allocation exists", parent);
Sho SHIMIZUc7ffdfe2016-05-06 18:38:41 -0700113 return false;
114 }
115
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700116 Set<ContinuousResource> oldValues = childMap.putIfAbsent(parent, new LinkedHashSet<>());
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700117 if (oldValues == null) {
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700118 log.trace("No-Op removing values. key {} did not exist", parent);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700119 return true;
120 }
121
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700122 if (resources.stream().allMatch(x -> !oldValues.contains(x))) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700123 // don't write map because none of the values are stored
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700124 log.trace("No-Op removing values. key {} did not contain {}", parent, resources);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700125 return true;
126 }
127
128 LinkedHashSet<ContinuousResource> newValues = new LinkedHashSet<>(oldValues);
Sho SHIMIZU0b4e9dd2016-06-28 17:42:05 -0700129 newValues.removeAll(resources);
130 return childMap.replace(parent, oldValues, newValues);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700131 }
132
Jordan Halterman6359c832017-03-29 16:53:21 -0700133 @Override
134 public boolean isAllocated(ContinuousResourceId id) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700135 ContinuousResourceAllocation allocations = consumers.get(id);
136 return allocations != null && !allocations.allocations().isEmpty();
137 }
138
Jordan Halterman6359c832017-03-29 16:53:21 -0700139 @Override
140 public boolean allocate(ResourceConsumerId consumerId, ContinuousResource request) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700141 // if the resource is not registered, then abort
Sho SHIMIZUa81141b2016-05-11 08:05:45 -0700142 Optional<ContinuousResource> lookedUp = lookup(request.id());
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700143 if (!lookedUp.isPresent()) {
144 return false;
145 }
146 // Down cast: this must be safe as ContinuousResource is associated with ContinuousResourceId
Sho SHIMIZUa81141b2016-05-11 08:05:45 -0700147 ContinuousResource original = lookedUp.get();
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700148 ContinuousResourceAllocation allocations = consumers.get(request.id());
Sho SHIMIZUdffe3b82016-05-13 15:44:57 -0700149 if (!Optional.ofNullable(allocations)
150 .orElse(ContinuousResourceAllocation.empty(original))
Sho SHIMIZU38bcfcf2016-05-13 15:48:54 -0700151 .hasEnoughResource(request)) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700152 return false;
153 }
154
Naoki Shiotabd1974c2016-04-29 18:44:17 -0700155 return appendValue(original, new ResourceAllocation(request, consumerId));
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700156 }
157
158 // Appends the specified ResourceAllocation to the existing values stored in the map
159 // computational complexity: O(n) where n is the number of the elements in the associated allocation
160 private boolean appendValue(ContinuousResource original, ResourceAllocation value) {
161 ContinuousResourceAllocation oldValue = consumers.putIfAbsent(original.id(),
162 new ContinuousResourceAllocation(original, ImmutableList.of(value)));
163 if (oldValue == null) {
164 return true;
165 }
166
Sho SHIMIZU3120d822016-05-17 09:28:10 -0700167 ContinuousResourceAllocation newValue = oldValue.allocate(value);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700168 return consumers.replace(original.id(), oldValue, newValue);
169 }
170
Jordan Halterman6359c832017-03-29 16:53:21 -0700171 @Override
172 public boolean release(ResourceConsumerId consumerId, ContinuousResource resource) {
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700173 ContinuousResourceAllocation oldAllocation = consumers.get(resource.id());
Sho SHIMIZUb2183962016-05-13 15:03:50 -0700174 ContinuousResourceAllocation newAllocation = oldAllocation.release(resource, consumerId);
Satish Kb05d70a2016-05-13 17:04:49 +0530175
Sho SHIMIZUb2183962016-05-13 15:03:50 -0700176 return consumers.replace(resource.id(), oldAllocation, newAllocation);
Sho SHIMIZU22fb2832016-05-06 11:44:03 -0700177 }
178}