blob: f9a53ec76a05cd88b704a8028547a87f8f6c58c7 [file] [log] [blame]
alshabib10580802015-02-18 18:30:33 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
alshabib10580802015-02-18 18:30:33 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.store.group.impl;
17
Jonathan Hart6ec029a2015-03-24 17:12:35 -070018import com.google.common.collect.FluentIterable;
Charles Chanf4838a72015-12-07 18:13:45 -080019import com.google.common.collect.ImmutableSet;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070020import com.google.common.collect.Iterables;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070021import com.google.common.collect.Sets;
alshabib10580802015-02-18 18:30:33 -080022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
alshabibb0285992016-03-28 23:30:37 -070025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
alshabib10580802015-02-18 18:30:33 -080029import org.apache.felix.scr.annotations.Service;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070030import org.onlab.util.KryoNamespace;
alshabibb0285992016-03-28 23:30:37 -070031import org.onosproject.cfg.ComponentConfigService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070032import org.onosproject.cluster.ClusterService;
Charles Chanf4838a72015-12-07 18:13:45 -080033import org.onosproject.cluster.NodeId;
alshabib10580802015-02-18 18:30:33 -080034import org.onosproject.core.GroupId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070035import org.onosproject.mastership.MastershipService;
alshabib10580802015-02-18 18:30:33 -080036import org.onosproject.net.DeviceId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070037import org.onosproject.net.MastershipRole;
alshabib10580802015-02-18 18:30:33 -080038import org.onosproject.net.group.DefaultGroup;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070039import org.onosproject.net.group.DefaultGroupBucket;
alshabib10580802015-02-18 18:30:33 -080040import org.onosproject.net.group.DefaultGroupDescription;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070041import org.onosproject.net.group.DefaultGroupKey;
alshabib10580802015-02-18 18:30:33 -080042import org.onosproject.net.group.Group;
43import org.onosproject.net.group.Group.GroupState;
44import org.onosproject.net.group.GroupBucket;
45import org.onosproject.net.group.GroupBuckets;
46import org.onosproject.net.group.GroupDescription;
47import org.onosproject.net.group.GroupEvent;
48import org.onosproject.net.group.GroupEvent.Type;
49import org.onosproject.net.group.GroupKey;
50import org.onosproject.net.group.GroupOperation;
51import org.onosproject.net.group.GroupStore;
52import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070053import org.onosproject.net.group.StoredGroupBucketEntry;
alshabib10580802015-02-18 18:30:33 -080054import org.onosproject.net.group.StoredGroupEntry;
55import org.onosproject.store.AbstractStore;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070056import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070057import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani0b847532016-03-03 13:44:15 -080058import org.onosproject.store.service.ConsistentMap;
59import org.onosproject.store.service.MapEvent;
60import org.onosproject.store.service.MapEventListener;
alshabibb0285992016-03-28 23:30:37 -070061import org.onosproject.store.service.MultiValuedTimestamp;
Madan Jampani0b847532016-03-03 13:44:15 -080062import org.onosproject.store.service.Serializer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070063import org.onosproject.store.service.StorageService;
helenyrwua1c41152016-08-18 16:16:14 -070064import org.onosproject.store.service.Topic;
Madan Jampani0b847532016-03-03 13:44:15 -080065import org.onosproject.store.service.Versioned;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053066import org.onosproject.store.service.DistributedPrimitive.Status;
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;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053088import java.util.concurrent.ScheduledExecutorService;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070089import java.util.concurrent.atomic.AtomicInteger;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053090import java.util.function.Consumer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070091import java.util.stream.Collectors;
92
alshabibb0285992016-03-28 23:30:37 -070093import static com.google.common.base.Strings.isNullOrEmpty;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053094import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
alshabibb0285992016-03-28 23:30:37 -070095import static org.onlab.util.Tools.get;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070096import static org.onlab.util.Tools.groupedThreads;
97import static org.slf4j.LoggerFactory.getLogger;
alshabib10580802015-02-18 18:30:33 -080098
99/**
Saurav Das0fd79d92016-03-07 10:58:36 -0800100 * Manages inventory of group entries using distributed group stores from the
101 * storage service.
alshabib10580802015-02-18 18:30:33 -0800102 */
103@Component(immediate = true)
104@Service
105public class DistributedGroupStore
106 extends AbstractStore<GroupEvent, GroupStoreDelegate>
107 implements GroupStore {
108
109 private final Logger log = getLogger(getClass());
110
alshabibb0285992016-03-28 23:30:37 -0700111 private static final boolean GARBAGE_COLLECT = false;
112 private static final int GC_THRESH = 6;
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530113 private static final boolean ALLOW_EXTRANEOUS_GROUPS = true;
alshabibb0285992016-03-28 23:30:37 -0700114
alshabib10580802015-02-18 18:30:33 -0800115 private final int dummyId = 0xffffffff;
Yi Tsengfa394de2017-02-01 11:26:40 -0800116 private final GroupId dummyGroupId = new GroupId(dummyId);
alshabib10580802015-02-18 18:30:33 -0800117
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected ClusterCommunicationService clusterCommunicator;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected ClusterService clusterService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700125 protected StorageService storageService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700128 protected MastershipService mastershipService;
129
alshabibb0285992016-03-28 23:30:37 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected ComponentConfigService cfgService;
132
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530133 private ScheduledExecutorService executor;
134 private Consumer<Status> statusChangeListener;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700135 // Per device group table with (device id + app cookie) as key
Madan Jampani0b847532016-03-03 13:44:15 -0800136 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700137 StoredGroupEntry> groupStoreEntriesByKey = null;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700138 // Per device group table with (device id + group id) as key
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700139 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
alshabibb0285992016-03-28 23:30:37 -0700140 groupEntriesById = new ConcurrentHashMap<>();
Madan Jampani0b847532016-03-03 13:44:15 -0800141 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700142 StoredGroupEntry> auditPendingReqQueue = null;
Frank Wange0eb5ce2016-07-01 18:21:25 +0800143 private MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry>
144 mapListener = new GroupStoreKeyMapListener();
alshabib10580802015-02-18 18:30:33 -0800145 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
146 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700147 private ExecutorService messageHandlingExecutor;
148 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700149 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
alshabib10580802015-02-18 18:30:33 -0800150
151 private final AtomicInteger groupIdGen = new AtomicInteger();
152
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700153 private KryoNamespace clusterMsgSerializer;
154
helenyrwua1c41152016-08-18 16:16:14 -0700155 private static Topic<GroupStoreMessage> groupTopic;
156
alshabibb0285992016-03-28 23:30:37 -0700157 @Property(name = "garbageCollect", boolValue = GARBAGE_COLLECT,
158 label = "Enable group garbage collection")
159 private boolean garbageCollect = GARBAGE_COLLECT;
160
161 @Property(name = "gcThresh", intValue = GC_THRESH,
162 label = "Number of rounds for group garbage collection")
163 private int gcThresh = GC_THRESH;
164
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530165 @Property(name = "allowExtraneousGroups", boolValue = ALLOW_EXTRANEOUS_GROUPS,
166 label = "Allow groups in switches not installed by ONOS")
167 private boolean allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS;
alshabibb0285992016-03-28 23:30:37 -0700168
alshabib10580802015-02-18 18:30:33 -0800169 @Activate
170 public void activate() {
alshabibb0285992016-03-28 23:30:37 -0700171 cfgService.registerProperties(getClass());
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700172 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
alshabibb0285992016-03-28 23:30:37 -0700173 .register(KryoNamespaces.API)
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700174 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
alshabibb0285992016-03-28 23:30:37 -0700175 .register(DefaultGroup.class,
176 DefaultGroupBucket.class,
177 DefaultGroupDescription.class,
178 DefaultGroupKey.class,
179 GroupDescription.Type.class,
180 Group.GroupState.class,
181 GroupBuckets.class,
alshabibb0285992016-03-28 23:30:37 -0700182 GroupStoreMessage.class,
183 GroupStoreMessage.Type.class,
184 UpdateType.class,
185 GroupStoreMessageSubjects.class,
186 MultiValuedTimestamp.class,
187 GroupStoreKeyMapKey.class,
188 GroupStoreIdMapKey.class,
189 GroupStoreMapKey.class
190 );
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700191
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700192 clusterMsgSerializer = kryoBuilder.build("GroupStore");
193 Serializer serializer = Serializer.using(clusterMsgSerializer);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700194
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700195 messageHandlingExecutor = Executors.
196 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
197 groupedThreads("onos/store/group",
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700198 "message-handlers",
199 log));
Madan Jampani01e05fb2015-08-13 13:29:36 -0700200
201 clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
alshabibb0285992016-03-28 23:30:37 -0700202 clusterMsgSerializer::deserialize,
203 this::process,
204 messageHandlingExecutor);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700205
Madan Jampani0b847532016-03-03 13:44:15 -0800206 log.debug("Creating Consistent map onos-group-store-keymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700207
Madan Jampani0b847532016-03-03 13:44:15 -0800208 groupStoreEntriesByKey = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
209 .withName("onos-group-store-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700210 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700211 .build();
Frank Wange0eb5ce2016-07-01 18:21:25 +0800212 groupStoreEntriesByKey.addListener(mapListener);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700213 log.debug("Current size of groupstorekeymap:{}",
214 groupStoreEntriesByKey.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700215
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530216 log.debug("Creating GroupStoreId Map From GroupStoreKey Map");
217 matchGroupEntries();
218 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/group", "store", log));
219 statusChangeListener = status -> {
220 if (status == Status.ACTIVE) {
221 executor.execute(this::matchGroupEntries);
222 }
223 };
224 groupStoreEntriesByKey.addStatusChangeListener(statusChangeListener);
225
Madan Jampani0b847532016-03-03 13:44:15 -0800226 log.debug("Creating Consistent map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700227
Madan Jampani0b847532016-03-03 13:44:15 -0800228 auditPendingReqQueue = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
229 .withName("onos-pending-group-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700230 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700231 .build();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700232 log.debug("Current size of pendinggroupkeymap:{}",
233 auditPendingReqQueue.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700234
helenyrwua1c41152016-08-18 16:16:14 -0700235 groupTopic = getOrCreateGroupTopic(serializer);
236 groupTopic.subscribe(this::processGroupMessage);
237
alshabib10580802015-02-18 18:30:33 -0800238 log.info("Started");
239 }
240
241 @Deactivate
242 public void deactivate() {
Frank Wange0eb5ce2016-07-01 18:21:25 +0800243 groupStoreEntriesByKey.removeListener(mapListener);
alshabibb0285992016-03-28 23:30:37 -0700244 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700245 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
alshabib10580802015-02-18 18:30:33 -0800246 log.info("Stopped");
247 }
248
alshabibb0285992016-03-28 23:30:37 -0700249 @Modified
250 public void modified(ComponentContext context) {
251 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
252
253 try {
254 String s = get(properties, "garbageCollect");
255 garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT : Boolean.parseBoolean(s.trim());
256
257 s = get(properties, "gcThresh");
258 gcThresh = isNullOrEmpty(s) ? GC_THRESH : Integer.parseInt(s.trim());
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530259
260 s = get(properties, "allowExtraneousGroups");
261 allowExtraneousGroups = isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_GROUPS : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700262 } catch (Exception e) {
263 gcThresh = GC_THRESH;
264 garbageCollect = GARBAGE_COLLECT;
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530265 allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS;
alshabibb0285992016-03-28 23:30:37 -0700266 }
267 }
268
helenyrwua1c41152016-08-18 16:16:14 -0700269 private Topic<GroupStoreMessage> getOrCreateGroupTopic(Serializer serializer) {
270 if (groupTopic == null) {
271 return storageService.getTopic("group-failover-notif", serializer);
272 } else {
273 return groupTopic;
274 }
Sho SHIMIZUa6285542017-01-12 15:08:24 -0800275 }
helenyrwua1c41152016-08-18 16:16:14 -0700276
alshabib10580802015-02-18 18:30:33 -0800277 /**
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530278 * Updating values of groupEntriesById.
279 */
280 private void matchGroupEntries() {
281 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupStoreEntriesByKey.asJavaMap().entrySet()) {
282 StoredGroupEntry group = entry.getValue();
283 getGroupIdTable(entry.getKey().deviceId()).put(group.id(), group);
284 }
285 }
286
287 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700288 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800289 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700290 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800291 */
Madan Jampani0b847532016-03-03 13:44:15 -0800292 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700293 getGroupStoreKeyMap() {
Madan Jampani0b847532016-03-03 13:44:15 -0800294 return groupStoreEntriesByKey.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800295 }
296
297 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700298 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800299 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700300 * @param deviceId identifier of the device
301 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800302 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700303 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700304 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800305 }
306
307 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700308 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800309 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700310 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800311 */
Madan Jampani0b847532016-03-03 13:44:15 -0800312 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700313 getPendingGroupKeyTable() {
Madan Jampani0b847532016-03-03 13:44:15 -0800314 return auditPendingReqQueue.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800315 }
316
317 /**
318 * Returns the extraneous group id table for specified device.
319 *
320 * @param deviceId identifier of the device
321 * @return Map representing group key table of given device.
322 */
323 private ConcurrentMap<GroupId, Group>
324 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700325 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800326 }
327
328 /**
329 * Returns the number of groups for the specified device in the store.
330 *
331 * @return number of groups for the specified device
332 */
333 @Override
334 public int getGroupCount(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700335 return (getGroups(deviceId) != null) ?
alshabibb0285992016-03-28 23:30:37 -0700336 Iterables.size(getGroups(deviceId)) : 0;
alshabib10580802015-02-18 18:30:33 -0800337 }
338
339 /**
340 * Returns the groups associated with a device.
341 *
342 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800343 * @return the group entries
344 */
345 @Override
346 public Iterable<Group> getGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800347 // Let ImmutableSet.copyOf do the type conversion
348 return ImmutableSet.copyOf(getStoredGroups(deviceId));
alshabib10580802015-02-18 18:30:33 -0800349 }
350
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700351 private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800352 NodeId master = mastershipService.getMasterFor(deviceId);
353 if (master == null) {
354 log.debug("Failed to getGroups: No master for {}", deviceId);
355 return Collections.emptySet();
356 }
357
358 Set<StoredGroupEntry> storedGroups = getGroupStoreKeyMap().values()
359 .stream()
360 .filter(input -> input.deviceId().equals(deviceId))
361 .collect(Collectors.toSet());
362 return ImmutableSet.copyOf(storedGroups);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700363 }
364
alshabib10580802015-02-18 18:30:33 -0800365 /**
366 * Returns the stored group entry.
367 *
alshabibb0285992016-03-28 23:30:37 -0700368 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800369 * @param appCookie the group key
alshabib10580802015-02-18 18:30:33 -0800370 * @return a group associated with the key
371 */
372 @Override
373 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700374 return getStoredGroupEntry(deviceId, appCookie);
375 }
376
377 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
378 GroupKey appCookie) {
379 return getGroupStoreKeyMap().get(new GroupStoreKeyMapKey(deviceId,
380 appCookie));
381 }
382
383 @Override
384 public Group getGroup(DeviceId deviceId, GroupId groupId) {
385 return getStoredGroupEntry(deviceId, groupId);
386 }
387
388 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
389 GroupId groupId) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700390 return getGroupIdTable(deviceId).get(groupId);
alshabib10580802015-02-18 18:30:33 -0800391 }
392
393 private int getFreeGroupIdValue(DeviceId deviceId) {
394 int freeId = groupIdGen.incrementAndGet();
395
396 while (true) {
Yi Tsengfa394de2017-02-01 11:26:40 -0800397 Group existing = getGroup(deviceId, new GroupId(freeId));
alshabib10580802015-02-18 18:30:33 -0800398 if (existing == null) {
399 existing = (
400 extraneousGroupEntriesById.get(deviceId) != null) ?
401 extraneousGroupEntriesById.get(deviceId).
Yi Tsengfa394de2017-02-01 11:26:40 -0800402 get(new GroupId(freeId)) :
alshabib10580802015-02-18 18:30:33 -0800403 null;
404 }
405 if (existing != null) {
406 freeId = groupIdGen.incrementAndGet();
407 } else {
408 break;
409 }
410 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700411 log.debug("getFreeGroupIdValue: Next Free ID is {}", freeId);
alshabib10580802015-02-18 18:30:33 -0800412 return freeId;
413 }
414
415 /**
416 * Stores a new group entry using the information from group description.
417 *
418 * @param groupDesc group description to be used to create group entry
419 */
420 @Override
421 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700422 log.debug("In storeGroupDescription");
alshabib10580802015-02-18 18:30:33 -0800423 // Check if a group is existing with the same key
Saurav Das8a0732e2015-11-20 15:27:53 -0800424 Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
425 if (existingGroup != null) {
Charles Chan216e3c82016-04-23 14:48:16 -0700426 log.info("Group already exists with the same key {} in dev:{} with id:0x{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800427 groupDesc.appCookie(), groupDesc.deviceId(),
428 Integer.toHexString(existingGroup.id().id()));
alshabib10580802015-02-18 18:30:33 -0800429 return;
430 }
431
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700432 // Check if group to be created by a remote instance
Madan Jampani175e8fd2015-05-20 14:10:45 -0700433 if (mastershipService.getLocalRole(groupDesc.deviceId()) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700434 log.debug("storeGroupDescription: Device {} local role is not MASTER",
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700435 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700436 if (mastershipService.getMasterFor(groupDesc.deviceId()) == null) {
437 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700438 + "Can not perform add group operation",
439 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700440 //TODO: Send Group operation failure event
441 return;
442 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700443 GroupStoreMessage groupOp = GroupStoreMessage.
444 createGroupAddRequestMsg(groupDesc.deviceId(),
445 groupDesc);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700446
Madan Jampani175e8fd2015-05-20 14:10:45 -0700447 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700448 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
449 clusterMsgSerializer::serialize,
450 mastershipService.getMasterFor(groupDesc.deviceId()))
451 .whenComplete((result, error) -> {
Madan Jampani175e8fd2015-05-20 14:10:45 -0700452 if (error != null) {
453 log.warn("Failed to send request to master: {} to {}",
alshabibb0285992016-03-28 23:30:37 -0700454 groupOp,
455 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700456 //TODO: Send Group operation failure event
457 } else {
458 log.debug("Sent Group operation request for device {} "
alshabibb0285992016-03-28 23:30:37 -0700459 + "to remote MASTER {}",
460 groupDesc.deviceId(),
461 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700462 }
463 });
alshabib10580802015-02-18 18:30:33 -0800464 return;
465 }
466
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700467 log.debug("Store group for device {} is getting handled locally",
468 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800469 storeGroupDescriptionInternal(groupDesc);
470 }
471
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700472 private Group getMatchingExtraneousGroupbyId(DeviceId deviceId, Integer groupId) {
473 ConcurrentMap<GroupId, Group> extraneousMap =
474 extraneousGroupEntriesById.get(deviceId);
475 if (extraneousMap == null) {
476 return null;
477 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800478 return extraneousMap.get(new GroupId(groupId));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700479 }
480
481 private Group getMatchingExtraneousGroupbyBuckets(DeviceId deviceId,
482 GroupBuckets buckets) {
483 ConcurrentMap<GroupId, Group> extraneousMap =
484 extraneousGroupEntriesById.get(deviceId);
485 if (extraneousMap == null) {
486 return null;
487 }
488
alshabibb0285992016-03-28 23:30:37 -0700489 for (Group extraneousGroup : extraneousMap.values()) {
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700490 if (extraneousGroup.buckets().equals(buckets)) {
491 return extraneousGroup;
492 }
493 }
494 return null;
495 }
496
alshabib10580802015-02-18 18:30:33 -0800497 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
498 // Check if a group is existing with the same key
499 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
500 return;
501 }
502
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700503 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
504 // Device group audit has not completed yet
505 // Add this group description to pending group key table
506 // Create a group entry object with Dummy Group ID
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700507 log.debug("storeGroupDescriptionInternal: Device {} AUDIT pending...Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700508 groupDesc.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700509 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
510 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
Madan Jampani0b847532016-03-03 13:44:15 -0800511 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700512 getPendingGroupKeyTable();
513 pendingKeyTable.put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
514 groupDesc.appCookie()),
515 group);
516 return;
517 }
518
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700519 Group matchingExtraneousGroup = null;
520 if (groupDesc.givenGroupId() != null) {
521 //Check if there is a extraneous group existing with the same Id
522 matchingExtraneousGroup = getMatchingExtraneousGroupbyId(
alshabibb0285992016-03-28 23:30:37 -0700523 groupDesc.deviceId(), groupDesc.givenGroupId());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700524 if (matchingExtraneousGroup != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800525 log.debug("storeGroupDescriptionInternal: Matching extraneous group "
alshabibb0285992016-03-28 23:30:37 -0700526 + "found in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700527 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800528 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700529 //Check if the group buckets matches with user provided buckets
530 if (matchingExtraneousGroup.buckets().equals(groupDesc.buckets())) {
531 //Group is already existing with the same buckets and Id
532 // Create a group entry object
Saurav Das0fd79d92016-03-07 10:58:36 -0800533 log.debug("storeGroupDescriptionInternal: Buckets also matching "
alshabibb0285992016-03-28 23:30:37 -0700534 + "in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700535 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800536 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700537 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700538 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700539 // Insert the newly created group entry into key and id maps
540 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700541 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
542 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700543 // Ensure it also inserted into group id based table to
544 // avoid any chances of duplication in group id generation
545 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700546 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700547 addOrUpdateGroupEntry(matchingExtraneousGroup);
548 removeExtraneousGroupEntry(matchingExtraneousGroup);
549 return;
550 } else {
551 //Group buckets are not matching. Update group
552 //with user provided buckets.
Saurav Das0fd79d92016-03-07 10:58:36 -0800553 log.debug("storeGroupDescriptionInternal: Buckets are not "
alshabibb0285992016-03-28 23:30:37 -0700554 + "matching in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700555 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800556 Integer.toHexString(groupDesc.givenGroupId()));
557 StoredGroupEntry modifiedGroup = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700558 matchingExtraneousGroup.id(), groupDesc);
Saurav Das0fd79d92016-03-07 10:58:36 -0800559 modifiedGroup.setState(GroupState.PENDING_UPDATE);
560 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700561 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
562 groupDesc.appCookie()), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800563 // Ensure it also inserted into group id based table to
564 // avoid any chances of duplication in group id generation
565 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700566 put(matchingExtraneousGroup.id(), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800567 removeExtraneousGroupEntry(matchingExtraneousGroup);
568 log.debug("storeGroupDescriptionInternal: Triggering Group "
alshabibb0285992016-03-28 23:30:37 -0700569 + "UPDATE request for {} in device {}",
Saurav Das0fd79d92016-03-07 10:58:36 -0800570 matchingExtraneousGroup.id(),
571 groupDesc.deviceId());
572 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, modifiedGroup));
573 return;
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700574 }
575 }
576 } else {
577 //Check if there is an extraneous group with user provided buckets
578 matchingExtraneousGroup = getMatchingExtraneousGroupbyBuckets(
alshabibb0285992016-03-28 23:30:37 -0700579 groupDesc.deviceId(), groupDesc.buckets());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700580 if (matchingExtraneousGroup != null) {
581 //Group is already existing with the same buckets.
582 //So reuse this group.
583 log.debug("storeGroupDescriptionInternal: Matching extraneous group found in Device {}",
584 groupDesc.deviceId());
585 //Create a group entry object
586 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700587 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700588 // Insert the newly created group entry into key and id maps
589 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700590 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
591 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700592 // Ensure it also inserted into group id based table to
593 // avoid any chances of duplication in group id generation
594 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700595 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700596 addOrUpdateGroupEntry(matchingExtraneousGroup);
597 removeExtraneousGroupEntry(matchingExtraneousGroup);
598 return;
599 } else {
600 //TODO: Check if there are any empty groups that can be used here
601 log.debug("storeGroupDescriptionInternal: No matching extraneous groups found in Device {}",
602 groupDesc.deviceId());
603 }
604 }
605
Saurav Das100e3b82015-04-30 11:12:10 -0700606 GroupId id = null;
607 if (groupDesc.givenGroupId() == null) {
608 // Get a new group identifier
Yi Tsengfa394de2017-02-01 11:26:40 -0800609 id = new GroupId(getFreeGroupIdValue(groupDesc.deviceId()));
Saurav Das100e3b82015-04-30 11:12:10 -0700610 } else {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800611 // we need to use the identifier passed in by caller, but check if
612 // already used
613 Group existing = getGroup(groupDesc.deviceId(),
Yi Tsengfa394de2017-02-01 11:26:40 -0800614 new GroupId(groupDesc.givenGroupId()));
Saurav Das8be4e3a2016-03-11 17:19:07 -0800615 if (existing != null) {
616 log.warn("Group already exists with the same id: 0x{} in dev:{} "
alshabibb0285992016-03-28 23:30:37 -0700617 + "but with different key: {} (request gkey: {})",
618 Integer.toHexString(groupDesc.givenGroupId()),
619 groupDesc.deviceId(),
620 existing.appCookie(),
621 groupDesc.appCookie());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800622 return;
623 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800624 id = new GroupId(groupDesc.givenGroupId());
Saurav Das100e3b82015-04-30 11:12:10 -0700625 }
alshabib10580802015-02-18 18:30:33 -0800626 // Create a group entry object
627 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700628 // Insert the newly created group entry into key and id maps
629 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700630 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
631 groupDesc.appCookie()), group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700632 // Ensure it also inserted into group id based table to
633 // avoid any chances of duplication in group id generation
634 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700635 put(id, group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700636 log.debug("storeGroupDescriptionInternal: Processing Group ADD request for Id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700637 id,
638 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800639 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
640 group));
641 }
642
643 /**
644 * Updates the existing group entry with the information
645 * from group description.
646 *
alshabibb0285992016-03-28 23:30:37 -0700647 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800648 * @param oldAppCookie the current group key
alshabibb0285992016-03-28 23:30:37 -0700649 * @param type update type
650 * @param newBuckets group buckets for updates
alshabib10580802015-02-18 18:30:33 -0800651 * @param newAppCookie optional new group key
652 */
653 @Override
654 public void updateGroupDescription(DeviceId deviceId,
655 GroupKey oldAppCookie,
656 UpdateType type,
657 GroupBuckets newBuckets,
658 GroupKey newAppCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700659 // Check if group update to be done by a remote instance
sangho52abe3a2015-05-05 14:13:34 -0700660 if (mastershipService.getMasterFor(deviceId) != null &&
661 mastershipService.getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700662 log.debug("updateGroupDescription: Device {} local role is not MASTER",
663 deviceId);
664 if (mastershipService.getMasterFor(deviceId) == null) {
665 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700666 + "Can not perform update group operation",
667 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700668 //TODO: Send Group operation failure event
669 return;
670 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700671 GroupStoreMessage groupOp = GroupStoreMessage.
672 createGroupUpdateRequestMsg(deviceId,
673 oldAppCookie,
674 type,
675 newBuckets,
676 newAppCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700677
Madan Jampani175e8fd2015-05-20 14:10:45 -0700678 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700679 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
680 clusterMsgSerializer::serialize,
681 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
682 if (error != null) {
683 log.warn("Failed to send request to master: {} to {}",
684 groupOp,
685 mastershipService.getMasterFor(deviceId), error);
686 }
687 //TODO: Send Group operation failure event
688 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700689 return;
690 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700691 log.debug("updateGroupDescription for device {} is getting handled locally",
692 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700693 updateGroupDescriptionInternal(deviceId,
694 oldAppCookie,
695 type,
696 newBuckets,
697 newAppCookie);
698 }
699
700 private void updateGroupDescriptionInternal(DeviceId deviceId,
alshabibb0285992016-03-28 23:30:37 -0700701 GroupKey oldAppCookie,
702 UpdateType type,
703 GroupBuckets newBuckets,
704 GroupKey newAppCookie) {
alshabib10580802015-02-18 18:30:33 -0800705 // Check if a group is existing with the provided key
706 Group oldGroup = getGroup(deviceId, oldAppCookie);
707 if (oldGroup == null) {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800708 log.warn("updateGroupDescriptionInternal: Group not found...strange. "
alshabibb0285992016-03-28 23:30:37 -0700709 + "GroupKey:{} DeviceId:{}", oldAppCookie, deviceId);
alshabib10580802015-02-18 18:30:33 -0800710 return;
711 }
712
713 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
714 type,
715 newBuckets);
716 if (newBucketList != null) {
717 // Create a new group object from the old group
718 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
719 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
720 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
721 oldGroup.deviceId(),
722 oldGroup.type(),
723 updatedBuckets,
724 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700725 oldGroup.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800726 oldGroup.appId());
727 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
728 updatedGroupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700729 log.debug("updateGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_UPDATE",
alshabibb0285992016-03-28 23:30:37 -0700730 oldGroup.id(),
731 oldGroup.deviceId(),
732 oldGroup.state());
alshabib10580802015-02-18 18:30:33 -0800733 newGroup.setState(GroupState.PENDING_UPDATE);
734 newGroup.setLife(oldGroup.life());
735 newGroup.setPackets(oldGroup.packets());
736 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700737 //Update the group entry in groupkey based map.
738 //Update to groupid based map will happen in the
739 //groupkey based map update listener
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700740 log.debug("updateGroupDescriptionInternal with type {}: Group updated with buckets",
741 type);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700742 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700743 put(new GroupStoreKeyMapKey(newGroup.deviceId(),
744 newGroup.appCookie()), newGroup);
alshabib10580802015-02-18 18:30:33 -0800745 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700746 } else {
747 log.warn("updateGroupDescriptionInternal with type {}: No "
alshabibb0285992016-03-28 23:30:37 -0700748 + "change in the buckets in update", type);
alshabib10580802015-02-18 18:30:33 -0800749 }
750 }
751
752 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
753 UpdateType type,
754 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300755 if (type == UpdateType.SET) {
756 return buckets.buckets();
757 }
758
Victor Silvadf1eeae2016-08-12 15:28:57 -0300759 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
760 List<GroupBucket> updatedBucketList = new ArrayList<>();
alshabib10580802015-02-18 18:30:33 -0800761 boolean groupDescUpdated = false;
762
763 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300764 List<GroupBucket> newBuckets = buckets.buckets();
765
766 // Add old buckets that will not be updated and check if any will be updated.
767 for (GroupBucket oldBucket : oldBuckets) {
768 int newBucketIndex = newBuckets.indexOf(oldBucket);
769
770 if (newBucketIndex != -1) {
771 GroupBucket newBucket = newBuckets.get(newBucketIndex);
772 if (!newBucket.hasSameParameters(oldBucket)) {
773 // Bucket will be updated
774 groupDescUpdated = true;
775 }
776 } else {
777 // Old bucket will remain the same - add it.
778 updatedBucketList.add(oldBucket);
alshabib10580802015-02-18 18:30:33 -0800779 }
780 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300781
782 // Add all new buckets
783 updatedBucketList.addAll(newBuckets);
784 if (!oldBuckets.containsAll(newBuckets)) {
785 groupDescUpdated = true;
786 }
787
alshabib10580802015-02-18 18:30:33 -0800788 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300789 List<GroupBucket> bucketsToRemove = buckets.buckets();
790
791 // Check which old buckets should remain
792 for (GroupBucket oldBucket : oldBuckets) {
793 if (!bucketsToRemove.contains(oldBucket)) {
794 updatedBucketList.add(oldBucket);
795 } else {
alshabib10580802015-02-18 18:30:33 -0800796 groupDescUpdated = true;
797 }
798 }
799 }
800
801 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300802 return updatedBucketList;
alshabib10580802015-02-18 18:30:33 -0800803 } else {
804 return null;
805 }
806 }
807
808 /**
809 * Triggers deleting the existing group entry.
810 *
alshabibb0285992016-03-28 23:30:37 -0700811 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800812 * @param appCookie the group key
813 */
814 @Override
815 public void deleteGroupDescription(DeviceId deviceId,
816 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700817 // Check if group to be deleted by a remote instance
818 if (mastershipService.
819 getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700820 log.debug("deleteGroupDescription: Device {} local role is not MASTER",
821 deviceId);
822 if (mastershipService.getMasterFor(deviceId) == null) {
823 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700824 + "Can not perform delete group operation",
825 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700826 //TODO: Send Group operation failure event
827 return;
828 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700829 GroupStoreMessage groupOp = GroupStoreMessage.
830 createGroupDeleteRequestMsg(deviceId,
831 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700832
Madan Jampani175e8fd2015-05-20 14:10:45 -0700833 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700834 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
835 clusterMsgSerializer::serialize,
836 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
837 if (error != null) {
838 log.warn("Failed to send request to master: {} to {}",
839 groupOp,
840 mastershipService.getMasterFor(deviceId), error);
841 }
842 //TODO: Send Group operation failure event
843 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700844 return;
845 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700846 log.debug("deleteGroupDescription in device {} is getting handled locally",
847 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700848 deleteGroupDescriptionInternal(deviceId, appCookie);
849 }
850
851 private void deleteGroupDescriptionInternal(DeviceId deviceId,
852 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800853 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700854 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800855 if (existing == null) {
856 return;
857 }
858
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700859 log.debug("deleteGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_DELETE",
alshabibb0285992016-03-28 23:30:37 -0700860 existing.id(),
861 existing.deviceId(),
862 existing.state());
alshabib10580802015-02-18 18:30:33 -0800863 synchronized (existing) {
864 existing.setState(GroupState.PENDING_DELETE);
Saurav Das80980c72016-03-23 11:22:49 -0700865 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700866 put(new GroupStoreKeyMapKey(existing.deviceId(), existing.appCookie()),
867 existing);
alshabib10580802015-02-18 18:30:33 -0800868 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700869 log.debug("deleteGroupDescriptionInternal: in device {} issuing GROUP_REMOVE_REQUESTED",
870 deviceId);
alshabib10580802015-02-18 18:30:33 -0800871 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
872 }
873
874 /**
875 * Stores a new group entry, or updates an existing entry.
876 *
877 * @param group group entry
878 */
879 @Override
880 public void addOrUpdateGroupEntry(Group group) {
881 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700882 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
883 group.id());
alshabib10580802015-02-18 18:30:33 -0800884 GroupEvent event = null;
885
886 if (existing != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800887 log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700888 group.id(),
889 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800890 synchronized (existing) {
alshabibb0285992016-03-28 23:30:37 -0700891 for (GroupBucket bucket : group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700892 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700893 existing.buckets().buckets()
alshabibb0285992016-03-28 23:30:37 -0700894 .stream()
895 .filter((existingBucket) -> (existingBucket.equals(bucket)))
896 .findFirst();
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700897 if (matchingBucket.isPresent()) {
898 ((StoredGroupBucketEntry) matchingBucket.
899 get()).setPackets(bucket.packets());
900 ((StoredGroupBucketEntry) matchingBucket.
901 get()).setBytes(bucket.bytes());
902 } else {
903 log.warn("addOrUpdateGroupEntry: No matching "
alshabibb0285992016-03-28 23:30:37 -0700904 + "buckets to update stats");
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700905 }
906 }
alshabib10580802015-02-18 18:30:33 -0800907 existing.setLife(group.life());
908 existing.setPackets(group.packets());
909 existing.setBytes(group.bytes());
alshabibb0285992016-03-28 23:30:37 -0700910 existing.setReferenceCount(group.referenceCount());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700911 if ((existing.state() == GroupState.PENDING_ADD) ||
alshabibb0285992016-03-28 23:30:37 -0700912 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800913 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700914 existing.id(),
915 existing.deviceId(),
916 existing.state());
alshabib10580802015-02-18 18:30:33 -0800917 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700918 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800919 event = new GroupEvent(Type.GROUP_ADDED, existing);
920 } else {
Saurav Das0fd79d92016-03-07 10:58:36 -0800921 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700922 existing.id(),
923 existing.deviceId(),
924 GroupState.PENDING_UPDATE);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700925 existing.setState(GroupState.ADDED);
926 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800927 event = new GroupEvent(Type.GROUP_UPDATED, existing);
928 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700929 //Re-PUT map entries to trigger map update events
930 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700931 put(new GroupStoreKeyMapKey(existing.deviceId(),
932 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800933 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700934 } else {
935 log.warn("addOrUpdateGroupEntry: Group update "
alshabibb0285992016-03-28 23:30:37 -0700936 + "happening for a non-existing entry in the map");
alshabib10580802015-02-18 18:30:33 -0800937 }
938
939 if (event != null) {
940 notifyDelegate(event);
941 }
942 }
943
944 /**
945 * Removes the group entry from store.
946 *
947 * @param group group entry
948 */
949 @Override
950 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700951 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
952 group.id());
alshabib10580802015-02-18 18:30:33 -0800953
954 if (existing != null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700955 log.debug("removeGroupEntry: removing group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700956 group.id(),
957 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700958 //Removal from groupid based map will happen in the
959 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700960 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
961 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800962 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700963 } else {
964 log.warn("removeGroupEntry for {} in device{} is "
alshabibb0285992016-03-28 23:30:37 -0700965 + "not existing in our maps",
966 group.id(),
967 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800968 }
969 }
970
Victor Silva4e8b7832016-08-17 17:11:19 -0300971 private void purgeGroupEntries(Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entries) {
972 entries.forEach(entry -> {
973 groupStoreEntriesByKey.remove(entry.getKey());
974 });
975 }
976
alshabib10580802015-02-18 18:30:33 -0800977 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800978 public void purgeGroupEntry(DeviceId deviceId) {
Victor Silva4e8b7832016-08-17 17:11:19 -0300979 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entriesPendingRemove =
Charles Chan0c7c43b2016-01-14 17:39:20 -0800980 new HashSet<>();
981
Madan Jampani0b847532016-03-03 13:44:15 -0800982 getGroupStoreKeyMap().entrySet().stream()
Charles Chan0c7c43b2016-01-14 17:39:20 -0800983 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Victor Silva4e8b7832016-08-17 17:11:19 -0300984 .forEach(entriesPendingRemove::add);
Charles Chan0c7c43b2016-01-14 17:39:20 -0800985
Victor Silva4e8b7832016-08-17 17:11:19 -0300986 purgeGroupEntries(entriesPendingRemove);
987 }
988
989 @Override
990 public void purgeGroupEntries() {
991 purgeGroupEntries(getGroupStoreKeyMap().entrySet());
Charles Chan0c7c43b2016-01-14 17:39:20 -0800992 }
993
994 @Override
alshabib10580802015-02-18 18:30:33 -0800995 public void deviceInitialAuditCompleted(DeviceId deviceId,
996 boolean completed) {
997 synchronized (deviceAuditStatus) {
998 if (completed) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700999 log.debug("AUDIT completed for device {}",
1000 deviceId);
alshabib10580802015-02-18 18:30:33 -08001001 deviceAuditStatus.put(deviceId, true);
1002 // Execute all pending group requests
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001003 List<StoredGroupEntry> pendingGroupRequests =
1004 getPendingGroupKeyTable().values()
alshabibb0285992016-03-28 23:30:37 -07001005 .stream()
1006 .filter(g -> g.deviceId().equals(deviceId))
1007 .collect(Collectors.toList());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001008 log.debug("processing pending group add requests for device {} and number of pending requests {}",
alshabibb0285992016-03-28 23:30:37 -07001009 deviceId,
1010 pendingGroupRequests.size());
1011 for (Group group : pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -08001012 GroupDescription tmp = new DefaultGroupDescription(
1013 group.deviceId(),
1014 group.type(),
1015 group.buckets(),
1016 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -07001017 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -08001018 group.appId());
1019 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001020 getPendingGroupKeyTable().
alshabibb0285992016-03-28 23:30:37 -07001021 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001022 }
alshabib10580802015-02-18 18:30:33 -08001023 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001024 Boolean audited = deviceAuditStatus.get(deviceId);
1025 if (audited != null && audited) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001026 log.debug("Clearing AUDIT status for device {}", deviceId);
alshabib10580802015-02-18 18:30:33 -08001027 deviceAuditStatus.put(deviceId, false);
1028 }
1029 }
1030 }
1031 }
1032
1033 @Override
1034 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
1035 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001036 Boolean audited = deviceAuditStatus.get(deviceId);
1037 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -08001038 }
1039 }
1040
1041 @Override
1042 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
1043
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001044 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
1045 operation.groupId());
alshabib10580802015-02-18 18:30:33 -08001046
1047 if (existing == null) {
1048 log.warn("No group entry with ID {} found ", operation.groupId());
1049 return;
1050 }
1051
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001052 log.warn("groupOperationFailed: group operation {} failed"
alshabibb0285992016-03-28 23:30:37 -07001053 + "for group {} in device {} with code {}",
1054 operation.opType(),
1055 existing.id(),
1056 existing.deviceId(),
1057 operation.failureCode());
Saurav Das0fd79d92016-03-07 10:58:36 -08001058 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
1059 log.warn("Current extraneous groups in device:{} are: {}",
1060 deviceId,
1061 getExtraneousGroups(deviceId));
Saurav Das8be4e3a2016-03-11 17:19:07 -08001062 if (operation.buckets().equals(existing.buckets())) {
1063 if (existing.state() == GroupState.PENDING_ADD) {
1064 log.info("GROUP_EXISTS: GroupID and Buckets match for group in pending "
alshabibb0285992016-03-28 23:30:37 -07001065 + "add state - moving to ADDED for group {} in device {}",
1066 existing.id(), deviceId);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001067 addOrUpdateGroupEntry(existing);
1068 return;
1069 } else {
1070 log.warn("GROUP EXISTS: Group ID matched but buckets did not. "
alshabibb0285992016-03-28 23:30:37 -07001071 + "Operation: {} Existing: {}", operation.buckets(),
1072 existing.buckets());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001073 }
1074 }
Saurav Das0fd79d92016-03-07 10:58:36 -08001075 }
alshabib10580802015-02-18 18:30:33 -08001076 switch (operation.opType()) {
1077 case ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001078 if (existing.state() == GroupState.PENDING_ADD) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001079 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
1080 log.warn("groupOperationFailed: cleaningup "
alshabibb0285992016-03-28 23:30:37 -07001081 + "group {} from store in device {}....",
1082 existing.id(),
1083 existing.deviceId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001084 //Removal from groupid based map will happen in the
1085 //map update listener
1086 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1087 existing.appCookie()));
1088 }
alshabib10580802015-02-18 18:30:33 -08001089 break;
1090 case MODIFY:
1091 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
1092 break;
1093 case DELETE:
1094 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
1095 break;
1096 default:
1097 log.warn("Unknown group operation type {}", operation.opType());
1098 }
alshabib10580802015-02-18 18:30:33 -08001099 }
1100
1101 @Override
1102 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001103 log.debug("add/update extraneous group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001104 group.id(),
1105 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001106 ConcurrentMap<GroupId, Group> extraneousIdTable =
1107 getExtraneousGroupIdTable(group.deviceId());
1108 extraneousIdTable.put(group.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -07001109 // Don't remove the extraneous groups, instead re-use it when
1110 // a group request comes with the same set of buckets
alshabib10580802015-02-18 18:30:33 -08001111 }
1112
1113 @Override
1114 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001115 log.debug("remove extraneous group entry {} of device {} from store",
alshabibb0285992016-03-28 23:30:37 -07001116 group.id(),
1117 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001118 ConcurrentMap<GroupId, Group> extraneousIdTable =
1119 getExtraneousGroupIdTable(group.deviceId());
1120 extraneousIdTable.remove(group.id());
1121 }
1122
1123 @Override
1124 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
1125 // flatten and make iterator unmodifiable
1126 return FluentIterable.from(
1127 getExtraneousGroupIdTable(deviceId).values());
1128 }
1129
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001130 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001131 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001132 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001133 private class GroupStoreKeyMapListener implements
Madan Jampani0b847532016-03-03 13:44:15 -08001134 MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001135
1136 @Override
Madan Jampani0b847532016-03-03 13:44:15 -08001137 public void event(MapEvent<GroupStoreKeyMapKey, StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001138 GroupEvent groupEvent = null;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001139 GroupStoreKeyMapKey key = mapEvent.key();
Madan Jampani0b847532016-03-03 13:44:15 -08001140 StoredGroupEntry group = Versioned.valueOrNull(mapEvent.newValue());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001141 if ((key == null) && (group == null)) {
1142 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001143 + "event {} with null entry", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001144 return;
1145 } else if (group == null) {
1146 group = getGroupIdTable(key.deviceId()).values()
1147 .stream()
1148 .filter((storedGroup) -> (storedGroup.appCookie().equals(key.appCookie)))
Yuta HIGUCHI6e5f4702016-11-21 11:42:11 -08001149 .findFirst().orElse(null);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001150 if (group == null) {
1151 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001152 + "event {} with null entry... can not process", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001153 return;
1154 }
1155 }
1156 log.trace("received groupid map event {} for id {} in device {}",
1157 mapEvent.type(),
1158 group.id(),
jaegonkim68e080c2016-12-01 22:31:01 +09001159 (key != null ? key.deviceId() : null));
Madan Jampani0b847532016-03-03 13:44:15 -08001160 if (mapEvent.type() == MapEvent.Type.INSERT || mapEvent.type() == MapEvent.Type.UPDATE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001161 // Update the group ID table
1162 getGroupIdTable(group.deviceId()).put(group.id(), group);
Madan Jampani0b847532016-03-03 13:44:15 -08001163 StoredGroupEntry value = Versioned.valueOrNull(mapEvent.newValue());
1164 if (value.state() == Group.GroupState.ADDED) {
1165 if (value.isGroupStateAddedFirstTime()) {
1166 groupEvent = new GroupEvent(Type.GROUP_ADDED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001167 log.trace("Received first time GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001168 group.id(),
1169 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001170 } else {
Madan Jampani0b847532016-03-03 13:44:15 -08001171 groupEvent = new GroupEvent(Type.GROUP_UPDATED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001172 log.trace("Received following GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001173 group.id(),
1174 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001175 }
1176 }
Madan Jampani0b847532016-03-03 13:44:15 -08001177 } else if (mapEvent.type() == MapEvent.Type.REMOVE) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001178 groupEvent = new GroupEvent(Type.GROUP_REMOVED, group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001179 // Remove the entry from the group ID table
1180 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001181 }
1182
1183 if (groupEvent != null) {
1184 notifyDelegate(groupEvent);
1185 }
1186 }
1187 }
Madan Jampani01e05fb2015-08-13 13:29:36 -07001188
helenyrwua1c41152016-08-18 16:16:14 -07001189 private void processGroupMessage(GroupStoreMessage message) {
1190 if (message.type() == GroupStoreMessage.Type.FAILOVER) {
1191 // FIXME: groupStoreEntriesByKey inaccessible here
1192 getGroupIdTable(message.deviceId()).values()
1193 .stream()
1194 .filter((storedGroup) -> (storedGroup.appCookie().equals(message.appCookie())))
1195 .findFirst().ifPresent(group -> notifyDelegate(new GroupEvent(Type.GROUP_BUCKET_FAILOVER, group)));
1196 }
1197 }
1198
Madan Jampani01e05fb2015-08-13 13:29:36 -07001199 private void process(GroupStoreMessage groupOp) {
1200 log.debug("Received remote group operation {} request for device {}",
alshabibb0285992016-03-28 23:30:37 -07001201 groupOp.type(),
1202 groupOp.deviceId());
1203 if (!mastershipService.isLocalMaster(groupOp.deviceId())) {
1204 log.warn("This node is not MASTER for device {}", groupOp.deviceId());
1205 return;
1206 }
1207 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
1208 storeGroupDescriptionInternal(groupOp.groupDesc());
1209 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
1210 updateGroupDescriptionInternal(groupOp.deviceId(),
1211 groupOp.appCookie(),
1212 groupOp.updateType(),
1213 groupOp.updateBuckets(),
1214 groupOp.newAppCookie());
1215 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
1216 deleteGroupDescriptionInternal(groupOp.deviceId(),
1217 groupOp.appCookie());
1218 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001219 }
1220
1221 /**
1222 * Flattened map key to be used to store group entries.
1223 */
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001224 protected static class GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001225 private final DeviceId deviceId;
1226
1227 public GroupStoreMapKey(DeviceId deviceId) {
1228 this.deviceId = deviceId;
1229 }
1230
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001231 public DeviceId deviceId() {
1232 return deviceId;
1233 }
1234
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001235 @Override
1236 public boolean equals(Object o) {
1237 if (this == o) {
1238 return true;
1239 }
1240 if (!(o instanceof GroupStoreMapKey)) {
1241 return false;
1242 }
1243 GroupStoreMapKey that = (GroupStoreMapKey) o;
1244 return this.deviceId.equals(that.deviceId);
1245 }
1246
1247 @Override
1248 public int hashCode() {
1249 int result = 17;
1250
1251 result = 31 * result + Objects.hash(this.deviceId);
1252
1253 return result;
1254 }
1255 }
1256
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001257 protected static class GroupStoreKeyMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001258 private final GroupKey appCookie;
alshabibb0285992016-03-28 23:30:37 -07001259
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001260 public GroupStoreKeyMapKey(DeviceId deviceId,
1261 GroupKey appCookie) {
1262 super(deviceId);
1263 this.appCookie = appCookie;
1264 }
1265
1266 @Override
1267 public boolean equals(Object o) {
1268 if (this == o) {
1269 return true;
1270 }
1271 if (!(o instanceof GroupStoreKeyMapKey)) {
1272 return false;
1273 }
1274 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
1275 return (super.equals(that) &&
1276 this.appCookie.equals(that.appCookie));
1277 }
1278
1279 @Override
1280 public int hashCode() {
1281 int result = 17;
1282
1283 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
1284
1285 return result;
1286 }
1287 }
1288
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001289 protected static class GroupStoreIdMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001290 private final GroupId groupId;
alshabibb0285992016-03-28 23:30:37 -07001291
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001292 public GroupStoreIdMapKey(DeviceId deviceId,
1293 GroupId groupId) {
1294 super(deviceId);
1295 this.groupId = groupId;
1296 }
1297
1298 @Override
1299 public boolean equals(Object o) {
1300 if (this == o) {
1301 return true;
1302 }
1303 if (!(o instanceof GroupStoreIdMapKey)) {
1304 return false;
1305 }
1306 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1307 return (super.equals(that) &&
1308 this.groupId.equals(that.groupId));
1309 }
1310
1311 @Override
1312 public int hashCode() {
1313 int result = 17;
1314
1315 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1316
1317 return result;
1318 }
1319 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001320
1321 @Override
1322 public void pushGroupMetrics(DeviceId deviceId,
1323 Collection<Group> groupEntries) {
1324 boolean deviceInitialAuditStatus =
1325 deviceInitialAuditStatus(deviceId);
1326 Set<Group> southboundGroupEntries =
1327 Sets.newHashSet(groupEntries);
1328 Set<StoredGroupEntry> storedGroupEntries =
1329 Sets.newHashSet(getStoredGroups(deviceId));
1330 Set<Group> extraneousStoredEntries =
1331 Sets.newHashSet(getExtraneousGroups(deviceId));
1332
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001333 if (log.isTraceEnabled()) {
1334 log.trace("pushGroupMetrics: Displaying all ({}) southboundGroupEntries for device {}",
1335 southboundGroupEntries.size(),
1336 deviceId);
1337 for (Group group : southboundGroupEntries) {
1338 log.trace("Group {} in device {}", group, deviceId);
1339 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001340
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001341 log.trace("Displaying all ({}) stored group entries for device {}",
1342 storedGroupEntries.size(),
1343 deviceId);
1344 for (StoredGroupEntry group : storedGroupEntries) {
1345 log.trace("Stored Group {} for device {}", group, deviceId);
1346 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001347 }
1348
alshabibb0285992016-03-28 23:30:37 -07001349 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1350
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001351 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
1352 Group group = it2.next();
1353 if (storedGroupEntries.remove(group)) {
1354 // we both have the group, let's update some info then.
1355 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001356 group.id(), deviceId);
1357
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001358 groupAdded(group);
1359 it2.remove();
1360 }
1361 }
1362 for (Group group : southboundGroupEntries) {
1363 if (getGroup(group.deviceId(), group.id()) != null) {
1364 // There is a group existing with the same id
1365 // It is possible that group update is
1366 // in progress while we got a stale info from switch
1367 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001368 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001369 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001370 + "Group exists in ID based table while "
1371 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001372 }
1373 } else {
1374 // there are groups in the switch that aren't in the store
1375 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001376 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001377 extraneousStoredEntries.remove(group);
Kavitha Alagesanc56cded2017-01-13 10:48:18 +05301378 if (allowExtraneousGroups) {
1379 extraneousGroup(group);
1380 } else {
1381 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
1382 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001383 }
1384 }
1385 for (Group group : storedGroupEntries) {
1386 // there are groups in the store that aren't in the switch
1387 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001388 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001389 groupMissing(group);
1390 }
1391 for (Group group : extraneousStoredEntries) {
1392 // there are groups in the extraneous store that
1393 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001394 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001395 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001396 removeExtraneousGroupEntry(group);
1397 }
1398
1399 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001400 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001401 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001402 deviceInitialAuditCompleted(deviceId, true);
1403 }
1404 }
1405
helenyrwu89470f12016-08-12 13:18:10 -07001406 @Override
1407 public void notifyOfFailovers(Collection<Group> failoverGroups) {
helenyrwu89470f12016-08-12 13:18:10 -07001408 failoverGroups.forEach(group -> {
1409 if (group.type() == Group.Type.FAILOVER) {
helenyrwua1c41152016-08-18 16:16:14 -07001410 groupTopic.publish(GroupStoreMessage.createGroupFailoverMsg(
1411 group.deviceId(), group));
helenyrwu89470f12016-08-12 13:18:10 -07001412 }
1413 });
helenyrwu89470f12016-08-12 13:18:10 -07001414 }
1415
alshabibb0285992016-03-28 23:30:37 -07001416 private void garbageCollect(DeviceId deviceId,
1417 Set<Group> southboundGroupEntries,
1418 Set<StoredGroupEntry> storedGroupEntries) {
1419 if (!garbageCollect) {
1420 return;
1421 }
1422
1423 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1424 while (it.hasNext()) {
1425 StoredGroupEntry group = it.next();
1426 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1427 log.debug("Garbage collecting group {} on {}", group, deviceId);
1428 deleteGroupDescription(deviceId, group.appCookie());
1429 southboundGroupEntries.remove(group);
1430 it.remove();
1431 }
1432 }
1433 }
1434
1435 private boolean checkGroupRefCount(Group group) {
1436 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1437 }
1438
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001439 private void groupMissing(Group group) {
1440 switch (group.state()) {
1441 case PENDING_DELETE:
1442 log.debug("Group {} delete confirmation from device {}",
1443 group, group.deviceId());
1444 removeGroupEntry(group);
1445 break;
1446 case ADDED:
1447 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001448 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001449 case PENDING_UPDATE:
1450 log.debug("Group {} is in store but not on device {}",
1451 group, group.deviceId());
1452 StoredGroupEntry existing =
1453 getStoredGroupEntry(group.deviceId(), group.id());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001454 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
alshabibb0285992016-03-28 23:30:37 -07001455 existing.id(),
1456 existing.deviceId(),
1457 existing.state());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001458 existing.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001459 //Re-PUT map entries to trigger map update events
1460 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -07001461 put(new GroupStoreKeyMapKey(existing.deviceId(),
1462 existing.appCookie()), existing);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001463 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1464 group));
1465 break;
1466 default:
1467 log.debug("Group {} has not been installed.", group);
1468 break;
1469 }
1470 }
1471
1472 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001473 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001474 group, group.deviceId());
1475 addOrUpdateExtraneousGroupEntry(group);
1476 }
1477
1478 private void groupAdded(Group group) {
1479 log.trace("Group {} Added or Updated in device {}",
1480 group, group.deviceId());
1481 addOrUpdateGroupEntry(group);
1482 }
alshabib10580802015-02-18 18:30:33 -08001483}