blob: e0d7b9d5590ebf01aa5c926be092842a2cffe949 [file] [log] [blame]
Thomas Vachuska48448082016-02-19 22:14:54 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
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;
Thomas Vachuska48448082016-02-19 22:14:54 -080021import org.onlab.util.Identifier;
22import org.onosproject.cluster.NodeId;
Simon Hunt53612212016-12-04 17:19:52 -080023import org.onosproject.net.Annotations;
Thomas Vachuska48448082016-02-19 22:14:54 -080024import org.onosproject.net.DeviceId;
25import org.onosproject.net.region.DefaultRegion;
26import org.onosproject.net.region.Region;
27import org.onosproject.net.region.RegionEvent;
28import org.onosproject.net.region.RegionId;
29import org.onosproject.net.region.RegionStore;
30import org.onosproject.net.region.RegionStoreDelegate;
31import org.onosproject.store.AbstractStore;
32import org.onosproject.store.serializers.KryoNamespaces;
33import org.onosproject.store.service.ConsistentMap;
34import org.onosproject.store.service.MapEvent;
35import org.onosproject.store.service.MapEventListener;
36import org.onosproject.store.service.Serializer;
37import org.onosproject.store.service.StorageService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070038import org.osgi.service.component.annotations.Activate;
39import org.osgi.service.component.annotations.Component;
40import org.osgi.service.component.annotations.Deactivate;
41import org.osgi.service.component.annotations.Reference;
42import org.osgi.service.component.annotations.ReferenceCardinality;
Thomas Vachuska48448082016-02-19 22:14:54 -080043import 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 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062@Component(immediate = true, service = RegionStore.class)
Thomas Vachuska48448082016-02-19 22:14:54 -080063public class DistributedRegionStore
64 extends AbstractStore<RegionEvent, RegionStoreDelegate>
65 implements RegionStore {
66
67 private static final String NO_REGION = "Region does not exist";
68 private static final String DUPLICATE_REGION = "Region already exists";
69
70 private final Logger log = getLogger(getClass());
71
Ray Milkeyd84f89b2018-08-17 14:54:17 -070072 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska48448082016-02-19 22:14:54 -080073 protected StorageService storageService;
74
75 private ConsistentMap<RegionId, Region> regionsRepo;
76 private Map<RegionId, Region> regionsById;
77
78 private ConsistentMap<RegionId, Set<DeviceId>> membershipRepo;
79 private Map<RegionId, Set<DeviceId>> regionDevices;
80
81 private Map<DeviceId, Region> regionsByDevice = new HashMap<>();
82
83 private final MapEventListener<RegionId, Region> listener =
84 new InternalRegionListener();
85 private final MapEventListener<RegionId, Set<DeviceId>> membershipListener =
86 new InternalMembershipListener();
87
88 @Activate
89 protected void activate() {
90 Serializer serializer =
91 Serializer.using(Arrays.asList(KryoNamespaces.API),
Simon Hunt53612212016-12-04 17:19:52 -080092 Identifier.class);
Thomas Vachuska48448082016-02-19 22:14:54 -080093
94 regionsRepo = storageService.<RegionId, Region>consistentMapBuilder()
95 .withSerializer(serializer)
96 .withName("onos-regions")
97 .withRelaxedReadConsistency()
98 .build();
99 regionsRepo.addListener(listener);
100 regionsById = regionsRepo.asJavaMap();
101
102 membershipRepo = storageService.<RegionId, Set<DeviceId>>consistentMapBuilder()
103 .withSerializer(serializer)
104 .withName("onos-region-devices")
105 .withRelaxedReadConsistency()
106 .build();
107 membershipRepo.addListener(membershipListener);
108 regionDevices = membershipRepo.asJavaMap();
109 log.info("Started");
110 }
111
112 @Deactivate
113 protected void deactivate() {
114 regionsRepo.removeListener(listener);
115 membershipRepo.removeListener(membershipListener);
116 regionsByDevice.clear();
117 log.info("Stopped");
118 }
119
120 @Override
121 public Set<Region> getRegions() {
122 return ImmutableSet.copyOf(regionsById.values());
123 }
124
125 @Override
126 public Region getRegion(RegionId regionId) {
127 return nullIsNotFound(regionsById.get(regionId), NO_REGION);
128 }
129
130 @Override
131 public Region getRegionForDevice(DeviceId deviceId) {
132 return regionsByDevice.get(deviceId);
133 }
134
135 @Override
136 public Set<DeviceId> getRegionDevices(RegionId regionId) {
137 Set<DeviceId> deviceIds = regionDevices.get(regionId);
138 return deviceIds != null ? ImmutableSet.copyOf(deviceIds) : ImmutableSet.of();
139 }
140
141 @Override
142 public Region createRegion(RegionId regionId, String name, Region.Type type,
Simon Hunt53612212016-12-04 17:19:52 -0800143 Annotations annots, List<Set<NodeId>> masterNodeIds) {
Thomas Vachuska48448082016-02-19 22:14:54 -0800144 return regionsRepo.compute(regionId, (id, region) -> {
145 checkArgument(region == null, DUPLICATE_REGION);
Simon Hunt53612212016-12-04 17:19:52 -0800146 return new DefaultRegion(regionId, name, type, annots, masterNodeIds);
Thomas Vachuska48448082016-02-19 22:14:54 -0800147 }).value();
148 }
149
150 @Override
151 public Region updateRegion(RegionId regionId, String name, Region.Type type,
Simon Hunt53612212016-12-04 17:19:52 -0800152 Annotations annots, List<Set<NodeId>> masterNodeIds) {
Thomas Vachuska48448082016-02-19 22:14:54 -0800153 return regionsRepo.compute(regionId, (id, region) -> {
154 nullIsNotFound(region, NO_REGION);
Simon Hunt53612212016-12-04 17:19:52 -0800155 return new DefaultRegion(regionId, name, type, annots, masterNodeIds);
Thomas Vachuska48448082016-02-19 22:14:54 -0800156 }).value();
157 }
158
159 @Override
160 public void removeRegion(RegionId regionId) {
161 membershipRepo.remove(regionId);
162 regionsRepo.remove(regionId);
163 }
164
165 @Override
166 public void addDevices(RegionId regionId, Collection<DeviceId> deviceIds) {
Simon Hunt53612212016-12-04 17:19:52 -0800167 // Devices can only be a member in one region. Remove the device if it
168 // belongs to a different region than the region for which we are
169 // attempting to add it.
Brian Stanked09e2ee2016-02-26 11:36:46 -0500170 for (DeviceId deviceId : deviceIds) {
171 Region region = getRegionForDevice(deviceId);
172 if ((region != null) && (!regionId.id().equals(region.id().id()))) {
173 Set<DeviceId> deviceIdSet1 = ImmutableSet.of(deviceId);
174 removeDevices(region.id(), deviceIdSet1);
175 }
176 }
177
Thomas Vachuska48448082016-02-19 22:14:54 -0800178 membershipRepo.compute(regionId, (id, existingDevices) -> {
179 if (existingDevices == null) {
180 return ImmutableSet.copyOf(deviceIds);
181 } else if (!existingDevices.containsAll(deviceIds)) {
182 return ImmutableSet.<DeviceId>builder()
183 .addAll(existingDevices)
184 .addAll(deviceIds)
185 .build();
186 } else {
187 return existingDevices;
188 }
189 });
190
191 Region region = regionsById.get(regionId);
192 deviceIds.forEach(deviceId -> regionsByDevice.put(deviceId, region));
193 }
194
195 @Override
196 public void removeDevices(RegionId regionId, Collection<DeviceId> deviceIds) {
197 membershipRepo.compute(regionId, (id, existingDevices) -> {
198 if (existingDevices == null || existingDevices.isEmpty()) {
199 return ImmutableSet.of();
200 } else {
201 return ImmutableSet.<DeviceId>builder()
202 .addAll(Sets.difference(existingDevices,
Simon Hunt53612212016-12-04 17:19:52 -0800203 ImmutableSet.copyOf(deviceIds)))
Thomas Vachuska48448082016-02-19 22:14:54 -0800204 .build();
205 }
206 });
207
208 deviceIds.forEach(deviceId -> regionsByDevice.remove(deviceId));
209 }
210
211 /**
212 * Listener class to map listener events to the region inventory events.
213 */
Simon Hunt53612212016-12-04 17:19:52 -0800214 private class InternalRegionListener
215 implements MapEventListener<RegionId, Region> {
Thomas Vachuska48448082016-02-19 22:14:54 -0800216 @Override
217 public void event(MapEvent<RegionId, Region> event) {
218 Region region = null;
219 RegionEvent.Type type = null;
220 switch (event.type()) {
221 case INSERT:
222 type = RegionEvent.Type.REGION_ADDED;
223 region = checkNotNull(event.newValue().value());
224 break;
225 case UPDATE:
226 type = RegionEvent.Type.REGION_UPDATED;
227 region = checkNotNull(event.newValue().value());
228 break;
229 case REMOVE:
230 type = RegionEvent.Type.REGION_REMOVED;
231 region = checkNotNull(event.oldValue().value());
232 break;
233 default:
234 log.error("Unsupported event type: " + event.type());
235 }
236 notifyDelegate(new RegionEvent(type, region));
237 }
238 }
239
240 /**
241 * Listener class to map listener events to the region membership events.
242 */
Simon Hunt53612212016-12-04 17:19:52 -0800243 private class InternalMembershipListener
244 implements MapEventListener<RegionId, Set<DeviceId>> {
Thomas Vachuska48448082016-02-19 22:14:54 -0800245 @Override
246 public void event(MapEvent<RegionId, Set<DeviceId>> event) {
247 if (event.type() != MapEvent.Type.REMOVE) {
Simon Hunt8f60ff82017-04-24 17:19:30 -0700248 Region r = regionsById.get(event.key());
249 if (r != null) {
250 notifyDelegate(new RegionEvent(REGION_MEMBERSHIP_CHANGED,
251 r,
252 event.newValue().value()));
253 }
Thomas Vachuska48448082016-02-19 22:14:54 -0800254 }
255 }
256 }
257}