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