blob: a142e5f9481d4250d2ed3b70c797c43d4f72816f [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;
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",
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700188 "message-handlers",
189 log));
Madan Jampani01e05fb2015-08-13 13:29:36 -0700190
191 clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
alshabibb0285992016-03-28 23:30:37 -0700192 clusterMsgSerializer::deserialize,
193 this::process,
194 messageHandlingExecutor);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700195
Madan Jampani0b847532016-03-03 13:44:15 -0800196 log.debug("Creating Consistent map onos-group-store-keymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700197
Madan Jampani0b847532016-03-03 13:44:15 -0800198 groupStoreEntriesByKey = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
199 .withName("onos-group-store-keymap")
200 .withSerializer(Serializer.using(kryoBuilder.build()))
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700201 .build();
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700202 groupStoreEntriesByKey.addListener(new GroupStoreKeyMapListener());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700203 log.debug("Current size of groupstorekeymap:{}",
204 groupStoreEntriesByKey.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700205
Madan Jampani0b847532016-03-03 13:44:15 -0800206 log.debug("Creating Consistent map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700207
Madan Jampani0b847532016-03-03 13:44:15 -0800208 auditPendingReqQueue = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
209 .withName("onos-pending-group-keymap")
210 .withSerializer(Serializer.using(kryoBuilder.build()))
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700211 .build();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700212 log.debug("Current size of pendinggroupkeymap:{}",
213 auditPendingReqQueue.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700214
alshabib10580802015-02-18 18:30:33 -0800215 log.info("Started");
216 }
217
218 @Deactivate
219 public void deactivate() {
alshabibb0285992016-03-28 23:30:37 -0700220 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700221 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
alshabib10580802015-02-18 18:30:33 -0800222 log.info("Stopped");
223 }
224
alshabibb0285992016-03-28 23:30:37 -0700225 @Modified
226 public void modified(ComponentContext context) {
227 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
228
229 try {
230 String s = get(properties, "garbageCollect");
231 garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT : Boolean.parseBoolean(s.trim());
232
233 s = get(properties, "gcThresh");
234 gcThresh = isNullOrEmpty(s) ? GC_THRESH : Integer.parseInt(s.trim());
235 } catch (Exception e) {
236 gcThresh = GC_THRESH;
237 garbageCollect = GARBAGE_COLLECT;
238 }
239 }
240
alshabib10580802015-02-18 18:30:33 -0800241 private static NewConcurrentHashMap<GroupId, Group>
alshabibb0285992016-03-28 23:30:37 -0700242 lazyEmptyExtraneousGroupIdTable() {
alshabib10580802015-02-18 18:30:33 -0800243 return NewConcurrentHashMap.<GroupId, Group>ifNeeded();
244 }
245
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700246 private static NewConcurrentHashMap<GroupId, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700247 lazyEmptyGroupIdTable() {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700248 return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded();
249 }
250
alshabib10580802015-02-18 18:30:33 -0800251 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700252 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800253 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700254 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800255 */
Madan Jampani0b847532016-03-03 13:44:15 -0800256 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700257 getGroupStoreKeyMap() {
Madan Jampani0b847532016-03-03 13:44:15 -0800258 return groupStoreEntriesByKey.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800259 }
260
261 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700262 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800263 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700264 * @param deviceId identifier of the device
265 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800266 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700267 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
268 return createIfAbsentUnchecked(groupEntriesById,
269 deviceId, lazyEmptyGroupIdTable());
alshabib10580802015-02-18 18:30:33 -0800270 }
271
272 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700273 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800274 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700275 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800276 */
Madan Jampani0b847532016-03-03 13:44:15 -0800277 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700278 getPendingGroupKeyTable() {
Madan Jampani0b847532016-03-03 13:44:15 -0800279 return auditPendingReqQueue.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800280 }
281
282 /**
283 * Returns the extraneous group id table for specified device.
284 *
285 * @param deviceId identifier of the device
286 * @return Map representing group key table of given device.
287 */
288 private ConcurrentMap<GroupId, Group>
289 getExtraneousGroupIdTable(DeviceId deviceId) {
290 return createIfAbsentUnchecked(extraneousGroupEntriesById,
291 deviceId,
292 lazyEmptyExtraneousGroupIdTable());
293 }
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) {
Saurav Das4ce45962015-11-24 23:21:05 -0800393 log.warn("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) {
722 GroupBuckets oldBuckets = oldGroup.buckets();
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700723 List<GroupBucket> newBucketList = new ArrayList<>(oldBuckets.buckets());
alshabib10580802015-02-18 18:30:33 -0800724 boolean groupDescUpdated = false;
725
726 if (type == UpdateType.ADD) {
727 // Check if the any of the new buckets are part of
728 // the old bucket list
alshabibb0285992016-03-28 23:30:37 -0700729 for (GroupBucket addBucket : buckets.buckets()) {
alshabib10580802015-02-18 18:30:33 -0800730 if (!newBucketList.contains(addBucket)) {
731 newBucketList.add(addBucket);
732 groupDescUpdated = true;
733 }
734 }
735 } else if (type == UpdateType.REMOVE) {
736 // Check if the to be removed buckets are part of the
737 // old bucket list
alshabibb0285992016-03-28 23:30:37 -0700738 for (GroupBucket removeBucket : buckets.buckets()) {
alshabib10580802015-02-18 18:30:33 -0800739 if (newBucketList.contains(removeBucket)) {
740 newBucketList.remove(removeBucket);
741 groupDescUpdated = true;
742 }
743 }
744 }
745
746 if (groupDescUpdated) {
747 return newBucketList;
748 } else {
749 return null;
750 }
751 }
752
753 /**
754 * Triggers deleting the existing group entry.
755 *
alshabibb0285992016-03-28 23:30:37 -0700756 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800757 * @param appCookie the group key
758 */
759 @Override
760 public void deleteGroupDescription(DeviceId deviceId,
761 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700762 // Check if group to be deleted by a remote instance
763 if (mastershipService.
764 getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700765 log.debug("deleteGroupDescription: Device {} local role is not MASTER",
766 deviceId);
767 if (mastershipService.getMasterFor(deviceId) == null) {
768 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700769 + "Can not perform delete group operation",
770 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700771 //TODO: Send Group operation failure event
772 return;
773 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700774 GroupStoreMessage groupOp = GroupStoreMessage.
775 createGroupDeleteRequestMsg(deviceId,
776 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700777
Madan Jampani175e8fd2015-05-20 14:10:45 -0700778 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700779 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
780 clusterMsgSerializer::serialize,
781 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
782 if (error != null) {
783 log.warn("Failed to send request to master: {} to {}",
784 groupOp,
785 mastershipService.getMasterFor(deviceId), error);
786 }
787 //TODO: Send Group operation failure event
788 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700789 return;
790 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700791 log.debug("deleteGroupDescription in device {} is getting handled locally",
792 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700793 deleteGroupDescriptionInternal(deviceId, appCookie);
794 }
795
796 private void deleteGroupDescriptionInternal(DeviceId deviceId,
797 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800798 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700799 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800800 if (existing == null) {
801 return;
802 }
803
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700804 log.debug("deleteGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_DELETE",
alshabibb0285992016-03-28 23:30:37 -0700805 existing.id(),
806 existing.deviceId(),
807 existing.state());
alshabib10580802015-02-18 18:30:33 -0800808 synchronized (existing) {
809 existing.setState(GroupState.PENDING_DELETE);
Saurav Das80980c72016-03-23 11:22:49 -0700810 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700811 put(new GroupStoreKeyMapKey(existing.deviceId(), existing.appCookie()),
812 existing);
alshabib10580802015-02-18 18:30:33 -0800813 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700814 log.debug("deleteGroupDescriptionInternal: in device {} issuing GROUP_REMOVE_REQUESTED",
815 deviceId);
alshabib10580802015-02-18 18:30:33 -0800816 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
817 }
818
819 /**
820 * Stores a new group entry, or updates an existing entry.
821 *
822 * @param group group entry
823 */
824 @Override
825 public void addOrUpdateGroupEntry(Group group) {
826 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700827 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
828 group.id());
alshabib10580802015-02-18 18:30:33 -0800829 GroupEvent event = null;
830
831 if (existing != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800832 log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700833 group.id(),
834 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800835 synchronized (existing) {
alshabibb0285992016-03-28 23:30:37 -0700836 for (GroupBucket bucket : group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700837 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700838 existing.buckets().buckets()
alshabibb0285992016-03-28 23:30:37 -0700839 .stream()
840 .filter((existingBucket) -> (existingBucket.equals(bucket)))
841 .findFirst();
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700842 if (matchingBucket.isPresent()) {
843 ((StoredGroupBucketEntry) matchingBucket.
844 get()).setPackets(bucket.packets());
845 ((StoredGroupBucketEntry) matchingBucket.
846 get()).setBytes(bucket.bytes());
847 } else {
848 log.warn("addOrUpdateGroupEntry: No matching "
alshabibb0285992016-03-28 23:30:37 -0700849 + "buckets to update stats");
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700850 }
851 }
alshabib10580802015-02-18 18:30:33 -0800852 existing.setLife(group.life());
853 existing.setPackets(group.packets());
854 existing.setBytes(group.bytes());
alshabibb0285992016-03-28 23:30:37 -0700855 existing.setReferenceCount(group.referenceCount());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700856 if ((existing.state() == GroupState.PENDING_ADD) ||
alshabibb0285992016-03-28 23:30:37 -0700857 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800858 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700859 existing.id(),
860 existing.deviceId(),
861 existing.state());
alshabib10580802015-02-18 18:30:33 -0800862 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700863 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800864 event = new GroupEvent(Type.GROUP_ADDED, existing);
865 } else {
Saurav Das0fd79d92016-03-07 10:58:36 -0800866 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700867 existing.id(),
868 existing.deviceId(),
869 GroupState.PENDING_UPDATE);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700870 existing.setState(GroupState.ADDED);
871 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800872 event = new GroupEvent(Type.GROUP_UPDATED, existing);
873 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700874 //Re-PUT map entries to trigger map update events
875 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700876 put(new GroupStoreKeyMapKey(existing.deviceId(),
877 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800878 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700879 } else {
880 log.warn("addOrUpdateGroupEntry: Group update "
alshabibb0285992016-03-28 23:30:37 -0700881 + "happening for a non-existing entry in the map");
alshabib10580802015-02-18 18:30:33 -0800882 }
883
884 if (event != null) {
885 notifyDelegate(event);
886 }
887 }
888
889 /**
890 * Removes the group entry from store.
891 *
892 * @param group group entry
893 */
894 @Override
895 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700896 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
897 group.id());
alshabib10580802015-02-18 18:30:33 -0800898
899 if (existing != null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700900 log.debug("removeGroupEntry: removing group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700901 group.id(),
902 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700903 //Removal from groupid based map will happen in the
904 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700905 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
906 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800907 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700908 } else {
909 log.warn("removeGroupEntry for {} in device{} is "
alshabibb0285992016-03-28 23:30:37 -0700910 + "not existing in our maps",
911 group.id(),
912 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800913 }
914 }
915
916 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800917 public void purgeGroupEntry(DeviceId deviceId) {
918 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entryPendingRemove =
919 new HashSet<>();
920
Madan Jampani0b847532016-03-03 13:44:15 -0800921 getGroupStoreKeyMap().entrySet().stream()
Charles Chan0c7c43b2016-01-14 17:39:20 -0800922 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
923 .forEach(entryPendingRemove::add);
924
925 entryPendingRemove.forEach(entry -> {
926 groupStoreEntriesByKey.remove(entry.getKey());
927 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
928 });
929 }
930
931 @Override
alshabib10580802015-02-18 18:30:33 -0800932 public void deviceInitialAuditCompleted(DeviceId deviceId,
933 boolean completed) {
934 synchronized (deviceAuditStatus) {
935 if (completed) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700936 log.debug("AUDIT completed for device {}",
937 deviceId);
alshabib10580802015-02-18 18:30:33 -0800938 deviceAuditStatus.put(deviceId, true);
939 // Execute all pending group requests
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700940 List<StoredGroupEntry> pendingGroupRequests =
941 getPendingGroupKeyTable().values()
alshabibb0285992016-03-28 23:30:37 -0700942 .stream()
943 .filter(g -> g.deviceId().equals(deviceId))
944 .collect(Collectors.toList());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700945 log.debug("processing pending group add requests for device {} and number of pending requests {}",
alshabibb0285992016-03-28 23:30:37 -0700946 deviceId,
947 pendingGroupRequests.size());
948 for (Group group : pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -0800949 GroupDescription tmp = new DefaultGroupDescription(
950 group.deviceId(),
951 group.type(),
952 group.buckets(),
953 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700954 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800955 group.appId());
956 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700957 getPendingGroupKeyTable().
alshabibb0285992016-03-28 23:30:37 -0700958 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800959 }
alshabib10580802015-02-18 18:30:33 -0800960 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700961 Boolean audited = deviceAuditStatus.get(deviceId);
962 if (audited != null && audited) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700963 log.debug("Clearing AUDIT status for device {}", deviceId);
alshabib10580802015-02-18 18:30:33 -0800964 deviceAuditStatus.put(deviceId, false);
965 }
966 }
967 }
968 }
969
970 @Override
971 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
972 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700973 Boolean audited = deviceAuditStatus.get(deviceId);
974 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -0800975 }
976 }
977
978 @Override
979 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
980
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700981 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
982 operation.groupId());
alshabib10580802015-02-18 18:30:33 -0800983
984 if (existing == null) {
985 log.warn("No group entry with ID {} found ", operation.groupId());
986 return;
987 }
988
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700989 log.warn("groupOperationFailed: group operation {} failed"
alshabibb0285992016-03-28 23:30:37 -0700990 + "for group {} in device {} with code {}",
991 operation.opType(),
992 existing.id(),
993 existing.deviceId(),
994 operation.failureCode());
Saurav Das0fd79d92016-03-07 10:58:36 -0800995 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
996 log.warn("Current extraneous groups in device:{} are: {}",
997 deviceId,
998 getExtraneousGroups(deviceId));
Saurav Das8be4e3a2016-03-11 17:19:07 -0800999 if (operation.buckets().equals(existing.buckets())) {
1000 if (existing.state() == GroupState.PENDING_ADD) {
1001 log.info("GROUP_EXISTS: GroupID and Buckets match for group in pending "
alshabibb0285992016-03-28 23:30:37 -07001002 + "add state - moving to ADDED for group {} in device {}",
1003 existing.id(), deviceId);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001004 addOrUpdateGroupEntry(existing);
1005 return;
1006 } else {
1007 log.warn("GROUP EXISTS: Group ID matched but buckets did not. "
alshabibb0285992016-03-28 23:30:37 -07001008 + "Operation: {} Existing: {}", operation.buckets(),
1009 existing.buckets());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001010 }
1011 }
Saurav Das0fd79d92016-03-07 10:58:36 -08001012 }
alshabib10580802015-02-18 18:30:33 -08001013 switch (operation.opType()) {
1014 case ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001015 if (existing.state() == GroupState.PENDING_ADD) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001016 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
1017 log.warn("groupOperationFailed: cleaningup "
alshabibb0285992016-03-28 23:30:37 -07001018 + "group {} from store in device {}....",
1019 existing.id(),
1020 existing.deviceId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001021 //Removal from groupid based map will happen in the
1022 //map update listener
1023 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1024 existing.appCookie()));
1025 }
alshabib10580802015-02-18 18:30:33 -08001026 break;
1027 case MODIFY:
1028 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
1029 break;
1030 case DELETE:
1031 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
1032 break;
1033 default:
1034 log.warn("Unknown group operation type {}", operation.opType());
1035 }
alshabib10580802015-02-18 18:30:33 -08001036 }
1037
1038 @Override
1039 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001040 log.debug("add/update extraneous group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001041 group.id(),
1042 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001043 ConcurrentMap<GroupId, Group> extraneousIdTable =
1044 getExtraneousGroupIdTable(group.deviceId());
1045 extraneousIdTable.put(group.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -07001046 // Don't remove the extraneous groups, instead re-use it when
1047 // a group request comes with the same set of buckets
alshabib10580802015-02-18 18:30:33 -08001048 }
1049
1050 @Override
1051 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001052 log.debug("remove extraneous group entry {} of device {} from store",
alshabibb0285992016-03-28 23:30:37 -07001053 group.id(),
1054 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001055 ConcurrentMap<GroupId, Group> extraneousIdTable =
1056 getExtraneousGroupIdTable(group.deviceId());
1057 extraneousIdTable.remove(group.id());
1058 }
1059
1060 @Override
1061 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
1062 // flatten and make iterator unmodifiable
1063 return FluentIterable.from(
1064 getExtraneousGroupIdTable(deviceId).values());
1065 }
1066
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001067 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001068 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001069 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001070 private class GroupStoreKeyMapListener implements
Madan Jampani0b847532016-03-03 13:44:15 -08001071 MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001072
1073 @Override
Madan Jampani0b847532016-03-03 13:44:15 -08001074 public void event(MapEvent<GroupStoreKeyMapKey, StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001075 GroupEvent groupEvent = null;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001076 GroupStoreKeyMapKey key = mapEvent.key();
Madan Jampani0b847532016-03-03 13:44:15 -08001077 StoredGroupEntry group = Versioned.valueOrNull(mapEvent.newValue());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001078 if ((key == null) && (group == null)) {
1079 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001080 + "event {} with null entry", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001081 return;
1082 } else if (group == null) {
1083 group = getGroupIdTable(key.deviceId()).values()
1084 .stream()
1085 .filter((storedGroup) -> (storedGroup.appCookie().equals(key.appCookie)))
1086 .findFirst().get();
1087 if (group == null) {
1088 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001089 + "event {} with null entry... can not process", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001090 return;
1091 }
1092 }
1093 log.trace("received groupid map event {} for id {} in device {}",
1094 mapEvent.type(),
1095 group.id(),
1096 key.deviceId());
Madan Jampani0b847532016-03-03 13:44:15 -08001097 if (mapEvent.type() == MapEvent.Type.INSERT || mapEvent.type() == MapEvent.Type.UPDATE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001098 // Update the group ID table
1099 getGroupIdTable(group.deviceId()).put(group.id(), group);
Madan Jampani0b847532016-03-03 13:44:15 -08001100 StoredGroupEntry value = Versioned.valueOrNull(mapEvent.newValue());
1101 if (value.state() == Group.GroupState.ADDED) {
1102 if (value.isGroupStateAddedFirstTime()) {
1103 groupEvent = new GroupEvent(Type.GROUP_ADDED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001104 log.trace("Received first time GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001105 group.id(),
1106 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001107 } else {
Madan Jampani0b847532016-03-03 13:44:15 -08001108 groupEvent = new GroupEvent(Type.GROUP_UPDATED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001109 log.trace("Received following GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001110 group.id(),
1111 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001112 }
1113 }
Madan Jampani0b847532016-03-03 13:44:15 -08001114 } else if (mapEvent.type() == MapEvent.Type.REMOVE) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001115 groupEvent = new GroupEvent(Type.GROUP_REMOVED, group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001116 // Remove the entry from the group ID table
1117 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001118 }
1119
1120 if (groupEvent != null) {
1121 notifyDelegate(groupEvent);
1122 }
1123 }
1124 }
Madan Jampani01e05fb2015-08-13 13:29:36 -07001125
1126 private void process(GroupStoreMessage groupOp) {
1127 log.debug("Received remote group operation {} request for device {}",
alshabibb0285992016-03-28 23:30:37 -07001128 groupOp.type(),
1129 groupOp.deviceId());
1130 if (!mastershipService.isLocalMaster(groupOp.deviceId())) {
1131 log.warn("This node is not MASTER for device {}", groupOp.deviceId());
1132 return;
1133 }
1134 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
1135 storeGroupDescriptionInternal(groupOp.groupDesc());
1136 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
1137 updateGroupDescriptionInternal(groupOp.deviceId(),
1138 groupOp.appCookie(),
1139 groupOp.updateType(),
1140 groupOp.updateBuckets(),
1141 groupOp.newAppCookie());
1142 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
1143 deleteGroupDescriptionInternal(groupOp.deviceId(),
1144 groupOp.appCookie());
1145 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001146 }
1147
1148 /**
1149 * Flattened map key to be used to store group entries.
1150 */
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001151 protected static class GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001152 private final DeviceId deviceId;
1153
1154 public GroupStoreMapKey(DeviceId deviceId) {
1155 this.deviceId = deviceId;
1156 }
1157
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001158 public DeviceId deviceId() {
1159 return deviceId;
1160 }
1161
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001162 @Override
1163 public boolean equals(Object o) {
1164 if (this == o) {
1165 return true;
1166 }
1167 if (!(o instanceof GroupStoreMapKey)) {
1168 return false;
1169 }
1170 GroupStoreMapKey that = (GroupStoreMapKey) o;
1171 return this.deviceId.equals(that.deviceId);
1172 }
1173
1174 @Override
1175 public int hashCode() {
1176 int result = 17;
1177
1178 result = 31 * result + Objects.hash(this.deviceId);
1179
1180 return result;
1181 }
1182 }
1183
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001184 protected static class GroupStoreKeyMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001185 private final GroupKey appCookie;
alshabibb0285992016-03-28 23:30:37 -07001186
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001187 public GroupStoreKeyMapKey(DeviceId deviceId,
1188 GroupKey appCookie) {
1189 super(deviceId);
1190 this.appCookie = appCookie;
1191 }
1192
1193 @Override
1194 public boolean equals(Object o) {
1195 if (this == o) {
1196 return true;
1197 }
1198 if (!(o instanceof GroupStoreKeyMapKey)) {
1199 return false;
1200 }
1201 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
1202 return (super.equals(that) &&
1203 this.appCookie.equals(that.appCookie));
1204 }
1205
1206 @Override
1207 public int hashCode() {
1208 int result = 17;
1209
1210 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
1211
1212 return result;
1213 }
1214 }
1215
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001216 protected static class GroupStoreIdMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001217 private final GroupId groupId;
alshabibb0285992016-03-28 23:30:37 -07001218
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001219 public GroupStoreIdMapKey(DeviceId deviceId,
1220 GroupId groupId) {
1221 super(deviceId);
1222 this.groupId = groupId;
1223 }
1224
1225 @Override
1226 public boolean equals(Object o) {
1227 if (this == o) {
1228 return true;
1229 }
1230 if (!(o instanceof GroupStoreIdMapKey)) {
1231 return false;
1232 }
1233 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1234 return (super.equals(that) &&
1235 this.groupId.equals(that.groupId));
1236 }
1237
1238 @Override
1239 public int hashCode() {
1240 int result = 17;
1241
1242 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1243
1244 return result;
1245 }
1246 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001247
1248 @Override
1249 public void pushGroupMetrics(DeviceId deviceId,
1250 Collection<Group> groupEntries) {
1251 boolean deviceInitialAuditStatus =
1252 deviceInitialAuditStatus(deviceId);
1253 Set<Group> southboundGroupEntries =
1254 Sets.newHashSet(groupEntries);
1255 Set<StoredGroupEntry> storedGroupEntries =
1256 Sets.newHashSet(getStoredGroups(deviceId));
1257 Set<Group> extraneousStoredEntries =
1258 Sets.newHashSet(getExtraneousGroups(deviceId));
1259
1260 log.trace("pushGroupMetrics: Displaying all ({}) southboundGroupEntries for device {}",
1261 southboundGroupEntries.size(),
1262 deviceId);
1263 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
1264 Group group = it.next();
1265 log.trace("Group {} in device {}", group, deviceId);
1266 }
1267
1268 log.trace("Displaying all ({}) stored group entries for device {}",
1269 storedGroupEntries.size(),
1270 deviceId);
1271 for (Iterator<StoredGroupEntry> it1 = storedGroupEntries.iterator();
alshabibb0285992016-03-28 23:30:37 -07001272 it1.hasNext();) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001273 Group group = it1.next();
1274 log.trace("Stored Group {} for device {}", group, deviceId);
1275 }
1276
alshabibb0285992016-03-28 23:30:37 -07001277 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1278
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001279 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
1280 Group group = it2.next();
1281 if (storedGroupEntries.remove(group)) {
1282 // we both have the group, let's update some info then.
1283 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001284 group.id(), deviceId);
1285
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001286 groupAdded(group);
1287 it2.remove();
1288 }
1289 }
1290 for (Group group : southboundGroupEntries) {
1291 if (getGroup(group.deviceId(), group.id()) != null) {
1292 // There is a group existing with the same id
1293 // It is possible that group update is
1294 // in progress while we got a stale info from switch
1295 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001296 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001297 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001298 + "Group exists in ID based table while "
1299 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001300 }
1301 } else {
1302 // there are groups in the switch that aren't in the store
1303 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001304 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001305 extraneousStoredEntries.remove(group);
1306 extraneousGroup(group);
1307 }
1308 }
1309 for (Group group : storedGroupEntries) {
1310 // there are groups in the store that aren't in the switch
1311 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001312 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001313 groupMissing(group);
1314 }
1315 for (Group group : extraneousStoredEntries) {
1316 // there are groups in the extraneous store that
1317 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001318 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001319 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001320 removeExtraneousGroupEntry(group);
1321 }
1322
1323 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001324 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001325 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001326 deviceInitialAuditCompleted(deviceId, true);
1327 }
1328 }
1329
alshabibb0285992016-03-28 23:30:37 -07001330 private void garbageCollect(DeviceId deviceId,
1331 Set<Group> southboundGroupEntries,
1332 Set<StoredGroupEntry> storedGroupEntries) {
1333 if (!garbageCollect) {
1334 return;
1335 }
1336
1337 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1338 while (it.hasNext()) {
1339 StoredGroupEntry group = it.next();
1340 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1341 log.debug("Garbage collecting group {} on {}", group, deviceId);
1342 deleteGroupDescription(deviceId, group.appCookie());
1343 southboundGroupEntries.remove(group);
1344 it.remove();
1345 }
1346 }
1347 }
1348
1349 private boolean checkGroupRefCount(Group group) {
1350 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1351 }
1352
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001353 private void groupMissing(Group group) {
1354 switch (group.state()) {
1355 case PENDING_DELETE:
1356 log.debug("Group {} delete confirmation from device {}",
1357 group, group.deviceId());
1358 removeGroupEntry(group);
1359 break;
1360 case ADDED:
1361 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001362 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001363 case PENDING_UPDATE:
1364 log.debug("Group {} is in store but not on device {}",
1365 group, group.deviceId());
1366 StoredGroupEntry existing =
1367 getStoredGroupEntry(group.deviceId(), group.id());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001368 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
alshabibb0285992016-03-28 23:30:37 -07001369 existing.id(),
1370 existing.deviceId(),
1371 existing.state());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001372 existing.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001373 //Re-PUT map entries to trigger map update events
1374 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -07001375 put(new GroupStoreKeyMapKey(existing.deviceId(),
1376 existing.appCookie()), existing);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001377 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1378 group));
1379 break;
1380 default:
1381 log.debug("Group {} has not been installed.", group);
1382 break;
1383 }
1384 }
1385
1386 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001387 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001388 group, group.deviceId());
1389 addOrUpdateExtraneousGroupEntry(group);
1390 }
1391
1392 private void groupAdded(Group group) {
1393 log.trace("Group {} Added or Updated in device {}",
1394 group, group.deviceId());
1395 addOrUpdateGroupEntry(group);
1396 }
alshabib10580802015-02-18 18:30:33 -08001397}