blob: 8fd32ab0ff4bfabe79055241b3300b5219542f8a [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 Campanella5daa7c462020-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;
33import org.onosproject.net.group.DefaultGroupDescription;
34import org.onosproject.net.group.Group;
35import org.onosproject.net.group.Group.GroupState;
36import org.onosproject.net.group.GroupBucket;
37import org.onosproject.net.group.GroupBuckets;
38import org.onosproject.net.group.GroupDescription;
39import org.onosproject.net.group.GroupEvent;
40import org.onosproject.net.group.GroupEvent.Type;
41import org.onosproject.net.group.GroupKey;
42import org.onosproject.net.group.GroupOperation;
43import org.onosproject.net.group.GroupStore;
44import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070045import org.onosproject.net.group.StoredGroupBucketEntry;
alshabib10580802015-02-18 18:30:33 -080046import org.onosproject.net.group.StoredGroupEntry;
47import org.onosproject.store.AbstractStore;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070048import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070049import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani0b847532016-03-03 13:44:15 -080050import org.onosproject.store.service.ConsistentMap;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070051import org.onosproject.store.service.DistributedPrimitive.Status;
Madan Jampani0b847532016-03-03 13:44:15 -080052import org.onosproject.store.service.MapEvent;
53import org.onosproject.store.service.MapEventListener;
alshabibb0285992016-03-28 23:30:37 -070054import org.onosproject.store.service.MultiValuedTimestamp;
Madan Jampani0b847532016-03-03 13:44:15 -080055import org.onosproject.store.service.Serializer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070056import org.onosproject.store.service.StorageService;
helenyrwua1c41152016-08-18 16:16:14 -070057import org.onosproject.store.service.Topic;
Madan Jampani0b847532016-03-03 13:44:15 -080058import org.onosproject.store.service.Versioned;
alshabibb0285992016-03-28 23:30:37 -070059import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070060import org.osgi.service.component.annotations.Activate;
61import org.osgi.service.component.annotations.Component;
62import org.osgi.service.component.annotations.Deactivate;
63import org.osgi.service.component.annotations.Modified;
64import org.osgi.service.component.annotations.Reference;
65import org.osgi.service.component.annotations.ReferenceCardinality;
alshabib10580802015-02-18 18:30:33 -080066import org.slf4j.Logger;
67
Jonathan Hart6ec029a2015-03-24 17:12:35 -070068import java.util.ArrayList;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070069import java.util.Collection;
Charles Chanf4838a72015-12-07 18:13:45 -080070import java.util.Collections;
alshabibb0285992016-03-28 23:30:37 -070071import java.util.Dictionary;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070072import java.util.HashMap;
Charles Chan0c7c43b2016-01-14 17:39:20 -080073import java.util.HashSet;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070074import java.util.Iterator;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070075import java.util.List;
Madan Jampani0b847532016-03-03 13:44:15 -080076import java.util.Map;
Charles Chan0c7c43b2016-01-14 17:39:20 -080077import java.util.Map.Entry;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070078import java.util.Objects;
Sho SHIMIZU30d639b2015-05-05 09:30:35 -070079import java.util.Optional;
alshabibb0285992016-03-28 23:30:37 -070080import java.util.Properties;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070081import java.util.Set;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070082import java.util.concurrent.ConcurrentHashMap;
83import java.util.concurrent.ConcurrentMap;
84import java.util.concurrent.ExecutorService;
85import java.util.concurrent.Executors;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053086import java.util.concurrent.ScheduledExecutorService;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070087import java.util.concurrent.atomic.AtomicInteger;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053088import java.util.function.Consumer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070089import java.util.stream.Collectors;
90
alshabibb0285992016-03-28 23:30:37 -070091import static com.google.common.base.Strings.isNullOrEmpty;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053092import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
alshabibb0285992016-03-28 23:30:37 -070093import static org.onlab.util.Tools.get;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070094import static org.onlab.util.Tools.groupedThreads;
Ray Milkeyb5646e62018-10-16 11:42:18 -070095import static org.onosproject.store.OsgiPropertyConstants.ALLOW_EXTRANEOUS_GROUPS;
96import static org.onosproject.store.OsgiPropertyConstants.ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
97import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT;
98import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_DEFAULT;
99import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_THRESH;
100import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_THRESH_DEFAULT;
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700101import static org.slf4j.LoggerFactory.getLogger;
alshabib10580802015-02-18 18:30:33 -0800102
103/**
Saurav Das0fd79d92016-03-07 10:58:36 -0800104 * Manages inventory of group entries using distributed group stores from the
105 * storage service.
alshabib10580802015-02-18 18:30:33 -0800106 */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700107@Component(
108 immediate = true,
109 service = GroupStore.class,
110 property = {
Ray Milkey2d7bca12018-10-17 14:51:52 -0700111 GARBAGE_COLLECT + ":Boolean=" + GARBAGE_COLLECT_DEFAULT,
112 GARBAGE_COLLECT_THRESH + ":Integer=" + GARBAGE_COLLECT_THRESH_DEFAULT,
113 ALLOW_EXTRANEOUS_GROUPS + ":Boolean=" + ALLOW_EXTRANEOUS_GROUPS_DEFAULT
Ray Milkeyb5646e62018-10-16 11:42:18 -0700114 }
115)
alshabib10580802015-02-18 18:30:33 -0800116public class DistributedGroupStore
117 extends AbstractStore<GroupEvent, GroupStoreDelegate>
118 implements GroupStore {
119
120 private final Logger log = getLogger(getClass());
121
Saurav Das137f27f2018-06-11 17:02:31 -0700122 private static final int MAX_FAILED_ATTEMPTS = 3;
alshabibb0285992016-03-28 23:30:37 -0700123
alshabib10580802015-02-18 18:30:33 -0800124 private final int dummyId = 0xffffffff;
Yi Tsengfa394de2017-02-01 11:26:40 -0800125 private final GroupId dummyGroupId = new GroupId(dummyId);
alshabib10580802015-02-18 18:30:33 -0800126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700128 protected ClusterCommunicationService clusterCommunicator;
129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700131 protected ClusterService clusterService;
132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700134 protected StorageService storageService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700137 protected MastershipService mastershipService;
138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibb0285992016-03-28 23:30:37 -0700140 protected ComponentConfigService cfgService;
141
Andrea Campanella5daa7c462020-03-13 12:04:23 +0100142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
143 protected DeviceService deviceService;
144
jaegonkim9477a9d2018-04-01 16:36:36 +0900145 // Guarantees enabling DriverService before enabling GroupStore
146 // (DriverService is used in serializing/de-serializing DefaultGroup)
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700147 @Reference(cardinality = ReferenceCardinality.MANDATORY)
jaegonkim9477a9d2018-04-01 16:36:36 +0900148 protected DriverService driverService;
149
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +0000150 private NodeId local;
151
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530152 private ScheduledExecutorService executor;
153 private Consumer<Status> statusChangeListener;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700154 // Per device group table with (device id + app cookie) as key
Madan Jampani0b847532016-03-03 13:44:15 -0800155 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700156 StoredGroupEntry> groupStoreEntriesByKey = null;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700157 // Per device group table with (device id + group id) as key
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700158 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
alshabibb0285992016-03-28 23:30:37 -0700159 groupEntriesById = new ConcurrentHashMap<>();
Madan Jampani0b847532016-03-03 13:44:15 -0800160 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700161 StoredGroupEntry> auditPendingReqQueue = null;
Frank Wange0eb5ce2016-07-01 18:21:25 +0800162 private MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry>
163 mapListener = new GroupStoreKeyMapListener();
alshabib10580802015-02-18 18:30:33 -0800164 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
165 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700166 private ExecutorService messageHandlingExecutor;
167 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
Saurav Das137f27f2018-06-11 17:02:31 -0700168
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700169 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
alshabib10580802015-02-18 18:30:33 -0800170
171 private final AtomicInteger groupIdGen = new AtomicInteger();
172
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700173 private KryoNamespace clusterMsgSerializer;
174
helenyrwua1c41152016-08-18 16:16:14 -0700175 private static Topic<GroupStoreMessage> groupTopic;
176
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700177 /** Enable group garbage collection. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700178 private boolean garbageCollect = GARBAGE_COLLECT_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700179
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700180 /** Number of rounds for group garbage collection. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700181 private int gcThresh = GARBAGE_COLLECT_THRESH_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700182
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700183 /** Allow groups in switches not installed by ONOS. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700184 private boolean allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700185
alshabib10580802015-02-18 18:30:33 -0800186 @Activate
sisubram4beea652017-08-09 10:38:14 +0000187 public void activate(ComponentContext context) {
alshabibb0285992016-03-28 23:30:37 -0700188 cfgService.registerProperties(getClass());
sisubram4beea652017-08-09 10:38:14 +0000189 modified(context);
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700190 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
alshabibb0285992016-03-28 23:30:37 -0700191 .register(KryoNamespaces.API)
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700192 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
pierventre1a833d42021-01-15 17:27:48 +0100193 .register(GroupStoreMessage.class,
alshabibb0285992016-03-28 23:30:37 -0700194 GroupStoreMessage.Type.class,
195 UpdateType.class,
196 GroupStoreMessageSubjects.class,
197 MultiValuedTimestamp.class,
198 GroupStoreKeyMapKey.class,
199 GroupStoreIdMapKey.class,
200 GroupStoreMapKey.class
201 );
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700202
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700203 clusterMsgSerializer = kryoBuilder.build("GroupStore");
204 Serializer serializer = Serializer.using(clusterMsgSerializer);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700205
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700206 messageHandlingExecutor = Executors.
207 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
208 groupedThreads("onos/store/group",
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700209 "message-handlers",
210 log));
Madan Jampani01e05fb2015-08-13 13:29:36 -0700211
212 clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
alshabibb0285992016-03-28 23:30:37 -0700213 clusterMsgSerializer::deserialize,
214 this::process,
215 messageHandlingExecutor);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700216
Madan Jampani0b847532016-03-03 13:44:15 -0800217 log.debug("Creating Consistent map onos-group-store-keymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700218
Madan Jampani0b847532016-03-03 13:44:15 -0800219 groupStoreEntriesByKey = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
220 .withName("onos-group-store-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700221 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700222 .build();
Frank Wange0eb5ce2016-07-01 18:21:25 +0800223 groupStoreEntriesByKey.addListener(mapListener);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700224 log.debug("Current size of groupstorekeymap:{}",
225 groupStoreEntriesByKey.size());
Thiago Santosfb73c502016-08-18 18:15:13 -0300226 synchronizeGroupStoreEntries();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700227
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530228 log.debug("Creating GroupStoreId Map From GroupStoreKey Map");
229 matchGroupEntries();
230 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/group", "store", log));
231 statusChangeListener = status -> {
232 if (status == Status.ACTIVE) {
233 executor.execute(this::matchGroupEntries);
234 }
235 };
236 groupStoreEntriesByKey.addStatusChangeListener(statusChangeListener);
237
Madan Jampani0b847532016-03-03 13:44:15 -0800238 log.debug("Creating Consistent map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700239
Madan Jampani0b847532016-03-03 13:44:15 -0800240 auditPendingReqQueue = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
241 .withName("onos-pending-group-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700242 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700243 .build();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700244 log.debug("Current size of pendinggroupkeymap:{}",
245 auditPendingReqQueue.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700246
helenyrwua1c41152016-08-18 16:16:14 -0700247 groupTopic = getOrCreateGroupTopic(serializer);
248 groupTopic.subscribe(this::processGroupMessage);
249
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +0000250 local = clusterService.getLocalNode().id();
251
alshabib10580802015-02-18 18:30:33 -0800252 log.info("Started");
253 }
254
255 @Deactivate
256 public void deactivate() {
Frank Wange0eb5ce2016-07-01 18:21:25 +0800257 groupStoreEntriesByKey.removeListener(mapListener);
alshabibb0285992016-03-28 23:30:37 -0700258 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700259 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
alshabib10580802015-02-18 18:30:33 -0800260 log.info("Stopped");
261 }
262
alshabibb0285992016-03-28 23:30:37 -0700263 @Modified
264 public void modified(ComponentContext context) {
265 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
266
267 try {
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700268 String s = get(properties, GARBAGE_COLLECT);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700269 garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT_DEFAULT : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700270
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700271 s = get(properties, GARBAGE_COLLECT_THRESH);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700272 gcThresh = isNullOrEmpty(s) ? GARBAGE_COLLECT_THRESH_DEFAULT : Integer.parseInt(s.trim());
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530273
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700274 s = get(properties, ALLOW_EXTRANEOUS_GROUPS);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700275 allowExtraneousGroups = isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_GROUPS_DEFAULT : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700276 } catch (Exception e) {
Ray Milkeyb5646e62018-10-16 11:42:18 -0700277 gcThresh = GARBAGE_COLLECT_THRESH_DEFAULT;
278 garbageCollect = GARBAGE_COLLECT_DEFAULT;
279 allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700280 }
281 }
282
helenyrwua1c41152016-08-18 16:16:14 -0700283 private Topic<GroupStoreMessage> getOrCreateGroupTopic(Serializer serializer) {
284 if (groupTopic == null) {
285 return storageService.getTopic("group-failover-notif", serializer);
286 } else {
287 return groupTopic;
288 }
Sho SHIMIZUa6285542017-01-12 15:08:24 -0800289 }
helenyrwua1c41152016-08-18 16:16:14 -0700290
alshabib10580802015-02-18 18:30:33 -0800291 /**
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530292 * Updating values of groupEntriesById.
293 */
294 private void matchGroupEntries() {
295 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupStoreEntriesByKey.asJavaMap().entrySet()) {
296 StoredGroupEntry group = entry.getValue();
297 getGroupIdTable(entry.getKey().deviceId()).put(group.id(), group);
298 }
299 }
300
Thiago Santosfb73c502016-08-18 18:15:13 -0300301
302 private void synchronizeGroupStoreEntries() {
303 Map<GroupStoreKeyMapKey, StoredGroupEntry> groupEntryMap = groupStoreEntriesByKey.asJavaMap();
304 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupEntryMap.entrySet()) {
Thiago Santosfb73c502016-08-18 18:15:13 -0300305 StoredGroupEntry value = entry.getValue();
Thiago Santosfb73c502016-08-18 18:15:13 -0300306 ConcurrentMap<GroupId, StoredGroupEntry> groupIdTable = getGroupIdTable(value.deviceId());
307 groupIdTable.put(value.id(), value);
308 }
309 }
310
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530311 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700312 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800313 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700314 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800315 */
Madan Jampani0b847532016-03-03 13:44:15 -0800316 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700317 getGroupStoreKeyMap() {
Madan Jampani0b847532016-03-03 13:44:15 -0800318 return groupStoreEntriesByKey.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800319 }
320
321 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700322 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800323 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700324 * @param deviceId identifier of the device
325 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800326 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700327 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700328 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800329 }
330
331 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700332 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800333 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700334 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800335 */
Madan Jampani0b847532016-03-03 13:44:15 -0800336 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700337 getPendingGroupKeyTable() {
Madan Jampani0b847532016-03-03 13:44:15 -0800338 return auditPendingReqQueue.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800339 }
340
341 /**
342 * Returns the extraneous group id table for specified device.
343 *
344 * @param deviceId identifier of the device
345 * @return Map representing group key table of given device.
346 */
347 private ConcurrentMap<GroupId, Group>
348 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700349 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800350 }
351
352 /**
353 * Returns the number of groups for the specified device in the store.
354 *
355 * @return number of groups for the specified device
356 */
357 @Override
358 public int getGroupCount(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700359 return (getGroups(deviceId) != null) ?
alshabibb0285992016-03-28 23:30:37 -0700360 Iterables.size(getGroups(deviceId)) : 0;
alshabib10580802015-02-18 18:30:33 -0800361 }
362
363 /**
364 * Returns the groups associated with a device.
365 *
366 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800367 * @return the group entries
368 */
369 @Override
370 public Iterable<Group> getGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800371 // Let ImmutableSet.copyOf do the type conversion
372 return ImmutableSet.copyOf(getStoredGroups(deviceId));
alshabib10580802015-02-18 18:30:33 -0800373 }
374
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700375 private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800376 NodeId master = mastershipService.getMasterFor(deviceId);
Andrea Campanella5daa7c462020-03-13 12:04:23 +0100377 if (master == null && deviceService.isAvailable(deviceId)) {
Charles Chanf4838a72015-12-07 18:13:45 -0800378 log.debug("Failed to getGroups: No master for {}", deviceId);
379 return Collections.emptySet();
380 }
381
382 Set<StoredGroupEntry> storedGroups = getGroupStoreKeyMap().values()
383 .stream()
384 .filter(input -> input.deviceId().equals(deviceId))
385 .collect(Collectors.toSet());
386 return ImmutableSet.copyOf(storedGroups);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700387 }
388
alshabib10580802015-02-18 18:30:33 -0800389 /**
390 * Returns the stored group entry.
391 *
alshabibb0285992016-03-28 23:30:37 -0700392 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800393 * @param appCookie the group key
alshabib10580802015-02-18 18:30:33 -0800394 * @return a group associated with the key
395 */
396 @Override
397 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700398 return getStoredGroupEntry(deviceId, appCookie);
399 }
400
401 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
402 GroupKey appCookie) {
403 return getGroupStoreKeyMap().get(new GroupStoreKeyMapKey(deviceId,
404 appCookie));
405 }
406
407 @Override
408 public Group getGroup(DeviceId deviceId, GroupId groupId) {
409 return getStoredGroupEntry(deviceId, groupId);
410 }
411
412 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
413 GroupId groupId) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700414 return getGroupIdTable(deviceId).get(groupId);
alshabib10580802015-02-18 18:30:33 -0800415 }
416
417 private int getFreeGroupIdValue(DeviceId deviceId) {
418 int freeId = groupIdGen.incrementAndGet();
419
420 while (true) {
Yi Tsengfa394de2017-02-01 11:26:40 -0800421 Group existing = getGroup(deviceId, new GroupId(freeId));
alshabib10580802015-02-18 18:30:33 -0800422 if (existing == null) {
423 existing = (
424 extraneousGroupEntriesById.get(deviceId) != null) ?
425 extraneousGroupEntriesById.get(deviceId).
Yi Tsengfa394de2017-02-01 11:26:40 -0800426 get(new GroupId(freeId)) :
alshabib10580802015-02-18 18:30:33 -0800427 null;
428 }
429 if (existing != null) {
430 freeId = groupIdGen.incrementAndGet();
431 } else {
432 break;
433 }
434 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700435 log.debug("getFreeGroupIdValue: Next Free ID is {}", freeId);
alshabib10580802015-02-18 18:30:33 -0800436 return freeId;
437 }
438
439 /**
440 * Stores a new group entry using the information from group description.
441 *
442 * @param groupDesc group description to be used to create group entry
443 */
444 @Override
445 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700446 log.debug("In storeGroupDescription");
alshabib10580802015-02-18 18:30:33 -0800447 // Check if a group is existing with the same key
Saurav Das8a0732e2015-11-20 15:27:53 -0800448 Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
449 if (existingGroup != null) {
Saurav Dasc568c342018-01-25 09:49:01 -0800450 log.debug("Group already exists with the same key {} in dev:{} with id:0x{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800451 groupDesc.appCookie(), groupDesc.deviceId(),
452 Integer.toHexString(existingGroup.id().id()));
alshabib10580802015-02-18 18:30:33 -0800453 return;
454 }
455
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700456 // Check if group to be created by a remote instance
Madan Jampani175e8fd2015-05-20 14:10:45 -0700457 if (mastershipService.getLocalRole(groupDesc.deviceId()) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700458 log.debug("storeGroupDescription: Device {} local role is not MASTER",
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700459 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700460 if (mastershipService.getMasterFor(groupDesc.deviceId()) == null) {
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530461 log.debug("No Master for device {}..."
462 + "Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700463 groupDesc.deviceId());
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530464 addToPendingAudit(groupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700465 return;
466 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700467 GroupStoreMessage groupOp = GroupStoreMessage.
468 createGroupAddRequestMsg(groupDesc.deviceId(),
469 groupDesc);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700470
Madan Jampani175e8fd2015-05-20 14:10:45 -0700471 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700472 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
473 clusterMsgSerializer::serialize,
474 mastershipService.getMasterFor(groupDesc.deviceId()))
475 .whenComplete((result, error) -> {
Madan Jampani175e8fd2015-05-20 14:10:45 -0700476 if (error != null) {
477 log.warn("Failed to send request to master: {} to {}",
alshabibb0285992016-03-28 23:30:37 -0700478 groupOp,
479 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700480 //TODO: Send Group operation failure event
481 } else {
482 log.debug("Sent Group operation request for device {} "
alshabibb0285992016-03-28 23:30:37 -0700483 + "to remote MASTER {}",
484 groupDesc.deviceId(),
485 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700486 }
487 });
alshabib10580802015-02-18 18:30:33 -0800488 return;
489 }
490
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700491 log.debug("Store group for device {} is getting handled locally",
492 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800493 storeGroupDescriptionInternal(groupDesc);
494 }
495
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530496 private void addToPendingAudit(GroupDescription groupDesc) {
497 Integer groupIdVal = groupDesc.givenGroupId();
498 GroupId groupId = (groupIdVal != null) ? new GroupId(groupIdVal) : dummyGroupId;
499 addToPendingKeyTable(new DefaultGroup(groupId, groupDesc));
500 }
501
502 private void addToPendingKeyTable(StoredGroupEntry group) {
503 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
504 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
505 getPendingGroupKeyTable();
506 pendingKeyTable.put(new GroupStoreKeyMapKey(group.deviceId(),
507 group.appCookie()),
508 group);
509 }
510
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700511 private Group getMatchingExtraneousGroupbyId(DeviceId deviceId, Integer groupId) {
512 ConcurrentMap<GroupId, Group> extraneousMap =
513 extraneousGroupEntriesById.get(deviceId);
514 if (extraneousMap == null) {
515 return null;
516 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800517 return extraneousMap.get(new GroupId(groupId));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700518 }
519
520 private Group getMatchingExtraneousGroupbyBuckets(DeviceId deviceId,
521 GroupBuckets buckets) {
522 ConcurrentMap<GroupId, Group> extraneousMap =
523 extraneousGroupEntriesById.get(deviceId);
524 if (extraneousMap == null) {
525 return null;
526 }
527
alshabibb0285992016-03-28 23:30:37 -0700528 for (Group extraneousGroup : extraneousMap.values()) {
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700529 if (extraneousGroup.buckets().equals(buckets)) {
530 return extraneousGroup;
531 }
532 }
533 return null;
534 }
535
alshabib10580802015-02-18 18:30:33 -0800536 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
537 // Check if a group is existing with the same key
538 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
539 return;
540 }
541
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700542 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
543 // Device group audit has not completed yet
544 // Add this group description to pending group key table
545 // Create a group entry object with Dummy Group ID
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700546 log.debug("storeGroupDescriptionInternal: Device {} AUDIT pending...Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700547 groupDesc.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700548 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
549 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
Madan Jampani0b847532016-03-03 13:44:15 -0800550 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700551 getPendingGroupKeyTable();
552 pendingKeyTable.put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
553 groupDesc.appCookie()),
554 group);
555 return;
556 }
557
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700558 Group matchingExtraneousGroup = null;
559 if (groupDesc.givenGroupId() != null) {
560 //Check if there is a extraneous group existing with the same Id
561 matchingExtraneousGroup = getMatchingExtraneousGroupbyId(
alshabibb0285992016-03-28 23:30:37 -0700562 groupDesc.deviceId(), groupDesc.givenGroupId());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700563 if (matchingExtraneousGroup != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800564 log.debug("storeGroupDescriptionInternal: Matching extraneous group "
alshabibb0285992016-03-28 23:30:37 -0700565 + "found in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700566 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800567 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700568 //Check if the group buckets matches with user provided buckets
569 if (matchingExtraneousGroup.buckets().equals(groupDesc.buckets())) {
570 //Group is already existing with the same buckets and Id
571 // Create a group entry object
Saurav Das0fd79d92016-03-07 10:58:36 -0800572 log.debug("storeGroupDescriptionInternal: Buckets also matching "
alshabibb0285992016-03-28 23:30:37 -0700573 + "in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700574 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800575 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700576 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700577 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700578 // Insert the newly created group entry into key and id maps
579 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700580 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
581 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700582 // Ensure it also inserted into group id based table to
583 // avoid any chances of duplication in group id generation
584 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700585 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700586 addOrUpdateGroupEntry(matchingExtraneousGroup);
587 removeExtraneousGroupEntry(matchingExtraneousGroup);
588 return;
589 } else {
590 //Group buckets are not matching. Update group
591 //with user provided buckets.
Saurav Das0fd79d92016-03-07 10:58:36 -0800592 log.debug("storeGroupDescriptionInternal: Buckets are not "
alshabibb0285992016-03-28 23:30:37 -0700593 + "matching in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700594 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800595 Integer.toHexString(groupDesc.givenGroupId()));
596 StoredGroupEntry modifiedGroup = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700597 matchingExtraneousGroup.id(), groupDesc);
Saurav Das0fd79d92016-03-07 10:58:36 -0800598 modifiedGroup.setState(GroupState.PENDING_UPDATE);
599 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700600 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
601 groupDesc.appCookie()), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800602 // Ensure it also inserted into group id based table to
603 // avoid any chances of duplication in group id generation
604 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700605 put(matchingExtraneousGroup.id(), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800606 removeExtraneousGroupEntry(matchingExtraneousGroup);
607 log.debug("storeGroupDescriptionInternal: Triggering Group "
alshabibb0285992016-03-28 23:30:37 -0700608 + "UPDATE request for {} in device {}",
Saurav Das0fd79d92016-03-07 10:58:36 -0800609 matchingExtraneousGroup.id(),
610 groupDesc.deviceId());
611 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, modifiedGroup));
612 return;
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700613 }
614 }
615 } else {
616 //Check if there is an extraneous group with user provided buckets
617 matchingExtraneousGroup = getMatchingExtraneousGroupbyBuckets(
alshabibb0285992016-03-28 23:30:37 -0700618 groupDesc.deviceId(), groupDesc.buckets());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700619 if (matchingExtraneousGroup != null) {
620 //Group is already existing with the same buckets.
621 //So reuse this group.
622 log.debug("storeGroupDescriptionInternal: Matching extraneous group found in Device {}",
623 groupDesc.deviceId());
624 //Create a group entry object
625 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700626 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700627 // Insert the newly created group entry into key and id maps
628 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700629 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
630 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700631 // Ensure it also inserted into group id based table to
632 // avoid any chances of duplication in group id generation
633 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700634 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700635 addOrUpdateGroupEntry(matchingExtraneousGroup);
636 removeExtraneousGroupEntry(matchingExtraneousGroup);
637 return;
638 } else {
639 //TODO: Check if there are any empty groups that can be used here
640 log.debug("storeGroupDescriptionInternal: No matching extraneous groups found in Device {}",
641 groupDesc.deviceId());
642 }
643 }
644
Saurav Das100e3b82015-04-30 11:12:10 -0700645 GroupId id = null;
646 if (groupDesc.givenGroupId() == null) {
647 // Get a new group identifier
Yi Tsengfa394de2017-02-01 11:26:40 -0800648 id = new GroupId(getFreeGroupIdValue(groupDesc.deviceId()));
Saurav Das100e3b82015-04-30 11:12:10 -0700649 } else {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800650 // we need to use the identifier passed in by caller, but check if
651 // already used
652 Group existing = getGroup(groupDesc.deviceId(),
Yi Tsengfa394de2017-02-01 11:26:40 -0800653 new GroupId(groupDesc.givenGroupId()));
Saurav Das8be4e3a2016-03-11 17:19:07 -0800654 if (existing != null) {
655 log.warn("Group already exists with the same id: 0x{} in dev:{} "
alshabibb0285992016-03-28 23:30:37 -0700656 + "but with different key: {} (request gkey: {})",
657 Integer.toHexString(groupDesc.givenGroupId()),
658 groupDesc.deviceId(),
659 existing.appCookie(),
660 groupDesc.appCookie());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800661 return;
662 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800663 id = new GroupId(groupDesc.givenGroupId());
Saurav Das100e3b82015-04-30 11:12:10 -0700664 }
alshabib10580802015-02-18 18:30:33 -0800665 // Create a group entry object
666 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700667 // Insert the newly created group entry into key and id maps
668 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700669 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
670 groupDesc.appCookie()), group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700671 // Ensure it also inserted into group id based table to
672 // avoid any chances of duplication in group id generation
673 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700674 put(id, group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700675 log.debug("storeGroupDescriptionInternal: Processing Group ADD request for Id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700676 id,
677 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800678 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
679 group));
680 }
681
682 /**
683 * Updates the existing group entry with the information
684 * from group description.
685 *
alshabibb0285992016-03-28 23:30:37 -0700686 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800687 * @param oldAppCookie the current group key
alshabibb0285992016-03-28 23:30:37 -0700688 * @param type update type
689 * @param newBuckets group buckets for updates
alshabib10580802015-02-18 18:30:33 -0800690 * @param newAppCookie optional new group key
691 */
692 @Override
693 public void updateGroupDescription(DeviceId deviceId,
694 GroupKey oldAppCookie,
695 UpdateType type,
696 GroupBuckets newBuckets,
697 GroupKey newAppCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700698 // Check if group update to be done by a remote instance
sangho52abe3a2015-05-05 14:13:34 -0700699 if (mastershipService.getMasterFor(deviceId) != null &&
700 mastershipService.getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700701 log.debug("updateGroupDescription: Device {} local role is not MASTER",
702 deviceId);
703 if (mastershipService.getMasterFor(deviceId) == null) {
704 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700705 + "Can not perform update group operation",
706 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700707 //TODO: Send Group operation failure event
708 return;
709 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700710 GroupStoreMessage groupOp = GroupStoreMessage.
711 createGroupUpdateRequestMsg(deviceId,
712 oldAppCookie,
713 type,
714 newBuckets,
715 newAppCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700716
Madan Jampani175e8fd2015-05-20 14:10:45 -0700717 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700718 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
719 clusterMsgSerializer::serialize,
720 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
721 if (error != null) {
722 log.warn("Failed to send request to master: {} to {}",
723 groupOp,
724 mastershipService.getMasterFor(deviceId), error);
725 }
726 //TODO: Send Group operation failure event
727 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700728 return;
729 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700730 log.debug("updateGroupDescription for device {} is getting handled locally",
731 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700732 updateGroupDescriptionInternal(deviceId,
733 oldAppCookie,
734 type,
735 newBuckets,
736 newAppCookie);
737 }
738
739 private void updateGroupDescriptionInternal(DeviceId deviceId,
alshabibb0285992016-03-28 23:30:37 -0700740 GroupKey oldAppCookie,
741 UpdateType type,
742 GroupBuckets newBuckets,
743 GroupKey newAppCookie) {
alshabib10580802015-02-18 18:30:33 -0800744 // Check if a group is existing with the provided key
745 Group oldGroup = getGroup(deviceId, oldAppCookie);
746 if (oldGroup == null) {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800747 log.warn("updateGroupDescriptionInternal: Group not found...strange. "
alshabibb0285992016-03-28 23:30:37 -0700748 + "GroupKey:{} DeviceId:{}", oldAppCookie, deviceId);
alshabib10580802015-02-18 18:30:33 -0800749 return;
750 }
751
752 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
753 type,
754 newBuckets);
755 if (newBucketList != null) {
756 // Create a new group object from the old group
757 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
758 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
759 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
760 oldGroup.deviceId(),
761 oldGroup.type(),
762 updatedBuckets,
763 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700764 oldGroup.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800765 oldGroup.appId());
766 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
767 updatedGroupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700768 log.debug("updateGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_UPDATE",
alshabibb0285992016-03-28 23:30:37 -0700769 oldGroup.id(),
770 oldGroup.deviceId(),
771 oldGroup.state());
alshabib10580802015-02-18 18:30:33 -0800772 newGroup.setState(GroupState.PENDING_UPDATE);
773 newGroup.setLife(oldGroup.life());
774 newGroup.setPackets(oldGroup.packets());
775 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700776 //Update the group entry in groupkey based map.
777 //Update to groupid based map will happen in the
778 //groupkey based map update listener
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700779 log.debug("updateGroupDescriptionInternal with type {}: Group updated with buckets",
780 type);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700781 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700782 put(new GroupStoreKeyMapKey(newGroup.deviceId(),
783 newGroup.appCookie()), newGroup);
alshabib10580802015-02-18 18:30:33 -0800784 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700785 } else {
786 log.warn("updateGroupDescriptionInternal with type {}: No "
alshabibb0285992016-03-28 23:30:37 -0700787 + "change in the buckets in update", type);
alshabib10580802015-02-18 18:30:33 -0800788 }
789 }
790
791 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
792 UpdateType type,
793 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300794 if (type == UpdateType.SET) {
795 return buckets.buckets();
796 }
797
Victor Silvadf1eeae2016-08-12 15:28:57 -0300798 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
799 List<GroupBucket> updatedBucketList = new ArrayList<>();
alshabib10580802015-02-18 18:30:33 -0800800 boolean groupDescUpdated = false;
801
802 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300803 List<GroupBucket> newBuckets = buckets.buckets();
804
805 // Add old buckets that will not be updated and check if any will be updated.
806 for (GroupBucket oldBucket : oldBuckets) {
807 int newBucketIndex = newBuckets.indexOf(oldBucket);
808
809 if (newBucketIndex != -1) {
810 GroupBucket newBucket = newBuckets.get(newBucketIndex);
811 if (!newBucket.hasSameParameters(oldBucket)) {
812 // Bucket will be updated
813 groupDescUpdated = true;
814 }
815 } else {
816 // Old bucket will remain the same - add it.
817 updatedBucketList.add(oldBucket);
alshabib10580802015-02-18 18:30:33 -0800818 }
819 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300820
821 // Add all new buckets
822 updatedBucketList.addAll(newBuckets);
823 if (!oldBuckets.containsAll(newBuckets)) {
824 groupDescUpdated = true;
825 }
826
alshabib10580802015-02-18 18:30:33 -0800827 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300828 List<GroupBucket> bucketsToRemove = buckets.buckets();
829
830 // Check which old buckets should remain
831 for (GroupBucket oldBucket : oldBuckets) {
832 if (!bucketsToRemove.contains(oldBucket)) {
833 updatedBucketList.add(oldBucket);
834 } else {
alshabib10580802015-02-18 18:30:33 -0800835 groupDescUpdated = true;
836 }
837 }
838 }
839
840 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300841 return updatedBucketList;
alshabib10580802015-02-18 18:30:33 -0800842 } else {
843 return null;
844 }
845 }
846
847 /**
848 * Triggers deleting the existing group entry.
849 *
alshabibb0285992016-03-28 23:30:37 -0700850 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800851 * @param appCookie the group key
852 */
853 @Override
854 public void deleteGroupDescription(DeviceId deviceId,
855 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700856 // Check if group to be deleted by a remote instance
857 if (mastershipService.
858 getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700859 log.debug("deleteGroupDescription: Device {} local role is not MASTER",
860 deviceId);
861 if (mastershipService.getMasterFor(deviceId) == null) {
862 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700863 + "Can not perform delete group operation",
864 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700865 //TODO: Send Group operation failure event
866 return;
867 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700868 GroupStoreMessage groupOp = GroupStoreMessage.
869 createGroupDeleteRequestMsg(deviceId,
870 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700871
Madan Jampani175e8fd2015-05-20 14:10:45 -0700872 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700873 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
874 clusterMsgSerializer::serialize,
875 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
876 if (error != null) {
877 log.warn("Failed to send request to master: {} to {}",
878 groupOp,
879 mastershipService.getMasterFor(deviceId), error);
880 }
881 //TODO: Send Group operation failure event
882 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700883 return;
884 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700885 log.debug("deleteGroupDescription in device {} is getting handled locally",
886 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700887 deleteGroupDescriptionInternal(deviceId, appCookie);
888 }
889
890 private void deleteGroupDescriptionInternal(DeviceId deviceId,
891 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800892 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700893 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800894 if (existing == null) {
895 return;
896 }
897
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700898 log.debug("deleteGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_DELETE",
alshabibb0285992016-03-28 23:30:37 -0700899 existing.id(),
900 existing.deviceId(),
901 existing.state());
pier1e80f362020-04-03 12:44:21 +0200902 // TODO is this really safe ?
alshabib10580802015-02-18 18:30:33 -0800903 synchronized (existing) {
904 existing.setState(GroupState.PENDING_DELETE);
Saurav Das80980c72016-03-23 11:22:49 -0700905 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700906 put(new GroupStoreKeyMapKey(existing.deviceId(), existing.appCookie()),
907 existing);
alshabib10580802015-02-18 18:30:33 -0800908 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700909 log.debug("deleteGroupDescriptionInternal: in device {} issuing GROUP_REMOVE_REQUESTED",
910 deviceId);
alshabib10580802015-02-18 18:30:33 -0800911 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
912 }
913
914 /**
pier1e80f362020-04-03 12:44:21 +0200915 * Updates the stats of an existing group entry.
916 *
917 * @param group the new stats
918 * @param existing the existing group
919 */
920 private void updateGroupEntryStatsInternal(Group group, StoredGroupEntry existing) {
921 for (GroupBucket bucket : group.buckets().buckets()) {
922 Optional<GroupBucket> matchingBucket =
923 existing.buckets().buckets()
924 .stream()
925 .filter((existingBucket) -> (existingBucket.equals(bucket)))
926 .findFirst();
927 if (matchingBucket.isPresent()) {
928 ((StoredGroupBucketEntry) matchingBucket.
929 get()).setPackets(bucket.packets());
930 ((StoredGroupBucketEntry) matchingBucket.
931 get()).setBytes(bucket.bytes());
932 } else {
933 log.warn("updateGroupEntryStatsInternal: No matching bucket {}" +
934 " to update stats", bucket);
935 }
936 }
937 existing.setLife(group.life());
938 existing.setPackets(group.packets());
939 existing.setBytes(group.bytes());
940 existing.setReferenceCount(group.referenceCount());
941 existing.setFailedRetryCount(0);
942 }
943
944 /**
alshabib10580802015-02-18 18:30:33 -0800945 * Stores a new group entry, or updates an existing entry.
946 *
947 * @param group group entry
948 */
949 @Override
950 public void addOrUpdateGroupEntry(Group group) {
951 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700952 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
953 group.id());
alshabib10580802015-02-18 18:30:33 -0800954 GroupEvent event = null;
955
956 if (existing != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800957 log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700958 group.id(),
959 group.deviceId());
pier1e80f362020-04-03 12:44:21 +0200960 // TODO is this really safe ?
alshabib10580802015-02-18 18:30:33 -0800961 synchronized (existing) {
pier1e80f362020-04-03 12:44:21 +0200962 // Update stats
963 updateGroupEntryStatsInternal(group, existing);
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700964 if ((existing.state() == GroupState.PENDING_ADD) ||
alshabibb0285992016-03-28 23:30:37 -0700965 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800966 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700967 existing.id(),
968 existing.deviceId(),
969 existing.state());
alshabib10580802015-02-18 18:30:33 -0800970 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700971 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800972 event = new GroupEvent(Type.GROUP_ADDED, existing);
973 } else {
Saurav Das0fd79d92016-03-07 10:58:36 -0800974 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700975 existing.id(),
976 existing.deviceId(),
977 GroupState.PENDING_UPDATE);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700978 existing.setState(GroupState.ADDED);
979 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800980 event = new GroupEvent(Type.GROUP_UPDATED, existing);
981 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700982 //Re-PUT map entries to trigger map update events
983 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700984 put(new GroupStoreKeyMapKey(existing.deviceId(),
985 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800986 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700987 } else {
pier1e80f362020-04-03 12:44:21 +0200988 log.warn("addOrUpdateGroupEntry: Group update {} " +
989 "happening for a non-existing entry in the map", group);
alshabib10580802015-02-18 18:30:33 -0800990 }
991
pier1e80f362020-04-03 12:44:21 +0200992 // TODO if map is going to trigger event, is this one needed?
alshabib10580802015-02-18 18:30:33 -0800993 if (event != null) {
994 notifyDelegate(event);
995 }
996 }
997
998 /**
pier1e80f362020-04-03 12:44:21 +0200999 * Updates stats of an existing entry.
1000 *
1001 * @param group group entry
1002 */
1003 private void updateGroupEntryStats(Group group) {
1004 // check if this new entry is an update to an existing entry
1005 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
1006 group.id());
1007 if (existing != null) {
1008 log.trace("updateStatsGroupEntry: updating group entry {} in device {}",
1009 group.id(),
1010 group.deviceId());
1011 // TODO is this really safe ?
1012 synchronized (existing) {
1013 // We don't make further update - it will be gone after the next update
1014 if (existing.state() == GroupState.PENDING_DELETE) {
1015 log.trace("updateStatsGroupEntry: group entry {} in device {} is in {} not updated",
1016 existing.id(),
1017 existing.deviceId(),
1018 existing.state());
1019 return;
1020 }
1021 // Update stats
1022 updateGroupEntryStatsInternal(group, existing);
1023 if ((existing.state() == GroupState.PENDING_ADD) ||
1024 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
1025 log.trace("updateStatsGroupEntry: group entry {} in device {} moving from {} to ADDED",
1026 existing.id(),
1027 existing.deviceId(),
1028 existing.state());
1029 existing.setState(GroupState.ADDED);
1030 existing.setIsGroupStateAddedFirstTime(true);
1031 } else {
1032 log.trace("updateStatsGroupEntry: group entry {} in device {} moving from {} to ADDED",
1033 existing.id(),
1034 existing.deviceId(),
1035 GroupState.PENDING_UPDATE);
1036 existing.setState(GroupState.ADDED);
1037 existing.setIsGroupStateAddedFirstTime(false);
1038 }
1039 //Re-PUT map entries to trigger map update events
1040 getGroupStoreKeyMap().
1041 put(new GroupStoreKeyMapKey(existing.deviceId(),
1042 existing.appCookie()), existing);
1043 }
1044 } else {
1045 log.warn("updateStatsGroupEntry: Group update {} "
1046 + "happening for a non-existing entry in the map", group);
1047 }
1048 }
1049
1050 /**
alshabib10580802015-02-18 18:30:33 -08001051 * Removes the group entry from store.
1052 *
1053 * @param group group entry
1054 */
1055 @Override
1056 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001057 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
1058 group.id());
alshabib10580802015-02-18 18:30:33 -08001059
1060 if (existing != null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001061 log.debug("removeGroupEntry: removing group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001062 group.id(),
1063 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001064 //Removal from groupid based map will happen in the
1065 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001066 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1067 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001068 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001069 } else {
1070 log.warn("removeGroupEntry for {} in device{} is "
alshabibb0285992016-03-28 23:30:37 -07001071 + "not existing in our maps",
1072 group.id(),
1073 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001074 }
1075 }
1076
Victor Silva4e8b7832016-08-17 17:11:19 -03001077 private void purgeGroupEntries(Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entries) {
1078 entries.forEach(entry -> {
1079 groupStoreEntriesByKey.remove(entry.getKey());
1080 });
1081 }
1082
alshabib10580802015-02-18 18:30:33 -08001083 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -08001084 public void purgeGroupEntry(DeviceId deviceId) {
Victor Silva4e8b7832016-08-17 17:11:19 -03001085 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entriesPendingRemove =
Charles Chan0c7c43b2016-01-14 17:39:20 -08001086 new HashSet<>();
1087
Madan Jampani0b847532016-03-03 13:44:15 -08001088 getGroupStoreKeyMap().entrySet().stream()
Charles Chan0c7c43b2016-01-14 17:39:20 -08001089 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Victor Silva4e8b7832016-08-17 17:11:19 -03001090 .forEach(entriesPendingRemove::add);
Charles Chan0c7c43b2016-01-14 17:39:20 -08001091
Victor Silva4e8b7832016-08-17 17:11:19 -03001092 purgeGroupEntries(entriesPendingRemove);
1093 }
1094
1095 @Override
1096 public void purgeGroupEntries() {
1097 purgeGroupEntries(getGroupStoreKeyMap().entrySet());
Charles Chan0c7c43b2016-01-14 17:39:20 -08001098 }
1099
1100 @Override
alshabib10580802015-02-18 18:30:33 -08001101 public void deviceInitialAuditCompleted(DeviceId deviceId,
1102 boolean completed) {
1103 synchronized (deviceAuditStatus) {
1104 if (completed) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001105 log.debug("AUDIT completed for device {}",
1106 deviceId);
alshabib10580802015-02-18 18:30:33 -08001107 deviceAuditStatus.put(deviceId, true);
1108 // Execute all pending group requests
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001109 List<StoredGroupEntry> pendingGroupRequests =
1110 getPendingGroupKeyTable().values()
alshabibb0285992016-03-28 23:30:37 -07001111 .stream()
1112 .filter(g -> g.deviceId().equals(deviceId))
1113 .collect(Collectors.toList());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001114 log.debug("processing pending group add requests for device {} and number of pending requests {}",
alshabibb0285992016-03-28 23:30:37 -07001115 deviceId,
1116 pendingGroupRequests.size());
1117 for (Group group : pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -08001118 GroupDescription tmp = new DefaultGroupDescription(
1119 group.deviceId(),
1120 group.type(),
1121 group.buckets(),
1122 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -07001123 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -08001124 group.appId());
1125 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001126 getPendingGroupKeyTable().
alshabibb0285992016-03-28 23:30:37 -07001127 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001128 }
alshabib10580802015-02-18 18:30:33 -08001129 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001130 Boolean audited = deviceAuditStatus.get(deviceId);
1131 if (audited != null && audited) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001132 log.debug("Clearing AUDIT status for device {}", deviceId);
alshabib10580802015-02-18 18:30:33 -08001133 deviceAuditStatus.put(deviceId, false);
1134 }
1135 }
1136 }
1137 }
1138
1139 @Override
1140 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
1141 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001142 Boolean audited = deviceAuditStatus.get(deviceId);
1143 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -08001144 }
1145 }
1146
1147 @Override
1148 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
1149
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001150 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
1151 operation.groupId());
alshabib10580802015-02-18 18:30:33 -08001152
1153 if (existing == null) {
1154 log.warn("No group entry with ID {} found ", operation.groupId());
1155 return;
1156 }
1157
Saurav Das137f27f2018-06-11 17:02:31 -07001158 log.warn("groupOperationFailed: group operation {} failed in state {} "
alshabibb0285992016-03-28 23:30:37 -07001159 + "for group {} in device {} with code {}",
1160 operation.opType(),
Saurav Das137f27f2018-06-11 17:02:31 -07001161 existing.state(),
alshabibb0285992016-03-28 23:30:37 -07001162 existing.id(),
1163 existing.deviceId(),
1164 operation.failureCode());
Saurav Das0fd79d92016-03-07 10:58:36 -08001165 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001166 if (operation.buckets().equals(existing.buckets())) {
Saurav Dasc88d4662017-05-15 15:34:25 -07001167 if (existing.state() == GroupState.PENDING_ADD ||
1168 existing.state() == GroupState.PENDING_ADD_RETRY) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001169 log.info("GROUP_EXISTS: GroupID and Buckets match for group in pending "
alshabibb0285992016-03-28 23:30:37 -07001170 + "add state - moving to ADDED for group {} in device {}",
1171 existing.id(), deviceId);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001172 addOrUpdateGroupEntry(existing);
1173 return;
1174 } else {
Saurav Dasc88d4662017-05-15 15:34:25 -07001175 log.warn("GROUP_EXISTS: GroupId and Buckets match but existing"
1176 + "group in state: {}", existing.state());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001177 }
Saurav Dasc88d4662017-05-15 15:34:25 -07001178 } else {
1179 log.warn("GROUP EXISTS: Group ID matched but buckets did not. "
1180 + "Operation: {} Existing: {}", operation.buckets(),
1181 existing.buckets());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001182 }
Saurav Das0fd79d92016-03-07 10:58:36 -08001183 }
Saurav Das137f27f2018-06-11 17:02:31 -07001184 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.INVALID_GROUP) {
1185 existing.incrFailedRetryCount();
1186 if (existing.failedRetryCount() < MAX_FAILED_ATTEMPTS) {
1187 log.warn("Group {} programming failed {} of {} times in dev {}, "
1188 + "retrying ..", existing.id(),
1189 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1190 deviceId);
1191 return;
1192 }
1193 log.warn("Group {} programming failed {} of {} times in dev {}, "
1194 + "removing group from store", existing.id(),
1195 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1196 deviceId);
1197 // fall through to case
1198 }
1199
alshabib10580802015-02-18 18:30:33 -08001200 switch (operation.opType()) {
1201 case ADD:
Saurav Das137f27f2018-06-11 17:02:31 -07001202 if (existing.state() == GroupState.PENDING_ADD
1203 || existing.state() == GroupState.PENDING_ADD_RETRY) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001204 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
1205 log.warn("groupOperationFailed: cleaningup "
alshabibb0285992016-03-28 23:30:37 -07001206 + "group {} from store in device {}....",
1207 existing.id(),
1208 existing.deviceId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001209 //Removal from groupid based map will happen in the
1210 //map update listener
1211 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1212 existing.appCookie()));
1213 }
alshabib10580802015-02-18 18:30:33 -08001214 break;
1215 case MODIFY:
1216 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
1217 break;
1218 case DELETE:
1219 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
1220 break;
1221 default:
1222 log.warn("Unknown group operation type {}", operation.opType());
1223 }
alshabib10580802015-02-18 18:30:33 -08001224 }
1225
1226 @Override
1227 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001228 log.debug("add/update extraneous group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001229 group.id(),
1230 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001231 ConcurrentMap<GroupId, Group> extraneousIdTable =
1232 getExtraneousGroupIdTable(group.deviceId());
1233 extraneousIdTable.put(group.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -07001234 // Don't remove the extraneous groups, instead re-use it when
1235 // a group request comes with the same set of buckets
alshabib10580802015-02-18 18:30:33 -08001236 }
1237
1238 @Override
1239 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001240 log.debug("remove extraneous group entry {} of device {} from store",
alshabibb0285992016-03-28 23:30:37 -07001241 group.id(),
1242 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001243 ConcurrentMap<GroupId, Group> extraneousIdTable =
1244 getExtraneousGroupIdTable(group.deviceId());
1245 extraneousIdTable.remove(group.id());
1246 }
1247
1248 @Override
1249 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
1250 // flatten and make iterator unmodifiable
1251 return FluentIterable.from(
1252 getExtraneousGroupIdTable(deviceId).values());
1253 }
1254
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001255 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001256 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001257 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001258 private class GroupStoreKeyMapListener implements
Madan Jampani0b847532016-03-03 13:44:15 -08001259 MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001260
1261 @Override
Madan Jampani0b847532016-03-03 13:44:15 -08001262 public void event(MapEvent<GroupStoreKeyMapKey, StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001263 GroupEvent groupEvent = null;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001264 GroupStoreKeyMapKey key = mapEvent.key();
Madan Jampani0b847532016-03-03 13:44:15 -08001265 StoredGroupEntry group = Versioned.valueOrNull(mapEvent.newValue());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001266 if ((key == null) && (group == null)) {
1267 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001268 + "event {} with null entry", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001269 return;
1270 } else if (group == null) {
1271 group = getGroupIdTable(key.deviceId()).values()
1272 .stream()
1273 .filter((storedGroup) -> (storedGroup.appCookie().equals(key.appCookie)))
Yuta HIGUCHI6e5f4702016-11-21 11:42:11 -08001274 .findFirst().orElse(null);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001275 if (group == null) {
1276 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001277 + "event {} with null entry... can not process", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001278 return;
1279 }
1280 }
1281 log.trace("received groupid map event {} for id {} in device {}",
1282 mapEvent.type(),
1283 group.id(),
jaegonkim68e080c2016-12-01 22:31:01 +09001284 (key != null ? key.deviceId() : null));
Madan Jampani0b847532016-03-03 13:44:15 -08001285 if (mapEvent.type() == MapEvent.Type.INSERT || mapEvent.type() == MapEvent.Type.UPDATE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001286 // Update the group ID table
1287 getGroupIdTable(group.deviceId()).put(group.id(), group);
Madan Jampani0b847532016-03-03 13:44:15 -08001288 StoredGroupEntry value = Versioned.valueOrNull(mapEvent.newValue());
1289 if (value.state() == Group.GroupState.ADDED) {
1290 if (value.isGroupStateAddedFirstTime()) {
1291 groupEvent = new GroupEvent(Type.GROUP_ADDED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001292 log.trace("Received first time GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001293 group.id(),
1294 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001295 } else {
Madan Jampani0b847532016-03-03 13:44:15 -08001296 groupEvent = new GroupEvent(Type.GROUP_UPDATED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001297 log.trace("Received following GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001298 group.id(),
1299 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001300 }
1301 }
Madan Jampani0b847532016-03-03 13:44:15 -08001302 } else if (mapEvent.type() == MapEvent.Type.REMOVE) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001303 groupEvent = new GroupEvent(Type.GROUP_REMOVED, group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001304 // Remove the entry from the group ID table
1305 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001306 }
1307
1308 if (groupEvent != null) {
1309 notifyDelegate(groupEvent);
1310 }
1311 }
1312 }
Madan Jampani01e05fb2015-08-13 13:29:36 -07001313
helenyrwua1c41152016-08-18 16:16:14 -07001314 private void processGroupMessage(GroupStoreMessage message) {
1315 if (message.type() == GroupStoreMessage.Type.FAILOVER) {
1316 // FIXME: groupStoreEntriesByKey inaccessible here
1317 getGroupIdTable(message.deviceId()).values()
1318 .stream()
1319 .filter((storedGroup) -> (storedGroup.appCookie().equals(message.appCookie())))
1320 .findFirst().ifPresent(group -> notifyDelegate(new GroupEvent(Type.GROUP_BUCKET_FAILOVER, group)));
1321 }
1322 }
1323
Madan Jampani01e05fb2015-08-13 13:29:36 -07001324 private void process(GroupStoreMessage groupOp) {
1325 log.debug("Received remote group operation {} request for device {}",
alshabibb0285992016-03-28 23:30:37 -07001326 groupOp.type(),
1327 groupOp.deviceId());
1328 if (!mastershipService.isLocalMaster(groupOp.deviceId())) {
1329 log.warn("This node is not MASTER for device {}", groupOp.deviceId());
1330 return;
1331 }
1332 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
1333 storeGroupDescriptionInternal(groupOp.groupDesc());
1334 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
1335 updateGroupDescriptionInternal(groupOp.deviceId(),
1336 groupOp.appCookie(),
1337 groupOp.updateType(),
1338 groupOp.updateBuckets(),
1339 groupOp.newAppCookie());
1340 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
1341 deleteGroupDescriptionInternal(groupOp.deviceId(),
1342 groupOp.appCookie());
1343 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001344 }
1345
1346 /**
1347 * Flattened map key to be used to store group entries.
1348 */
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001349 protected static class GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001350 private final DeviceId deviceId;
1351
1352 public GroupStoreMapKey(DeviceId deviceId) {
1353 this.deviceId = deviceId;
1354 }
1355
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001356 public DeviceId deviceId() {
1357 return deviceId;
1358 }
1359
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001360 @Override
1361 public boolean equals(Object o) {
1362 if (this == o) {
1363 return true;
1364 }
1365 if (!(o instanceof GroupStoreMapKey)) {
1366 return false;
1367 }
1368 GroupStoreMapKey that = (GroupStoreMapKey) o;
1369 return this.deviceId.equals(that.deviceId);
1370 }
1371
1372 @Override
1373 public int hashCode() {
1374 int result = 17;
1375
1376 result = 31 * result + Objects.hash(this.deviceId);
1377
1378 return result;
1379 }
1380 }
1381
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001382 protected static class GroupStoreKeyMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001383 private final GroupKey appCookie;
alshabibb0285992016-03-28 23:30:37 -07001384
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001385 public GroupStoreKeyMapKey(DeviceId deviceId,
1386 GroupKey appCookie) {
1387 super(deviceId);
1388 this.appCookie = appCookie;
1389 }
1390
1391 @Override
1392 public boolean equals(Object o) {
1393 if (this == o) {
1394 return true;
1395 }
1396 if (!(o instanceof GroupStoreKeyMapKey)) {
1397 return false;
1398 }
1399 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
1400 return (super.equals(that) &&
1401 this.appCookie.equals(that.appCookie));
1402 }
1403
1404 @Override
1405 public int hashCode() {
1406 int result = 17;
1407
1408 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
1409
1410 return result;
1411 }
1412 }
1413
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001414 protected static class GroupStoreIdMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001415 private final GroupId groupId;
alshabibb0285992016-03-28 23:30:37 -07001416
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001417 public GroupStoreIdMapKey(DeviceId deviceId,
1418 GroupId groupId) {
1419 super(deviceId);
1420 this.groupId = groupId;
1421 }
1422
1423 @Override
1424 public boolean equals(Object o) {
1425 if (this == o) {
1426 return true;
1427 }
1428 if (!(o instanceof GroupStoreIdMapKey)) {
1429 return false;
1430 }
1431 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1432 return (super.equals(that) &&
1433 this.groupId.equals(that.groupId));
1434 }
1435
1436 @Override
1437 public int hashCode() {
1438 int result = 17;
1439
1440 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1441
1442 return result;
1443 }
1444 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001445
1446 @Override
1447 public void pushGroupMetrics(DeviceId deviceId,
1448 Collection<Group> groupEntries) {
1449 boolean deviceInitialAuditStatus =
1450 deviceInitialAuditStatus(deviceId);
1451 Set<Group> southboundGroupEntries =
1452 Sets.newHashSet(groupEntries);
1453 Set<StoredGroupEntry> storedGroupEntries =
1454 Sets.newHashSet(getStoredGroups(deviceId));
1455 Set<Group> extraneousStoredEntries =
1456 Sets.newHashSet(getExtraneousGroups(deviceId));
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001457 NodeId master;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001458
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001459 if (log.isTraceEnabled()) {
1460 log.trace("pushGroupMetrics: Displaying all ({}) southboundGroupEntries for device {}",
1461 southboundGroupEntries.size(),
1462 deviceId);
1463 for (Group group : southboundGroupEntries) {
1464 log.trace("Group {} in device {}", group, deviceId);
1465 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001466
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001467 log.trace("Displaying all ({}) stored group entries for device {}",
1468 storedGroupEntries.size(),
1469 deviceId);
1470 for (StoredGroupEntry group : storedGroupEntries) {
1471 log.trace("Stored Group {} for device {}", group, deviceId);
1472 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001473 }
1474
alshabibb0285992016-03-28 23:30:37 -07001475 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1476
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001477 // update stats
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001478 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001479 // Mastership change can occur during this iteration
1480 master = mastershipService.getMasterFor(deviceId);
1481 if (!Objects.equals(local, master)) {
1482 log.warn("Tried to update the group stats while the node was not the master");
1483 return;
1484 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001485 Group group = it2.next();
1486 if (storedGroupEntries.remove(group)) {
1487 // we both have the group, let's update some info then.
1488 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001489 group.id(), deviceId);
1490
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001491 groupAdded(group);
1492 it2.remove();
1493 }
1494 }
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001495
1496 // extraneous groups in the dataplane
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001497 for (Group group : southboundGroupEntries) {
1498 if (getGroup(group.deviceId(), group.id()) != null) {
1499 // There is a group existing with the same id
1500 // It is possible that group update is
1501 // in progress while we got a stale info from switch
1502 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001503 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001504 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001505 + "Group exists in ID based table while "
1506 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001507 }
1508 } else {
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001509 // Mastership change can occur during this iteration
1510 master = mastershipService.getMasterFor(deviceId);
1511 if (!Objects.equals(local, master)) {
1512 log.warn("Tried to process extraneous groups while the node was not the master");
1513 return;
1514 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001515 // there are groups in the switch that aren't in the store
1516 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001517 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001518 extraneousStoredEntries.remove(group);
Kavitha Alagesanc56cded2017-01-13 10:48:18 +05301519 if (allowExtraneousGroups) {
1520 extraneousGroup(group);
1521 } else {
1522 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
1523 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001524 }
1525 }
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001526
1527 // missing groups in the dataplane
Charles Chan07f15f22018-05-08 21:35:50 -07001528 for (StoredGroupEntry group : storedGroupEntries) {
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001529 // Mastership change can occur during this iteration
1530 master = mastershipService.getMasterFor(deviceId);
1531 if (!Objects.equals(local, master)) {
1532 log.warn("Tried to process missing groups while the node was not the master");
1533 return;
1534 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001535 // there are groups in the store that aren't in the switch
1536 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001537 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001538 groupMissing(group);
1539 }
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001540
1541 // extraneous groups in the store
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001542 for (Group group : extraneousStoredEntries) {
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001543 // Mastership change can occur during this iteration
1544 master = mastershipService.getMasterFor(deviceId);
1545 if (!Objects.equals(local, master)) {
1546 log.warn("Tried to process node extraneous groups while the node was not the master");
1547 return;
1548 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001549 // there are groups in the extraneous store that
1550 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001551 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001552 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001553 removeExtraneousGroupEntry(group);
1554 }
1555
1556 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001557 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001558 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001559 deviceInitialAuditCompleted(deviceId, true);
1560 }
1561 }
1562
helenyrwu89470f12016-08-12 13:18:10 -07001563 @Override
1564 public void notifyOfFailovers(Collection<Group> failoverGroups) {
helenyrwu89470f12016-08-12 13:18:10 -07001565 failoverGroups.forEach(group -> {
1566 if (group.type() == Group.Type.FAILOVER) {
helenyrwua1c41152016-08-18 16:16:14 -07001567 groupTopic.publish(GroupStoreMessage.createGroupFailoverMsg(
1568 group.deviceId(), group));
helenyrwu89470f12016-08-12 13:18:10 -07001569 }
1570 });
helenyrwu89470f12016-08-12 13:18:10 -07001571 }
1572
alshabibb0285992016-03-28 23:30:37 -07001573 private void garbageCollect(DeviceId deviceId,
1574 Set<Group> southboundGroupEntries,
1575 Set<StoredGroupEntry> storedGroupEntries) {
1576 if (!garbageCollect) {
1577 return;
1578 }
1579
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001580 NodeId master;
alshabibb0285992016-03-28 23:30:37 -07001581 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1582 while (it.hasNext()) {
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001583 // Mastership change can occur during this iteration
1584 master = mastershipService.getMasterFor(deviceId);
1585 if (!Objects.equals(local, master)) {
1586 log.warn("Tried to run garbage collector while the node was not the master");
1587 return;
1588 }
alshabibb0285992016-03-28 23:30:37 -07001589 StoredGroupEntry group = it.next();
1590 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1591 log.debug("Garbage collecting group {} on {}", group, deviceId);
1592 deleteGroupDescription(deviceId, group.appCookie());
1593 southboundGroupEntries.remove(group);
1594 it.remove();
1595 }
1596 }
1597 }
1598
1599 private boolean checkGroupRefCount(Group group) {
1600 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1601 }
1602
Charles Chan07f15f22018-05-08 21:35:50 -07001603 private void groupMissing(StoredGroupEntry group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001604 switch (group.state()) {
1605 case PENDING_DELETE:
1606 log.debug("Group {} delete confirmation from device {}",
1607 group, group.deviceId());
1608 removeGroupEntry(group);
1609 break;
1610 case ADDED:
1611 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001612 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001613 case PENDING_UPDATE:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001614 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
Charles Chan07f15f22018-05-08 21:35:50 -07001615 group.id(),
1616 group.deviceId(),
1617 group.state());
1618 group.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001619 //Re-PUT map entries to trigger map update events
Charles Chan07f15f22018-05-08 21:35:50 -07001620 getGroupStoreKeyMap().put(new GroupStoreKeyMapKey(group.deviceId(), group.appCookie()), group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001621 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1622 group));
1623 break;
1624 default:
1625 log.debug("Group {} has not been installed.", group);
1626 break;
1627 }
1628 }
1629
1630 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001631 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001632 group, group.deviceId());
1633 addOrUpdateExtraneousGroupEntry(group);
1634 }
1635
1636 private void groupAdded(Group group) {
1637 log.trace("Group {} Added or Updated in device {}",
1638 group, group.deviceId());
pier1e80f362020-04-03 12:44:21 +02001639 updateGroupEntryStats(group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001640 }
alshabib10580802015-02-18 18:30:33 -08001641}