blob: 88a1d54fc7e6632077b3cc90aa9264d6aae81a98 [file] [log] [blame]
alshabib10580802015-02-18 18:30:33 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
alshabib10580802015-02-18 18:30:33 -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 */
16package org.onosproject.store.group.impl;
17
Jonathan Hart6ec029a2015-03-24 17:12:35 -070018import com.google.common.collect.FluentIterable;
Charles Chanf4838a72015-12-07 18:13:45 -080019import com.google.common.collect.ImmutableSet;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070020import com.google.common.collect.Iterables;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070021import com.google.common.collect.Sets;
alshabib10580802015-02-18 18:30:33 -080022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
alshabibb0285992016-03-28 23:30:37 -070025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
alshabib10580802015-02-18 18:30:33 -080029import org.apache.felix.scr.annotations.Service;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070030import org.onlab.util.KryoNamespace;
alshabibb0285992016-03-28 23:30:37 -070031import org.onosproject.cfg.ComponentConfigService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070032import org.onosproject.cluster.ClusterService;
Charles Chanf4838a72015-12-07 18:13:45 -080033import org.onosproject.cluster.NodeId;
alshabib10580802015-02-18 18:30:33 -080034import org.onosproject.core.DefaultGroupId;
35import org.onosproject.core.GroupId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070036import org.onosproject.mastership.MastershipService;
alshabib10580802015-02-18 18:30:33 -080037import org.onosproject.net.DeviceId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070038import org.onosproject.net.MastershipRole;
alshabib10580802015-02-18 18:30:33 -080039import org.onosproject.net.group.DefaultGroup;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070040import org.onosproject.net.group.DefaultGroupBucket;
alshabib10580802015-02-18 18:30:33 -080041import org.onosproject.net.group.DefaultGroupDescription;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070042import org.onosproject.net.group.DefaultGroupKey;
alshabib10580802015-02-18 18:30:33 -080043import org.onosproject.net.group.Group;
44import org.onosproject.net.group.Group.GroupState;
45import org.onosproject.net.group.GroupBucket;
46import org.onosproject.net.group.GroupBuckets;
47import org.onosproject.net.group.GroupDescription;
48import org.onosproject.net.group.GroupEvent;
49import org.onosproject.net.group.GroupEvent.Type;
50import org.onosproject.net.group.GroupKey;
51import org.onosproject.net.group.GroupOperation;
52import org.onosproject.net.group.GroupStore;
53import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070054import org.onosproject.net.group.StoredGroupBucketEntry;
alshabib10580802015-02-18 18:30:33 -080055import org.onosproject.net.group.StoredGroupEntry;
56import org.onosproject.store.AbstractStore;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070057import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070058import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani0b847532016-03-03 13:44:15 -080059import org.onosproject.store.service.ConsistentMap;
60import org.onosproject.store.service.MapEvent;
61import org.onosproject.store.service.MapEventListener;
alshabibb0285992016-03-28 23:30:37 -070062import org.onosproject.store.service.MultiValuedTimestamp;
Madan Jampani0b847532016-03-03 13:44:15 -080063import org.onosproject.store.service.Serializer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070064import org.onosproject.store.service.StorageService;
helenyrwua1c41152016-08-18 16:16:14 -070065import org.onosproject.store.service.Topic;
Madan Jampani0b847532016-03-03 13:44:15 -080066import org.onosproject.store.service.Versioned;
alshabibb0285992016-03-28 23:30:37 -070067import org.osgi.service.component.ComponentContext;
alshabib10580802015-02-18 18:30:33 -080068import org.slf4j.Logger;
69
Jonathan Hart6ec029a2015-03-24 17:12:35 -070070import java.util.ArrayList;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070071import java.util.Collection;
Charles Chanf4838a72015-12-07 18:13:45 -080072import java.util.Collections;
alshabibb0285992016-03-28 23:30:37 -070073import java.util.Dictionary;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070074import java.util.HashMap;
Charles Chan0c7c43b2016-01-14 17:39:20 -080075import java.util.HashSet;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070076import java.util.Iterator;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070077import java.util.List;
Madan Jampani0b847532016-03-03 13:44:15 -080078import java.util.Map;
Charles Chan0c7c43b2016-01-14 17:39:20 -080079import java.util.Map.Entry;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070080import java.util.Objects;
Sho SHIMIZU30d639b2015-05-05 09:30:35 -070081import java.util.Optional;
alshabibb0285992016-03-28 23:30:37 -070082import java.util.Properties;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070083import java.util.Set;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070084import java.util.concurrent.ConcurrentHashMap;
85import java.util.concurrent.ConcurrentMap;
86import java.util.concurrent.ExecutorService;
87import java.util.concurrent.Executors;
88import java.util.concurrent.atomic.AtomicInteger;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070089import java.util.stream.Collectors;
90
alshabibb0285992016-03-28 23:30:37 -070091import static com.google.common.base.Strings.isNullOrEmpty;
alshabibb0285992016-03-28 23:30:37 -070092import static org.onlab.util.Tools.get;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070093import static org.onlab.util.Tools.groupedThreads;
94import static org.slf4j.LoggerFactory.getLogger;
alshabib10580802015-02-18 18:30:33 -080095
96/**
Saurav Das0fd79d92016-03-07 10:58:36 -080097 * Manages inventory of group entries using distributed group stores from the
98 * storage service.
alshabib10580802015-02-18 18:30:33 -080099 */
100@Component(immediate = true)
101@Service
102public class DistributedGroupStore
103 extends AbstractStore<GroupEvent, GroupStoreDelegate>
104 implements GroupStore {
105
106 private final Logger log = getLogger(getClass());
107
alshabibb0285992016-03-28 23:30:37 -0700108 private static final boolean GARBAGE_COLLECT = false;
109 private static final int GC_THRESH = 6;
110
alshabib10580802015-02-18 18:30:33 -0800111 private final int dummyId = 0xffffffff;
112 private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
113
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected ClusterCommunicationService clusterCommunicator;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected ClusterService clusterService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700121 protected StorageService storageService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700124 protected MastershipService mastershipService;
125
alshabibb0285992016-03-28 23:30:37 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected ComponentConfigService cfgService;
128
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700129 // Per device group table with (device id + app cookie) as key
Madan Jampani0b847532016-03-03 13:44:15 -0800130 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700131 StoredGroupEntry> groupStoreEntriesByKey = null;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700132 // Per device group table with (device id + group id) as key
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700133 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
alshabibb0285992016-03-28 23:30:37 -0700134 groupEntriesById = new ConcurrentHashMap<>();
Madan Jampani0b847532016-03-03 13:44:15 -0800135 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700136 StoredGroupEntry> auditPendingReqQueue = null;
Frank Wange0eb5ce2016-07-01 18:21:25 +0800137 private MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry>
138 mapListener = new GroupStoreKeyMapListener();
alshabib10580802015-02-18 18:30:33 -0800139 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
140 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700141 private ExecutorService messageHandlingExecutor;
142 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700143 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
alshabib10580802015-02-18 18:30:33 -0800144
145 private final AtomicInteger groupIdGen = new AtomicInteger();
146
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700147 private KryoNamespace clusterMsgSerializer;
148
helenyrwua1c41152016-08-18 16:16:14 -0700149 private static Topic<GroupStoreMessage> groupTopic;
150
alshabibb0285992016-03-28 23:30:37 -0700151 @Property(name = "garbageCollect", boolValue = GARBAGE_COLLECT,
152 label = "Enable group garbage collection")
153 private boolean garbageCollect = GARBAGE_COLLECT;
154
155 @Property(name = "gcThresh", intValue = GC_THRESH,
156 label = "Number of rounds for group garbage collection")
157 private int gcThresh = GC_THRESH;
158
159
alshabib10580802015-02-18 18:30:33 -0800160 @Activate
161 public void activate() {
alshabibb0285992016-03-28 23:30:37 -0700162 cfgService.registerProperties(getClass());
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700163 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
alshabibb0285992016-03-28 23:30:37 -0700164 .register(KryoNamespaces.API)
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700165 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
alshabibb0285992016-03-28 23:30:37 -0700166 .register(DefaultGroup.class,
167 DefaultGroupBucket.class,
168 DefaultGroupDescription.class,
169 DefaultGroupKey.class,
170 GroupDescription.Type.class,
171 Group.GroupState.class,
172 GroupBuckets.class,
alshabibb0285992016-03-28 23:30:37 -0700173 GroupStoreMessage.class,
174 GroupStoreMessage.Type.class,
175 UpdateType.class,
176 GroupStoreMessageSubjects.class,
177 MultiValuedTimestamp.class,
178 GroupStoreKeyMapKey.class,
179 GroupStoreIdMapKey.class,
180 GroupStoreMapKey.class
181 );
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700182
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700183 clusterMsgSerializer = kryoBuilder.build("GroupStore");
184 Serializer serializer = Serializer.using(clusterMsgSerializer);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700185
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700186 messageHandlingExecutor = Executors.
187 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
188 groupedThreads("onos/store/group",
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700189 "message-handlers",
190 log));
Madan Jampani01e05fb2015-08-13 13:29:36 -0700191
192 clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
alshabibb0285992016-03-28 23:30:37 -0700193 clusterMsgSerializer::deserialize,
194 this::process,
195 messageHandlingExecutor);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700196
Madan Jampani0b847532016-03-03 13:44:15 -0800197 log.debug("Creating Consistent map onos-group-store-keymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700198
Madan Jampani0b847532016-03-03 13:44:15 -0800199 groupStoreEntriesByKey = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
200 .withName("onos-group-store-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700201 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700202 .build();
Frank Wange0eb5ce2016-07-01 18:21:25 +0800203 groupStoreEntriesByKey.addListener(mapListener);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700204 log.debug("Current size of groupstorekeymap:{}",
205 groupStoreEntriesByKey.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700206
Madan Jampani0b847532016-03-03 13:44:15 -0800207 log.debug("Creating Consistent map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700208
Madan Jampani0b847532016-03-03 13:44:15 -0800209 auditPendingReqQueue = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
210 .withName("onos-pending-group-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700211 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700212 .build();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700213 log.debug("Current size of pendinggroupkeymap:{}",
214 auditPendingReqQueue.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700215
helenyrwua1c41152016-08-18 16:16:14 -0700216 groupTopic = getOrCreateGroupTopic(serializer);
217 groupTopic.subscribe(this::processGroupMessage);
218
alshabib10580802015-02-18 18:30:33 -0800219 log.info("Started");
220 }
221
222 @Deactivate
223 public void deactivate() {
Frank Wange0eb5ce2016-07-01 18:21:25 +0800224 groupStoreEntriesByKey.removeListener(mapListener);
alshabibb0285992016-03-28 23:30:37 -0700225 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700226 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
alshabib10580802015-02-18 18:30:33 -0800227 log.info("Stopped");
228 }
229
alshabibb0285992016-03-28 23:30:37 -0700230 @Modified
231 public void modified(ComponentContext context) {
232 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
233
234 try {
235 String s = get(properties, "garbageCollect");
236 garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT : Boolean.parseBoolean(s.trim());
237
238 s = get(properties, "gcThresh");
239 gcThresh = isNullOrEmpty(s) ? GC_THRESH : Integer.parseInt(s.trim());
240 } catch (Exception e) {
241 gcThresh = GC_THRESH;
242 garbageCollect = GARBAGE_COLLECT;
243 }
244 }
245
helenyrwua1c41152016-08-18 16:16:14 -0700246 private Topic<GroupStoreMessage> getOrCreateGroupTopic(Serializer serializer) {
247 if (groupTopic == null) {
248 return storageService.getTopic("group-failover-notif", serializer);
249 } else {
250 return groupTopic;
251 }
Sho SHIMIZUa6285542017-01-12 15:08:24 -0800252 }
helenyrwua1c41152016-08-18 16:16:14 -0700253
alshabib10580802015-02-18 18:30:33 -0800254 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700255 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800256 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700257 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800258 */
Madan Jampani0b847532016-03-03 13:44:15 -0800259 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700260 getGroupStoreKeyMap() {
Madan Jampani0b847532016-03-03 13:44:15 -0800261 return groupStoreEntriesByKey.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800262 }
263
264 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700265 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800266 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700267 * @param deviceId identifier of the device
268 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800269 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700270 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700271 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800272 }
273
274 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700275 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800276 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700277 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800278 */
Madan Jampani0b847532016-03-03 13:44:15 -0800279 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700280 getPendingGroupKeyTable() {
Madan Jampani0b847532016-03-03 13:44:15 -0800281 return auditPendingReqQueue.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800282 }
283
284 /**
285 * Returns the extraneous group id table for specified device.
286 *
287 * @param deviceId identifier of the device
288 * @return Map representing group key table of given device.
289 */
290 private ConcurrentMap<GroupId, Group>
291 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700292 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800293 }
294
295 /**
296 * Returns the number of groups for the specified device in the store.
297 *
298 * @return number of groups for the specified device
299 */
300 @Override
301 public int getGroupCount(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700302 return (getGroups(deviceId) != null) ?
alshabibb0285992016-03-28 23:30:37 -0700303 Iterables.size(getGroups(deviceId)) : 0;
alshabib10580802015-02-18 18:30:33 -0800304 }
305
306 /**
307 * Returns the groups associated with a device.
308 *
309 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800310 * @return the group entries
311 */
312 @Override
313 public Iterable<Group> getGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800314 // Let ImmutableSet.copyOf do the type conversion
315 return ImmutableSet.copyOf(getStoredGroups(deviceId));
alshabib10580802015-02-18 18:30:33 -0800316 }
317
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700318 private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800319 NodeId master = mastershipService.getMasterFor(deviceId);
320 if (master == null) {
321 log.debug("Failed to getGroups: No master for {}", deviceId);
322 return Collections.emptySet();
323 }
324
325 Set<StoredGroupEntry> storedGroups = getGroupStoreKeyMap().values()
326 .stream()
327 .filter(input -> input.deviceId().equals(deviceId))
328 .collect(Collectors.toSet());
329 return ImmutableSet.copyOf(storedGroups);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700330 }
331
alshabib10580802015-02-18 18:30:33 -0800332 /**
333 * Returns the stored group entry.
334 *
alshabibb0285992016-03-28 23:30:37 -0700335 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800336 * @param appCookie the group key
alshabib10580802015-02-18 18:30:33 -0800337 * @return a group associated with the key
338 */
339 @Override
340 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700341 return getStoredGroupEntry(deviceId, appCookie);
342 }
343
344 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
345 GroupKey appCookie) {
346 return getGroupStoreKeyMap().get(new GroupStoreKeyMapKey(deviceId,
347 appCookie));
348 }
349
350 @Override
351 public Group getGroup(DeviceId deviceId, GroupId groupId) {
352 return getStoredGroupEntry(deviceId, groupId);
353 }
354
355 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
356 GroupId groupId) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700357 return getGroupIdTable(deviceId).get(groupId);
alshabib10580802015-02-18 18:30:33 -0800358 }
359
360 private int getFreeGroupIdValue(DeviceId deviceId) {
361 int freeId = groupIdGen.incrementAndGet();
362
363 while (true) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700364 Group existing = getGroup(deviceId, new DefaultGroupId(freeId));
alshabib10580802015-02-18 18:30:33 -0800365 if (existing == null) {
366 existing = (
367 extraneousGroupEntriesById.get(deviceId) != null) ?
368 extraneousGroupEntriesById.get(deviceId).
369 get(new DefaultGroupId(freeId)) :
370 null;
371 }
372 if (existing != null) {
373 freeId = groupIdGen.incrementAndGet();
374 } else {
375 break;
376 }
377 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700378 log.debug("getFreeGroupIdValue: Next Free ID is {}", freeId);
alshabib10580802015-02-18 18:30:33 -0800379 return freeId;
380 }
381
382 /**
383 * Stores a new group entry using the information from group description.
384 *
385 * @param groupDesc group description to be used to create group entry
386 */
387 @Override
388 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700389 log.debug("In storeGroupDescription");
alshabib10580802015-02-18 18:30:33 -0800390 // Check if a group is existing with the same key
Saurav Das8a0732e2015-11-20 15:27:53 -0800391 Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
392 if (existingGroup != null) {
Charles Chan216e3c82016-04-23 14:48:16 -0700393 log.info("Group already exists with the same key {} in dev:{} with id:0x{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800394 groupDesc.appCookie(), groupDesc.deviceId(),
395 Integer.toHexString(existingGroup.id().id()));
alshabib10580802015-02-18 18:30:33 -0800396 return;
397 }
398
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700399 // Check if group to be created by a remote instance
Madan Jampani175e8fd2015-05-20 14:10:45 -0700400 if (mastershipService.getLocalRole(groupDesc.deviceId()) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700401 log.debug("storeGroupDescription: Device {} local role is not MASTER",
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700402 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700403 if (mastershipService.getMasterFor(groupDesc.deviceId()) == null) {
404 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700405 + "Can not perform add group operation",
406 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700407 //TODO: Send Group operation failure event
408 return;
409 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700410 GroupStoreMessage groupOp = GroupStoreMessage.
411 createGroupAddRequestMsg(groupDesc.deviceId(),
412 groupDesc);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700413
Madan Jampani175e8fd2015-05-20 14:10:45 -0700414 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700415 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
416 clusterMsgSerializer::serialize,
417 mastershipService.getMasterFor(groupDesc.deviceId()))
418 .whenComplete((result, error) -> {
Madan Jampani175e8fd2015-05-20 14:10:45 -0700419 if (error != null) {
420 log.warn("Failed to send request to master: {} to {}",
alshabibb0285992016-03-28 23:30:37 -0700421 groupOp,
422 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700423 //TODO: Send Group operation failure event
424 } else {
425 log.debug("Sent Group operation request for device {} "
alshabibb0285992016-03-28 23:30:37 -0700426 + "to remote MASTER {}",
427 groupDesc.deviceId(),
428 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700429 }
430 });
alshabib10580802015-02-18 18:30:33 -0800431 return;
432 }
433
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700434 log.debug("Store group for device {} is getting handled locally",
435 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800436 storeGroupDescriptionInternal(groupDesc);
437 }
438
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700439 private Group getMatchingExtraneousGroupbyId(DeviceId deviceId, Integer groupId) {
440 ConcurrentMap<GroupId, Group> extraneousMap =
441 extraneousGroupEntriesById.get(deviceId);
442 if (extraneousMap == null) {
443 return null;
444 }
445 return extraneousMap.get(new DefaultGroupId(groupId));
446 }
447
448 private Group getMatchingExtraneousGroupbyBuckets(DeviceId deviceId,
449 GroupBuckets buckets) {
450 ConcurrentMap<GroupId, Group> extraneousMap =
451 extraneousGroupEntriesById.get(deviceId);
452 if (extraneousMap == null) {
453 return null;
454 }
455
alshabibb0285992016-03-28 23:30:37 -0700456 for (Group extraneousGroup : extraneousMap.values()) {
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700457 if (extraneousGroup.buckets().equals(buckets)) {
458 return extraneousGroup;
459 }
460 }
461 return null;
462 }
463
alshabib10580802015-02-18 18:30:33 -0800464 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
465 // Check if a group is existing with the same key
466 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
467 return;
468 }
469
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700470 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
471 // Device group audit has not completed yet
472 // Add this group description to pending group key table
473 // Create a group entry object with Dummy Group ID
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700474 log.debug("storeGroupDescriptionInternal: Device {} AUDIT pending...Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700475 groupDesc.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700476 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
477 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
Madan Jampani0b847532016-03-03 13:44:15 -0800478 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700479 getPendingGroupKeyTable();
480 pendingKeyTable.put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
481 groupDesc.appCookie()),
482 group);
483 return;
484 }
485
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700486 Group matchingExtraneousGroup = null;
487 if (groupDesc.givenGroupId() != null) {
488 //Check if there is a extraneous group existing with the same Id
489 matchingExtraneousGroup = getMatchingExtraneousGroupbyId(
alshabibb0285992016-03-28 23:30:37 -0700490 groupDesc.deviceId(), groupDesc.givenGroupId());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700491 if (matchingExtraneousGroup != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800492 log.debug("storeGroupDescriptionInternal: Matching extraneous group "
alshabibb0285992016-03-28 23:30:37 -0700493 + "found in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700494 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800495 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700496 //Check if the group buckets matches with user provided buckets
497 if (matchingExtraneousGroup.buckets().equals(groupDesc.buckets())) {
498 //Group is already existing with the same buckets and Id
499 // Create a group entry object
Saurav Das0fd79d92016-03-07 10:58:36 -0800500 log.debug("storeGroupDescriptionInternal: Buckets also matching "
alshabibb0285992016-03-28 23:30:37 -0700501 + "in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700502 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800503 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700504 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700505 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700506 // Insert the newly created group entry into key and id maps
507 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700508 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
509 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700510 // Ensure it also inserted into group id based table to
511 // avoid any chances of duplication in group id generation
512 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700513 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700514 addOrUpdateGroupEntry(matchingExtraneousGroup);
515 removeExtraneousGroupEntry(matchingExtraneousGroup);
516 return;
517 } else {
518 //Group buckets are not matching. Update group
519 //with user provided buckets.
Saurav Das0fd79d92016-03-07 10:58:36 -0800520 log.debug("storeGroupDescriptionInternal: Buckets are not "
alshabibb0285992016-03-28 23:30:37 -0700521 + "matching in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700522 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800523 Integer.toHexString(groupDesc.givenGroupId()));
524 StoredGroupEntry modifiedGroup = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700525 matchingExtraneousGroup.id(), groupDesc);
Saurav Das0fd79d92016-03-07 10:58:36 -0800526 modifiedGroup.setState(GroupState.PENDING_UPDATE);
527 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700528 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
529 groupDesc.appCookie()), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800530 // Ensure it also inserted into group id based table to
531 // avoid any chances of duplication in group id generation
532 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700533 put(matchingExtraneousGroup.id(), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800534 removeExtraneousGroupEntry(matchingExtraneousGroup);
535 log.debug("storeGroupDescriptionInternal: Triggering Group "
alshabibb0285992016-03-28 23:30:37 -0700536 + "UPDATE request for {} in device {}",
Saurav Das0fd79d92016-03-07 10:58:36 -0800537 matchingExtraneousGroup.id(),
538 groupDesc.deviceId());
539 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, modifiedGroup));
540 return;
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700541 }
542 }
543 } else {
544 //Check if there is an extraneous group with user provided buckets
545 matchingExtraneousGroup = getMatchingExtraneousGroupbyBuckets(
alshabibb0285992016-03-28 23:30:37 -0700546 groupDesc.deviceId(), groupDesc.buckets());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700547 if (matchingExtraneousGroup != null) {
548 //Group is already existing with the same buckets.
549 //So reuse this group.
550 log.debug("storeGroupDescriptionInternal: Matching extraneous group found in Device {}",
551 groupDesc.deviceId());
552 //Create a group entry object
553 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700554 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700555 // Insert the newly created group entry into key and id maps
556 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700557 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
558 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700559 // Ensure it also inserted into group id based table to
560 // avoid any chances of duplication in group id generation
561 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700562 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700563 addOrUpdateGroupEntry(matchingExtraneousGroup);
564 removeExtraneousGroupEntry(matchingExtraneousGroup);
565 return;
566 } else {
567 //TODO: Check if there are any empty groups that can be used here
568 log.debug("storeGroupDescriptionInternal: No matching extraneous groups found in Device {}",
569 groupDesc.deviceId());
570 }
571 }
572
Saurav Das100e3b82015-04-30 11:12:10 -0700573 GroupId id = null;
574 if (groupDesc.givenGroupId() == null) {
575 // Get a new group identifier
576 id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
577 } else {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800578 // we need to use the identifier passed in by caller, but check if
579 // already used
580 Group existing = getGroup(groupDesc.deviceId(),
581 new DefaultGroupId(groupDesc.givenGroupId()));
582 if (existing != null) {
583 log.warn("Group already exists with the same id: 0x{} in dev:{} "
alshabibb0285992016-03-28 23:30:37 -0700584 + "but with different key: {} (request gkey: {})",
585 Integer.toHexString(groupDesc.givenGroupId()),
586 groupDesc.deviceId(),
587 existing.appCookie(),
588 groupDesc.appCookie());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800589 return;
590 }
Saurav Das100e3b82015-04-30 11:12:10 -0700591 id = new DefaultGroupId(groupDesc.givenGroupId());
592 }
alshabib10580802015-02-18 18:30:33 -0800593 // Create a group entry object
594 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700595 // Insert the newly created group entry into key and id maps
596 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700597 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
598 groupDesc.appCookie()), group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700599 // Ensure it also inserted into group id based table to
600 // avoid any chances of duplication in group id generation
601 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700602 put(id, group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700603 log.debug("storeGroupDescriptionInternal: Processing Group ADD request for Id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700604 id,
605 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800606 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
607 group));
608 }
609
610 /**
611 * Updates the existing group entry with the information
612 * from group description.
613 *
alshabibb0285992016-03-28 23:30:37 -0700614 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800615 * @param oldAppCookie the current group key
alshabibb0285992016-03-28 23:30:37 -0700616 * @param type update type
617 * @param newBuckets group buckets for updates
alshabib10580802015-02-18 18:30:33 -0800618 * @param newAppCookie optional new group key
619 */
620 @Override
621 public void updateGroupDescription(DeviceId deviceId,
622 GroupKey oldAppCookie,
623 UpdateType type,
624 GroupBuckets newBuckets,
625 GroupKey newAppCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700626 // Check if group update to be done by a remote instance
sangho52abe3a2015-05-05 14:13:34 -0700627 if (mastershipService.getMasterFor(deviceId) != null &&
628 mastershipService.getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700629 log.debug("updateGroupDescription: Device {} local role is not MASTER",
630 deviceId);
631 if (mastershipService.getMasterFor(deviceId) == null) {
632 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700633 + "Can not perform update group operation",
634 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700635 //TODO: Send Group operation failure event
636 return;
637 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700638 GroupStoreMessage groupOp = GroupStoreMessage.
639 createGroupUpdateRequestMsg(deviceId,
640 oldAppCookie,
641 type,
642 newBuckets,
643 newAppCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700644
Madan Jampani175e8fd2015-05-20 14:10:45 -0700645 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700646 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
647 clusterMsgSerializer::serialize,
648 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
649 if (error != null) {
650 log.warn("Failed to send request to master: {} to {}",
651 groupOp,
652 mastershipService.getMasterFor(deviceId), error);
653 }
654 //TODO: Send Group operation failure event
655 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700656 return;
657 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700658 log.debug("updateGroupDescription for device {} is getting handled locally",
659 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700660 updateGroupDescriptionInternal(deviceId,
661 oldAppCookie,
662 type,
663 newBuckets,
664 newAppCookie);
665 }
666
667 private void updateGroupDescriptionInternal(DeviceId deviceId,
alshabibb0285992016-03-28 23:30:37 -0700668 GroupKey oldAppCookie,
669 UpdateType type,
670 GroupBuckets newBuckets,
671 GroupKey newAppCookie) {
alshabib10580802015-02-18 18:30:33 -0800672 // Check if a group is existing with the provided key
673 Group oldGroup = getGroup(deviceId, oldAppCookie);
674 if (oldGroup == null) {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800675 log.warn("updateGroupDescriptionInternal: Group not found...strange. "
alshabibb0285992016-03-28 23:30:37 -0700676 + "GroupKey:{} DeviceId:{}", oldAppCookie, deviceId);
alshabib10580802015-02-18 18:30:33 -0800677 return;
678 }
679
680 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
681 type,
682 newBuckets);
683 if (newBucketList != null) {
684 // Create a new group object from the old group
685 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
686 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
687 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
688 oldGroup.deviceId(),
689 oldGroup.type(),
690 updatedBuckets,
691 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700692 oldGroup.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800693 oldGroup.appId());
694 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
695 updatedGroupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700696 log.debug("updateGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_UPDATE",
alshabibb0285992016-03-28 23:30:37 -0700697 oldGroup.id(),
698 oldGroup.deviceId(),
699 oldGroup.state());
alshabib10580802015-02-18 18:30:33 -0800700 newGroup.setState(GroupState.PENDING_UPDATE);
701 newGroup.setLife(oldGroup.life());
702 newGroup.setPackets(oldGroup.packets());
703 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700704 //Update the group entry in groupkey based map.
705 //Update to groupid based map will happen in the
706 //groupkey based map update listener
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700707 log.debug("updateGroupDescriptionInternal with type {}: Group updated with buckets",
708 type);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700709 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700710 put(new GroupStoreKeyMapKey(newGroup.deviceId(),
711 newGroup.appCookie()), newGroup);
alshabib10580802015-02-18 18:30:33 -0800712 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700713 } else {
714 log.warn("updateGroupDescriptionInternal with type {}: No "
alshabibb0285992016-03-28 23:30:37 -0700715 + "change in the buckets in update", type);
alshabib10580802015-02-18 18:30:33 -0800716 }
717 }
718
719 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
720 UpdateType type,
721 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300722 if (type == UpdateType.SET) {
723 return buckets.buckets();
724 }
725
Victor Silvadf1eeae2016-08-12 15:28:57 -0300726 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
727 List<GroupBucket> updatedBucketList = new ArrayList<>();
alshabib10580802015-02-18 18:30:33 -0800728 boolean groupDescUpdated = false;
729
730 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300731 List<GroupBucket> newBuckets = buckets.buckets();
732
733 // Add old buckets that will not be updated and check if any will be updated.
734 for (GroupBucket oldBucket : oldBuckets) {
735 int newBucketIndex = newBuckets.indexOf(oldBucket);
736
737 if (newBucketIndex != -1) {
738 GroupBucket newBucket = newBuckets.get(newBucketIndex);
739 if (!newBucket.hasSameParameters(oldBucket)) {
740 // Bucket will be updated
741 groupDescUpdated = true;
742 }
743 } else {
744 // Old bucket will remain the same - add it.
745 updatedBucketList.add(oldBucket);
alshabib10580802015-02-18 18:30:33 -0800746 }
747 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300748
749 // Add all new buckets
750 updatedBucketList.addAll(newBuckets);
751 if (!oldBuckets.containsAll(newBuckets)) {
752 groupDescUpdated = true;
753 }
754
alshabib10580802015-02-18 18:30:33 -0800755 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300756 List<GroupBucket> bucketsToRemove = buckets.buckets();
757
758 // Check which old buckets should remain
759 for (GroupBucket oldBucket : oldBuckets) {
760 if (!bucketsToRemove.contains(oldBucket)) {
761 updatedBucketList.add(oldBucket);
762 } else {
alshabib10580802015-02-18 18:30:33 -0800763 groupDescUpdated = true;
764 }
765 }
766 }
767
768 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300769 return updatedBucketList;
alshabib10580802015-02-18 18:30:33 -0800770 } else {
771 return null;
772 }
773 }
774
775 /**
776 * Triggers deleting the existing group entry.
777 *
alshabibb0285992016-03-28 23:30:37 -0700778 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800779 * @param appCookie the group key
780 */
781 @Override
782 public void deleteGroupDescription(DeviceId deviceId,
783 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700784 // Check if group to be deleted by a remote instance
785 if (mastershipService.
786 getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700787 log.debug("deleteGroupDescription: Device {} local role is not MASTER",
788 deviceId);
789 if (mastershipService.getMasterFor(deviceId) == null) {
790 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700791 + "Can not perform delete group operation",
792 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700793 //TODO: Send Group operation failure event
794 return;
795 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700796 GroupStoreMessage groupOp = GroupStoreMessage.
797 createGroupDeleteRequestMsg(deviceId,
798 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700799
Madan Jampani175e8fd2015-05-20 14:10:45 -0700800 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700801 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
802 clusterMsgSerializer::serialize,
803 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
804 if (error != null) {
805 log.warn("Failed to send request to master: {} to {}",
806 groupOp,
807 mastershipService.getMasterFor(deviceId), error);
808 }
809 //TODO: Send Group operation failure event
810 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700811 return;
812 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700813 log.debug("deleteGroupDescription in device {} is getting handled locally",
814 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700815 deleteGroupDescriptionInternal(deviceId, appCookie);
816 }
817
818 private void deleteGroupDescriptionInternal(DeviceId deviceId,
819 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800820 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700821 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800822 if (existing == null) {
823 return;
824 }
825
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700826 log.debug("deleteGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_DELETE",
alshabibb0285992016-03-28 23:30:37 -0700827 existing.id(),
828 existing.deviceId(),
829 existing.state());
alshabib10580802015-02-18 18:30:33 -0800830 synchronized (existing) {
831 existing.setState(GroupState.PENDING_DELETE);
Saurav Das80980c72016-03-23 11:22:49 -0700832 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700833 put(new GroupStoreKeyMapKey(existing.deviceId(), existing.appCookie()),
834 existing);
alshabib10580802015-02-18 18:30:33 -0800835 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700836 log.debug("deleteGroupDescriptionInternal: in device {} issuing GROUP_REMOVE_REQUESTED",
837 deviceId);
alshabib10580802015-02-18 18:30:33 -0800838 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
839 }
840
841 /**
842 * Stores a new group entry, or updates an existing entry.
843 *
844 * @param group group entry
845 */
846 @Override
847 public void addOrUpdateGroupEntry(Group group) {
848 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700849 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
850 group.id());
alshabib10580802015-02-18 18:30:33 -0800851 GroupEvent event = null;
852
853 if (existing != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800854 log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700855 group.id(),
856 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800857 synchronized (existing) {
alshabibb0285992016-03-28 23:30:37 -0700858 for (GroupBucket bucket : group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700859 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700860 existing.buckets().buckets()
alshabibb0285992016-03-28 23:30:37 -0700861 .stream()
862 .filter((existingBucket) -> (existingBucket.equals(bucket)))
863 .findFirst();
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700864 if (matchingBucket.isPresent()) {
865 ((StoredGroupBucketEntry) matchingBucket.
866 get()).setPackets(bucket.packets());
867 ((StoredGroupBucketEntry) matchingBucket.
868 get()).setBytes(bucket.bytes());
869 } else {
870 log.warn("addOrUpdateGroupEntry: No matching "
alshabibb0285992016-03-28 23:30:37 -0700871 + "buckets to update stats");
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700872 }
873 }
alshabib10580802015-02-18 18:30:33 -0800874 existing.setLife(group.life());
875 existing.setPackets(group.packets());
876 existing.setBytes(group.bytes());
alshabibb0285992016-03-28 23:30:37 -0700877 existing.setReferenceCount(group.referenceCount());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700878 if ((existing.state() == GroupState.PENDING_ADD) ||
alshabibb0285992016-03-28 23:30:37 -0700879 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800880 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700881 existing.id(),
882 existing.deviceId(),
883 existing.state());
alshabib10580802015-02-18 18:30:33 -0800884 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700885 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800886 event = new GroupEvent(Type.GROUP_ADDED, existing);
887 } else {
Saurav Das0fd79d92016-03-07 10:58:36 -0800888 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700889 existing.id(),
890 existing.deviceId(),
891 GroupState.PENDING_UPDATE);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700892 existing.setState(GroupState.ADDED);
893 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800894 event = new GroupEvent(Type.GROUP_UPDATED, existing);
895 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700896 //Re-PUT map entries to trigger map update events
897 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700898 put(new GroupStoreKeyMapKey(existing.deviceId(),
899 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800900 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700901 } else {
902 log.warn("addOrUpdateGroupEntry: Group update "
alshabibb0285992016-03-28 23:30:37 -0700903 + "happening for a non-existing entry in the map");
alshabib10580802015-02-18 18:30:33 -0800904 }
905
906 if (event != null) {
907 notifyDelegate(event);
908 }
909 }
910
911 /**
912 * Removes the group entry from store.
913 *
914 * @param group group entry
915 */
916 @Override
917 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700918 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
919 group.id());
alshabib10580802015-02-18 18:30:33 -0800920
921 if (existing != null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700922 log.debug("removeGroupEntry: removing group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700923 group.id(),
924 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700925 //Removal from groupid based map will happen in the
926 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700927 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
928 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800929 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700930 } else {
931 log.warn("removeGroupEntry for {} in device{} is "
alshabibb0285992016-03-28 23:30:37 -0700932 + "not existing in our maps",
933 group.id(),
934 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800935 }
936 }
937
Victor Silva4e8b7832016-08-17 17:11:19 -0300938 private void purgeGroupEntries(Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entries) {
939 entries.forEach(entry -> {
940 groupStoreEntriesByKey.remove(entry.getKey());
941 });
942 }
943
alshabib10580802015-02-18 18:30:33 -0800944 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800945 public void purgeGroupEntry(DeviceId deviceId) {
Victor Silva4e8b7832016-08-17 17:11:19 -0300946 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entriesPendingRemove =
Charles Chan0c7c43b2016-01-14 17:39:20 -0800947 new HashSet<>();
948
Madan Jampani0b847532016-03-03 13:44:15 -0800949 getGroupStoreKeyMap().entrySet().stream()
Charles Chan0c7c43b2016-01-14 17:39:20 -0800950 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Victor Silva4e8b7832016-08-17 17:11:19 -0300951 .forEach(entriesPendingRemove::add);
Charles Chan0c7c43b2016-01-14 17:39:20 -0800952
Victor Silva4e8b7832016-08-17 17:11:19 -0300953 purgeGroupEntries(entriesPendingRemove);
954 }
955
956 @Override
957 public void purgeGroupEntries() {
958 purgeGroupEntries(getGroupStoreKeyMap().entrySet());
Charles Chan0c7c43b2016-01-14 17:39:20 -0800959 }
960
961 @Override
alshabib10580802015-02-18 18:30:33 -0800962 public void deviceInitialAuditCompleted(DeviceId deviceId,
963 boolean completed) {
964 synchronized (deviceAuditStatus) {
965 if (completed) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700966 log.debug("AUDIT completed for device {}",
967 deviceId);
alshabib10580802015-02-18 18:30:33 -0800968 deviceAuditStatus.put(deviceId, true);
969 // Execute all pending group requests
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700970 List<StoredGroupEntry> pendingGroupRequests =
971 getPendingGroupKeyTable().values()
alshabibb0285992016-03-28 23:30:37 -0700972 .stream()
973 .filter(g -> g.deviceId().equals(deviceId))
974 .collect(Collectors.toList());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700975 log.debug("processing pending group add requests for device {} and number of pending requests {}",
alshabibb0285992016-03-28 23:30:37 -0700976 deviceId,
977 pendingGroupRequests.size());
978 for (Group group : pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -0800979 GroupDescription tmp = new DefaultGroupDescription(
980 group.deviceId(),
981 group.type(),
982 group.buckets(),
983 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700984 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800985 group.appId());
986 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700987 getPendingGroupKeyTable().
alshabibb0285992016-03-28 23:30:37 -0700988 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800989 }
alshabib10580802015-02-18 18:30:33 -0800990 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700991 Boolean audited = deviceAuditStatus.get(deviceId);
992 if (audited != null && audited) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700993 log.debug("Clearing AUDIT status for device {}", deviceId);
alshabib10580802015-02-18 18:30:33 -0800994 deviceAuditStatus.put(deviceId, false);
995 }
996 }
997 }
998 }
999
1000 @Override
1001 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
1002 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001003 Boolean audited = deviceAuditStatus.get(deviceId);
1004 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -08001005 }
1006 }
1007
1008 @Override
1009 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
1010
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001011 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
1012 operation.groupId());
alshabib10580802015-02-18 18:30:33 -08001013
1014 if (existing == null) {
1015 log.warn("No group entry with ID {} found ", operation.groupId());
1016 return;
1017 }
1018
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001019 log.warn("groupOperationFailed: group operation {} failed"
alshabibb0285992016-03-28 23:30:37 -07001020 + "for group {} in device {} with code {}",
1021 operation.opType(),
1022 existing.id(),
1023 existing.deviceId(),
1024 operation.failureCode());
Saurav Das0fd79d92016-03-07 10:58:36 -08001025 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
1026 log.warn("Current extraneous groups in device:{} are: {}",
1027 deviceId,
1028 getExtraneousGroups(deviceId));
Saurav Das8be4e3a2016-03-11 17:19:07 -08001029 if (operation.buckets().equals(existing.buckets())) {
1030 if (existing.state() == GroupState.PENDING_ADD) {
1031 log.info("GROUP_EXISTS: GroupID and Buckets match for group in pending "
alshabibb0285992016-03-28 23:30:37 -07001032 + "add state - moving to ADDED for group {} in device {}",
1033 existing.id(), deviceId);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001034 addOrUpdateGroupEntry(existing);
1035 return;
1036 } else {
1037 log.warn("GROUP EXISTS: Group ID matched but buckets did not. "
alshabibb0285992016-03-28 23:30:37 -07001038 + "Operation: {} Existing: {}", operation.buckets(),
1039 existing.buckets());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001040 }
1041 }
Saurav Das0fd79d92016-03-07 10:58:36 -08001042 }
alshabib10580802015-02-18 18:30:33 -08001043 switch (operation.opType()) {
1044 case ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001045 if (existing.state() == GroupState.PENDING_ADD) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001046 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
1047 log.warn("groupOperationFailed: cleaningup "
alshabibb0285992016-03-28 23:30:37 -07001048 + "group {} from store in device {}....",
1049 existing.id(),
1050 existing.deviceId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001051 //Removal from groupid based map will happen in the
1052 //map update listener
1053 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1054 existing.appCookie()));
1055 }
alshabib10580802015-02-18 18:30:33 -08001056 break;
1057 case MODIFY:
1058 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
1059 break;
1060 case DELETE:
1061 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
1062 break;
1063 default:
1064 log.warn("Unknown group operation type {}", operation.opType());
1065 }
alshabib10580802015-02-18 18:30:33 -08001066 }
1067
1068 @Override
1069 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001070 log.debug("add/update extraneous group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001071 group.id(),
1072 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001073 ConcurrentMap<GroupId, Group> extraneousIdTable =
1074 getExtraneousGroupIdTable(group.deviceId());
1075 extraneousIdTable.put(group.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -07001076 // Don't remove the extraneous groups, instead re-use it when
1077 // a group request comes with the same set of buckets
alshabib10580802015-02-18 18:30:33 -08001078 }
1079
1080 @Override
1081 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001082 log.debug("remove extraneous group entry {} of device {} from store",
alshabibb0285992016-03-28 23:30:37 -07001083 group.id(),
1084 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001085 ConcurrentMap<GroupId, Group> extraneousIdTable =
1086 getExtraneousGroupIdTable(group.deviceId());
1087 extraneousIdTable.remove(group.id());
1088 }
1089
1090 @Override
1091 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
1092 // flatten and make iterator unmodifiable
1093 return FluentIterable.from(
1094 getExtraneousGroupIdTable(deviceId).values());
1095 }
1096
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001097 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001098 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001099 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001100 private class GroupStoreKeyMapListener implements
Madan Jampani0b847532016-03-03 13:44:15 -08001101 MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001102
1103 @Override
Madan Jampani0b847532016-03-03 13:44:15 -08001104 public void event(MapEvent<GroupStoreKeyMapKey, StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001105 GroupEvent groupEvent = null;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001106 GroupStoreKeyMapKey key = mapEvent.key();
Madan Jampani0b847532016-03-03 13:44:15 -08001107 StoredGroupEntry group = Versioned.valueOrNull(mapEvent.newValue());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001108 if ((key == null) && (group == null)) {
1109 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001110 + "event {} with null entry", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001111 return;
1112 } else if (group == null) {
1113 group = getGroupIdTable(key.deviceId()).values()
1114 .stream()
1115 .filter((storedGroup) -> (storedGroup.appCookie().equals(key.appCookie)))
Yuta HIGUCHI6e5f4702016-11-21 11:42:11 -08001116 .findFirst().orElse(null);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001117 if (group == null) {
1118 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001119 + "event {} with null entry... can not process", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001120 return;
1121 }
1122 }
1123 log.trace("received groupid map event {} for id {} in device {}",
1124 mapEvent.type(),
1125 group.id(),
jaegonkim68e080c2016-12-01 22:31:01 +09001126 (key != null ? key.deviceId() : null));
Madan Jampani0b847532016-03-03 13:44:15 -08001127 if (mapEvent.type() == MapEvent.Type.INSERT || mapEvent.type() == MapEvent.Type.UPDATE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001128 // Update the group ID table
1129 getGroupIdTable(group.deviceId()).put(group.id(), group);
Madan Jampani0b847532016-03-03 13:44:15 -08001130 StoredGroupEntry value = Versioned.valueOrNull(mapEvent.newValue());
1131 if (value.state() == Group.GroupState.ADDED) {
1132 if (value.isGroupStateAddedFirstTime()) {
1133 groupEvent = new GroupEvent(Type.GROUP_ADDED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001134 log.trace("Received first time GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001135 group.id(),
1136 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001137 } else {
Madan Jampani0b847532016-03-03 13:44:15 -08001138 groupEvent = new GroupEvent(Type.GROUP_UPDATED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001139 log.trace("Received following GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001140 group.id(),
1141 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001142 }
1143 }
Madan Jampani0b847532016-03-03 13:44:15 -08001144 } else if (mapEvent.type() == MapEvent.Type.REMOVE) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001145 groupEvent = new GroupEvent(Type.GROUP_REMOVED, group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001146 // Remove the entry from the group ID table
1147 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001148 }
1149
1150 if (groupEvent != null) {
1151 notifyDelegate(groupEvent);
1152 }
1153 }
1154 }
Madan Jampani01e05fb2015-08-13 13:29:36 -07001155
helenyrwua1c41152016-08-18 16:16:14 -07001156 private void processGroupMessage(GroupStoreMessage message) {
1157 if (message.type() == GroupStoreMessage.Type.FAILOVER) {
1158 // FIXME: groupStoreEntriesByKey inaccessible here
1159 getGroupIdTable(message.deviceId()).values()
1160 .stream()
1161 .filter((storedGroup) -> (storedGroup.appCookie().equals(message.appCookie())))
1162 .findFirst().ifPresent(group -> notifyDelegate(new GroupEvent(Type.GROUP_BUCKET_FAILOVER, group)));
1163 }
1164 }
1165
Madan Jampani01e05fb2015-08-13 13:29:36 -07001166 private void process(GroupStoreMessage groupOp) {
1167 log.debug("Received remote group operation {} request for device {}",
alshabibb0285992016-03-28 23:30:37 -07001168 groupOp.type(),
1169 groupOp.deviceId());
1170 if (!mastershipService.isLocalMaster(groupOp.deviceId())) {
1171 log.warn("This node is not MASTER for device {}", groupOp.deviceId());
1172 return;
1173 }
1174 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
1175 storeGroupDescriptionInternal(groupOp.groupDesc());
1176 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
1177 updateGroupDescriptionInternal(groupOp.deviceId(),
1178 groupOp.appCookie(),
1179 groupOp.updateType(),
1180 groupOp.updateBuckets(),
1181 groupOp.newAppCookie());
1182 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
1183 deleteGroupDescriptionInternal(groupOp.deviceId(),
1184 groupOp.appCookie());
1185 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001186 }
1187
1188 /**
1189 * Flattened map key to be used to store group entries.
1190 */
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001191 protected static class GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001192 private final DeviceId deviceId;
1193
1194 public GroupStoreMapKey(DeviceId deviceId) {
1195 this.deviceId = deviceId;
1196 }
1197
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001198 public DeviceId deviceId() {
1199 return deviceId;
1200 }
1201
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001202 @Override
1203 public boolean equals(Object o) {
1204 if (this == o) {
1205 return true;
1206 }
1207 if (!(o instanceof GroupStoreMapKey)) {
1208 return false;
1209 }
1210 GroupStoreMapKey that = (GroupStoreMapKey) o;
1211 return this.deviceId.equals(that.deviceId);
1212 }
1213
1214 @Override
1215 public int hashCode() {
1216 int result = 17;
1217
1218 result = 31 * result + Objects.hash(this.deviceId);
1219
1220 return result;
1221 }
1222 }
1223
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001224 protected static class GroupStoreKeyMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001225 private final GroupKey appCookie;
alshabibb0285992016-03-28 23:30:37 -07001226
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001227 public GroupStoreKeyMapKey(DeviceId deviceId,
1228 GroupKey appCookie) {
1229 super(deviceId);
1230 this.appCookie = appCookie;
1231 }
1232
1233 @Override
1234 public boolean equals(Object o) {
1235 if (this == o) {
1236 return true;
1237 }
1238 if (!(o instanceof GroupStoreKeyMapKey)) {
1239 return false;
1240 }
1241 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
1242 return (super.equals(that) &&
1243 this.appCookie.equals(that.appCookie));
1244 }
1245
1246 @Override
1247 public int hashCode() {
1248 int result = 17;
1249
1250 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
1251
1252 return result;
1253 }
1254 }
1255
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001256 protected static class GroupStoreIdMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001257 private final GroupId groupId;
alshabibb0285992016-03-28 23:30:37 -07001258
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001259 public GroupStoreIdMapKey(DeviceId deviceId,
1260 GroupId groupId) {
1261 super(deviceId);
1262 this.groupId = groupId;
1263 }
1264
1265 @Override
1266 public boolean equals(Object o) {
1267 if (this == o) {
1268 return true;
1269 }
1270 if (!(o instanceof GroupStoreIdMapKey)) {
1271 return false;
1272 }
1273 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1274 return (super.equals(that) &&
1275 this.groupId.equals(that.groupId));
1276 }
1277
1278 @Override
1279 public int hashCode() {
1280 int result = 17;
1281
1282 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1283
1284 return result;
1285 }
1286 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001287
1288 @Override
1289 public void pushGroupMetrics(DeviceId deviceId,
1290 Collection<Group> groupEntries) {
1291 boolean deviceInitialAuditStatus =
1292 deviceInitialAuditStatus(deviceId);
1293 Set<Group> southboundGroupEntries =
1294 Sets.newHashSet(groupEntries);
1295 Set<StoredGroupEntry> storedGroupEntries =
1296 Sets.newHashSet(getStoredGroups(deviceId));
1297 Set<Group> extraneousStoredEntries =
1298 Sets.newHashSet(getExtraneousGroups(deviceId));
1299
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001300 if (log.isTraceEnabled()) {
1301 log.trace("pushGroupMetrics: Displaying all ({}) southboundGroupEntries for device {}",
1302 southboundGroupEntries.size(),
1303 deviceId);
1304 for (Group group : southboundGroupEntries) {
1305 log.trace("Group {} in device {}", group, deviceId);
1306 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001307
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001308 log.trace("Displaying all ({}) stored group entries for device {}",
1309 storedGroupEntries.size(),
1310 deviceId);
1311 for (StoredGroupEntry group : storedGroupEntries) {
1312 log.trace("Stored Group {} for device {}", group, deviceId);
1313 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001314 }
1315
alshabibb0285992016-03-28 23:30:37 -07001316 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1317
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001318 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
1319 Group group = it2.next();
1320 if (storedGroupEntries.remove(group)) {
1321 // we both have the group, let's update some info then.
1322 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001323 group.id(), deviceId);
1324
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001325 groupAdded(group);
1326 it2.remove();
1327 }
1328 }
1329 for (Group group : southboundGroupEntries) {
1330 if (getGroup(group.deviceId(), group.id()) != null) {
1331 // There is a group existing with the same id
1332 // It is possible that group update is
1333 // in progress while we got a stale info from switch
1334 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001335 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001336 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001337 + "Group exists in ID based table while "
1338 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001339 }
1340 } else {
1341 // there are groups in the switch that aren't in the store
1342 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001343 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001344 extraneousStoredEntries.remove(group);
1345 extraneousGroup(group);
1346 }
1347 }
1348 for (Group group : storedGroupEntries) {
1349 // there are groups in the store that aren't in the switch
1350 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001351 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001352 groupMissing(group);
1353 }
1354 for (Group group : extraneousStoredEntries) {
1355 // there are groups in the extraneous store that
1356 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001357 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001358 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001359 removeExtraneousGroupEntry(group);
1360 }
1361
1362 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001363 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001364 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001365 deviceInitialAuditCompleted(deviceId, true);
1366 }
1367 }
1368
helenyrwu89470f12016-08-12 13:18:10 -07001369 @Override
1370 public void notifyOfFailovers(Collection<Group> failoverGroups) {
helenyrwu89470f12016-08-12 13:18:10 -07001371 failoverGroups.forEach(group -> {
1372 if (group.type() == Group.Type.FAILOVER) {
helenyrwua1c41152016-08-18 16:16:14 -07001373 groupTopic.publish(GroupStoreMessage.createGroupFailoverMsg(
1374 group.deviceId(), group));
helenyrwu89470f12016-08-12 13:18:10 -07001375 }
1376 });
helenyrwu89470f12016-08-12 13:18:10 -07001377 }
1378
alshabibb0285992016-03-28 23:30:37 -07001379 private void garbageCollect(DeviceId deviceId,
1380 Set<Group> southboundGroupEntries,
1381 Set<StoredGroupEntry> storedGroupEntries) {
1382 if (!garbageCollect) {
1383 return;
1384 }
1385
1386 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1387 while (it.hasNext()) {
1388 StoredGroupEntry group = it.next();
1389 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1390 log.debug("Garbage collecting group {} on {}", group, deviceId);
1391 deleteGroupDescription(deviceId, group.appCookie());
1392 southboundGroupEntries.remove(group);
1393 it.remove();
1394 }
1395 }
1396 }
1397
1398 private boolean checkGroupRefCount(Group group) {
1399 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1400 }
1401
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001402 private void groupMissing(Group group) {
1403 switch (group.state()) {
1404 case PENDING_DELETE:
1405 log.debug("Group {} delete confirmation from device {}",
1406 group, group.deviceId());
1407 removeGroupEntry(group);
1408 break;
1409 case ADDED:
1410 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001411 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001412 case PENDING_UPDATE:
1413 log.debug("Group {} is in store but not on device {}",
1414 group, group.deviceId());
1415 StoredGroupEntry existing =
1416 getStoredGroupEntry(group.deviceId(), group.id());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001417 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
alshabibb0285992016-03-28 23:30:37 -07001418 existing.id(),
1419 existing.deviceId(),
1420 existing.state());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001421 existing.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001422 //Re-PUT map entries to trigger map update events
1423 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -07001424 put(new GroupStoreKeyMapKey(existing.deviceId(),
1425 existing.appCookie()), existing);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001426 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1427 group));
1428 break;
1429 default:
1430 log.debug("Group {} has not been installed.", group);
1431 break;
1432 }
1433 }
1434
1435 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001436 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001437 group, group.deviceId());
1438 addOrUpdateExtraneousGroupEntry(group);
1439 }
1440
1441 private void groupAdded(Group group) {
1442 log.trace("Group {} Added or Updated in device {}",
1443 group, group.deviceId());
1444 addOrUpdateGroupEntry(group);
1445 }
alshabib10580802015-02-18 18:30:33 -08001446}