blob: 2734b8bdb46a9fe3f96db7f5531dc99ea51d9fb1 [file] [log] [blame]
alshabib10580802015-02-18 18:30:33 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070022import org.onlab.util.KryoNamespace;
alshabibb0285992016-03-28 23:30:37 -070023import org.onosproject.cfg.ComponentConfigService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070024import org.onosproject.cluster.ClusterService;
Charles Chanf4838a72015-12-07 18:13:45 -080025import org.onosproject.cluster.NodeId;
alshabib10580802015-02-18 18:30:33 -080026import org.onosproject.core.GroupId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070027import org.onosproject.mastership.MastershipService;
alshabib10580802015-02-18 18:30:33 -080028import org.onosproject.net.DeviceId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070029import org.onosproject.net.MastershipRole;
Andrea Campanella72ab6e52020-03-13 12:04:23 +010030import org.onosproject.net.device.DeviceService;
jaegonkim9477a9d2018-04-01 16:36:36 +090031import org.onosproject.net.driver.DriverService;
alshabib10580802015-02-18 18:30:33 -080032import org.onosproject.net.group.DefaultGroup;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070033import org.onosproject.net.group.DefaultGroupBucket;
alshabib10580802015-02-18 18:30:33 -080034import org.onosproject.net.group.DefaultGroupDescription;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070035import org.onosproject.net.group.DefaultGroupKey;
alshabib10580802015-02-18 18:30:33 -080036import org.onosproject.net.group.Group;
37import org.onosproject.net.group.Group.GroupState;
38import org.onosproject.net.group.GroupBucket;
39import org.onosproject.net.group.GroupBuckets;
40import org.onosproject.net.group.GroupDescription;
41import org.onosproject.net.group.GroupEvent;
42import org.onosproject.net.group.GroupEvent.Type;
43import org.onosproject.net.group.GroupKey;
44import org.onosproject.net.group.GroupOperation;
45import org.onosproject.net.group.GroupStore;
46import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070047import org.onosproject.net.group.StoredGroupBucketEntry;
alshabib10580802015-02-18 18:30:33 -080048import org.onosproject.net.group.StoredGroupEntry;
49import org.onosproject.store.AbstractStore;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070050import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070051import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani0b847532016-03-03 13:44:15 -080052import org.onosproject.store.service.ConsistentMap;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070053import org.onosproject.store.service.DistributedPrimitive.Status;
Madan Jampani0b847532016-03-03 13:44:15 -080054import org.onosproject.store.service.MapEvent;
55import org.onosproject.store.service.MapEventListener;
alshabibb0285992016-03-28 23:30:37 -070056import org.onosproject.store.service.MultiValuedTimestamp;
Madan Jampani0b847532016-03-03 13:44:15 -080057import org.onosproject.store.service.Serializer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070058import org.onosproject.store.service.StorageService;
helenyrwua1c41152016-08-18 16:16:14 -070059import org.onosproject.store.service.Topic;
Madan Jampani0b847532016-03-03 13:44:15 -080060import org.onosproject.store.service.Versioned;
alshabibb0285992016-03-28 23:30:37 -070061import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062import org.osgi.service.component.annotations.Activate;
63import org.osgi.service.component.annotations.Component;
64import org.osgi.service.component.annotations.Deactivate;
65import org.osgi.service.component.annotations.Modified;
66import org.osgi.service.component.annotations.Reference;
67import org.osgi.service.component.annotations.ReferenceCardinality;
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;
Ray Milkeyb5646e62018-10-16 11:42:18 -070097import static org.onosproject.store.OsgiPropertyConstants.ALLOW_EXTRANEOUS_GROUPS;
98import static org.onosproject.store.OsgiPropertyConstants.ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
99import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT;
100import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_DEFAULT;
101import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_THRESH;
102import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_THRESH_DEFAULT;
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700103import static org.slf4j.LoggerFactory.getLogger;
alshabib10580802015-02-18 18:30:33 -0800104
105/**
Saurav Das0fd79d92016-03-07 10:58:36 -0800106 * Manages inventory of group entries using distributed group stores from the
107 * storage service.
alshabib10580802015-02-18 18:30:33 -0800108 */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700109@Component(
110 immediate = true,
111 service = GroupStore.class,
112 property = {
Ray Milkey2d7bca12018-10-17 14:51:52 -0700113 GARBAGE_COLLECT + ":Boolean=" + GARBAGE_COLLECT_DEFAULT,
114 GARBAGE_COLLECT_THRESH + ":Integer=" + GARBAGE_COLLECT_THRESH_DEFAULT,
115 ALLOW_EXTRANEOUS_GROUPS + ":Boolean=" + ALLOW_EXTRANEOUS_GROUPS_DEFAULT
Ray Milkeyb5646e62018-10-16 11:42:18 -0700116 }
117)
alshabib10580802015-02-18 18:30:33 -0800118public class DistributedGroupStore
119 extends AbstractStore<GroupEvent, GroupStoreDelegate>
120 implements GroupStore {
121
122 private final Logger log = getLogger(getClass());
123
Saurav Das137f27f2018-06-11 17:02:31 -0700124 private static final int MAX_FAILED_ATTEMPTS = 3;
alshabibb0285992016-03-28 23:30:37 -0700125
alshabib10580802015-02-18 18:30:33 -0800126 private final int dummyId = 0xffffffff;
Yi Tsengfa394de2017-02-01 11:26:40 -0800127 private final GroupId dummyGroupId = new GroupId(dummyId);
alshabib10580802015-02-18 18:30:33 -0800128
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700130 protected ClusterCommunicationService clusterCommunicator;
131
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700133 protected ClusterService clusterService;
134
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700136 protected StorageService storageService;
137
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700139 protected MastershipService mastershipService;
140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibb0285992016-03-28 23:30:37 -0700142 protected ComponentConfigService cfgService;
143
Andrea Campanella72ab6e52020-03-13 12:04:23 +0100144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
145 protected DeviceService deviceService;
146
jaegonkim9477a9d2018-04-01 16:36:36 +0900147 // Guarantees enabling DriverService before enabling GroupStore
148 // (DriverService is used in serializing/de-serializing DefaultGroup)
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700149 @Reference(cardinality = ReferenceCardinality.MANDATORY)
jaegonkim9477a9d2018-04-01 16:36:36 +0900150 protected DriverService driverService;
151
pier00ac83e2019-12-19 16:04:23 +0100152 private NodeId local;
153
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530154 private ScheduledExecutorService executor;
155 private Consumer<Status> statusChangeListener;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700156 // Per device group table with (device id + app cookie) as key
Madan Jampani0b847532016-03-03 13:44:15 -0800157 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700158 StoredGroupEntry> groupStoreEntriesByKey = null;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700159 // Per device group table with (device id + group id) as key
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700160 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
alshabibb0285992016-03-28 23:30:37 -0700161 groupEntriesById = new ConcurrentHashMap<>();
Madan Jampani0b847532016-03-03 13:44:15 -0800162 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700163 StoredGroupEntry> auditPendingReqQueue = null;
Frank Wange0eb5ce2016-07-01 18:21:25 +0800164 private MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry>
165 mapListener = new GroupStoreKeyMapListener();
alshabib10580802015-02-18 18:30:33 -0800166 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
167 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700168 private ExecutorService messageHandlingExecutor;
169 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
Saurav Das137f27f2018-06-11 17:02:31 -0700170
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700171 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
alshabib10580802015-02-18 18:30:33 -0800172
173 private final AtomicInteger groupIdGen = new AtomicInteger();
174
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700175 private KryoNamespace clusterMsgSerializer;
176
helenyrwua1c41152016-08-18 16:16:14 -0700177 private static Topic<GroupStoreMessage> groupTopic;
178
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700179 /** Enable group garbage collection. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700180 private boolean garbageCollect = GARBAGE_COLLECT_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700181
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700182 /** Number of rounds for group garbage collection. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700183 private int gcThresh = GARBAGE_COLLECT_THRESH_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700184
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700185 /** Allow groups in switches not installed by ONOS. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700186 private boolean allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700187
alshabib10580802015-02-18 18:30:33 -0800188 @Activate
sisubram4beea652017-08-09 10:38:14 +0000189 public void activate(ComponentContext context) {
alshabibb0285992016-03-28 23:30:37 -0700190 cfgService.registerProperties(getClass());
sisubram4beea652017-08-09 10:38:14 +0000191 modified(context);
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700192 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
alshabibb0285992016-03-28 23:30:37 -0700193 .register(KryoNamespaces.API)
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700194 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
alshabibb0285992016-03-28 23:30:37 -0700195 .register(DefaultGroup.class,
196 DefaultGroupBucket.class,
197 DefaultGroupDescription.class,
198 DefaultGroupKey.class,
199 GroupDescription.Type.class,
200 Group.GroupState.class,
201 GroupBuckets.class,
alshabibb0285992016-03-28 23:30:37 -0700202 GroupStoreMessage.class,
203 GroupStoreMessage.Type.class,
204 UpdateType.class,
205 GroupStoreMessageSubjects.class,
206 MultiValuedTimestamp.class,
207 GroupStoreKeyMapKey.class,
208 GroupStoreIdMapKey.class,
209 GroupStoreMapKey.class
210 );
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700211
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700212 clusterMsgSerializer = kryoBuilder.build("GroupStore");
213 Serializer serializer = Serializer.using(clusterMsgSerializer);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700214
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700215 messageHandlingExecutor = Executors.
216 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
217 groupedThreads("onos/store/group",
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700218 "message-handlers",
219 log));
Madan Jampani01e05fb2015-08-13 13:29:36 -0700220
221 clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
alshabibb0285992016-03-28 23:30:37 -0700222 clusterMsgSerializer::deserialize,
223 this::process,
224 messageHandlingExecutor);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700225
Madan Jampani0b847532016-03-03 13:44:15 -0800226 log.debug("Creating Consistent map onos-group-store-keymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700227
Madan Jampani0b847532016-03-03 13:44:15 -0800228 groupStoreEntriesByKey = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
229 .withName("onos-group-store-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700230 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700231 .build();
Frank Wange0eb5ce2016-07-01 18:21:25 +0800232 groupStoreEntriesByKey.addListener(mapListener);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700233 log.debug("Current size of groupstorekeymap:{}",
234 groupStoreEntriesByKey.size());
Thiago Santosfb73c502016-08-18 18:15:13 -0300235 synchronizeGroupStoreEntries();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700236
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530237 log.debug("Creating GroupStoreId Map From GroupStoreKey Map");
238 matchGroupEntries();
239 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/group", "store", log));
240 statusChangeListener = status -> {
241 if (status == Status.ACTIVE) {
242 executor.execute(this::matchGroupEntries);
243 }
244 };
245 groupStoreEntriesByKey.addStatusChangeListener(statusChangeListener);
246
Madan Jampani0b847532016-03-03 13:44:15 -0800247 log.debug("Creating Consistent map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700248
Madan Jampani0b847532016-03-03 13:44:15 -0800249 auditPendingReqQueue = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
250 .withName("onos-pending-group-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700251 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700252 .build();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700253 log.debug("Current size of pendinggroupkeymap:{}",
254 auditPendingReqQueue.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700255
helenyrwua1c41152016-08-18 16:16:14 -0700256 groupTopic = getOrCreateGroupTopic(serializer);
257 groupTopic.subscribe(this::processGroupMessage);
258
pier00ac83e2019-12-19 16:04:23 +0100259 local = clusterService.getLocalNode().id();
260
alshabib10580802015-02-18 18:30:33 -0800261 log.info("Started");
262 }
263
264 @Deactivate
265 public void deactivate() {
Frank Wange0eb5ce2016-07-01 18:21:25 +0800266 groupStoreEntriesByKey.removeListener(mapListener);
alshabibb0285992016-03-28 23:30:37 -0700267 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700268 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
alshabib10580802015-02-18 18:30:33 -0800269 log.info("Stopped");
270 }
271
alshabibb0285992016-03-28 23:30:37 -0700272 @Modified
273 public void modified(ComponentContext context) {
274 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
275
276 try {
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700277 String s = get(properties, GARBAGE_COLLECT);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700278 garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT_DEFAULT : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700279
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700280 s = get(properties, GARBAGE_COLLECT_THRESH);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700281 gcThresh = isNullOrEmpty(s) ? GARBAGE_COLLECT_THRESH_DEFAULT : Integer.parseInt(s.trim());
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530282
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700283 s = get(properties, ALLOW_EXTRANEOUS_GROUPS);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700284 allowExtraneousGroups = isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_GROUPS_DEFAULT : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700285 } catch (Exception e) {
Ray Milkeyb5646e62018-10-16 11:42:18 -0700286 gcThresh = GARBAGE_COLLECT_THRESH_DEFAULT;
287 garbageCollect = GARBAGE_COLLECT_DEFAULT;
288 allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700289 }
290 }
291
helenyrwua1c41152016-08-18 16:16:14 -0700292 private Topic<GroupStoreMessage> getOrCreateGroupTopic(Serializer serializer) {
293 if (groupTopic == null) {
294 return storageService.getTopic("group-failover-notif", serializer);
295 } else {
296 return groupTopic;
297 }
Sho SHIMIZUa6285542017-01-12 15:08:24 -0800298 }
helenyrwua1c41152016-08-18 16:16:14 -0700299
alshabib10580802015-02-18 18:30:33 -0800300 /**
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530301 * Updating values of groupEntriesById.
302 */
303 private void matchGroupEntries() {
304 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupStoreEntriesByKey.asJavaMap().entrySet()) {
305 StoredGroupEntry group = entry.getValue();
306 getGroupIdTable(entry.getKey().deviceId()).put(group.id(), group);
307 }
308 }
309
Thiago Santosfb73c502016-08-18 18:15:13 -0300310
311 private void synchronizeGroupStoreEntries() {
312 Map<GroupStoreKeyMapKey, StoredGroupEntry> groupEntryMap = groupStoreEntriesByKey.asJavaMap();
313 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupEntryMap.entrySet()) {
Thiago Santosfb73c502016-08-18 18:15:13 -0300314 StoredGroupEntry value = entry.getValue();
Thiago Santosfb73c502016-08-18 18:15:13 -0300315 ConcurrentMap<GroupId, StoredGroupEntry> groupIdTable = getGroupIdTable(value.deviceId());
316 groupIdTable.put(value.id(), value);
317 }
318 }
319
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530320 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700321 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800322 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700323 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800324 */
Madan Jampani0b847532016-03-03 13:44:15 -0800325 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700326 getGroupStoreKeyMap() {
Madan Jampani0b847532016-03-03 13:44:15 -0800327 return groupStoreEntriesByKey.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800328 }
329
330 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700331 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800332 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700333 * @param deviceId identifier of the device
334 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800335 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700336 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700337 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800338 }
339
340 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700341 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800342 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700343 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800344 */
Madan Jampani0b847532016-03-03 13:44:15 -0800345 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700346 getPendingGroupKeyTable() {
Madan Jampani0b847532016-03-03 13:44:15 -0800347 return auditPendingReqQueue.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800348 }
349
350 /**
351 * Returns the extraneous group id table for specified device.
352 *
353 * @param deviceId identifier of the device
354 * @return Map representing group key table of given device.
355 */
356 private ConcurrentMap<GroupId, Group>
357 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700358 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800359 }
360
361 /**
362 * Returns the number of groups for the specified device in the store.
363 *
364 * @return number of groups for the specified device
365 */
366 @Override
367 public int getGroupCount(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700368 return (getGroups(deviceId) != null) ?
alshabibb0285992016-03-28 23:30:37 -0700369 Iterables.size(getGroups(deviceId)) : 0;
alshabib10580802015-02-18 18:30:33 -0800370 }
371
372 /**
373 * Returns the groups associated with a device.
374 *
375 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800376 * @return the group entries
377 */
378 @Override
379 public Iterable<Group> getGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800380 // Let ImmutableSet.copyOf do the type conversion
381 return ImmutableSet.copyOf(getStoredGroups(deviceId));
alshabib10580802015-02-18 18:30:33 -0800382 }
383
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700384 private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800385 NodeId master = mastershipService.getMasterFor(deviceId);
Andrea Campanella72ab6e52020-03-13 12:04:23 +0100386 if (master == null && deviceService.isAvailable(deviceId)) {
Charles Chanf4838a72015-12-07 18:13:45 -0800387 log.debug("Failed to getGroups: No master for {}", deviceId);
388 return Collections.emptySet();
389 }
390
391 Set<StoredGroupEntry> storedGroups = getGroupStoreKeyMap().values()
392 .stream()
393 .filter(input -> input.deviceId().equals(deviceId))
394 .collect(Collectors.toSet());
395 return ImmutableSet.copyOf(storedGroups);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700396 }
397
alshabib10580802015-02-18 18:30:33 -0800398 /**
399 * Returns the stored group entry.
400 *
alshabibb0285992016-03-28 23:30:37 -0700401 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800402 * @param appCookie the group key
alshabib10580802015-02-18 18:30:33 -0800403 * @return a group associated with the key
404 */
405 @Override
406 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700407 return getStoredGroupEntry(deviceId, appCookie);
408 }
409
410 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
411 GroupKey appCookie) {
412 return getGroupStoreKeyMap().get(new GroupStoreKeyMapKey(deviceId,
413 appCookie));
414 }
415
416 @Override
417 public Group getGroup(DeviceId deviceId, GroupId groupId) {
418 return getStoredGroupEntry(deviceId, groupId);
419 }
420
421 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
422 GroupId groupId) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700423 return getGroupIdTable(deviceId).get(groupId);
alshabib10580802015-02-18 18:30:33 -0800424 }
425
426 private int getFreeGroupIdValue(DeviceId deviceId) {
427 int freeId = groupIdGen.incrementAndGet();
428
429 while (true) {
Yi Tsengfa394de2017-02-01 11:26:40 -0800430 Group existing = getGroup(deviceId, new GroupId(freeId));
alshabib10580802015-02-18 18:30:33 -0800431 if (existing == null) {
432 existing = (
433 extraneousGroupEntriesById.get(deviceId) != null) ?
434 extraneousGroupEntriesById.get(deviceId).
Yi Tsengfa394de2017-02-01 11:26:40 -0800435 get(new GroupId(freeId)) :
alshabib10580802015-02-18 18:30:33 -0800436 null;
437 }
438 if (existing != null) {
439 freeId = groupIdGen.incrementAndGet();
440 } else {
441 break;
442 }
443 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700444 log.debug("getFreeGroupIdValue: Next Free ID is {}", freeId);
alshabib10580802015-02-18 18:30:33 -0800445 return freeId;
446 }
447
448 /**
449 * Stores a new group entry using the information from group description.
450 *
451 * @param groupDesc group description to be used to create group entry
452 */
453 @Override
454 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700455 log.debug("In storeGroupDescription");
alshabib10580802015-02-18 18:30:33 -0800456 // Check if a group is existing with the same key
Saurav Das8a0732e2015-11-20 15:27:53 -0800457 Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
458 if (existingGroup != null) {
Saurav Dasc568c342018-01-25 09:49:01 -0800459 log.debug("Group already exists with the same key {} in dev:{} with id:0x{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800460 groupDesc.appCookie(), groupDesc.deviceId(),
461 Integer.toHexString(existingGroup.id().id()));
alshabib10580802015-02-18 18:30:33 -0800462 return;
463 }
464
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700465 // Check if group to be created by a remote instance
Madan Jampani175e8fd2015-05-20 14:10:45 -0700466 if (mastershipService.getLocalRole(groupDesc.deviceId()) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700467 log.debug("storeGroupDescription: Device {} local role is not MASTER",
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700468 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700469 if (mastershipService.getMasterFor(groupDesc.deviceId()) == null) {
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530470 log.debug("No Master for device {}..."
471 + "Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700472 groupDesc.deviceId());
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530473 addToPendingAudit(groupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700474 return;
475 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700476 GroupStoreMessage groupOp = GroupStoreMessage.
477 createGroupAddRequestMsg(groupDesc.deviceId(),
478 groupDesc);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700479
Madan Jampani175e8fd2015-05-20 14:10:45 -0700480 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700481 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
482 clusterMsgSerializer::serialize,
483 mastershipService.getMasterFor(groupDesc.deviceId()))
484 .whenComplete((result, error) -> {
Madan Jampani175e8fd2015-05-20 14:10:45 -0700485 if (error != null) {
486 log.warn("Failed to send request to master: {} to {}",
alshabibb0285992016-03-28 23:30:37 -0700487 groupOp,
488 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700489 //TODO: Send Group operation failure event
490 } else {
491 log.debug("Sent Group operation request for device {} "
alshabibb0285992016-03-28 23:30:37 -0700492 + "to remote MASTER {}",
493 groupDesc.deviceId(),
494 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700495 }
496 });
alshabib10580802015-02-18 18:30:33 -0800497 return;
498 }
499
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700500 log.debug("Store group for device {} is getting handled locally",
501 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800502 storeGroupDescriptionInternal(groupDesc);
503 }
504
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530505 private void addToPendingAudit(GroupDescription groupDesc) {
506 Integer groupIdVal = groupDesc.givenGroupId();
507 GroupId groupId = (groupIdVal != null) ? new GroupId(groupIdVal) : dummyGroupId;
508 addToPendingKeyTable(new DefaultGroup(groupId, groupDesc));
509 }
510
511 private void addToPendingKeyTable(StoredGroupEntry group) {
512 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
513 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
514 getPendingGroupKeyTable();
515 pendingKeyTable.put(new GroupStoreKeyMapKey(group.deviceId(),
516 group.appCookie()),
517 group);
518 }
519
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700520 private Group getMatchingExtraneousGroupbyId(DeviceId deviceId, Integer groupId) {
521 ConcurrentMap<GroupId, Group> extraneousMap =
522 extraneousGroupEntriesById.get(deviceId);
523 if (extraneousMap == null) {
524 return null;
525 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800526 return extraneousMap.get(new GroupId(groupId));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700527 }
528
529 private Group getMatchingExtraneousGroupbyBuckets(DeviceId deviceId,
530 GroupBuckets buckets) {
531 ConcurrentMap<GroupId, Group> extraneousMap =
532 extraneousGroupEntriesById.get(deviceId);
533 if (extraneousMap == null) {
534 return null;
535 }
536
alshabibb0285992016-03-28 23:30:37 -0700537 for (Group extraneousGroup : extraneousMap.values()) {
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700538 if (extraneousGroup.buckets().equals(buckets)) {
539 return extraneousGroup;
540 }
541 }
542 return null;
543 }
544
alshabib10580802015-02-18 18:30:33 -0800545 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
546 // Check if a group is existing with the same key
547 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
548 return;
549 }
550
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700551 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
552 // Device group audit has not completed yet
553 // Add this group description to pending group key table
554 // Create a group entry object with Dummy Group ID
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700555 log.debug("storeGroupDescriptionInternal: Device {} AUDIT pending...Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700556 groupDesc.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700557 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
558 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
Madan Jampani0b847532016-03-03 13:44:15 -0800559 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700560 getPendingGroupKeyTable();
561 pendingKeyTable.put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
562 groupDesc.appCookie()),
563 group);
564 return;
565 }
566
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700567 Group matchingExtraneousGroup = null;
568 if (groupDesc.givenGroupId() != null) {
569 //Check if there is a extraneous group existing with the same Id
570 matchingExtraneousGroup = getMatchingExtraneousGroupbyId(
alshabibb0285992016-03-28 23:30:37 -0700571 groupDesc.deviceId(), groupDesc.givenGroupId());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700572 if (matchingExtraneousGroup != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800573 log.debug("storeGroupDescriptionInternal: Matching extraneous group "
alshabibb0285992016-03-28 23:30:37 -0700574 + "found in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700575 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800576 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700577 //Check if the group buckets matches with user provided buckets
578 if (matchingExtraneousGroup.buckets().equals(groupDesc.buckets())) {
579 //Group is already existing with the same buckets and Id
580 // Create a group entry object
Saurav Das0fd79d92016-03-07 10:58:36 -0800581 log.debug("storeGroupDescriptionInternal: Buckets also matching "
alshabibb0285992016-03-28 23:30:37 -0700582 + "in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700583 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800584 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700585 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700586 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700587 // Insert the newly created group entry into key and id maps
588 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700589 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
590 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700591 // Ensure it also inserted into group id based table to
592 // avoid any chances of duplication in group id generation
593 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700594 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700595 addOrUpdateGroupEntry(matchingExtraneousGroup);
596 removeExtraneousGroupEntry(matchingExtraneousGroup);
597 return;
598 } else {
599 //Group buckets are not matching. Update group
600 //with user provided buckets.
Saurav Das0fd79d92016-03-07 10:58:36 -0800601 log.debug("storeGroupDescriptionInternal: Buckets are not "
alshabibb0285992016-03-28 23:30:37 -0700602 + "matching in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700603 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800604 Integer.toHexString(groupDesc.givenGroupId()));
605 StoredGroupEntry modifiedGroup = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700606 matchingExtraneousGroup.id(), groupDesc);
Saurav Das0fd79d92016-03-07 10:58:36 -0800607 modifiedGroup.setState(GroupState.PENDING_UPDATE);
608 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700609 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
610 groupDesc.appCookie()), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800611 // Ensure it also inserted into group id based table to
612 // avoid any chances of duplication in group id generation
613 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700614 put(matchingExtraneousGroup.id(), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800615 removeExtraneousGroupEntry(matchingExtraneousGroup);
616 log.debug("storeGroupDescriptionInternal: Triggering Group "
alshabibb0285992016-03-28 23:30:37 -0700617 + "UPDATE request for {} in device {}",
Saurav Das0fd79d92016-03-07 10:58:36 -0800618 matchingExtraneousGroup.id(),
619 groupDesc.deviceId());
620 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, modifiedGroup));
621 return;
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700622 }
623 }
624 } else {
625 //Check if there is an extraneous group with user provided buckets
626 matchingExtraneousGroup = getMatchingExtraneousGroupbyBuckets(
alshabibb0285992016-03-28 23:30:37 -0700627 groupDesc.deviceId(), groupDesc.buckets());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700628 if (matchingExtraneousGroup != null) {
629 //Group is already existing with the same buckets.
630 //So reuse this group.
631 log.debug("storeGroupDescriptionInternal: Matching extraneous group found in Device {}",
632 groupDesc.deviceId());
633 //Create a group entry object
634 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700635 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700636 // Insert the newly created group entry into key and id maps
637 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700638 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
639 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700640 // Ensure it also inserted into group id based table to
641 // avoid any chances of duplication in group id generation
642 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700643 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700644 addOrUpdateGroupEntry(matchingExtraneousGroup);
645 removeExtraneousGroupEntry(matchingExtraneousGroup);
646 return;
647 } else {
648 //TODO: Check if there are any empty groups that can be used here
649 log.debug("storeGroupDescriptionInternal: No matching extraneous groups found in Device {}",
650 groupDesc.deviceId());
651 }
652 }
653
Saurav Das100e3b82015-04-30 11:12:10 -0700654 GroupId id = null;
655 if (groupDesc.givenGroupId() == null) {
656 // Get a new group identifier
Yi Tsengfa394de2017-02-01 11:26:40 -0800657 id = new GroupId(getFreeGroupIdValue(groupDesc.deviceId()));
Saurav Das100e3b82015-04-30 11:12:10 -0700658 } else {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800659 // we need to use the identifier passed in by caller, but check if
660 // already used
661 Group existing = getGroup(groupDesc.deviceId(),
Yi Tsengfa394de2017-02-01 11:26:40 -0800662 new GroupId(groupDesc.givenGroupId()));
Saurav Das8be4e3a2016-03-11 17:19:07 -0800663 if (existing != null) {
664 log.warn("Group already exists with the same id: 0x{} in dev:{} "
alshabibb0285992016-03-28 23:30:37 -0700665 + "but with different key: {} (request gkey: {})",
666 Integer.toHexString(groupDesc.givenGroupId()),
667 groupDesc.deviceId(),
668 existing.appCookie(),
669 groupDesc.appCookie());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800670 return;
671 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800672 id = new GroupId(groupDesc.givenGroupId());
Saurav Das100e3b82015-04-30 11:12:10 -0700673 }
alshabib10580802015-02-18 18:30:33 -0800674 // Create a group entry object
675 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700676 // Insert the newly created group entry into key and id maps
677 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700678 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
679 groupDesc.appCookie()), group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700680 // Ensure it also inserted into group id based table to
681 // avoid any chances of duplication in group id generation
682 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700683 put(id, group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700684 log.debug("storeGroupDescriptionInternal: Processing Group ADD request for Id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700685 id,
686 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800687 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
688 group));
689 }
690
691 /**
692 * Updates the existing group entry with the information
693 * from group description.
694 *
alshabibb0285992016-03-28 23:30:37 -0700695 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800696 * @param oldAppCookie the current group key
alshabibb0285992016-03-28 23:30:37 -0700697 * @param type update type
698 * @param newBuckets group buckets for updates
alshabib10580802015-02-18 18:30:33 -0800699 * @param newAppCookie optional new group key
700 */
701 @Override
702 public void updateGroupDescription(DeviceId deviceId,
703 GroupKey oldAppCookie,
704 UpdateType type,
705 GroupBuckets newBuckets,
706 GroupKey newAppCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700707 // Check if group update to be done by a remote instance
sangho52abe3a2015-05-05 14:13:34 -0700708 if (mastershipService.getMasterFor(deviceId) != null &&
709 mastershipService.getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700710 log.debug("updateGroupDescription: Device {} local role is not MASTER",
711 deviceId);
712 if (mastershipService.getMasterFor(deviceId) == null) {
713 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700714 + "Can not perform update group operation",
715 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700716 //TODO: Send Group operation failure event
717 return;
718 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700719 GroupStoreMessage groupOp = GroupStoreMessage.
720 createGroupUpdateRequestMsg(deviceId,
721 oldAppCookie,
722 type,
723 newBuckets,
724 newAppCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700725
Madan Jampani175e8fd2015-05-20 14:10:45 -0700726 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700727 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
728 clusterMsgSerializer::serialize,
729 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
730 if (error != null) {
731 log.warn("Failed to send request to master: {} to {}",
732 groupOp,
733 mastershipService.getMasterFor(deviceId), error);
734 }
735 //TODO: Send Group operation failure event
736 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700737 return;
738 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700739 log.debug("updateGroupDescription for device {} is getting handled locally",
740 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700741 updateGroupDescriptionInternal(deviceId,
742 oldAppCookie,
743 type,
744 newBuckets,
745 newAppCookie);
746 }
747
748 private void updateGroupDescriptionInternal(DeviceId deviceId,
alshabibb0285992016-03-28 23:30:37 -0700749 GroupKey oldAppCookie,
750 UpdateType type,
751 GroupBuckets newBuckets,
752 GroupKey newAppCookie) {
alshabib10580802015-02-18 18:30:33 -0800753 // Check if a group is existing with the provided key
754 Group oldGroup = getGroup(deviceId, oldAppCookie);
755 if (oldGroup == null) {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800756 log.warn("updateGroupDescriptionInternal: Group not found...strange. "
alshabibb0285992016-03-28 23:30:37 -0700757 + "GroupKey:{} DeviceId:{}", oldAppCookie, deviceId);
alshabib10580802015-02-18 18:30:33 -0800758 return;
759 }
760
761 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
762 type,
763 newBuckets);
764 if (newBucketList != null) {
765 // Create a new group object from the old group
766 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
767 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
768 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
769 oldGroup.deviceId(),
770 oldGroup.type(),
771 updatedBuckets,
772 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700773 oldGroup.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800774 oldGroup.appId());
775 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
776 updatedGroupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700777 log.debug("updateGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_UPDATE",
alshabibb0285992016-03-28 23:30:37 -0700778 oldGroup.id(),
779 oldGroup.deviceId(),
780 oldGroup.state());
alshabib10580802015-02-18 18:30:33 -0800781 newGroup.setState(GroupState.PENDING_UPDATE);
782 newGroup.setLife(oldGroup.life());
783 newGroup.setPackets(oldGroup.packets());
784 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700785 //Update the group entry in groupkey based map.
786 //Update to groupid based map will happen in the
787 //groupkey based map update listener
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700788 log.debug("updateGroupDescriptionInternal with type {}: Group updated with buckets",
789 type);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700790 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700791 put(new GroupStoreKeyMapKey(newGroup.deviceId(),
792 newGroup.appCookie()), newGroup);
alshabib10580802015-02-18 18:30:33 -0800793 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700794 } else {
795 log.warn("updateGroupDescriptionInternal with type {}: No "
alshabibb0285992016-03-28 23:30:37 -0700796 + "change in the buckets in update", type);
alshabib10580802015-02-18 18:30:33 -0800797 }
798 }
799
800 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
801 UpdateType type,
802 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300803 if (type == UpdateType.SET) {
804 return buckets.buckets();
805 }
806
Victor Silvadf1eeae2016-08-12 15:28:57 -0300807 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
808 List<GroupBucket> updatedBucketList = new ArrayList<>();
alshabib10580802015-02-18 18:30:33 -0800809 boolean groupDescUpdated = false;
810
811 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300812 List<GroupBucket> newBuckets = buckets.buckets();
813
814 // Add old buckets that will not be updated and check if any will be updated.
815 for (GroupBucket oldBucket : oldBuckets) {
816 int newBucketIndex = newBuckets.indexOf(oldBucket);
817
818 if (newBucketIndex != -1) {
819 GroupBucket newBucket = newBuckets.get(newBucketIndex);
820 if (!newBucket.hasSameParameters(oldBucket)) {
821 // Bucket will be updated
822 groupDescUpdated = true;
823 }
824 } else {
825 // Old bucket will remain the same - add it.
826 updatedBucketList.add(oldBucket);
alshabib10580802015-02-18 18:30:33 -0800827 }
828 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300829
830 // Add all new buckets
831 updatedBucketList.addAll(newBuckets);
832 if (!oldBuckets.containsAll(newBuckets)) {
833 groupDescUpdated = true;
834 }
835
alshabib10580802015-02-18 18:30:33 -0800836 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300837 List<GroupBucket> bucketsToRemove = buckets.buckets();
838
839 // Check which old buckets should remain
840 for (GroupBucket oldBucket : oldBuckets) {
841 if (!bucketsToRemove.contains(oldBucket)) {
842 updatedBucketList.add(oldBucket);
843 } else {
alshabib10580802015-02-18 18:30:33 -0800844 groupDescUpdated = true;
845 }
846 }
847 }
848
849 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300850 return updatedBucketList;
alshabib10580802015-02-18 18:30:33 -0800851 } else {
852 return null;
853 }
854 }
855
856 /**
857 * Triggers deleting the existing group entry.
858 *
alshabibb0285992016-03-28 23:30:37 -0700859 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800860 * @param appCookie the group key
861 */
862 @Override
863 public void deleteGroupDescription(DeviceId deviceId,
864 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700865 // Check if group to be deleted by a remote instance
866 if (mastershipService.
867 getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700868 log.debug("deleteGroupDescription: Device {} local role is not MASTER",
869 deviceId);
870 if (mastershipService.getMasterFor(deviceId) == null) {
871 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700872 + "Can not perform delete group operation",
873 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700874 //TODO: Send Group operation failure event
875 return;
876 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700877 GroupStoreMessage groupOp = GroupStoreMessage.
878 createGroupDeleteRequestMsg(deviceId,
879 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700880
Madan Jampani175e8fd2015-05-20 14:10:45 -0700881 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700882 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
883 clusterMsgSerializer::serialize,
884 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
885 if (error != null) {
886 log.warn("Failed to send request to master: {} to {}",
887 groupOp,
888 mastershipService.getMasterFor(deviceId), error);
889 }
890 //TODO: Send Group operation failure event
891 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700892 return;
893 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700894 log.debug("deleteGroupDescription in device {} is getting handled locally",
895 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700896 deleteGroupDescriptionInternal(deviceId, appCookie);
897 }
898
899 private void deleteGroupDescriptionInternal(DeviceId deviceId,
900 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800901 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700902 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800903 if (existing == null) {
904 return;
905 }
906
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700907 log.debug("deleteGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_DELETE",
alshabibb0285992016-03-28 23:30:37 -0700908 existing.id(),
909 existing.deviceId(),
910 existing.state());
pier0ee285a2020-04-03 12:44:21 +0200911 // TODO is this really safe ?
alshabib10580802015-02-18 18:30:33 -0800912 synchronized (existing) {
913 existing.setState(GroupState.PENDING_DELETE);
Saurav Das80980c72016-03-23 11:22:49 -0700914 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700915 put(new GroupStoreKeyMapKey(existing.deviceId(), existing.appCookie()),
916 existing);
alshabib10580802015-02-18 18:30:33 -0800917 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700918 log.debug("deleteGroupDescriptionInternal: in device {} issuing GROUP_REMOVE_REQUESTED",
919 deviceId);
alshabib10580802015-02-18 18:30:33 -0800920 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
921 }
922
923 /**
pier0ee285a2020-04-03 12:44:21 +0200924 * Updates the stats of an existing group entry.
925 *
926 * @param group the new stats
927 * @param existing the existing group
928 */
929 private void updateGroupEntryStatsInternal(Group group, StoredGroupEntry existing) {
930 for (GroupBucket bucket : group.buckets().buckets()) {
931 Optional<GroupBucket> matchingBucket =
932 existing.buckets().buckets()
933 .stream()
934 .filter((existingBucket) -> (existingBucket.equals(bucket)))
935 .findFirst();
936 if (matchingBucket.isPresent()) {
937 ((StoredGroupBucketEntry) matchingBucket.
938 get()).setPackets(bucket.packets());
939 ((StoredGroupBucketEntry) matchingBucket.
940 get()).setBytes(bucket.bytes());
941 } else {
942 log.warn("updateGroupEntryStatsInternal: No matching bucket {}" +
943 " to update stats", bucket);
944 }
945 }
946 existing.setLife(group.life());
947 existing.setPackets(group.packets());
948 existing.setBytes(group.bytes());
949 existing.setReferenceCount(group.referenceCount());
950 existing.setFailedRetryCount(0);
951 }
952
953 /**
alshabib10580802015-02-18 18:30:33 -0800954 * Stores a new group entry, or updates an existing entry.
955 *
956 * @param group group entry
957 */
958 @Override
959 public void addOrUpdateGroupEntry(Group group) {
960 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700961 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
962 group.id());
alshabib10580802015-02-18 18:30:33 -0800963 GroupEvent event = null;
964
965 if (existing != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800966 log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700967 group.id(),
968 group.deviceId());
pier0ee285a2020-04-03 12:44:21 +0200969 // TODO is this really safe ?
alshabib10580802015-02-18 18:30:33 -0800970 synchronized (existing) {
pier0ee285a2020-04-03 12:44:21 +0200971 // Update stats
972 updateGroupEntryStatsInternal(group, existing);
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700973 if ((existing.state() == GroupState.PENDING_ADD) ||
alshabibb0285992016-03-28 23:30:37 -0700974 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800975 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700976 existing.id(),
977 existing.deviceId(),
978 existing.state());
alshabib10580802015-02-18 18:30:33 -0800979 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700980 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800981 event = new GroupEvent(Type.GROUP_ADDED, existing);
982 } else {
Saurav Das0fd79d92016-03-07 10:58:36 -0800983 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700984 existing.id(),
985 existing.deviceId(),
986 GroupState.PENDING_UPDATE);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700987 existing.setState(GroupState.ADDED);
988 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800989 event = new GroupEvent(Type.GROUP_UPDATED, existing);
990 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700991 //Re-PUT map entries to trigger map update events
992 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700993 put(new GroupStoreKeyMapKey(existing.deviceId(),
994 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800995 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700996 } else {
pier0ee285a2020-04-03 12:44:21 +0200997 log.warn("addOrUpdateGroupEntry: Group update {} " +
998 "happening for a non-existing entry in the map", group);
alshabib10580802015-02-18 18:30:33 -0800999 }
1000
pier0ee285a2020-04-03 12:44:21 +02001001 // TODO if map is going to trigger event, is this one needed?
alshabib10580802015-02-18 18:30:33 -08001002 if (event != null) {
1003 notifyDelegate(event);
1004 }
1005 }
1006
1007 /**
pier0ee285a2020-04-03 12:44:21 +02001008 * Updates stats of an existing entry.
1009 *
1010 * @param group group entry
1011 */
1012 private void updateGroupEntryStats(Group group) {
1013 // check if this new entry is an update to an existing entry
1014 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
1015 group.id());
1016 if (existing != null) {
1017 log.trace("updateStatsGroupEntry: updating group entry {} in device {}",
1018 group.id(),
1019 group.deviceId());
1020 // TODO is this really safe ?
1021 synchronized (existing) {
1022 // We don't make further update - it will be gone after the next update
1023 if (existing.state() == GroupState.PENDING_DELETE) {
1024 log.trace("updateStatsGroupEntry: group entry {} in device {} is in {} not updated",
1025 existing.id(),
1026 existing.deviceId(),
1027 existing.state());
1028 return;
1029 }
1030 // Update stats
1031 updateGroupEntryStatsInternal(group, existing);
1032 if ((existing.state() == GroupState.PENDING_ADD) ||
1033 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
1034 log.trace("updateStatsGroupEntry: group entry {} in device {} moving from {} to ADDED",
1035 existing.id(),
1036 existing.deviceId(),
1037 existing.state());
1038 existing.setState(GroupState.ADDED);
1039 existing.setIsGroupStateAddedFirstTime(true);
1040 } else {
1041 log.trace("updateStatsGroupEntry: group entry {} in device {} moving from {} to ADDED",
1042 existing.id(),
1043 existing.deviceId(),
1044 GroupState.PENDING_UPDATE);
1045 existing.setState(GroupState.ADDED);
1046 existing.setIsGroupStateAddedFirstTime(false);
1047 }
1048 //Re-PUT map entries to trigger map update events
1049 getGroupStoreKeyMap().
1050 put(new GroupStoreKeyMapKey(existing.deviceId(),
1051 existing.appCookie()), existing);
1052 }
1053 } else {
1054 log.warn("updateStatsGroupEntry: Group update {} "
1055 + "happening for a non-existing entry in the map", group);
1056 }
1057 }
1058
1059 /**
alshabib10580802015-02-18 18:30:33 -08001060 * Removes the group entry from store.
1061 *
1062 * @param group group entry
1063 */
1064 @Override
1065 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001066 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
1067 group.id());
alshabib10580802015-02-18 18:30:33 -08001068
1069 if (existing != null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001070 log.debug("removeGroupEntry: removing group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001071 group.id(),
1072 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001073 //Removal from groupid based map will happen in the
1074 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001075 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1076 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001077 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001078 } else {
1079 log.warn("removeGroupEntry for {} in device{} is "
alshabibb0285992016-03-28 23:30:37 -07001080 + "not existing in our maps",
1081 group.id(),
1082 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001083 }
1084 }
1085
Victor Silva4e8b7832016-08-17 17:11:19 -03001086 private void purgeGroupEntries(Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entries) {
1087 entries.forEach(entry -> {
1088 groupStoreEntriesByKey.remove(entry.getKey());
1089 });
1090 }
1091
alshabib10580802015-02-18 18:30:33 -08001092 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -08001093 public void purgeGroupEntry(DeviceId deviceId) {
Victor Silva4e8b7832016-08-17 17:11:19 -03001094 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entriesPendingRemove =
Charles Chan0c7c43b2016-01-14 17:39:20 -08001095 new HashSet<>();
1096
Madan Jampani0b847532016-03-03 13:44:15 -08001097 getGroupStoreKeyMap().entrySet().stream()
Charles Chan0c7c43b2016-01-14 17:39:20 -08001098 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Victor Silva4e8b7832016-08-17 17:11:19 -03001099 .forEach(entriesPendingRemove::add);
Charles Chan0c7c43b2016-01-14 17:39:20 -08001100
Victor Silva4e8b7832016-08-17 17:11:19 -03001101 purgeGroupEntries(entriesPendingRemove);
1102 }
1103
1104 @Override
1105 public void purgeGroupEntries() {
1106 purgeGroupEntries(getGroupStoreKeyMap().entrySet());
Charles Chan0c7c43b2016-01-14 17:39:20 -08001107 }
1108
1109 @Override
alshabib10580802015-02-18 18:30:33 -08001110 public void deviceInitialAuditCompleted(DeviceId deviceId,
1111 boolean completed) {
1112 synchronized (deviceAuditStatus) {
1113 if (completed) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001114 log.debug("AUDIT completed for device {}",
1115 deviceId);
alshabib10580802015-02-18 18:30:33 -08001116 deviceAuditStatus.put(deviceId, true);
1117 // Execute all pending group requests
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001118 List<StoredGroupEntry> pendingGroupRequests =
1119 getPendingGroupKeyTable().values()
alshabibb0285992016-03-28 23:30:37 -07001120 .stream()
1121 .filter(g -> g.deviceId().equals(deviceId))
1122 .collect(Collectors.toList());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001123 log.debug("processing pending group add requests for device {} and number of pending requests {}",
alshabibb0285992016-03-28 23:30:37 -07001124 deviceId,
1125 pendingGroupRequests.size());
1126 for (Group group : pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -08001127 GroupDescription tmp = new DefaultGroupDescription(
1128 group.deviceId(),
1129 group.type(),
1130 group.buckets(),
1131 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -07001132 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -08001133 group.appId());
1134 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001135 getPendingGroupKeyTable().
alshabibb0285992016-03-28 23:30:37 -07001136 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001137 }
alshabib10580802015-02-18 18:30:33 -08001138 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001139 Boolean audited = deviceAuditStatus.get(deviceId);
1140 if (audited != null && audited) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001141 log.debug("Clearing AUDIT status for device {}", deviceId);
alshabib10580802015-02-18 18:30:33 -08001142 deviceAuditStatus.put(deviceId, false);
1143 }
1144 }
1145 }
1146 }
1147
1148 @Override
1149 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
1150 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001151 Boolean audited = deviceAuditStatus.get(deviceId);
1152 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -08001153 }
1154 }
1155
1156 @Override
1157 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
1158
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001159 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
1160 operation.groupId());
alshabib10580802015-02-18 18:30:33 -08001161
1162 if (existing == null) {
1163 log.warn("No group entry with ID {} found ", operation.groupId());
1164 return;
1165 }
1166
Saurav Das137f27f2018-06-11 17:02:31 -07001167 log.warn("groupOperationFailed: group operation {} failed in state {} "
alshabibb0285992016-03-28 23:30:37 -07001168 + "for group {} in device {} with code {}",
1169 operation.opType(),
Saurav Das137f27f2018-06-11 17:02:31 -07001170 existing.state(),
alshabibb0285992016-03-28 23:30:37 -07001171 existing.id(),
1172 existing.deviceId(),
1173 operation.failureCode());
Saurav Das0fd79d92016-03-07 10:58:36 -08001174 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001175 if (operation.buckets().equals(existing.buckets())) {
Saurav Dasc88d4662017-05-15 15:34:25 -07001176 if (existing.state() == GroupState.PENDING_ADD ||
1177 existing.state() == GroupState.PENDING_ADD_RETRY) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001178 log.info("GROUP_EXISTS: GroupID and Buckets match for group in pending "
alshabibb0285992016-03-28 23:30:37 -07001179 + "add state - moving to ADDED for group {} in device {}",
1180 existing.id(), deviceId);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001181 addOrUpdateGroupEntry(existing);
1182 return;
1183 } else {
Saurav Dasc88d4662017-05-15 15:34:25 -07001184 log.warn("GROUP_EXISTS: GroupId and Buckets match but existing"
1185 + "group in state: {}", existing.state());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001186 }
Saurav Dasc88d4662017-05-15 15:34:25 -07001187 } else {
1188 log.warn("GROUP EXISTS: Group ID matched but buckets did not. "
1189 + "Operation: {} Existing: {}", operation.buckets(),
1190 existing.buckets());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001191 }
Saurav Das0fd79d92016-03-07 10:58:36 -08001192 }
Saurav Das137f27f2018-06-11 17:02:31 -07001193 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.INVALID_GROUP) {
1194 existing.incrFailedRetryCount();
1195 if (existing.failedRetryCount() < MAX_FAILED_ATTEMPTS) {
1196 log.warn("Group {} programming failed {} of {} times in dev {}, "
1197 + "retrying ..", existing.id(),
1198 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1199 deviceId);
1200 return;
1201 }
1202 log.warn("Group {} programming failed {} of {} times in dev {}, "
1203 + "removing group from store", existing.id(),
1204 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1205 deviceId);
1206 // fall through to case
1207 }
1208
alshabib10580802015-02-18 18:30:33 -08001209 switch (operation.opType()) {
1210 case ADD:
Saurav Das137f27f2018-06-11 17:02:31 -07001211 if (existing.state() == GroupState.PENDING_ADD
1212 || existing.state() == GroupState.PENDING_ADD_RETRY) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001213 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
1214 log.warn("groupOperationFailed: cleaningup "
alshabibb0285992016-03-28 23:30:37 -07001215 + "group {} from store in device {}....",
1216 existing.id(),
1217 existing.deviceId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001218 //Removal from groupid based map will happen in the
1219 //map update listener
1220 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1221 existing.appCookie()));
1222 }
alshabib10580802015-02-18 18:30:33 -08001223 break;
1224 case MODIFY:
1225 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
1226 break;
1227 case DELETE:
1228 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
1229 break;
1230 default:
1231 log.warn("Unknown group operation type {}", operation.opType());
1232 }
alshabib10580802015-02-18 18:30:33 -08001233 }
1234
1235 @Override
1236 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001237 log.debug("add/update extraneous group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001238 group.id(),
1239 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001240 ConcurrentMap<GroupId, Group> extraneousIdTable =
1241 getExtraneousGroupIdTable(group.deviceId());
1242 extraneousIdTable.put(group.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -07001243 // Don't remove the extraneous groups, instead re-use it when
1244 // a group request comes with the same set of buckets
alshabib10580802015-02-18 18:30:33 -08001245 }
1246
1247 @Override
1248 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001249 log.debug("remove extraneous group entry {} of device {} from store",
alshabibb0285992016-03-28 23:30:37 -07001250 group.id(),
1251 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001252 ConcurrentMap<GroupId, Group> extraneousIdTable =
1253 getExtraneousGroupIdTable(group.deviceId());
1254 extraneousIdTable.remove(group.id());
1255 }
1256
1257 @Override
1258 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
1259 // flatten and make iterator unmodifiable
1260 return FluentIterable.from(
1261 getExtraneousGroupIdTable(deviceId).values());
1262 }
1263
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001264 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001265 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001266 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001267 private class GroupStoreKeyMapListener implements
Madan Jampani0b847532016-03-03 13:44:15 -08001268 MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001269
1270 @Override
Madan Jampani0b847532016-03-03 13:44:15 -08001271 public void event(MapEvent<GroupStoreKeyMapKey, StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001272 GroupEvent groupEvent = null;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001273 GroupStoreKeyMapKey key = mapEvent.key();
Madan Jampani0b847532016-03-03 13:44:15 -08001274 StoredGroupEntry group = Versioned.valueOrNull(mapEvent.newValue());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001275 if ((key == null) && (group == null)) {
1276 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001277 + "event {} with null entry", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001278 return;
1279 } else if (group == null) {
1280 group = getGroupIdTable(key.deviceId()).values()
1281 .stream()
1282 .filter((storedGroup) -> (storedGroup.appCookie().equals(key.appCookie)))
Yuta HIGUCHI6e5f4702016-11-21 11:42:11 -08001283 .findFirst().orElse(null);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001284 if (group == null) {
1285 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001286 + "event {} with null entry... can not process", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001287 return;
1288 }
1289 }
1290 log.trace("received groupid map event {} for id {} in device {}",
1291 mapEvent.type(),
1292 group.id(),
jaegonkim68e080c2016-12-01 22:31:01 +09001293 (key != null ? key.deviceId() : null));
Madan Jampani0b847532016-03-03 13:44:15 -08001294 if (mapEvent.type() == MapEvent.Type.INSERT || mapEvent.type() == MapEvent.Type.UPDATE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001295 // Update the group ID table
1296 getGroupIdTable(group.deviceId()).put(group.id(), group);
Madan Jampani0b847532016-03-03 13:44:15 -08001297 StoredGroupEntry value = Versioned.valueOrNull(mapEvent.newValue());
1298 if (value.state() == Group.GroupState.ADDED) {
1299 if (value.isGroupStateAddedFirstTime()) {
1300 groupEvent = new GroupEvent(Type.GROUP_ADDED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001301 log.trace("Received first time GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001302 group.id(),
1303 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001304 } else {
Madan Jampani0b847532016-03-03 13:44:15 -08001305 groupEvent = new GroupEvent(Type.GROUP_UPDATED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001306 log.trace("Received following GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001307 group.id(),
1308 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001309 }
1310 }
Madan Jampani0b847532016-03-03 13:44:15 -08001311 } else if (mapEvent.type() == MapEvent.Type.REMOVE) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001312 groupEvent = new GroupEvent(Type.GROUP_REMOVED, group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001313 // Remove the entry from the group ID table
1314 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001315 }
1316
1317 if (groupEvent != null) {
1318 notifyDelegate(groupEvent);
1319 }
1320 }
1321 }
Madan Jampani01e05fb2015-08-13 13:29:36 -07001322
helenyrwua1c41152016-08-18 16:16:14 -07001323 private void processGroupMessage(GroupStoreMessage message) {
1324 if (message.type() == GroupStoreMessage.Type.FAILOVER) {
1325 // FIXME: groupStoreEntriesByKey inaccessible here
1326 getGroupIdTable(message.deviceId()).values()
1327 .stream()
1328 .filter((storedGroup) -> (storedGroup.appCookie().equals(message.appCookie())))
1329 .findFirst().ifPresent(group -> notifyDelegate(new GroupEvent(Type.GROUP_BUCKET_FAILOVER, group)));
1330 }
1331 }
1332
Madan Jampani01e05fb2015-08-13 13:29:36 -07001333 private void process(GroupStoreMessage groupOp) {
1334 log.debug("Received remote group operation {} request for device {}",
alshabibb0285992016-03-28 23:30:37 -07001335 groupOp.type(),
1336 groupOp.deviceId());
1337 if (!mastershipService.isLocalMaster(groupOp.deviceId())) {
1338 log.warn("This node is not MASTER for device {}", groupOp.deviceId());
1339 return;
1340 }
1341 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
1342 storeGroupDescriptionInternal(groupOp.groupDesc());
1343 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
1344 updateGroupDescriptionInternal(groupOp.deviceId(),
1345 groupOp.appCookie(),
1346 groupOp.updateType(),
1347 groupOp.updateBuckets(),
1348 groupOp.newAppCookie());
1349 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
1350 deleteGroupDescriptionInternal(groupOp.deviceId(),
1351 groupOp.appCookie());
1352 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001353 }
1354
1355 /**
1356 * Flattened map key to be used to store group entries.
1357 */
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001358 protected static class GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001359 private final DeviceId deviceId;
1360
1361 public GroupStoreMapKey(DeviceId deviceId) {
1362 this.deviceId = deviceId;
1363 }
1364
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001365 public DeviceId deviceId() {
1366 return deviceId;
1367 }
1368
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001369 @Override
1370 public boolean equals(Object o) {
1371 if (this == o) {
1372 return true;
1373 }
1374 if (!(o instanceof GroupStoreMapKey)) {
1375 return false;
1376 }
1377 GroupStoreMapKey that = (GroupStoreMapKey) o;
1378 return this.deviceId.equals(that.deviceId);
1379 }
1380
1381 @Override
1382 public int hashCode() {
1383 int result = 17;
1384
1385 result = 31 * result + Objects.hash(this.deviceId);
1386
1387 return result;
1388 }
1389 }
1390
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001391 protected static class GroupStoreKeyMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001392 private final GroupKey appCookie;
alshabibb0285992016-03-28 23:30:37 -07001393
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001394 public GroupStoreKeyMapKey(DeviceId deviceId,
1395 GroupKey appCookie) {
1396 super(deviceId);
1397 this.appCookie = appCookie;
1398 }
1399
1400 @Override
1401 public boolean equals(Object o) {
1402 if (this == o) {
1403 return true;
1404 }
1405 if (!(o instanceof GroupStoreKeyMapKey)) {
1406 return false;
1407 }
1408 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
1409 return (super.equals(that) &&
1410 this.appCookie.equals(that.appCookie));
1411 }
1412
1413 @Override
1414 public int hashCode() {
1415 int result = 17;
1416
1417 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
1418
1419 return result;
1420 }
1421 }
1422
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001423 protected static class GroupStoreIdMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001424 private final GroupId groupId;
alshabibb0285992016-03-28 23:30:37 -07001425
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001426 public GroupStoreIdMapKey(DeviceId deviceId,
1427 GroupId groupId) {
1428 super(deviceId);
1429 this.groupId = groupId;
1430 }
1431
1432 @Override
1433 public boolean equals(Object o) {
1434 if (this == o) {
1435 return true;
1436 }
1437 if (!(o instanceof GroupStoreIdMapKey)) {
1438 return false;
1439 }
1440 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1441 return (super.equals(that) &&
1442 this.groupId.equals(that.groupId));
1443 }
1444
1445 @Override
1446 public int hashCode() {
1447 int result = 17;
1448
1449 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1450
1451 return result;
1452 }
1453 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001454
1455 @Override
1456 public void pushGroupMetrics(DeviceId deviceId,
1457 Collection<Group> groupEntries) {
1458 boolean deviceInitialAuditStatus =
1459 deviceInitialAuditStatus(deviceId);
1460 Set<Group> southboundGroupEntries =
1461 Sets.newHashSet(groupEntries);
1462 Set<StoredGroupEntry> storedGroupEntries =
1463 Sets.newHashSet(getStoredGroups(deviceId));
1464 Set<Group> extraneousStoredEntries =
1465 Sets.newHashSet(getExtraneousGroups(deviceId));
pier00ac83e2019-12-19 16:04:23 +01001466 NodeId master;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001467
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001468 if (log.isTraceEnabled()) {
1469 log.trace("pushGroupMetrics: Displaying all ({}) southboundGroupEntries for device {}",
1470 southboundGroupEntries.size(),
1471 deviceId);
1472 for (Group group : southboundGroupEntries) {
1473 log.trace("Group {} in device {}", group, deviceId);
1474 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001475
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001476 log.trace("Displaying all ({}) stored group entries for device {}",
1477 storedGroupEntries.size(),
1478 deviceId);
1479 for (StoredGroupEntry group : storedGroupEntries) {
1480 log.trace("Stored Group {} for device {}", group, deviceId);
1481 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001482 }
1483
alshabibb0285992016-03-28 23:30:37 -07001484 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1485
pier00ac83e2019-12-19 16:04:23 +01001486 // update stats
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001487 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
pier00ac83e2019-12-19 16:04:23 +01001488 // Mastership change can occur during this iteration
1489 master = mastershipService.getMasterFor(deviceId);
1490 if (!Objects.equals(local, master)) {
1491 log.warn("Tried to update the group stats while the node was not the master");
1492 return;
1493 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001494 Group group = it2.next();
1495 if (storedGroupEntries.remove(group)) {
1496 // we both have the group, let's update some info then.
1497 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001498 group.id(), deviceId);
1499
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001500 groupAdded(group);
1501 it2.remove();
1502 }
1503 }
pier00ac83e2019-12-19 16:04:23 +01001504
1505 // extraneous groups in the dataplane
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001506 for (Group group : southboundGroupEntries) {
1507 if (getGroup(group.deviceId(), group.id()) != null) {
1508 // There is a group existing with the same id
1509 // It is possible that group update is
1510 // in progress while we got a stale info from switch
1511 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001512 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001513 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001514 + "Group exists in ID based table while "
1515 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001516 }
1517 } else {
pier00ac83e2019-12-19 16:04:23 +01001518 // Mastership change can occur during this iteration
1519 master = mastershipService.getMasterFor(deviceId);
1520 if (!Objects.equals(local, master)) {
1521 log.warn("Tried to process extraneous groups while the node was not the master");
1522 return;
1523 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001524 // there are groups in the switch that aren't in the store
1525 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001526 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001527 extraneousStoredEntries.remove(group);
Kavitha Alagesanc56cded2017-01-13 10:48:18 +05301528 if (allowExtraneousGroups) {
1529 extraneousGroup(group);
1530 } else {
1531 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
1532 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001533 }
1534 }
pier00ac83e2019-12-19 16:04:23 +01001535
1536 // missing groups in the dataplane
Charles Chan07f15f22018-05-08 21:35:50 -07001537 for (StoredGroupEntry group : storedGroupEntries) {
pier00ac83e2019-12-19 16:04:23 +01001538 // Mastership change can occur during this iteration
1539 master = mastershipService.getMasterFor(deviceId);
1540 if (!Objects.equals(local, master)) {
1541 log.warn("Tried to process missing groups while the node was not the master");
1542 return;
1543 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001544 // there are groups in the store that aren't in the switch
1545 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001546 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001547 groupMissing(group);
1548 }
pier00ac83e2019-12-19 16:04:23 +01001549
1550 // extraneous groups in the store
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001551 for (Group group : extraneousStoredEntries) {
pier00ac83e2019-12-19 16:04:23 +01001552 // Mastership change can occur during this iteration
1553 master = mastershipService.getMasterFor(deviceId);
1554 if (!Objects.equals(local, master)) {
1555 log.warn("Tried to process node extraneous groups while the node was not the master");
1556 return;
1557 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001558 // there are groups in the extraneous store that
1559 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001560 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001561 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001562 removeExtraneousGroupEntry(group);
1563 }
1564
1565 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001566 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001567 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001568 deviceInitialAuditCompleted(deviceId, true);
1569 }
1570 }
1571
helenyrwu89470f12016-08-12 13:18:10 -07001572 @Override
1573 public void notifyOfFailovers(Collection<Group> failoverGroups) {
helenyrwu89470f12016-08-12 13:18:10 -07001574 failoverGroups.forEach(group -> {
1575 if (group.type() == Group.Type.FAILOVER) {
helenyrwua1c41152016-08-18 16:16:14 -07001576 groupTopic.publish(GroupStoreMessage.createGroupFailoverMsg(
1577 group.deviceId(), group));
helenyrwu89470f12016-08-12 13:18:10 -07001578 }
1579 });
helenyrwu89470f12016-08-12 13:18:10 -07001580 }
1581
alshabibb0285992016-03-28 23:30:37 -07001582 private void garbageCollect(DeviceId deviceId,
1583 Set<Group> southboundGroupEntries,
1584 Set<StoredGroupEntry> storedGroupEntries) {
1585 if (!garbageCollect) {
1586 return;
1587 }
1588
pier00ac83e2019-12-19 16:04:23 +01001589 NodeId master;
alshabibb0285992016-03-28 23:30:37 -07001590 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1591 while (it.hasNext()) {
pier00ac83e2019-12-19 16:04:23 +01001592 // Mastership change can occur during this iteration
1593 master = mastershipService.getMasterFor(deviceId);
1594 if (!Objects.equals(local, master)) {
1595 log.warn("Tried to run garbage collector while the node was not the master");
1596 return;
1597 }
alshabibb0285992016-03-28 23:30:37 -07001598 StoredGroupEntry group = it.next();
1599 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1600 log.debug("Garbage collecting group {} on {}", group, deviceId);
1601 deleteGroupDescription(deviceId, group.appCookie());
1602 southboundGroupEntries.remove(group);
1603 it.remove();
1604 }
1605 }
1606 }
1607
1608 private boolean checkGroupRefCount(Group group) {
1609 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1610 }
1611
Charles Chan07f15f22018-05-08 21:35:50 -07001612 private void groupMissing(StoredGroupEntry group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001613 switch (group.state()) {
1614 case PENDING_DELETE:
1615 log.debug("Group {} delete confirmation from device {}",
1616 group, group.deviceId());
1617 removeGroupEntry(group);
1618 break;
1619 case ADDED:
1620 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001621 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001622 case PENDING_UPDATE:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001623 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
Charles Chan07f15f22018-05-08 21:35:50 -07001624 group.id(),
1625 group.deviceId(),
1626 group.state());
1627 group.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001628 //Re-PUT map entries to trigger map update events
Charles Chan07f15f22018-05-08 21:35:50 -07001629 getGroupStoreKeyMap().put(new GroupStoreKeyMapKey(group.deviceId(), group.appCookie()), group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001630 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1631 group));
1632 break;
1633 default:
1634 log.debug("Group {} has not been installed.", group);
1635 break;
1636 }
1637 }
1638
1639 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001640 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001641 group, group.deviceId());
1642 addOrUpdateExtraneousGroupEntry(group);
1643 }
1644
1645 private void groupAdded(Group group) {
1646 log.trace("Group {} Added or Updated in device {}",
1647 group, group.deviceId());
pier0ee285a2020-04-03 12:44:21 +02001648 updateGroupEntryStats(group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001649 }
alshabib10580802015-02-18 18:30:33 -08001650}