Sho SHIMIZU
Committed by Gerrit Code Review

ONOS-2387: Initial implement ResourceService

The current limitation are
- Lack of unit tests for ResourceManager and ConsistentResourceStore
- No means to declare the resource space (range) of a resource type
- Not taking care of resource hierarchy (dependency)
- Lack of automatic resource registration mechanism

Change-Id: Ib2d6734de1e498827d6cc11b9d3e224b157a27aa
1 +package org.onosproject.net.newresource;
2 +
3 +import com.google.common.annotations.Beta;
4 +
5 +import java.util.Collection;
6 +import java.util.List;
7 +import java.util.Optional;
8 +
9 +/**
10 + * Service for storing resource and consumer information.
11 + */
12 +@Beta
13 +public interface ResourceStore {
14 + /**
15 + * Allocates the specified resources to the specified consumer in transactional way.
16 + * The state after completion of this method is all the resources are allocated to the consumer,
17 + * or no resource is allocated to the consumer. The whole allocation fails when any one of
18 + * the resource can't be allocated.
19 + *
20 + * @param resources resources to be allocated
21 + * @param consumer resource consumer which the resources are allocated to
22 + * @return true if the allocation succeeds, false otherwise.
23 + */
24 + boolean allocate(List<? extends Resource<?, ?>> resources, ResourceConsumer consumer);
25 +
26 + /**
27 + * Releases the specified resources allocated to the specified corresponding consumers
28 + * in transactional way. The state after completion of this method is all the resources
29 + * are released from the consumer, or no resource is released. The whole release fails
30 + * when any one of the resource can't be released. The size of the list of resources and
31 + * that of consumers must be equal. The resource and consumer with the same position from
32 + * the head of each list correspond to each other.
33 + *
34 + * @param resources resources to be released
35 + * @param consumers resource consumers to whom the resource allocated to
36 + * @return true if succeeds, otherwise false
37 + */
38 + boolean release(List<? extends Resource<?, ?>> resources, List<ResourceConsumer> consumers);
39 +
40 + /**
41 + * Returns the resource consumer to whom the specified resource is allocated.
42 + *
43 + * @param resource resource whose allocated consumer to be returned
44 + * @param <S> type of subject of the resource
45 + * @param <T> type of resource
46 + * @return resource consumer who are allocated the resource
47 + */
48 + <S, T> Optional<ResourceConsumer> getConsumer(Resource<S, T> resource);
49 +
50 + /**
51 + * Returns a collection of the resources allocated to the specified consumer.
52 + *
53 + * @param consumer resource consumer whose allocated resource are searched for
54 + * @return a collection of the resources allocated to the specified consumer
55 + */
56 + Collection<Resource<?, ?>> getResources(ResourceConsumer consumer);
57 +
58 + /**
59 + * Returns a collection of the resources which belongs to the specified subject and
60 + * whose type is the specified class.
61 + *
62 + * @param subject subject of the resources to be returned
63 + * @param cls class instance of the resources
64 + * @param <S> type of the subject
65 + * @param <T> type of the resource
66 + * @return a collection of the resources which belongs to the specified subject and
67 + * whose type is the specified class.
68 + */
69 + <S, T> Collection<Resource<S, T>> getAllocatedResources(S subject, Class<T> cls);
70 +}
1 +/*
2 + * Copyright 2015 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 + */
16 +package org.onosproject.net.newresource.impl;
17 +
18 +import com.google.common.annotations.Beta;
19 +import com.google.common.collect.ImmutableList;
20 +import org.apache.felix.scr.annotations.Component;
21 +import org.apache.felix.scr.annotations.Reference;
22 +import org.apache.felix.scr.annotations.ReferenceCardinality;
23 +import org.apache.felix.scr.annotations.Service;
24 +import org.onosproject.net.newresource.DefaultResource;
25 +import org.onosproject.net.newresource.DefaultResourceAllocation;
26 +import org.onosproject.net.newresource.Resource;
27 +import org.onosproject.net.newresource.ResourceAllocation;
28 +import org.onosproject.net.newresource.ResourceConsumer;
29 +import org.onosproject.net.newresource.ResourceService;
30 +import org.onosproject.net.newresource.ResourceStore;
31 +
32 +import java.util.ArrayList;
33 +import java.util.Arrays;
34 +import java.util.Collection;
35 +import java.util.List;
36 +import java.util.Optional;
37 +import java.util.stream.Collectors;
38 +
39 +import static com.google.common.base.Preconditions.checkNotNull;
40 +
41 +/**
42 + * An implementation of ResourceService.
43 + */
44 +@Component(immediate = true, enabled = false)
45 +@Service
46 +@Beta
47 +public final class ResourceManager implements ResourceService {
48 +
49 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
50 + protected ResourceStore store;
51 +
52 + @SuppressWarnings("unchecked")
53 + @Override
54 + public <S, T> Optional<ResourceAllocation<S, T>> allocate(ResourceConsumer consumer, Resource<S, T> resource) {
55 + checkNotNull(consumer);
56 + checkNotNull(resource);
57 +
58 + List<ResourceAllocation<?, ?>> allocations = allocate(consumer, ImmutableList.of(resource));
59 + if (allocations.isEmpty()) {
60 + return Optional.empty();
61 + }
62 +
63 + assert allocations.size() == 1;
64 +
65 + ResourceAllocation<?, ?> allocation = allocations.get(0);
66 +
67 + assert allocation.subject().getClass() == resource.subject().getClass();
68 + assert allocation.resource().getClass() == resource.resource().getClass();
69 +
70 + // cast is ensured by the assertions above
71 + return Optional.of((ResourceAllocation<S, T>) allocation);
72 + }
73 +
74 + @Override
75 + public List<ResourceAllocation<?, ?>> allocate(ResourceConsumer consumer,
76 + List<? extends Resource<?, ?>> resources) {
77 + checkNotNull(consumer);
78 + checkNotNull(resources);
79 +
80 + if (resources.stream().anyMatch(x -> !isValid(x))) {
81 + return ImmutableList.of();
82 + }
83 +
84 + // TODO: implement support of resource hierarchy
85 + // allocation for a particular resource implies allocations for all of the sub-resources need to be done
86 +
87 + boolean success = store.allocate(resources, consumer);
88 + if (!success) {
89 + return ImmutableList.of();
90 + }
91 +
92 + return resources.stream()
93 + .map(x -> new DefaultResourceAllocation<>(x.subject(), x.resource(), consumer))
94 + .collect(Collectors.toList());
95 + }
96 +
97 + @Override
98 + public List<ResourceAllocation<?, ?>> allocate(ResourceConsumer consumer, Resource<?, ?>... resources) {
99 + checkNotNull(consumer);
100 + checkNotNull(resources);
101 +
102 + return allocate(consumer, Arrays.asList(resources));
103 + }
104 +
105 + @Override
106 + public <S, T> boolean release(ResourceAllocation<S, T> allocation) {
107 + checkNotNull(allocation);
108 +
109 + return release(ImmutableList.of(allocation));
110 + }
111 +
112 + @Override
113 + public boolean release(List<? extends ResourceAllocation<?, ?>> allocations) {
114 + checkNotNull(allocations);
115 +
116 + List<DefaultResource<?, ?>> resources = allocations.stream()
117 + .map(x -> new DefaultResource<>(x.subject(), x.resource()))
118 + .collect(Collectors.toList());
119 + List<ResourceConsumer> consumers = allocations.stream()
120 + .map(ResourceAllocation::consumer)
121 + .collect(Collectors.toList());
122 +
123 + return store.release(resources, consumers);
124 + }
125 +
126 + @Override
127 + public boolean release(ResourceAllocation<?, ?>... allocations) {
128 + checkNotNull(allocations);
129 +
130 + return release(ImmutableList.copyOf(allocations));
131 + }
132 +
133 + @Override
134 + public boolean release(ResourceConsumer consumer) {
135 + checkNotNull(consumer);
136 +
137 + Collection<ResourceAllocation<?, ?>> allocations = getResourceAllocations(consumer);
138 + return release(ImmutableList.copyOf(allocations));
139 + }
140 +
141 + @Override
142 + public <S, T> Collection<ResourceAllocation<S, T>> getResourceAllocations(S subject, Class<T> cls) {
143 + checkNotNull(subject);
144 + checkNotNull(cls);
145 +
146 + Collection<Resource<S, T>> resources = store.getAllocatedResources(subject, cls);
147 + List<ResourceAllocation<S, T>> allocations = new ArrayList<>(resources.size());
148 + for (Resource<S, T> resource: resources) {
149 + // We access store twice in this method, then the store may be updated by others
150 + Optional<ResourceConsumer> consumer = store.getConsumer(resource);
151 + if (consumer.isPresent()) {
152 + allocations.add(
153 + new DefaultResourceAllocation<>(resource.subject(), resource.resource(), consumer.get()));
154 + }
155 + }
156 +
157 + return allocations;
158 + }
159 +
160 + @Override
161 + public Collection<ResourceAllocation<?, ?>> getResourceAllocations(ResourceConsumer consumer) {
162 + checkNotNull(consumer);
163 +
164 + Collection<Resource<?, ?>> resources = store.getResources(consumer);
165 + return resources.stream()
166 + .map(x -> new DefaultResourceAllocation<>(x.subject(), x.resource(), consumer))
167 + .collect(Collectors.toList());
168 + }
169 +
170 + @Override
171 + public <S, T> boolean isAvailable(Resource<S, T> resource) {
172 + checkNotNull(resource);
173 +
174 + Optional<ResourceConsumer> consumer = store.getConsumer(resource);
175 + return !consumer.isPresent();
176 + }
177 +
178 + /**
179 + * Returns if the specified resource is in the resource range.
180 + * E.g. VLAN ID against a link must be within 12 bit address space.
181 + *
182 + * @param resource resource to be checked if it is within the resource range
183 + * @param <S> type of the subject
184 + * @param <T> type of the resource
185 + * @return true if the resource within the range, false otherwise
186 + */
187 + private <S, T> boolean isValid(Resource<S, T> resource) {
188 + // TODO: implement
189 + return true;
190 + }
191 +}
1 +/*
2 + * Copyright 2015 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 + */
16 +package org.onosproject.store.newresource.impl;
17 +
18 +import com.google.common.annotations.Beta;
19 +import org.apache.felix.scr.annotations.Activate;
20 +import org.apache.felix.scr.annotations.Component;
21 +import org.apache.felix.scr.annotations.Reference;
22 +import org.apache.felix.scr.annotations.ReferenceCardinality;
23 +import org.apache.felix.scr.annotations.Service;
24 +import org.onosproject.net.newresource.Resource;
25 +import org.onosproject.net.newresource.ResourceConsumer;
26 +import org.onosproject.net.newresource.ResourceStore;
27 +import org.onosproject.store.serializers.KryoNamespaces;
28 +import org.onosproject.store.service.ConsistentMap;
29 +import org.onosproject.store.service.Serializer;
30 +import org.onosproject.store.service.StorageService;
31 +import org.onosproject.store.service.TransactionContext;
32 +import org.onosproject.store.service.TransactionException;
33 +import org.onosproject.store.service.TransactionalMap;
34 +import org.onosproject.store.service.Versioned;
35 +import org.slf4j.Logger;
36 +import org.slf4j.LoggerFactory;
37 +
38 +import java.util.Collection;
39 +import java.util.Iterator;
40 +import java.util.List;
41 +import java.util.Map;
42 +import java.util.Optional;
43 +import java.util.stream.Collectors;
44 +
45 +import static com.google.common.base.Preconditions.checkArgument;
46 +import static com.google.common.base.Preconditions.checkNotNull;
47 +
48 +/**
49 + * Implementation of ResourceStore using TransactionalMap.
50 + */
51 +@Component(immediate = true, enabled = false)
52 +@Service
53 +@Beta
54 +public class ConsistentResourceStore implements ResourceStore {
55 + private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class);
56 +
57 + private static final String MAP_NAME = "onos-resource-consumers";
58 + private static final Serializer SERIALIZER = Serializer.using(KryoNamespaces.API);
59 +
60 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
61 + protected StorageService service;
62 +
63 + private ConsistentMap<Resource<?, ?>, ResourceConsumer> consumers;
64 +
65 + @Activate
66 + public void activate() {
67 + consumers = service.<Resource<?, ?>, ResourceConsumer>consistentMapBuilder()
68 + .withName(MAP_NAME)
69 + .withSerializer(SERIALIZER)
70 + .build();
71 + }
72 +
73 + @Override
74 + public <S, T> Optional<ResourceConsumer> getConsumer(Resource<S, T> resource) {
75 + checkNotNull(resource);
76 +
77 + Versioned<ResourceConsumer> consumer = consumers.get(resource);
78 + if (consumer == null) {
79 + return Optional.empty();
80 + }
81 +
82 + return Optional.of(consumer.value());
83 + }
84 +
85 + @Override
86 + public boolean allocate(List<? extends Resource<?, ?>> resources, ResourceConsumer consumer) {
87 + checkNotNull(resources);
88 + checkNotNull(consumer);
89 +
90 + TransactionContext tx = service.transactionContextBuilder().build();
91 + tx.begin();
92 +
93 + try {
94 + TransactionalMap<Resource<?, ?>, ResourceConsumer> txMap = tx.getTransactionalMap(MAP_NAME, SERIALIZER);
95 + for (Resource<?, ?> resource: resources) {
96 + ResourceConsumer existing = txMap.putIfAbsent(resource, consumer);
97 + // if the resource is already allocated to another consumer, the whole allocation fails
98 + if (existing != null) {
99 + tx.abort();
100 + return false;
101 + }
102 + }
103 + tx.commit();
104 + return true;
105 + } catch (Exception e) {
106 + log.error("Exception thrown, abort the transaction", e);
107 + tx.abort();
108 + return false;
109 + }
110 + }
111 +
112 + @Override
113 + public boolean release(List<? extends Resource<?, ?>> resources, List<ResourceConsumer> consumers) {
114 + checkNotNull(resources);
115 + checkNotNull(consumers);
116 + checkArgument(resources.size() == consumers.size());
117 +
118 + TransactionContext tx = service.transactionContextBuilder().build();
119 + tx.begin();
120 +
121 + try {
122 + TransactionalMap<Resource<?, ?>, ResourceConsumer> txMap = tx.getTransactionalMap(MAP_NAME, SERIALIZER);
123 + Iterator<? extends Resource<?, ?>> resourceIte = resources.iterator();
124 + Iterator<ResourceConsumer> consumerIte = consumers.iterator();
125 +
126 + while (resourceIte.hasNext() && consumerIte.hasNext()) {
127 + Resource<?, ?> resource = resourceIte.next();
128 + ResourceConsumer consumer = consumerIte.next();
129 +
130 + // if this single release fails (because the resource is allocated to another consumer,
131 + // the whole release fails
132 + if (!txMap.remove(resource, consumer)) {
133 + tx.abort();
134 + return false;
135 + }
136 + }
137 +
138 + return true;
139 + } catch (TransactionException e) {
140 + log.error("Exception thrown, abort the transaction", e);
141 + tx.abort();
142 + return false;
143 + }
144 + }
145 +
146 + @Override
147 + public Collection<Resource<?, ?>> getResources(ResourceConsumer consumer) {
148 + checkNotNull(consumer);
149 +
150 + // NOTE: getting all entries may become performance bottleneck
151 + // TODO: revisit for better backend data structure
152 + return consumers.entrySet().stream()
153 + .filter(x -> x.getValue().value().equals(consumer))
154 + .map(Map.Entry::getKey)
155 + .collect(Collectors.toList());
156 + }
157 +
158 + @SuppressWarnings("unchecked")
159 + @Override
160 + public <S, T> Collection<Resource<S, T>> getAllocatedResources(S subject, Class<T> cls) {
161 + checkNotNull(subject);
162 + checkNotNull(cls);
163 +
164 + // NOTE: getting all entries may become performance bottleneck
165 + // TODO: revisit for better backend data structure
166 + return consumers.entrySet().stream()
167 + .filter(x -> x.getKey().subject().equals(subject) && x.getKey().resource().getClass() == cls)
168 + // cast is ensured by the above filter method
169 + .map(x -> (Resource<S, T>) x.getKey())
170 + .collect(Collectors.toList());
171 + }
172 +}