blob: 996356a301faab318073400232302052fc763185 [file] [log] [blame]
alshabib10580802015-02-18 18:30:33 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
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;
alshabib10580802015-02-18 18:30:33 -080031import org.onlab.util.NewConcurrentHashMap;
alshabibb0285992016-03-28 23:30:37 -070032import org.onosproject.cfg.ComponentConfigService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070033import org.onosproject.cluster.ClusterService;
Charles Chanf4838a72015-12-07 18:13:45 -080034import org.onosproject.cluster.NodeId;
alshabib10580802015-02-18 18:30:33 -080035import org.onosproject.core.DefaultGroupId;
36import org.onosproject.core.GroupId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070037import org.onosproject.mastership.MastershipService;
alshabib10580802015-02-18 18:30:33 -080038import org.onosproject.net.DeviceId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070039import org.onosproject.net.MastershipRole;
alshabib10580802015-02-18 18:30:33 -080040import org.onosproject.net.group.DefaultGroup;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070041import org.onosproject.net.group.DefaultGroupBucket;
alshabib10580802015-02-18 18:30:33 -080042import org.onosproject.net.group.DefaultGroupDescription;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070043import org.onosproject.net.group.DefaultGroupKey;
alshabib10580802015-02-18 18:30:33 -080044import org.onosproject.net.group.Group;
45import org.onosproject.net.group.Group.GroupState;
46import org.onosproject.net.group.GroupBucket;
47import org.onosproject.net.group.GroupBuckets;
48import org.onosproject.net.group.GroupDescription;
49import org.onosproject.net.group.GroupEvent;
50import org.onosproject.net.group.GroupEvent.Type;
51import org.onosproject.net.group.GroupKey;
52import org.onosproject.net.group.GroupOperation;
53import org.onosproject.net.group.GroupStore;
54import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070055import org.onosproject.net.group.StoredGroupBucketEntry;
alshabib10580802015-02-18 18:30:33 -080056import org.onosproject.net.group.StoredGroupEntry;
57import org.onosproject.store.AbstractStore;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070058import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070059import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani0b847532016-03-03 13:44:15 -080060import org.onosproject.store.service.ConsistentMap;
61import org.onosproject.store.service.MapEvent;
62import org.onosproject.store.service.MapEventListener;
alshabibb0285992016-03-28 23:30:37 -070063import org.onosproject.store.service.MultiValuedTimestamp;
Madan Jampani0b847532016-03-03 13:44:15 -080064import org.onosproject.store.service.Serializer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070065import org.onosproject.store.service.StorageService;
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;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070092import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
alshabibb0285992016-03-28 23:30:37 -070093import static org.onlab.util.Tools.get;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070094import static org.onlab.util.Tools.groupedThreads;
95import static org.slf4j.LoggerFactory.getLogger;
alshabib10580802015-02-18 18:30:33 -080096
97/**
Saurav Das0fd79d92016-03-07 10:58:36 -080098 * Manages inventory of group entries using distributed group stores from the
99 * storage service.
alshabib10580802015-02-18 18:30:33 -0800100 */
101@Component(immediate = true)
102@Service
103public class DistributedGroupStore
104 extends AbstractStore<GroupEvent, GroupStoreDelegate>
105 implements GroupStore {
106
107 private final Logger log = getLogger(getClass());
108
alshabibb0285992016-03-28 23:30:37 -0700109 private static final boolean GARBAGE_COLLECT = false;
110 private static final int GC_THRESH = 6;
111
alshabib10580802015-02-18 18:30:33 -0800112 private final int dummyId = 0xffffffff;
113 private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
114
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected ClusterCommunicationService clusterCommunicator;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected ClusterService clusterService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700122 protected StorageService storageService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700125 protected MastershipService mastershipService;
126
alshabibb0285992016-03-28 23:30:37 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected ComponentConfigService cfgService;
129
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700130 // Per device group table with (device id + app cookie) as key
Madan Jampani0b847532016-03-03 13:44:15 -0800131 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700132 StoredGroupEntry> groupStoreEntriesByKey = null;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700133 // Per device group table with (device id + group id) as key
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700134 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
alshabibb0285992016-03-28 23:30:37 -0700135 groupEntriesById = new ConcurrentHashMap<>();
Madan Jampani0b847532016-03-03 13:44:15 -0800136 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700137 StoredGroupEntry> auditPendingReqQueue = null;
alshabib10580802015-02-18 18:30:33 -0800138 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
139 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700140 private ExecutorService messageHandlingExecutor;
141 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
alshabib10580802015-02-18 18:30:33 -0800142
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
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700147 private KryoNamespace.Builder kryoBuilder = null;
148
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700149 private KryoNamespace clusterMsgSerializer;
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());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700163 kryoBuilder = new KryoNamespace.Builder()
alshabibb0285992016-03-28 23:30:37 -0700164 .register(KryoNamespaces.API)
165 .register(DefaultGroup.class,
166 DefaultGroupBucket.class,
167 DefaultGroupDescription.class,
168 DefaultGroupKey.class,
169 GroupDescription.Type.class,
170 Group.GroupState.class,
171 GroupBuckets.class,
172 DefaultGroupId.class,
173 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 Yuta180d70f2015-10-01 16:13:56 -0700183 clusterMsgSerializer = kryoBuilder.build();
184
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700185 messageHandlingExecutor = Executors.
186 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
187 groupedThreads("onos/store/group",
188 "message-handlers"));
Madan Jampani01e05fb2015-08-13 13:29:36 -0700189
190 clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
alshabibb0285992016-03-28 23:30:37 -0700191 clusterMsgSerializer::deserialize,
192 this::process,
193 messageHandlingExecutor);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700194
Madan Jampani0b847532016-03-03 13:44:15 -0800195 log.debug("Creating Consistent map onos-group-store-keymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700196
Madan Jampani0b847532016-03-03 13:44:15 -0800197 groupStoreEntriesByKey = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
198 .withName("onos-group-store-keymap")
199 .withSerializer(Serializer.using(kryoBuilder.build()))
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700200 .build();
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700201 groupStoreEntriesByKey.addListener(new GroupStoreKeyMapListener());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700202 log.debug("Current size of groupstorekeymap:{}",
203 groupStoreEntriesByKey.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700204
Madan Jampani0b847532016-03-03 13:44:15 -0800205 log.debug("Creating Consistent map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700206
Madan Jampani0b847532016-03-03 13:44:15 -0800207 auditPendingReqQueue = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
208 .withName("onos-pending-group-keymap")
209 .withSerializer(Serializer.using(kryoBuilder.build()))
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700210 .build();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700211 log.debug("Current size of pendinggroupkeymap:{}",
212 auditPendingReqQueue.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700213
alshabib10580802015-02-18 18:30:33 -0800214 log.info("Started");
215 }
216
217 @Deactivate
218 public void deactivate() {
alshabibb0285992016-03-28 23:30:37 -0700219 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700220 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
Thomas Vachuska152f9fd2015-04-02 16:28:13 -0700221 groupStoreEntriesByKey.destroy();
Thomas Vachuska152f9fd2015-04-02 16:28:13 -0700222 auditPendingReqQueue.destroy();
alshabib10580802015-02-18 18:30:33 -0800223 log.info("Stopped");
224 }
225
alshabibb0285992016-03-28 23:30:37 -0700226 @Modified
227 public void modified(ComponentContext context) {
228 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
229
230 try {
231 String s = get(properties, "garbageCollect");
232 garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT : Boolean.parseBoolean(s.trim());
233
234 s = get(properties, "gcThresh");
235 gcThresh = isNullOrEmpty(s) ? GC_THRESH : Integer.parseInt(s.trim());
236 } catch (Exception e) {
237 gcThresh = GC_THRESH;
238 garbageCollect = GARBAGE_COLLECT;
239 }
240 }
241
alshabib10580802015-02-18 18:30:33 -0800242 private static NewConcurrentHashMap<GroupId, Group>
alshabibb0285992016-03-28 23:30:37 -0700243 lazyEmptyExtraneousGroupIdTable() {
alshabib10580802015-02-18 18:30:33 -0800244 return NewConcurrentHashMap.<GroupId, Group>ifNeeded();
245 }
246
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700247 private static NewConcurrentHashMap<GroupId, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700248 lazyEmptyGroupIdTable() {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700249 return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded();
250 }
251
alshabib10580802015-02-18 18:30:33 -0800252 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700253 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800254 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700255 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800256 */
Madan Jampani0b847532016-03-03 13:44:15 -0800257 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700258 getGroupStoreKeyMap() {
Madan Jampani0b847532016-03-03 13:44:15 -0800259 return groupStoreEntriesByKey.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800260 }
261
262 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700263 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800264 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700265 * @param deviceId identifier of the device
266 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800267 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700268 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
269 return createIfAbsentUnchecked(groupEntriesById,
270 deviceId, lazyEmptyGroupIdTable());
alshabib10580802015-02-18 18:30:33 -0800271 }
272
273 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700274 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800275 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700276 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800277 */
Madan Jampani0b847532016-03-03 13:44:15 -0800278 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700279 getPendingGroupKeyTable() {
Madan Jampani0b847532016-03-03 13:44:15 -0800280 return auditPendingReqQueue.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800281 }
282
283 /**
284 * Returns the extraneous group id table for specified device.
285 *
286 * @param deviceId identifier of the device
287 * @return Map representing group key table of given device.
288 */
289 private ConcurrentMap<GroupId, Group>
290 getExtraneousGroupIdTable(DeviceId deviceId) {
291 return createIfAbsentUnchecked(extraneousGroupEntriesById,
292 deviceId,
293 lazyEmptyExtraneousGroupIdTable());
294 }
295
296 /**
297 * Returns the number of groups for the specified device in the store.
298 *
299 * @return number of groups for the specified device
300 */
301 @Override
302 public int getGroupCount(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700303 return (getGroups(deviceId) != null) ?
alshabibb0285992016-03-28 23:30:37 -0700304 Iterables.size(getGroups(deviceId)) : 0;
alshabib10580802015-02-18 18:30:33 -0800305 }
306
307 /**
308 * Returns the groups associated with a device.
309 *
310 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800311 * @return the group entries
312 */
313 @Override
314 public Iterable<Group> getGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800315 // Let ImmutableSet.copyOf do the type conversion
316 return ImmutableSet.copyOf(getStoredGroups(deviceId));
alshabib10580802015-02-18 18:30:33 -0800317 }
318
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700319 private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800320 NodeId master = mastershipService.getMasterFor(deviceId);
321 if (master == null) {
322 log.debug("Failed to getGroups: No master for {}", deviceId);
323 return Collections.emptySet();
324 }
325
326 Set<StoredGroupEntry> storedGroups = getGroupStoreKeyMap().values()
327 .stream()
328 .filter(input -> input.deviceId().equals(deviceId))
329 .collect(Collectors.toSet());
330 return ImmutableSet.copyOf(storedGroups);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700331 }
332
alshabib10580802015-02-18 18:30:33 -0800333 /**
334 * Returns the stored group entry.
335 *
alshabibb0285992016-03-28 23:30:37 -0700336 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800337 * @param appCookie the group key
alshabib10580802015-02-18 18:30:33 -0800338 * @return a group associated with the key
339 */
340 @Override
341 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700342 return getStoredGroupEntry(deviceId, appCookie);
343 }
344
345 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
346 GroupKey appCookie) {
347 return getGroupStoreKeyMap().get(new GroupStoreKeyMapKey(deviceId,
348 appCookie));
349 }
350
351 @Override
352 public Group getGroup(DeviceId deviceId, GroupId groupId) {
353 return getStoredGroupEntry(deviceId, groupId);
354 }
355
356 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
357 GroupId groupId) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700358 return getGroupIdTable(deviceId).get(groupId);
alshabib10580802015-02-18 18:30:33 -0800359 }
360
361 private int getFreeGroupIdValue(DeviceId deviceId) {
362 int freeId = groupIdGen.incrementAndGet();
363
364 while (true) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700365 Group existing = getGroup(deviceId, new DefaultGroupId(freeId));
alshabib10580802015-02-18 18:30:33 -0800366 if (existing == null) {
367 existing = (
368 extraneousGroupEntriesById.get(deviceId) != null) ?
369 extraneousGroupEntriesById.get(deviceId).
370 get(new DefaultGroupId(freeId)) :
371 null;
372 }
373 if (existing != null) {
374 freeId = groupIdGen.incrementAndGet();
375 } else {
376 break;
377 }
378 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700379 log.debug("getFreeGroupIdValue: Next Free ID is {}", freeId);
alshabib10580802015-02-18 18:30:33 -0800380 return freeId;
381 }
382
383 /**
384 * Stores a new group entry using the information from group description.
385 *
386 * @param groupDesc group description to be used to create group entry
387 */
388 @Override
389 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700390 log.debug("In storeGroupDescription");
alshabib10580802015-02-18 18:30:33 -0800391 // Check if a group is existing with the same key
Saurav Das8a0732e2015-11-20 15:27:53 -0800392 Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
393 if (existingGroup != null) {
Saurav Das4ce45962015-11-24 23:21:05 -0800394 log.warn("Group already exists with the same key {} in dev:{} with id:0x{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800395 groupDesc.appCookie(), groupDesc.deviceId(),
396 Integer.toHexString(existingGroup.id().id()));
alshabib10580802015-02-18 18:30:33 -0800397 return;
398 }
399
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700400 // Check if group to be created by a remote instance
Madan Jampani175e8fd2015-05-20 14:10:45 -0700401 if (mastershipService.getLocalRole(groupDesc.deviceId()) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700402 log.debug("storeGroupDescription: Device {} local role is not MASTER",
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700403 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700404 if (mastershipService.getMasterFor(groupDesc.deviceId()) == null) {
405 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700406 + "Can not perform add group operation",
407 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700408 //TODO: Send Group operation failure event
409 return;
410 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700411 GroupStoreMessage groupOp = GroupStoreMessage.
412 createGroupAddRequestMsg(groupDesc.deviceId(),
413 groupDesc);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700414
Madan Jampani175e8fd2015-05-20 14:10:45 -0700415 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700416 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
417 clusterMsgSerializer::serialize,
418 mastershipService.getMasterFor(groupDesc.deviceId()))
419 .whenComplete((result, error) -> {
Madan Jampani175e8fd2015-05-20 14:10:45 -0700420 if (error != null) {
421 log.warn("Failed to send request to master: {} to {}",
alshabibb0285992016-03-28 23:30:37 -0700422 groupOp,
423 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700424 //TODO: Send Group operation failure event
425 } else {
426 log.debug("Sent Group operation request for device {} "
alshabibb0285992016-03-28 23:30:37 -0700427 + "to remote MASTER {}",
428 groupDesc.deviceId(),
429 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700430 }
431 });
alshabib10580802015-02-18 18:30:33 -0800432 return;
433 }
434
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700435 log.debug("Store group for device {} is getting handled locally",
436 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800437 storeGroupDescriptionInternal(groupDesc);
438 }
439
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700440 private Group getMatchingExtraneousGroupbyId(DeviceId deviceId, Integer groupId) {
441 ConcurrentMap<GroupId, Group> extraneousMap =
442 extraneousGroupEntriesById.get(deviceId);
443 if (extraneousMap == null) {
444 return null;
445 }
446 return extraneousMap.get(new DefaultGroupId(groupId));
447 }
448
449 private Group getMatchingExtraneousGroupbyBuckets(DeviceId deviceId,
450 GroupBuckets buckets) {
451 ConcurrentMap<GroupId, Group> extraneousMap =
452 extraneousGroupEntriesById.get(deviceId);
453 if (extraneousMap == null) {
454 return null;
455 }
456
alshabibb0285992016-03-28 23:30:37 -0700457 for (Group extraneousGroup : extraneousMap.values()) {
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700458 if (extraneousGroup.buckets().equals(buckets)) {
459 return extraneousGroup;
460 }
461 }
462 return null;
463 }
464
alshabib10580802015-02-18 18:30:33 -0800465 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
466 // Check if a group is existing with the same key
467 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
468 return;
469 }
470
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700471 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
472 // Device group audit has not completed yet
473 // Add this group description to pending group key table
474 // Create a group entry object with Dummy Group ID
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700475 log.debug("storeGroupDescriptionInternal: Device {} AUDIT pending...Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700476 groupDesc.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700477 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
478 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
Madan Jampani0b847532016-03-03 13:44:15 -0800479 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700480 getPendingGroupKeyTable();
481 pendingKeyTable.put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
482 groupDesc.appCookie()),
483 group);
484 return;
485 }
486
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700487 Group matchingExtraneousGroup = null;
488 if (groupDesc.givenGroupId() != null) {
489 //Check if there is a extraneous group existing with the same Id
490 matchingExtraneousGroup = getMatchingExtraneousGroupbyId(
alshabibb0285992016-03-28 23:30:37 -0700491 groupDesc.deviceId(), groupDesc.givenGroupId());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700492 if (matchingExtraneousGroup != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800493 log.debug("storeGroupDescriptionInternal: Matching extraneous group "
alshabibb0285992016-03-28 23:30:37 -0700494 + "found in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700495 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800496 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700497 //Check if the group buckets matches with user provided buckets
498 if (matchingExtraneousGroup.buckets().equals(groupDesc.buckets())) {
499 //Group is already existing with the same buckets and Id
500 // Create a group entry object
Saurav Das0fd79d92016-03-07 10:58:36 -0800501 log.debug("storeGroupDescriptionInternal: Buckets also matching "
alshabibb0285992016-03-28 23:30:37 -0700502 + "in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700503 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800504 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700505 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700506 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700507 // Insert the newly created group entry into key and id maps
508 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700509 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
510 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700511 // Ensure it also inserted into group id based table to
512 // avoid any chances of duplication in group id generation
513 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700514 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700515 addOrUpdateGroupEntry(matchingExtraneousGroup);
516 removeExtraneousGroupEntry(matchingExtraneousGroup);
517 return;
518 } else {
519 //Group buckets are not matching. Update group
520 //with user provided buckets.
Saurav Das0fd79d92016-03-07 10:58:36 -0800521 log.debug("storeGroupDescriptionInternal: Buckets are not "
alshabibb0285992016-03-28 23:30:37 -0700522 + "matching in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700523 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800524 Integer.toHexString(groupDesc.givenGroupId()));
525 StoredGroupEntry modifiedGroup = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700526 matchingExtraneousGroup.id(), groupDesc);
Saurav Das0fd79d92016-03-07 10:58:36 -0800527 modifiedGroup.setState(GroupState.PENDING_UPDATE);
528 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700529 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
530 groupDesc.appCookie()), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800531 // Ensure it also inserted into group id based table to
532 // avoid any chances of duplication in group id generation
533 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700534 put(matchingExtraneousGroup.id(), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800535 removeExtraneousGroupEntry(matchingExtraneousGroup);
536 log.debug("storeGroupDescriptionInternal: Triggering Group "
alshabibb0285992016-03-28 23:30:37 -0700537 + "UPDATE request for {} in device {}",
Saurav Das0fd79d92016-03-07 10:58:36 -0800538 matchingExtraneousGroup.id(),
539 groupDesc.deviceId());
540 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, modifiedGroup));
541 return;
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700542 }
543 }
544 } else {
545 //Check if there is an extraneous group with user provided buckets
546 matchingExtraneousGroup = getMatchingExtraneousGroupbyBuckets(
alshabibb0285992016-03-28 23:30:37 -0700547 groupDesc.deviceId(), groupDesc.buckets());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700548 if (matchingExtraneousGroup != null) {
549 //Group is already existing with the same buckets.
550 //So reuse this group.
551 log.debug("storeGroupDescriptionInternal: Matching extraneous group found in Device {}",
552 groupDesc.deviceId());
553 //Create a group entry object
554 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700555 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700556 // Insert the newly created group entry into key and id maps
557 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700558 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
559 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700560 // Ensure it also inserted into group id based table to
561 // avoid any chances of duplication in group id generation
562 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700563 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700564 addOrUpdateGroupEntry(matchingExtraneousGroup);
565 removeExtraneousGroupEntry(matchingExtraneousGroup);
566 return;
567 } else {
568 //TODO: Check if there are any empty groups that can be used here
569 log.debug("storeGroupDescriptionInternal: No matching extraneous groups found in Device {}",
570 groupDesc.deviceId());
571 }
572 }
573
Saurav Das100e3b82015-04-30 11:12:10 -0700574 GroupId id = null;
575 if (groupDesc.givenGroupId() == null) {
576 // Get a new group identifier
577 id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
578 } else {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800579 // we need to use the identifier passed in by caller, but check if
580 // already used
581 Group existing = getGroup(groupDesc.deviceId(),
582 new DefaultGroupId(groupDesc.givenGroupId()));
583 if (existing != null) {
584 log.warn("Group already exists with the same id: 0x{} in dev:{} "
alshabibb0285992016-03-28 23:30:37 -0700585 + "but with different key: {} (request gkey: {})",
586 Integer.toHexString(groupDesc.givenGroupId()),
587 groupDesc.deviceId(),
588 existing.appCookie(),
589 groupDesc.appCookie());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800590 return;
591 }
Saurav Das100e3b82015-04-30 11:12:10 -0700592 id = new DefaultGroupId(groupDesc.givenGroupId());
593 }
alshabib10580802015-02-18 18:30:33 -0800594 // Create a group entry object
595 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700596 // Insert the newly created group entry into key and id maps
597 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700598 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
599 groupDesc.appCookie()), group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700600 // Ensure it also inserted into group id based table to
601 // avoid any chances of duplication in group id generation
602 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700603 put(id, group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700604 log.debug("storeGroupDescriptionInternal: Processing Group ADD request for Id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700605 id,
606 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800607 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
608 group));
609 }
610
611 /**
612 * Updates the existing group entry with the information
613 * from group description.
614 *
alshabibb0285992016-03-28 23:30:37 -0700615 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800616 * @param oldAppCookie the current group key
alshabibb0285992016-03-28 23:30:37 -0700617 * @param type update type
618 * @param newBuckets group buckets for updates
alshabib10580802015-02-18 18:30:33 -0800619 * @param newAppCookie optional new group key
620 */
621 @Override
622 public void updateGroupDescription(DeviceId deviceId,
623 GroupKey oldAppCookie,
624 UpdateType type,
625 GroupBuckets newBuckets,
626 GroupKey newAppCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700627 // Check if group update to be done by a remote instance
sangho52abe3a2015-05-05 14:13:34 -0700628 if (mastershipService.getMasterFor(deviceId) != null &&
629 mastershipService.getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700630 log.debug("updateGroupDescription: Device {} local role is not MASTER",
631 deviceId);
632 if (mastershipService.getMasterFor(deviceId) == null) {
633 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700634 + "Can not perform update group operation",
635 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700636 //TODO: Send Group operation failure event
637 return;
638 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700639 GroupStoreMessage groupOp = GroupStoreMessage.
640 createGroupUpdateRequestMsg(deviceId,
641 oldAppCookie,
642 type,
643 newBuckets,
644 newAppCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700645
Madan Jampani175e8fd2015-05-20 14:10:45 -0700646 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700647 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
648 clusterMsgSerializer::serialize,
649 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
650 if (error != null) {
651 log.warn("Failed to send request to master: {} to {}",
652 groupOp,
653 mastershipService.getMasterFor(deviceId), error);
654 }
655 //TODO: Send Group operation failure event
656 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700657 return;
658 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700659 log.debug("updateGroupDescription for device {} is getting handled locally",
660 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700661 updateGroupDescriptionInternal(deviceId,
662 oldAppCookie,
663 type,
664 newBuckets,
665 newAppCookie);
666 }
667
668 private void updateGroupDescriptionInternal(DeviceId deviceId,
alshabibb0285992016-03-28 23:30:37 -0700669 GroupKey oldAppCookie,
670 UpdateType type,
671 GroupBuckets newBuckets,
672 GroupKey newAppCookie) {
alshabib10580802015-02-18 18:30:33 -0800673 // Check if a group is existing with the provided key
674 Group oldGroup = getGroup(deviceId, oldAppCookie);
675 if (oldGroup == null) {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800676 log.warn("updateGroupDescriptionInternal: Group not found...strange. "
alshabibb0285992016-03-28 23:30:37 -0700677 + "GroupKey:{} DeviceId:{}", oldAppCookie, deviceId);
alshabib10580802015-02-18 18:30:33 -0800678 return;
679 }
680
681 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
682 type,
683 newBuckets);
684 if (newBucketList != null) {
685 // Create a new group object from the old group
686 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
687 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
688 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
689 oldGroup.deviceId(),
690 oldGroup.type(),
691 updatedBuckets,
692 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700693 oldGroup.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800694 oldGroup.appId());
695 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
696 updatedGroupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700697 log.debug("updateGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_UPDATE",
alshabibb0285992016-03-28 23:30:37 -0700698 oldGroup.id(),
699 oldGroup.deviceId(),
700 oldGroup.state());
alshabib10580802015-02-18 18:30:33 -0800701 newGroup.setState(GroupState.PENDING_UPDATE);
702 newGroup.setLife(oldGroup.life());
703 newGroup.setPackets(oldGroup.packets());
704 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700705 //Update the group entry in groupkey based map.
706 //Update to groupid based map will happen in the
707 //groupkey based map update listener
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700708 log.debug("updateGroupDescriptionInternal with type {}: Group updated with buckets",
709 type);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700710 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700711 put(new GroupStoreKeyMapKey(newGroup.deviceId(),
712 newGroup.appCookie()), newGroup);
alshabib10580802015-02-18 18:30:33 -0800713 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700714 } else {
715 log.warn("updateGroupDescriptionInternal with type {}: No "
alshabibb0285992016-03-28 23:30:37 -0700716 + "change in the buckets in update", type);
alshabib10580802015-02-18 18:30:33 -0800717 }
718 }
719
720 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
721 UpdateType type,
722 GroupBuckets buckets) {
723 GroupBuckets oldBuckets = oldGroup.buckets();
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700724 List<GroupBucket> newBucketList = new ArrayList<>(oldBuckets.buckets());
alshabib10580802015-02-18 18:30:33 -0800725 boolean groupDescUpdated = false;
726
727 if (type == UpdateType.ADD) {
728 // Check if the any of the new buckets are part of
729 // the old bucket list
alshabibb0285992016-03-28 23:30:37 -0700730 for (GroupBucket addBucket : buckets.buckets()) {
alshabib10580802015-02-18 18:30:33 -0800731 if (!newBucketList.contains(addBucket)) {
732 newBucketList.add(addBucket);
733 groupDescUpdated = true;
734 }
735 }
736 } else if (type == UpdateType.REMOVE) {
737 // Check if the to be removed buckets are part of the
738 // old bucket list
alshabibb0285992016-03-28 23:30:37 -0700739 for (GroupBucket removeBucket : buckets.buckets()) {
alshabib10580802015-02-18 18:30:33 -0800740 if (newBucketList.contains(removeBucket)) {
741 newBucketList.remove(removeBucket);
742 groupDescUpdated = true;
743 }
744 }
745 }
746
747 if (groupDescUpdated) {
748 return newBucketList;
749 } else {
750 return null;
751 }
752 }
753
754 /**
755 * Triggers deleting the existing group entry.
756 *
alshabibb0285992016-03-28 23:30:37 -0700757 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800758 * @param appCookie the group key
759 */
760 @Override
761 public void deleteGroupDescription(DeviceId deviceId,
762 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700763 // Check if group to be deleted by a remote instance
764 if (mastershipService.
765 getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700766 log.debug("deleteGroupDescription: Device {} local role is not MASTER",
767 deviceId);
768 if (mastershipService.getMasterFor(deviceId) == null) {
769 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700770 + "Can not perform delete group operation",
771 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700772 //TODO: Send Group operation failure event
773 return;
774 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700775 GroupStoreMessage groupOp = GroupStoreMessage.
776 createGroupDeleteRequestMsg(deviceId,
777 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700778
Madan Jampani175e8fd2015-05-20 14:10:45 -0700779 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700780 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
781 clusterMsgSerializer::serialize,
782 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
783 if (error != null) {
784 log.warn("Failed to send request to master: {} to {}",
785 groupOp,
786 mastershipService.getMasterFor(deviceId), error);
787 }
788 //TODO: Send Group operation failure event
789 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700790 return;
791 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700792 log.debug("deleteGroupDescription in device {} is getting handled locally",
793 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700794 deleteGroupDescriptionInternal(deviceId, appCookie);
795 }
796
797 private void deleteGroupDescriptionInternal(DeviceId deviceId,
798 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800799 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700800 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800801 if (existing == null) {
802 return;
803 }
804
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700805 log.debug("deleteGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_DELETE",
alshabibb0285992016-03-28 23:30:37 -0700806 existing.id(),
807 existing.deviceId(),
808 existing.state());
alshabib10580802015-02-18 18:30:33 -0800809 synchronized (existing) {
810 existing.setState(GroupState.PENDING_DELETE);
Saurav Das80980c72016-03-23 11:22:49 -0700811 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700812 put(new GroupStoreKeyMapKey(existing.deviceId(), existing.appCookie()),
813 existing);
alshabib10580802015-02-18 18:30:33 -0800814 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700815 log.debug("deleteGroupDescriptionInternal: in device {} issuing GROUP_REMOVE_REQUESTED",
816 deviceId);
alshabib10580802015-02-18 18:30:33 -0800817 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
818 }
819
820 /**
821 * Stores a new group entry, or updates an existing entry.
822 *
823 * @param group group entry
824 */
825 @Override
826 public void addOrUpdateGroupEntry(Group group) {
827 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700828 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
829 group.id());
alshabib10580802015-02-18 18:30:33 -0800830 GroupEvent event = null;
831
832 if (existing != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800833 log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700834 group.id(),
835 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800836 synchronized (existing) {
alshabibb0285992016-03-28 23:30:37 -0700837 for (GroupBucket bucket : group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700838 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700839 existing.buckets().buckets()
alshabibb0285992016-03-28 23:30:37 -0700840 .stream()
841 .filter((existingBucket) -> (existingBucket.equals(bucket)))
842 .findFirst();
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700843 if (matchingBucket.isPresent()) {
844 ((StoredGroupBucketEntry) matchingBucket.
845 get()).setPackets(bucket.packets());
846 ((StoredGroupBucketEntry) matchingBucket.
847 get()).setBytes(bucket.bytes());
848 } else {
849 log.warn("addOrUpdateGroupEntry: No matching "
alshabibb0285992016-03-28 23:30:37 -0700850 + "buckets to update stats");
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700851 }
852 }
alshabib10580802015-02-18 18:30:33 -0800853 existing.setLife(group.life());
854 existing.setPackets(group.packets());
855 existing.setBytes(group.bytes());
alshabibb0285992016-03-28 23:30:37 -0700856 existing.setReferenceCount(group.referenceCount());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700857 if ((existing.state() == GroupState.PENDING_ADD) ||
alshabibb0285992016-03-28 23:30:37 -0700858 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800859 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700860 existing.id(),
861 existing.deviceId(),
862 existing.state());
alshabib10580802015-02-18 18:30:33 -0800863 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700864 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800865 event = new GroupEvent(Type.GROUP_ADDED, existing);
866 } else {
Saurav Das0fd79d92016-03-07 10:58:36 -0800867 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700868 existing.id(),
869 existing.deviceId(),
870 GroupState.PENDING_UPDATE);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700871 existing.setState(GroupState.ADDED);
872 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800873 event = new GroupEvent(Type.GROUP_UPDATED, existing);
874 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700875 //Re-PUT map entries to trigger map update events
876 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700877 put(new GroupStoreKeyMapKey(existing.deviceId(),
878 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800879 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700880 } else {
881 log.warn("addOrUpdateGroupEntry: Group update "
alshabibb0285992016-03-28 23:30:37 -0700882 + "happening for a non-existing entry in the map");
alshabib10580802015-02-18 18:30:33 -0800883 }
884
885 if (event != null) {
886 notifyDelegate(event);
887 }
888 }
889
890 /**
891 * Removes the group entry from store.
892 *
893 * @param group group entry
894 */
895 @Override
896 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700897 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
898 group.id());
alshabib10580802015-02-18 18:30:33 -0800899
900 if (existing != null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700901 log.debug("removeGroupEntry: removing group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700902 group.id(),
903 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700904 //Removal from groupid based map will happen in the
905 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700906 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
907 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800908 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700909 } else {
910 log.warn("removeGroupEntry for {} in device{} is "
alshabibb0285992016-03-28 23:30:37 -0700911 + "not existing in our maps",
912 group.id(),
913 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800914 }
915 }
916
917 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800918 public void purgeGroupEntry(DeviceId deviceId) {
919 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entryPendingRemove =
920 new HashSet<>();
921
Madan Jampani0b847532016-03-03 13:44:15 -0800922 getGroupStoreKeyMap().entrySet().stream()
Charles Chan0c7c43b2016-01-14 17:39:20 -0800923 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
924 .forEach(entryPendingRemove::add);
925
926 entryPendingRemove.forEach(entry -> {
927 groupStoreEntriesByKey.remove(entry.getKey());
928 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
929 });
930 }
931
932 @Override
alshabib10580802015-02-18 18:30:33 -0800933 public void deviceInitialAuditCompleted(DeviceId deviceId,
934 boolean completed) {
935 synchronized (deviceAuditStatus) {
936 if (completed) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700937 log.debug("AUDIT completed for device {}",
938 deviceId);
alshabib10580802015-02-18 18:30:33 -0800939 deviceAuditStatus.put(deviceId, true);
940 // Execute all pending group requests
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700941 List<StoredGroupEntry> pendingGroupRequests =
942 getPendingGroupKeyTable().values()
alshabibb0285992016-03-28 23:30:37 -0700943 .stream()
944 .filter(g -> g.deviceId().equals(deviceId))
945 .collect(Collectors.toList());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700946 log.debug("processing pending group add requests for device {} and number of pending requests {}",
alshabibb0285992016-03-28 23:30:37 -0700947 deviceId,
948 pendingGroupRequests.size());
949 for (Group group : pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -0800950 GroupDescription tmp = new DefaultGroupDescription(
951 group.deviceId(),
952 group.type(),
953 group.buckets(),
954 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700955 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800956 group.appId());
957 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700958 getPendingGroupKeyTable().
alshabibb0285992016-03-28 23:30:37 -0700959 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800960 }
alshabib10580802015-02-18 18:30:33 -0800961 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700962 Boolean audited = deviceAuditStatus.get(deviceId);
963 if (audited != null && audited) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700964 log.debug("Clearing AUDIT status for device {}", deviceId);
alshabib10580802015-02-18 18:30:33 -0800965 deviceAuditStatus.put(deviceId, false);
966 }
967 }
968 }
969 }
970
971 @Override
972 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
973 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700974 Boolean audited = deviceAuditStatus.get(deviceId);
975 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -0800976 }
977 }
978
979 @Override
980 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
981
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700982 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
983 operation.groupId());
alshabib10580802015-02-18 18:30:33 -0800984
985 if (existing == null) {
986 log.warn("No group entry with ID {} found ", operation.groupId());
987 return;
988 }
989
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700990 log.warn("groupOperationFailed: group operation {} failed"
alshabibb0285992016-03-28 23:30:37 -0700991 + "for group {} in device {} with code {}",
992 operation.opType(),
993 existing.id(),
994 existing.deviceId(),
995 operation.failureCode());
Saurav Das0fd79d92016-03-07 10:58:36 -0800996 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
997 log.warn("Current extraneous groups in device:{} are: {}",
998 deviceId,
999 getExtraneousGroups(deviceId));
Saurav Das8be4e3a2016-03-11 17:19:07 -08001000 if (operation.buckets().equals(existing.buckets())) {
1001 if (existing.state() == GroupState.PENDING_ADD) {
1002 log.info("GROUP_EXISTS: GroupID and Buckets match for group in pending "
alshabibb0285992016-03-28 23:30:37 -07001003 + "add state - moving to ADDED for group {} in device {}",
1004 existing.id(), deviceId);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001005 addOrUpdateGroupEntry(existing);
1006 return;
1007 } else {
1008 log.warn("GROUP EXISTS: Group ID matched but buckets did not. "
alshabibb0285992016-03-28 23:30:37 -07001009 + "Operation: {} Existing: {}", operation.buckets(),
1010 existing.buckets());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001011 }
1012 }
Saurav Das0fd79d92016-03-07 10:58:36 -08001013 }
alshabib10580802015-02-18 18:30:33 -08001014 switch (operation.opType()) {
1015 case ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001016 if (existing.state() == GroupState.PENDING_ADD) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001017 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
1018 log.warn("groupOperationFailed: cleaningup "
alshabibb0285992016-03-28 23:30:37 -07001019 + "group {} from store in device {}....",
1020 existing.id(),
1021 existing.deviceId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001022 //Removal from groupid based map will happen in the
1023 //map update listener
1024 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1025 existing.appCookie()));
1026 }
alshabib10580802015-02-18 18:30:33 -08001027 break;
1028 case MODIFY:
1029 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
1030 break;
1031 case DELETE:
1032 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
1033 break;
1034 default:
1035 log.warn("Unknown group operation type {}", operation.opType());
1036 }
alshabib10580802015-02-18 18:30:33 -08001037 }
1038
1039 @Override
1040 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001041 log.debug("add/update extraneous group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001042 group.id(),
1043 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001044 ConcurrentMap<GroupId, Group> extraneousIdTable =
1045 getExtraneousGroupIdTable(group.deviceId());
1046 extraneousIdTable.put(group.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -07001047 // Don't remove the extraneous groups, instead re-use it when
1048 // a group request comes with the same set of buckets
alshabib10580802015-02-18 18:30:33 -08001049 }
1050
1051 @Override
1052 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001053 log.debug("remove extraneous group entry {} of device {} from store",
alshabibb0285992016-03-28 23:30:37 -07001054 group.id(),
1055 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001056 ConcurrentMap<GroupId, Group> extraneousIdTable =
1057 getExtraneousGroupIdTable(group.deviceId());
1058 extraneousIdTable.remove(group.id());
1059 }
1060
1061 @Override
1062 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
1063 // flatten and make iterator unmodifiable
1064 return FluentIterable.from(
1065 getExtraneousGroupIdTable(deviceId).values());
1066 }
1067
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001068 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001069 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001070 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001071 private class GroupStoreKeyMapListener implements
Madan Jampani0b847532016-03-03 13:44:15 -08001072 MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001073
1074 @Override
Madan Jampani0b847532016-03-03 13:44:15 -08001075 public void event(MapEvent<GroupStoreKeyMapKey, StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001076 GroupEvent groupEvent = null;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001077 GroupStoreKeyMapKey key = mapEvent.key();
Madan Jampani0b847532016-03-03 13:44:15 -08001078 StoredGroupEntry group = Versioned.valueOrNull(mapEvent.newValue());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001079 if ((key == null) && (group == null)) {
1080 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001081 + "event {} with null entry", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001082 return;
1083 } else if (group == null) {
1084 group = getGroupIdTable(key.deviceId()).values()
1085 .stream()
1086 .filter((storedGroup) -> (storedGroup.appCookie().equals(key.appCookie)))
1087 .findFirst().get();
1088 if (group == null) {
1089 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001090 + "event {} with null entry... can not process", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001091 return;
1092 }
1093 }
1094 log.trace("received groupid map event {} for id {} in device {}",
1095 mapEvent.type(),
1096 group.id(),
1097 key.deviceId());
Madan Jampani0b847532016-03-03 13:44:15 -08001098 if (mapEvent.type() == MapEvent.Type.INSERT || mapEvent.type() == MapEvent.Type.UPDATE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001099 // Update the group ID table
1100 getGroupIdTable(group.deviceId()).put(group.id(), group);
Madan Jampani0b847532016-03-03 13:44:15 -08001101 StoredGroupEntry value = Versioned.valueOrNull(mapEvent.newValue());
1102 if (value.state() == Group.GroupState.ADDED) {
1103 if (value.isGroupStateAddedFirstTime()) {
1104 groupEvent = new GroupEvent(Type.GROUP_ADDED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001105 log.trace("Received first time GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001106 group.id(),
1107 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001108 } else {
Madan Jampani0b847532016-03-03 13:44:15 -08001109 groupEvent = new GroupEvent(Type.GROUP_UPDATED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001110 log.trace("Received following GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001111 group.id(),
1112 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001113 }
1114 }
Madan Jampani0b847532016-03-03 13:44:15 -08001115 } else if (mapEvent.type() == MapEvent.Type.REMOVE) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001116 groupEvent = new GroupEvent(Type.GROUP_REMOVED, group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001117 // Remove the entry from the group ID table
1118 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001119 }
1120
1121 if (groupEvent != null) {
1122 notifyDelegate(groupEvent);
1123 }
1124 }
1125 }
Madan Jampani01e05fb2015-08-13 13:29:36 -07001126
1127 private void process(GroupStoreMessage groupOp) {
1128 log.debug("Received remote group operation {} request for device {}",
alshabibb0285992016-03-28 23:30:37 -07001129 groupOp.type(),
1130 groupOp.deviceId());
1131 if (!mastershipService.isLocalMaster(groupOp.deviceId())) {
1132 log.warn("This node is not MASTER for device {}", groupOp.deviceId());
1133 return;
1134 }
1135 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
1136 storeGroupDescriptionInternal(groupOp.groupDesc());
1137 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
1138 updateGroupDescriptionInternal(groupOp.deviceId(),
1139 groupOp.appCookie(),
1140 groupOp.updateType(),
1141 groupOp.updateBuckets(),
1142 groupOp.newAppCookie());
1143 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
1144 deleteGroupDescriptionInternal(groupOp.deviceId(),
1145 groupOp.appCookie());
1146 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001147 }
1148
1149 /**
1150 * Flattened map key to be used to store group entries.
1151 */
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001152 protected static class GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001153 private final DeviceId deviceId;
1154
1155 public GroupStoreMapKey(DeviceId deviceId) {
1156 this.deviceId = deviceId;
1157 }
1158
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001159 public DeviceId deviceId() {
1160 return deviceId;
1161 }
1162
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001163 @Override
1164 public boolean equals(Object o) {
1165 if (this == o) {
1166 return true;
1167 }
1168 if (!(o instanceof GroupStoreMapKey)) {
1169 return false;
1170 }
1171 GroupStoreMapKey that = (GroupStoreMapKey) o;
1172 return this.deviceId.equals(that.deviceId);
1173 }
1174
1175 @Override
1176 public int hashCode() {
1177 int result = 17;
1178
1179 result = 31 * result + Objects.hash(this.deviceId);
1180
1181 return result;
1182 }
1183 }
1184
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001185 protected static class GroupStoreKeyMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001186 private final GroupKey appCookie;
alshabibb0285992016-03-28 23:30:37 -07001187
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001188 public GroupStoreKeyMapKey(DeviceId deviceId,
1189 GroupKey appCookie) {
1190 super(deviceId);
1191 this.appCookie = appCookie;
1192 }
1193
1194 @Override
1195 public boolean equals(Object o) {
1196 if (this == o) {
1197 return true;
1198 }
1199 if (!(o instanceof GroupStoreKeyMapKey)) {
1200 return false;
1201 }
1202 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
1203 return (super.equals(that) &&
1204 this.appCookie.equals(that.appCookie));
1205 }
1206
1207 @Override
1208 public int hashCode() {
1209 int result = 17;
1210
1211 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
1212
1213 return result;
1214 }
1215 }
1216
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001217 protected static class GroupStoreIdMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001218 private final GroupId groupId;
alshabibb0285992016-03-28 23:30:37 -07001219
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001220 public GroupStoreIdMapKey(DeviceId deviceId,
1221 GroupId groupId) {
1222 super(deviceId);
1223 this.groupId = groupId;
1224 }
1225
1226 @Override
1227 public boolean equals(Object o) {
1228 if (this == o) {
1229 return true;
1230 }
1231 if (!(o instanceof GroupStoreIdMapKey)) {
1232 return false;
1233 }
1234 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1235 return (super.equals(that) &&
1236 this.groupId.equals(that.groupId));
1237 }
1238
1239 @Override
1240 public int hashCode() {
1241 int result = 17;
1242
1243 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1244
1245 return result;
1246 }
1247 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001248
1249 @Override
1250 public void pushGroupMetrics(DeviceId deviceId,
1251 Collection<Group> groupEntries) {
1252 boolean deviceInitialAuditStatus =
1253 deviceInitialAuditStatus(deviceId);
1254 Set<Group> southboundGroupEntries =
1255 Sets.newHashSet(groupEntries);
1256 Set<StoredGroupEntry> storedGroupEntries =
1257 Sets.newHashSet(getStoredGroups(deviceId));
1258 Set<Group> extraneousStoredEntries =
1259 Sets.newHashSet(getExtraneousGroups(deviceId));
1260
1261 log.trace("pushGroupMetrics: Displaying all ({}) southboundGroupEntries for device {}",
1262 southboundGroupEntries.size(),
1263 deviceId);
1264 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
1265 Group group = it.next();
1266 log.trace("Group {} in device {}", group, deviceId);
1267 }
1268
1269 log.trace("Displaying all ({}) stored group entries for device {}",
1270 storedGroupEntries.size(),
1271 deviceId);
1272 for (Iterator<StoredGroupEntry> it1 = storedGroupEntries.iterator();
alshabibb0285992016-03-28 23:30:37 -07001273 it1.hasNext();) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001274 Group group = it1.next();
1275 log.trace("Stored Group {} for device {}", group, deviceId);
1276 }
1277
alshabibb0285992016-03-28 23:30:37 -07001278 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1279
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001280 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
1281 Group group = it2.next();
1282 if (storedGroupEntries.remove(group)) {
1283 // we both have the group, let's update some info then.
1284 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001285 group.id(), deviceId);
1286
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001287 groupAdded(group);
1288 it2.remove();
1289 }
1290 }
1291 for (Group group : southboundGroupEntries) {
1292 if (getGroup(group.deviceId(), group.id()) != null) {
1293 // There is a group existing with the same id
1294 // It is possible that group update is
1295 // in progress while we got a stale info from switch
1296 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001297 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001298 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001299 + "Group exists in ID based table while "
1300 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001301 }
1302 } else {
1303 // there are groups in the switch that aren't in the store
1304 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001305 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001306 extraneousStoredEntries.remove(group);
1307 extraneousGroup(group);
1308 }
1309 }
1310 for (Group group : storedGroupEntries) {
1311 // there are groups in the store that aren't in the switch
1312 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001313 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001314 groupMissing(group);
1315 }
1316 for (Group group : extraneousStoredEntries) {
1317 // there are groups in the extraneous store that
1318 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001319 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001320 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001321 removeExtraneousGroupEntry(group);
1322 }
1323
1324 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001325 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001326 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001327 deviceInitialAuditCompleted(deviceId, true);
1328 }
1329 }
1330
alshabibb0285992016-03-28 23:30:37 -07001331 private void garbageCollect(DeviceId deviceId,
1332 Set<Group> southboundGroupEntries,
1333 Set<StoredGroupEntry> storedGroupEntries) {
1334 if (!garbageCollect) {
1335 return;
1336 }
1337
1338 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1339 while (it.hasNext()) {
1340 StoredGroupEntry group = it.next();
1341 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1342 log.debug("Garbage collecting group {} on {}", group, deviceId);
1343 deleteGroupDescription(deviceId, group.appCookie());
1344 southboundGroupEntries.remove(group);
1345 it.remove();
1346 }
1347 }
1348 }
1349
1350 private boolean checkGroupRefCount(Group group) {
1351 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1352 }
1353
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001354 private void groupMissing(Group group) {
1355 switch (group.state()) {
1356 case PENDING_DELETE:
1357 log.debug("Group {} delete confirmation from device {}",
1358 group, group.deviceId());
1359 removeGroupEntry(group);
1360 break;
1361 case ADDED:
1362 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001363 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001364 case PENDING_UPDATE:
1365 log.debug("Group {} is in store but not on device {}",
1366 group, group.deviceId());
1367 StoredGroupEntry existing =
1368 getStoredGroupEntry(group.deviceId(), group.id());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001369 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
alshabibb0285992016-03-28 23:30:37 -07001370 existing.id(),
1371 existing.deviceId(),
1372 existing.state());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001373 existing.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001374 //Re-PUT map entries to trigger map update events
1375 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -07001376 put(new GroupStoreKeyMapKey(existing.deviceId(),
1377 existing.appCookie()), existing);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001378 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1379 group));
1380 break;
1381 default:
1382 log.debug("Group {} has not been installed.", group);
1383 break;
1384 }
1385 }
1386
1387 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001388 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001389 group, group.deviceId());
1390 addOrUpdateExtraneousGroupEntry(group);
1391 }
1392
1393 private void groupAdded(Group group) {
1394 log.trace("Group {} Added or Updated in device {}",
1395 group, group.deviceId());
1396 addOrUpdateGroupEntry(group);
1397 }
alshabib10580802015-02-18 18:30:33 -08001398}