blob: 764fe9e7e402682463d3b049d5bf9afa8f7ee439 [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;
Frank Wange0eb5ce2016-07-01 18:21:25 +0800138 private MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry>
139 mapListener = new GroupStoreKeyMapListener();
alshabib10580802015-02-18 18:30:33 -0800140 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
141 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700142 private ExecutorService messageHandlingExecutor;
143 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700144 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
alshabib10580802015-02-18 18:30:33 -0800145
146 private final AtomicInteger groupIdGen = new AtomicInteger();
147
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700148 private KryoNamespace clusterMsgSerializer;
149
alshabibb0285992016-03-28 23:30:37 -0700150 @Property(name = "garbageCollect", boolValue = GARBAGE_COLLECT,
151 label = "Enable group garbage collection")
152 private boolean garbageCollect = GARBAGE_COLLECT;
153
154 @Property(name = "gcThresh", intValue = GC_THRESH,
155 label = "Number of rounds for group garbage collection")
156 private int gcThresh = GC_THRESH;
157
158
alshabib10580802015-02-18 18:30:33 -0800159 @Activate
160 public void activate() {
alshabibb0285992016-03-28 23:30:37 -0700161 cfgService.registerProperties(getClass());
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700162 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
alshabibb0285992016-03-28 23:30:37 -0700163 .register(KryoNamespaces.API)
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700164 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
alshabibb0285992016-03-28 23:30:37 -0700165 .register(DefaultGroup.class,
166 DefaultGroupBucket.class,
167 DefaultGroupDescription.class,
168 DefaultGroupKey.class,
169 GroupDescription.Type.class,
170 Group.GroupState.class,
171 GroupBuckets.class,
alshabibb0285992016-03-28 23:30:37 -0700172 GroupStoreMessage.class,
173 GroupStoreMessage.Type.class,
174 UpdateType.class,
175 GroupStoreMessageSubjects.class,
176 MultiValuedTimestamp.class,
177 GroupStoreKeyMapKey.class,
178 GroupStoreIdMapKey.class,
179 GroupStoreMapKey.class
180 );
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700181
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700182 clusterMsgSerializer = kryoBuilder.build("GroupStore");
183 Serializer serializer = Serializer.using(clusterMsgSerializer);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700184
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")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700200 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700201 .build();
Frank Wange0eb5ce2016-07-01 18:21:25 +0800202 groupStoreEntriesByKey.addListener(mapListener);
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")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700210 .withSerializer(serializer)
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() {
Frank Wange0eb5ce2016-07-01 18:21:25 +0800220 groupStoreEntriesByKey.removeListener(mapListener);
alshabibb0285992016-03-28 23:30:37 -0700221 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700222 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
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) {
Charles Chan216e3c82016-04-23 14:48:16 -0700394 log.info("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);
Sho SHIMIZU15780422016-08-12 15:59:20 -07001264 for (Group group : southboundGroupEntries) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001265 log.trace("Group {} in device {}", group, deviceId);
1266 }
1267
1268 log.trace("Displaying all ({}) stored group entries for device {}",
1269 storedGroupEntries.size(),
1270 deviceId);
Sho SHIMIZU15780422016-08-12 15:59:20 -07001271 for (StoredGroupEntry group : storedGroupEntries) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001272 log.trace("Stored Group {} for device {}", group, deviceId);
1273 }
1274
alshabibb0285992016-03-28 23:30:37 -07001275 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1276
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001277 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
1278 Group group = it2.next();
1279 if (storedGroupEntries.remove(group)) {
1280 // we both have the group, let's update some info then.
1281 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001282 group.id(), deviceId);
1283
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001284 groupAdded(group);
1285 it2.remove();
1286 }
1287 }
1288 for (Group group : southboundGroupEntries) {
1289 if (getGroup(group.deviceId(), group.id()) != null) {
1290 // There is a group existing with the same id
1291 // It is possible that group update is
1292 // in progress while we got a stale info from switch
1293 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001294 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001295 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001296 + "Group exists in ID based table while "
1297 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001298 }
1299 } else {
1300 // there are groups in the switch that aren't in the store
1301 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001302 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001303 extraneousStoredEntries.remove(group);
1304 extraneousGroup(group);
1305 }
1306 }
1307 for (Group group : storedGroupEntries) {
1308 // there are groups in the store that aren't in the switch
1309 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001310 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001311 groupMissing(group);
1312 }
1313 for (Group group : extraneousStoredEntries) {
1314 // there are groups in the extraneous store that
1315 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001316 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001317 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001318 removeExtraneousGroupEntry(group);
1319 }
1320
1321 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001322 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001323 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001324 deviceInitialAuditCompleted(deviceId, true);
1325 }
1326 }
1327
alshabibb0285992016-03-28 23:30:37 -07001328 private void garbageCollect(DeviceId deviceId,
1329 Set<Group> southboundGroupEntries,
1330 Set<StoredGroupEntry> storedGroupEntries) {
1331 if (!garbageCollect) {
1332 return;
1333 }
1334
1335 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1336 while (it.hasNext()) {
1337 StoredGroupEntry group = it.next();
1338 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1339 log.debug("Garbage collecting group {} on {}", group, deviceId);
1340 deleteGroupDescription(deviceId, group.appCookie());
1341 southboundGroupEntries.remove(group);
1342 it.remove();
1343 }
1344 }
1345 }
1346
1347 private boolean checkGroupRefCount(Group group) {
1348 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1349 }
1350
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001351 private void groupMissing(Group group) {
1352 switch (group.state()) {
1353 case PENDING_DELETE:
1354 log.debug("Group {} delete confirmation from device {}",
1355 group, group.deviceId());
1356 removeGroupEntry(group);
1357 break;
1358 case ADDED:
1359 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001360 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001361 case PENDING_UPDATE:
1362 log.debug("Group {} is in store but not on device {}",
1363 group, group.deviceId());
1364 StoredGroupEntry existing =
1365 getStoredGroupEntry(group.deviceId(), group.id());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001366 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
alshabibb0285992016-03-28 23:30:37 -07001367 existing.id(),
1368 existing.deviceId(),
1369 existing.state());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001370 existing.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001371 //Re-PUT map entries to trigger map update events
1372 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -07001373 put(new GroupStoreKeyMapKey(existing.deviceId(),
1374 existing.appCookie()), existing);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001375 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1376 group));
1377 break;
1378 default:
1379 log.debug("Group {} has not been installed.", group);
1380 break;
1381 }
1382 }
1383
1384 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001385 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001386 group, group.deviceId());
1387 addOrUpdateExtraneousGroupEntry(group);
1388 }
1389
1390 private void groupAdded(Group group) {
1391 log.trace("Group {} Added or Updated in device {}",
1392 group, group.deviceId());
1393 addOrUpdateGroupEntry(group);
1394 }
alshabib10580802015-02-18 18:30:33 -08001395}