blob: e00af16dc20d56cceb208aabf50e4536c47b3e25 [file] [log] [blame]
Thomas Vachuska48448082016-02-19 22:14:54 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Thomas Vachuska48448082016-02-19 22:14:54 -08003 *
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
17package org.onosproject.store.region.impl;
18
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Sets;
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.apache.felix.scr.annotations.Service;
27import org.onlab.util.Identifier;
28import org.onosproject.cluster.NodeId;
Simon Hunt53612212016-12-04 17:19:52 -080029import org.onosproject.net.Annotations;
Thomas Vachuska48448082016-02-19 22:14:54 -080030import org.onosproject.net.DeviceId;
31import org.onosproject.net.region.DefaultRegion;
32import org.onosproject.net.region.Region;
33import org.onosproject.net.region.RegionEvent;
34import org.onosproject.net.region.RegionId;
35import org.onosproject.net.region.RegionStore;
36import org.onosproject.net.region.RegionStoreDelegate;
37import org.onosproject.store.AbstractStore;
38import org.onosproject.store.serializers.KryoNamespaces;
39import org.onosproject.store.service.ConsistentMap;
40import org.onosproject.store.service.MapEvent;
41import org.onosproject.store.service.MapEventListener;
42import org.onosproject.store.service.Serializer;
43import org.onosproject.store.service.StorageService;
44import org.slf4j.Logger;
45
46import java.util.Arrays;
47import java.util.Collection;
48import java.util.HashMap;
49import java.util.List;
50import java.util.Map;
51import java.util.Set;
52
53import static com.google.common.base.Preconditions.checkArgument;
54import static com.google.common.base.Preconditions.checkNotNull;
55import static org.onlab.util.Tools.nullIsNotFound;
56import static org.onosproject.net.region.RegionEvent.Type.REGION_MEMBERSHIP_CHANGED;
57import static org.slf4j.LoggerFactory.getLogger;
58
59/**
60 * Consistent store implementation for tracking region definitions and device
61 * region affiliation.
62 */
63@Component(immediate = true)
64@Service
65public class DistributedRegionStore
66 extends AbstractStore<RegionEvent, RegionStoreDelegate>
67 implements RegionStore {
68
69 private static final String NO_REGION = "Region does not exist";
70 private static final String DUPLICATE_REGION = "Region already exists";
71
72 private final Logger log = getLogger(getClass());
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected StorageService storageService;
76
77 private ConsistentMap<RegionId, Region> regionsRepo;
78 private Map<RegionId, Region> regionsById;
79
80 private ConsistentMap<RegionId, Set<DeviceId>> membershipRepo;
81 private Map<RegionId, Set<DeviceId>> regionDevices;
82
83 private Map<DeviceId, Region> regionsByDevice = new HashMap<>();
84
85 private final MapEventListener<RegionId, Region> listener =
86 new InternalRegionListener();
87 private final MapEventListener<RegionId, Set<DeviceId>> membershipListener =
88 new InternalMembershipListener();
89
90 @Activate
91 protected void activate() {
92 Serializer serializer =
93 Serializer.using(Arrays.asList(KryoNamespaces.API),
Simon Hunt53612212016-12-04 17:19:52 -080094 Identifier.class);
Thomas Vachuska48448082016-02-19 22:14:54 -080095
96 regionsRepo = storageService.<RegionId, Region>consistentMapBuilder()
97 .withSerializer(serializer)
98 .withName("onos-regions")
99 .withRelaxedReadConsistency()
100 .build();
101 regionsRepo.addListener(listener);
102 regionsById = regionsRepo.asJavaMap();
103
104 membershipRepo = storageService.<RegionId, Set<DeviceId>>consistentMapBuilder()
105 .withSerializer(serializer)
106 .withName("onos-region-devices")
107 .withRelaxedReadConsistency()
108 .build();
109 membershipRepo.addListener(membershipListener);
110 regionDevices = membershipRepo.asJavaMap();
111 log.info("Started");
112 }
113
114 @Deactivate
115 protected void deactivate() {
116 regionsRepo.removeListener(listener);
117 membershipRepo.removeListener(membershipListener);
118 regionsByDevice.clear();
119 log.info("Stopped");
120 }
121
122 @Override
123 public Set<Region> getRegions() {
124 return ImmutableSet.copyOf(regionsById.values());
125 }
126
127 @Override
128 public Region getRegion(RegionId regionId) {
129 return nullIsNotFound(regionsById.get(regionId), NO_REGION);
130 }
131
132 @Override
133 public Region getRegionForDevice(DeviceId deviceId) {
134 return regionsByDevice.get(deviceId);
135 }
136
137 @Override
138 public Set<DeviceId> getRegionDevices(RegionId regionId) {
139 Set<DeviceId> deviceIds = regionDevices.get(regionId);
140 return deviceIds != null ? ImmutableSet.copyOf(deviceIds) : ImmutableSet.of();
141 }
142
143 @Override
144 public Region createRegion(RegionId regionId, String name, Region.Type type,
Simon Hunt53612212016-12-04 17:19:52 -0800145 Annotations annots, List<Set<NodeId>> masterNodeIds) {
Thomas Vachuska48448082016-02-19 22:14:54 -0800146 return regionsRepo.compute(regionId, (id, region) -> {
147 checkArgument(region == null, DUPLICATE_REGION);
Simon Hunt53612212016-12-04 17:19:52 -0800148 return new DefaultRegion(regionId, name, type, annots, masterNodeIds);
Thomas Vachuska48448082016-02-19 22:14:54 -0800149 }).value();
150 }
151
152 @Override
153 public Region updateRegion(RegionId regionId, String name, Region.Type type,
Simon Hunt53612212016-12-04 17:19:52 -0800154 Annotations annots, List<Set<NodeId>> masterNodeIds) {
Thomas Vachuska48448082016-02-19 22:14:54 -0800155 return regionsRepo.compute(regionId, (id, region) -> {
156 nullIsNotFound(region, NO_REGION);
Simon Hunt53612212016-12-04 17:19:52 -0800157 return new DefaultRegion(regionId, name, type, annots, masterNodeIds);
Thomas Vachuska48448082016-02-19 22:14:54 -0800158 }).value();
159 }
160
161 @Override
162 public void removeRegion(RegionId regionId) {
163 membershipRepo.remove(regionId);
164 regionsRepo.remove(regionId);
165 }
166
167 @Override
168 public void addDevices(RegionId regionId, Collection<DeviceId> deviceIds) {
Simon Hunt53612212016-12-04 17:19:52 -0800169 // Devices can only be a member in one region. Remove the device if it
170 // belongs to a different region than the region for which we are
171 // attempting to add it.
Brian Stanked09e2ee2016-02-26 11:36:46 -0500172 for (DeviceId deviceId : deviceIds) {
173 Region region = getRegionForDevice(deviceId);
174 if ((region != null) && (!regionId.id().equals(region.id().id()))) {
175 Set<DeviceId> deviceIdSet1 = ImmutableSet.of(deviceId);
176 removeDevices(region.id(), deviceIdSet1);
177 }
178 }
179
Thomas Vachuska48448082016-02-19 22:14:54 -0800180 membershipRepo.compute(regionId, (id, existingDevices) -> {
181 if (existingDevices == null) {
182 return ImmutableSet.copyOf(deviceIds);
183 } else if (!existingDevices.containsAll(deviceIds)) {
184 return ImmutableSet.<DeviceId>builder()
185 .addAll(existingDevices)
186 .addAll(deviceIds)
187 .build();
188 } else {
189 return existingDevices;
190 }
191 });
192
193 Region region = regionsById.get(regionId);
194 deviceIds.forEach(deviceId -> regionsByDevice.put(deviceId, region));
195 }
196
197 @Override
198 public void removeDevices(RegionId regionId, Collection<DeviceId> deviceIds) {
199 membershipRepo.compute(regionId, (id, existingDevices) -> {
200 if (existingDevices == null || existingDevices.isEmpty()) {
201 return ImmutableSet.of();
202 } else {
203 return ImmutableSet.<DeviceId>builder()
204 .addAll(Sets.difference(existingDevices,
Simon Hunt53612212016-12-04 17:19:52 -0800205 ImmutableSet.copyOf(deviceIds)))
Thomas Vachuska48448082016-02-19 22:14:54 -0800206 .build();
207 }
208 });
209
210 deviceIds.forEach(deviceId -> regionsByDevice.remove(deviceId));
211 }
212
213 /**
214 * Listener class to map listener events to the region inventory events.
215 */
Simon Hunt53612212016-12-04 17:19:52 -0800216 private class InternalRegionListener
217 implements MapEventListener<RegionId, Region> {
Thomas Vachuska48448082016-02-19 22:14:54 -0800218 @Override
219 public void event(MapEvent<RegionId, Region> event) {
220 Region region = null;
221 RegionEvent.Type type = null;
222 switch (event.type()) {
223 case INSERT:
224 type = RegionEvent.Type.REGION_ADDED;
225 region = checkNotNull(event.newValue().value());
226 break;
227 case UPDATE:
228 type = RegionEvent.Type.REGION_UPDATED;
229 region = checkNotNull(event.newValue().value());
230 break;
231 case REMOVE:
232 type = RegionEvent.Type.REGION_REMOVED;
233 region = checkNotNull(event.oldValue().value());
234 break;
235 default:
236 log.error("Unsupported event type: " + event.type());
237 }
238 notifyDelegate(new RegionEvent(type, region));
239 }
240 }
241
242 /**
243 * Listener class to map listener events to the region membership events.
244 */
Simon Hunt53612212016-12-04 17:19:52 -0800245 private class InternalMembershipListener
246 implements MapEventListener<RegionId, Set<DeviceId>> {
Thomas Vachuska48448082016-02-19 22:14:54 -0800247 @Override
248 public void event(MapEvent<RegionId, Set<DeviceId>> event) {
249 if (event.type() != MapEvent.Type.REMOVE) {
250 notifyDelegate(new RegionEvent(REGION_MEMBERSHIP_CHANGED,
Simon Hunt53612212016-12-04 17:19:52 -0800251 regionsById.get(event.key()),
252 event.newValue().value()));
Thomas Vachuska48448082016-02-19 22:14:54 -0800253 }
254 }
255 }
256}