blob: d358a77627d4c04575ead791e9f62e74cb3ba97d [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;
Daniele Morocfd77402021-07-15 17:02:59 +020026import org.onosproject.core.ApplicationId;
alshabib10580802015-02-18 18:30:33 -080027import org.onosproject.core.GroupId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070028import org.onosproject.mastership.MastershipService;
alshabib10580802015-02-18 18:30:33 -080029import org.onosproject.net.DeviceId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070030import org.onosproject.net.MastershipRole;
Andrea Campanella5daa7c462020-03-13 12:04:23 +010031import org.onosproject.net.device.DeviceService;
jaegonkim9477a9d2018-04-01 16:36:36 +090032import org.onosproject.net.driver.DriverService;
alshabib10580802015-02-18 18:30:33 -080033import org.onosproject.net.group.DefaultGroup;
34import org.onosproject.net.group.DefaultGroupDescription;
35import org.onosproject.net.group.Group;
36import org.onosproject.net.group.Group.GroupState;
37import org.onosproject.net.group.GroupBucket;
38import org.onosproject.net.group.GroupBuckets;
39import org.onosproject.net.group.GroupDescription;
40import org.onosproject.net.group.GroupEvent;
41import org.onosproject.net.group.GroupEvent.Type;
42import org.onosproject.net.group.GroupKey;
43import org.onosproject.net.group.GroupOperation;
44import org.onosproject.net.group.GroupStore;
45import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070046import org.onosproject.net.group.StoredGroupBucketEntry;
alshabib10580802015-02-18 18:30:33 -080047import org.onosproject.net.group.StoredGroupEntry;
48import org.onosproject.store.AbstractStore;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070049import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070050import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani0b847532016-03-03 13:44:15 -080051import org.onosproject.store.service.ConsistentMap;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070052import org.onosproject.store.service.DistributedPrimitive.Status;
Madan Jampani0b847532016-03-03 13:44:15 -080053import org.onosproject.store.service.MapEvent;
54import org.onosproject.store.service.MapEventListener;
alshabibb0285992016-03-28 23:30:37 -070055import org.onosproject.store.service.MultiValuedTimestamp;
Madan Jampani0b847532016-03-03 13:44:15 -080056import org.onosproject.store.service.Serializer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070057import org.onosproject.store.service.StorageService;
helenyrwua1c41152016-08-18 16:16:14 -070058import org.onosproject.store.service.Topic;
Madan Jampani0b847532016-03-03 13:44:15 -080059import org.onosproject.store.service.Versioned;
alshabibb0285992016-03-28 23:30:37 -070060import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070061import org.osgi.service.component.annotations.Activate;
62import org.osgi.service.component.annotations.Component;
63import org.osgi.service.component.annotations.Deactivate;
64import org.osgi.service.component.annotations.Modified;
65import org.osgi.service.component.annotations.Reference;
66import org.osgi.service.component.annotations.ReferenceCardinality;
alshabib10580802015-02-18 18:30:33 -080067import org.slf4j.Logger;
68
Jonathan Hart6ec029a2015-03-24 17:12:35 -070069import java.util.ArrayList;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070070import java.util.Collection;
Charles Chanf4838a72015-12-07 18:13:45 -080071import java.util.Collections;
alshabibb0285992016-03-28 23:30:37 -070072import java.util.Dictionary;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070073import java.util.HashMap;
Charles Chan0c7c43b2016-01-14 17:39:20 -080074import java.util.HashSet;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070075import java.util.Iterator;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070076import java.util.List;
Madan Jampani0b847532016-03-03 13:44:15 -080077import java.util.Map;
Charles Chan0c7c43b2016-01-14 17:39:20 -080078import java.util.Map.Entry;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070079import java.util.Objects;
Sho SHIMIZU30d639b2015-05-05 09:30:35 -070080import java.util.Optional;
alshabibb0285992016-03-28 23:30:37 -070081import java.util.Properties;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070082import java.util.Set;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070083import java.util.concurrent.ConcurrentHashMap;
84import java.util.concurrent.ConcurrentMap;
85import java.util.concurrent.ExecutorService;
86import java.util.concurrent.Executors;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053087import java.util.concurrent.ScheduledExecutorService;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070088import java.util.concurrent.atomic.AtomicInteger;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053089import java.util.function.Consumer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070090import java.util.stream.Collectors;
91
alshabibb0285992016-03-28 23:30:37 -070092import static com.google.common.base.Strings.isNullOrEmpty;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053093import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
alshabibb0285992016-03-28 23:30:37 -070094import static org.onlab.util.Tools.get;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070095import static org.onlab.util.Tools.groupedThreads;
Ray Milkeyb5646e62018-10-16 11:42:18 -070096import static org.onosproject.store.OsgiPropertyConstants.ALLOW_EXTRANEOUS_GROUPS;
97import static org.onosproject.store.OsgiPropertyConstants.ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
98import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT;
99import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_DEFAULT;
100import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_THRESH;
101import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_THRESH_DEFAULT;
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700102import static org.slf4j.LoggerFactory.getLogger;
alshabib10580802015-02-18 18:30:33 -0800103
104/**
Saurav Das0fd79d92016-03-07 10:58:36 -0800105 * Manages inventory of group entries using distributed group stores from the
106 * storage service.
alshabib10580802015-02-18 18:30:33 -0800107 */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700108@Component(
109 immediate = true,
110 service = GroupStore.class,
111 property = {
Ray Milkey2d7bca12018-10-17 14:51:52 -0700112 GARBAGE_COLLECT + ":Boolean=" + GARBAGE_COLLECT_DEFAULT,
113 GARBAGE_COLLECT_THRESH + ":Integer=" + GARBAGE_COLLECT_THRESH_DEFAULT,
114 ALLOW_EXTRANEOUS_GROUPS + ":Boolean=" + ALLOW_EXTRANEOUS_GROUPS_DEFAULT
Ray Milkeyb5646e62018-10-16 11:42:18 -0700115 }
116)
alshabib10580802015-02-18 18:30:33 -0800117public class DistributedGroupStore
118 extends AbstractStore<GroupEvent, GroupStoreDelegate>
119 implements GroupStore {
120
121 private final Logger log = getLogger(getClass());
122
Saurav Das137f27f2018-06-11 17:02:31 -0700123 private static final int MAX_FAILED_ATTEMPTS = 3;
alshabibb0285992016-03-28 23:30:37 -0700124
alshabib10580802015-02-18 18:30:33 -0800125 private final int dummyId = 0xffffffff;
Yi Tsengfa394de2017-02-01 11:26:40 -0800126 private final GroupId dummyGroupId = new GroupId(dummyId);
alshabib10580802015-02-18 18:30:33 -0800127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700129 protected ClusterCommunicationService clusterCommunicator;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700132 protected ClusterService clusterService;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700135 protected StorageService storageService;
136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700138 protected MastershipService mastershipService;
139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibb0285992016-03-28 23:30:37 -0700141 protected ComponentConfigService cfgService;
142
Andrea Campanella5daa7c462020-03-13 12:04:23 +0100143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
144 protected DeviceService deviceService;
145
jaegonkim9477a9d2018-04-01 16:36:36 +0900146 // Guarantees enabling DriverService before enabling GroupStore
147 // (DriverService is used in serializing/de-serializing DefaultGroup)
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
jaegonkim9477a9d2018-04-01 16:36:36 +0900149 protected DriverService driverService;
150
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +0000151 private NodeId local;
152
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530153 private ScheduledExecutorService executor;
154 private Consumer<Status> statusChangeListener;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700155 // Per device group table with (device id + app cookie) as key
Madan Jampani0b847532016-03-03 13:44:15 -0800156 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700157 StoredGroupEntry> groupStoreEntriesByKey = null;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700158 // Per device group table with (device id + group id) as key
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700159 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
alshabibb0285992016-03-28 23:30:37 -0700160 groupEntriesById = new ConcurrentHashMap<>();
Madan Jampani0b847532016-03-03 13:44:15 -0800161 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700162 StoredGroupEntry> auditPendingReqQueue = null;
Frank Wange0eb5ce2016-07-01 18:21:25 +0800163 private MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry>
164 mapListener = new GroupStoreKeyMapListener();
alshabib10580802015-02-18 18:30:33 -0800165 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
166 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700167 private ExecutorService messageHandlingExecutor;
168 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
Saurav Das137f27f2018-06-11 17:02:31 -0700169
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700170 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
alshabib10580802015-02-18 18:30:33 -0800171
172 private final AtomicInteger groupIdGen = new AtomicInteger();
173
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700174 private KryoNamespace clusterMsgSerializer;
175
helenyrwua1c41152016-08-18 16:16:14 -0700176 private static Topic<GroupStoreMessage> groupTopic;
177
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700178 /** Enable group garbage collection. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700179 private boolean garbageCollect = GARBAGE_COLLECT_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700180
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700181 /** Number of rounds for group garbage collection. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700182 private int gcThresh = GARBAGE_COLLECT_THRESH_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700183
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700184 /** Allow groups in switches not installed by ONOS. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700185 private boolean allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700186
alshabib10580802015-02-18 18:30:33 -0800187 @Activate
sisubram4beea652017-08-09 10:38:14 +0000188 public void activate(ComponentContext context) {
alshabibb0285992016-03-28 23:30:37 -0700189 cfgService.registerProperties(getClass());
sisubram4beea652017-08-09 10:38:14 +0000190 modified(context);
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700191 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
alshabibb0285992016-03-28 23:30:37 -0700192 .register(KryoNamespaces.API)
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700193 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
pierventrea731bb82021-01-15 17:27:48 +0100194 .register(GroupStoreMessage.class,
alshabibb0285992016-03-28 23:30:37 -0700195 GroupStoreMessage.Type.class,
196 UpdateType.class,
197 GroupStoreMessageSubjects.class,
198 MultiValuedTimestamp.class,
199 GroupStoreKeyMapKey.class,
200 GroupStoreIdMapKey.class,
201 GroupStoreMapKey.class
202 );
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700203
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700204 clusterMsgSerializer = kryoBuilder.build("GroupStore");
205 Serializer serializer = Serializer.using(clusterMsgSerializer);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700206
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700207 messageHandlingExecutor = Executors.
208 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
209 groupedThreads("onos/store/group",
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700210 "message-handlers",
211 log));
Madan Jampani01e05fb2015-08-13 13:29:36 -0700212
213 clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
alshabibb0285992016-03-28 23:30:37 -0700214 clusterMsgSerializer::deserialize,
215 this::process,
216 messageHandlingExecutor);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700217
Madan Jampani0b847532016-03-03 13:44:15 -0800218 log.debug("Creating Consistent map onos-group-store-keymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700219
Madan Jampani0b847532016-03-03 13:44:15 -0800220 groupStoreEntriesByKey = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
221 .withName("onos-group-store-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700222 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700223 .build();
Frank Wange0eb5ce2016-07-01 18:21:25 +0800224 groupStoreEntriesByKey.addListener(mapListener);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700225 log.debug("Current size of groupstorekeymap:{}",
226 groupStoreEntriesByKey.size());
Thiago Santosfb73c502016-08-18 18:15:13 -0300227 synchronizeGroupStoreEntries();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700228
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530229 log.debug("Creating GroupStoreId Map From GroupStoreKey Map");
230 matchGroupEntries();
231 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/group", "store", log));
232 statusChangeListener = status -> {
233 if (status == Status.ACTIVE) {
234 executor.execute(this::matchGroupEntries);
235 }
236 };
237 groupStoreEntriesByKey.addStatusChangeListener(statusChangeListener);
238
Madan Jampani0b847532016-03-03 13:44:15 -0800239 log.debug("Creating Consistent map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700240
Madan Jampani0b847532016-03-03 13:44:15 -0800241 auditPendingReqQueue = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
242 .withName("onos-pending-group-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700243 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700244 .build();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700245 log.debug("Current size of pendinggroupkeymap:{}",
246 auditPendingReqQueue.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700247
helenyrwua1c41152016-08-18 16:16:14 -0700248 groupTopic = getOrCreateGroupTopic(serializer);
249 groupTopic.subscribe(this::processGroupMessage);
250
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +0000251 local = clusterService.getLocalNode().id();
252
alshabib10580802015-02-18 18:30:33 -0800253 log.info("Started");
254 }
255
256 @Deactivate
257 public void deactivate() {
Frank Wange0eb5ce2016-07-01 18:21:25 +0800258 groupStoreEntriesByKey.removeListener(mapListener);
alshabibb0285992016-03-28 23:30:37 -0700259 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700260 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
alshabib10580802015-02-18 18:30:33 -0800261 log.info("Stopped");
262 }
263
alshabibb0285992016-03-28 23:30:37 -0700264 @Modified
265 public void modified(ComponentContext context) {
266 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
267
268 try {
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700269 String s = get(properties, GARBAGE_COLLECT);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700270 garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT_DEFAULT : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700271
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700272 s = get(properties, GARBAGE_COLLECT_THRESH);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700273 gcThresh = isNullOrEmpty(s) ? GARBAGE_COLLECT_THRESH_DEFAULT : Integer.parseInt(s.trim());
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530274
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700275 s = get(properties, ALLOW_EXTRANEOUS_GROUPS);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700276 allowExtraneousGroups = isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_GROUPS_DEFAULT : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700277 } catch (Exception e) {
Ray Milkeyb5646e62018-10-16 11:42:18 -0700278 gcThresh = GARBAGE_COLLECT_THRESH_DEFAULT;
279 garbageCollect = GARBAGE_COLLECT_DEFAULT;
280 allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700281 }
282 }
283
helenyrwua1c41152016-08-18 16:16:14 -0700284 private Topic<GroupStoreMessage> getOrCreateGroupTopic(Serializer serializer) {
285 if (groupTopic == null) {
286 return storageService.getTopic("group-failover-notif", serializer);
287 } else {
288 return groupTopic;
289 }
Sho SHIMIZUa6285542017-01-12 15:08:24 -0800290 }
helenyrwua1c41152016-08-18 16:16:14 -0700291
alshabib10580802015-02-18 18:30:33 -0800292 /**
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530293 * Updating values of groupEntriesById.
294 */
295 private void matchGroupEntries() {
296 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupStoreEntriesByKey.asJavaMap().entrySet()) {
297 StoredGroupEntry group = entry.getValue();
298 getGroupIdTable(entry.getKey().deviceId()).put(group.id(), group);
299 }
300 }
301
Thiago Santosfb73c502016-08-18 18:15:13 -0300302
303 private void synchronizeGroupStoreEntries() {
304 Map<GroupStoreKeyMapKey, StoredGroupEntry> groupEntryMap = groupStoreEntriesByKey.asJavaMap();
305 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupEntryMap.entrySet()) {
Thiago Santosfb73c502016-08-18 18:15:13 -0300306 StoredGroupEntry value = entry.getValue();
Thiago Santosfb73c502016-08-18 18:15:13 -0300307 ConcurrentMap<GroupId, StoredGroupEntry> groupIdTable = getGroupIdTable(value.deviceId());
308 groupIdTable.put(value.id(), value);
309 }
310 }
311
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530312 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700313 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800314 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700315 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800316 */
Madan Jampani0b847532016-03-03 13:44:15 -0800317 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700318 getGroupStoreKeyMap() {
Madan Jampani0b847532016-03-03 13:44:15 -0800319 return groupStoreEntriesByKey.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800320 }
321
322 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700323 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800324 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700325 * @param deviceId identifier of the device
326 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800327 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700328 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700329 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800330 }
331
332 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700333 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800334 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700335 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800336 */
Madan Jampani0b847532016-03-03 13:44:15 -0800337 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700338 getPendingGroupKeyTable() {
Madan Jampani0b847532016-03-03 13:44:15 -0800339 return auditPendingReqQueue.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800340 }
341
342 /**
343 * Returns the extraneous group id table for specified device.
344 *
345 * @param deviceId identifier of the device
346 * @return Map representing group key table of given device.
347 */
348 private ConcurrentMap<GroupId, Group>
349 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700350 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800351 }
352
353 /**
354 * Returns the number of groups for the specified device in the store.
355 *
356 * @return number of groups for the specified device
357 */
358 @Override
359 public int getGroupCount(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700360 return (getGroups(deviceId) != null) ?
alshabibb0285992016-03-28 23:30:37 -0700361 Iterables.size(getGroups(deviceId)) : 0;
alshabib10580802015-02-18 18:30:33 -0800362 }
363
364 /**
365 * Returns the groups associated with a device.
366 *
367 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800368 * @return the group entries
369 */
370 @Override
371 public Iterable<Group> getGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800372 // Let ImmutableSet.copyOf do the type conversion
373 return ImmutableSet.copyOf(getStoredGroups(deviceId));
alshabib10580802015-02-18 18:30:33 -0800374 }
375
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700376 private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800377 NodeId master = mastershipService.getMasterFor(deviceId);
Andrea Campanella5daa7c462020-03-13 12:04:23 +0100378 if (master == null && deviceService.isAvailable(deviceId)) {
Charles Chanf4838a72015-12-07 18:13:45 -0800379 log.debug("Failed to getGroups: No master for {}", deviceId);
380 return Collections.emptySet();
381 }
382
383 Set<StoredGroupEntry> storedGroups = getGroupStoreKeyMap().values()
384 .stream()
385 .filter(input -> input.deviceId().equals(deviceId))
386 .collect(Collectors.toSet());
387 return ImmutableSet.copyOf(storedGroups);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700388 }
389
alshabib10580802015-02-18 18:30:33 -0800390 /**
391 * Returns the stored group entry.
392 *
alshabibb0285992016-03-28 23:30:37 -0700393 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800394 * @param appCookie the group key
alshabib10580802015-02-18 18:30:33 -0800395 * @return a group associated with the key
396 */
397 @Override
398 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700399 return getStoredGroupEntry(deviceId, appCookie);
400 }
401
402 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
403 GroupKey appCookie) {
404 return getGroupStoreKeyMap().get(new GroupStoreKeyMapKey(deviceId,
405 appCookie));
406 }
407
408 @Override
409 public Group getGroup(DeviceId deviceId, GroupId groupId) {
410 return getStoredGroupEntry(deviceId, groupId);
411 }
412
413 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
414 GroupId groupId) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700415 return getGroupIdTable(deviceId).get(groupId);
alshabib10580802015-02-18 18:30:33 -0800416 }
417
418 private int getFreeGroupIdValue(DeviceId deviceId) {
419 int freeId = groupIdGen.incrementAndGet();
420
421 while (true) {
Yi Tsengfa394de2017-02-01 11:26:40 -0800422 Group existing = getGroup(deviceId, new GroupId(freeId));
alshabib10580802015-02-18 18:30:33 -0800423 if (existing == null) {
424 existing = (
425 extraneousGroupEntriesById.get(deviceId) != null) ?
426 extraneousGroupEntriesById.get(deviceId).
Yi Tsengfa394de2017-02-01 11:26:40 -0800427 get(new GroupId(freeId)) :
alshabib10580802015-02-18 18:30:33 -0800428 null;
429 }
430 if (existing != null) {
431 freeId = groupIdGen.incrementAndGet();
432 } else {
433 break;
434 }
435 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700436 log.debug("getFreeGroupIdValue: Next Free ID is {}", freeId);
alshabib10580802015-02-18 18:30:33 -0800437 return freeId;
438 }
439
440 /**
441 * Stores a new group entry using the information from group description.
442 *
443 * @param groupDesc group description to be used to create group entry
444 */
445 @Override
446 public void storeGroupDescription(GroupDescription groupDesc) {
pierventree3e577f2022-02-10 19:18:13 +0100447 log.debug("In storeGroupDescription id {} for dev {}",
448 groupDesc.givenGroupId() != null ? "0x" + Integer.toHexString(groupDesc.givenGroupId()) : "N/A",
449 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800450 // Check if a group is existing with the same key
Saurav Das8a0732e2015-11-20 15:27:53 -0800451 Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
452 if (existingGroup != null) {
pierventree3e577f2022-02-10 19:18:13 +0100453 log.debug("Group already exists with the same key {} in dev:{} with id:{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800454 groupDesc.appCookie(), groupDesc.deviceId(),
455 Integer.toHexString(existingGroup.id().id()));
alshabib10580802015-02-18 18:30:33 -0800456 return;
457 }
458
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700459 // Check if group to be created by a remote instance
Madan Jampani175e8fd2015-05-20 14:10:45 -0700460 if (mastershipService.getLocalRole(groupDesc.deviceId()) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700461 log.debug("storeGroupDescription: Device {} local role is not MASTER",
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700462 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700463 if (mastershipService.getMasterFor(groupDesc.deviceId()) == null) {
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530464 log.debug("No Master for device {}..."
pierventree3e577f2022-02-10 19:18:13 +0100465 + "Queuing Group id {} ADD request",
466 groupDesc.deviceId(),
467 groupDesc.givenGroupId() != null ? "0x" + Integer.toHexString(groupDesc.givenGroupId()) : "N/A");
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530468 addToPendingAudit(groupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700469 return;
470 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700471 GroupStoreMessage groupOp = GroupStoreMessage.
472 createGroupAddRequestMsg(groupDesc.deviceId(),
473 groupDesc);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700474
Madan Jampani175e8fd2015-05-20 14:10:45 -0700475 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700476 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
477 clusterMsgSerializer::serialize,
478 mastershipService.getMasterFor(groupDesc.deviceId()))
479 .whenComplete((result, error) -> {
Madan Jampani175e8fd2015-05-20 14:10:45 -0700480 if (error != null) {
pierventree3e577f2022-02-10 19:18:13 +0100481 log.warn("Failed to send request to master: id {} to {}",
482 groupDesc.givenGroupId() != null ?
483 "0x" + Integer.toHexString(groupDesc.givenGroupId()) : "N/A",
alshabibb0285992016-03-28 23:30:37 -0700484 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700485 //TODO: Send Group operation failure event
486 } else {
pierventree3e577f2022-02-10 19:18:13 +0100487 log.debug("Sent Group operation id {} request for device {} "
alshabibb0285992016-03-28 23:30:37 -0700488 + "to remote MASTER {}",
pierventree3e577f2022-02-10 19:18:13 +0100489 groupDesc.givenGroupId() != null ?
490 "0x" + Integer.toHexString(groupDesc.givenGroupId()) : "N/A",
491 groupDesc.deviceId(), mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700492 }
493 });
alshabib10580802015-02-18 18:30:33 -0800494 return;
495 }
496
pierventree3e577f2022-02-10 19:18:13 +0100497 log.debug("Store group id {} for device {} is getting handled locally",
498 groupDesc.givenGroupId() != null ? "0x" + Integer.toHexString(groupDesc.givenGroupId()) : "N/A",
499 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800500 storeGroupDescriptionInternal(groupDesc);
501 }
502
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530503 private void addToPendingAudit(GroupDescription groupDesc) {
504 Integer groupIdVal = groupDesc.givenGroupId();
505 GroupId groupId = (groupIdVal != null) ? new GroupId(groupIdVal) : dummyGroupId;
506 addToPendingKeyTable(new DefaultGroup(groupId, groupDesc));
507 }
508
509 private void addToPendingKeyTable(StoredGroupEntry group) {
510 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
511 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
512 getPendingGroupKeyTable();
513 pendingKeyTable.put(new GroupStoreKeyMapKey(group.deviceId(),
514 group.appCookie()),
515 group);
516 }
517
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700518 private Group getMatchingExtraneousGroupbyId(DeviceId deviceId, Integer groupId) {
519 ConcurrentMap<GroupId, Group> extraneousMap =
520 extraneousGroupEntriesById.get(deviceId);
521 if (extraneousMap == null) {
522 return null;
523 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800524 return extraneousMap.get(new GroupId(groupId));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700525 }
526
527 private Group getMatchingExtraneousGroupbyBuckets(DeviceId deviceId,
528 GroupBuckets buckets) {
529 ConcurrentMap<GroupId, Group> extraneousMap =
530 extraneousGroupEntriesById.get(deviceId);
531 if (extraneousMap == null) {
532 return null;
533 }
534
alshabibb0285992016-03-28 23:30:37 -0700535 for (Group extraneousGroup : extraneousMap.values()) {
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700536 if (extraneousGroup.buckets().equals(buckets)) {
537 return extraneousGroup;
538 }
539 }
540 return null;
541 }
542
alshabib10580802015-02-18 18:30:33 -0800543 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
544 // Check if a group is existing with the same key
545 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
546 return;
547 }
548
pierventree3e577f2022-02-10 19:18:13 +0100549 synchronized (deviceAuditStatus) {
550 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
551 // Device group audit has not completed yet
552 // Add this group description to pending group key table
553 // Create a group entry object with Dummy Group ID
554 log.debug("storeGroupDescriptionInternal: Device {} AUDIT pending...Queuing Group id {} ADD request",
555 groupDesc.deviceId(),
556 groupDesc.givenGroupId() != null ?
557 "0x" + Integer.toHexString(groupDesc.givenGroupId()) : "N/A");
558 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
559 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
560 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
561 getPendingGroupKeyTable();
562 pendingKeyTable.put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
563 groupDesc.appCookie()),
564 group);
565 return;
566 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700567 }
568
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700569 Group matchingExtraneousGroup = null;
570 if (groupDesc.givenGroupId() != null) {
571 //Check if there is a extraneous group existing with the same Id
572 matchingExtraneousGroup = getMatchingExtraneousGroupbyId(
alshabibb0285992016-03-28 23:30:37 -0700573 groupDesc.deviceId(), groupDesc.givenGroupId());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700574 if (matchingExtraneousGroup != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800575 log.debug("storeGroupDescriptionInternal: Matching extraneous group "
alshabibb0285992016-03-28 23:30:37 -0700576 + "found in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700577 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800578 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700579 //Check if the group buckets matches with user provided buckets
580 if (matchingExtraneousGroup.buckets().equals(groupDesc.buckets())) {
581 //Group is already existing with the same buckets and Id
582 // Create a group entry object
Saurav Das0fd79d92016-03-07 10:58:36 -0800583 log.debug("storeGroupDescriptionInternal: Buckets also matching "
alshabibb0285992016-03-28 23:30:37 -0700584 + "in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700585 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800586 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700587 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700588 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700589 // Insert the newly created group entry into key and id maps
590 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700591 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
592 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700593 // Ensure it also inserted into group id based table to
594 // avoid any chances of duplication in group id generation
595 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700596 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700597 addOrUpdateGroupEntry(matchingExtraneousGroup);
598 removeExtraneousGroupEntry(matchingExtraneousGroup);
599 return;
600 } else {
601 //Group buckets are not matching. Update group
602 //with user provided buckets.
Saurav Das0fd79d92016-03-07 10:58:36 -0800603 log.debug("storeGroupDescriptionInternal: Buckets are not "
alshabibb0285992016-03-28 23:30:37 -0700604 + "matching in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700605 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800606 Integer.toHexString(groupDesc.givenGroupId()));
607 StoredGroupEntry modifiedGroup = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700608 matchingExtraneousGroup.id(), groupDesc);
Saurav Das0fd79d92016-03-07 10:58:36 -0800609 modifiedGroup.setState(GroupState.PENDING_UPDATE);
610 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700611 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
612 groupDesc.appCookie()), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800613 // Ensure it also inserted into group id based table to
614 // avoid any chances of duplication in group id generation
615 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700616 put(matchingExtraneousGroup.id(), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800617 removeExtraneousGroupEntry(matchingExtraneousGroup);
618 log.debug("storeGroupDescriptionInternal: Triggering Group "
alshabibb0285992016-03-28 23:30:37 -0700619 + "UPDATE request for {} in device {}",
Saurav Das0fd79d92016-03-07 10:58:36 -0800620 matchingExtraneousGroup.id(),
621 groupDesc.deviceId());
622 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, modifiedGroup));
623 return;
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700624 }
625 }
626 } else {
627 //Check if there is an extraneous group with user provided buckets
628 matchingExtraneousGroup = getMatchingExtraneousGroupbyBuckets(
alshabibb0285992016-03-28 23:30:37 -0700629 groupDesc.deviceId(), groupDesc.buckets());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700630 if (matchingExtraneousGroup != null) {
631 //Group is already existing with the same buckets.
632 //So reuse this group.
633 log.debug("storeGroupDescriptionInternal: Matching extraneous group found in Device {}",
634 groupDesc.deviceId());
635 //Create a group entry object
636 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700637 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700638 // Insert the newly created group entry into key and id maps
639 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700640 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
641 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700642 // Ensure it also inserted into group id based table to
643 // avoid any chances of duplication in group id generation
644 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700645 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700646 addOrUpdateGroupEntry(matchingExtraneousGroup);
647 removeExtraneousGroupEntry(matchingExtraneousGroup);
648 return;
649 } else {
650 //TODO: Check if there are any empty groups that can be used here
651 log.debug("storeGroupDescriptionInternal: No matching extraneous groups found in Device {}",
652 groupDesc.deviceId());
653 }
654 }
655
Saurav Das100e3b82015-04-30 11:12:10 -0700656 GroupId id = null;
657 if (groupDesc.givenGroupId() == null) {
658 // Get a new group identifier
Yi Tsengfa394de2017-02-01 11:26:40 -0800659 id = new GroupId(getFreeGroupIdValue(groupDesc.deviceId()));
Saurav Das100e3b82015-04-30 11:12:10 -0700660 } else {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800661 // we need to use the identifier passed in by caller, but check if
662 // already used
663 Group existing = getGroup(groupDesc.deviceId(),
Yi Tsengfa394de2017-02-01 11:26:40 -0800664 new GroupId(groupDesc.givenGroupId()));
Saurav Das8be4e3a2016-03-11 17:19:07 -0800665 if (existing != null) {
666 log.warn("Group already exists with the same id: 0x{} in dev:{} "
alshabibb0285992016-03-28 23:30:37 -0700667 + "but with different key: {} (request gkey: {})",
668 Integer.toHexString(groupDesc.givenGroupId()),
669 groupDesc.deviceId(),
670 existing.appCookie(),
671 groupDesc.appCookie());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800672 return;
673 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800674 id = new GroupId(groupDesc.givenGroupId());
Saurav Das100e3b82015-04-30 11:12:10 -0700675 }
alshabib10580802015-02-18 18:30:33 -0800676 // Create a group entry object
677 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700678 // Insert the newly created group entry into key and id maps
679 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700680 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
681 groupDesc.appCookie()), group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700682 // Ensure it also inserted into group id based table to
683 // avoid any chances of duplication in group id generation
684 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700685 put(id, group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700686 log.debug("storeGroupDescriptionInternal: Processing Group ADD request for Id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700687 id,
688 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800689 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
690 group));
691 }
692
693 /**
694 * Updates the existing group entry with the information
695 * from group description.
696 *
alshabibb0285992016-03-28 23:30:37 -0700697 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800698 * @param oldAppCookie the current group key
alshabibb0285992016-03-28 23:30:37 -0700699 * @param type update type
700 * @param newBuckets group buckets for updates
alshabib10580802015-02-18 18:30:33 -0800701 * @param newAppCookie optional new group key
702 */
703 @Override
704 public void updateGroupDescription(DeviceId deviceId,
705 GroupKey oldAppCookie,
706 UpdateType type,
707 GroupBuckets newBuckets,
708 GroupKey newAppCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700709 // Check if group update to be done by a remote instance
sangho52abe3a2015-05-05 14:13:34 -0700710 if (mastershipService.getMasterFor(deviceId) != null &&
711 mastershipService.getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700712 log.debug("updateGroupDescription: Device {} local role is not MASTER",
713 deviceId);
714 if (mastershipService.getMasterFor(deviceId) == null) {
715 log.error("No Master for device {}..."
pierventree3e577f2022-02-10 19:18:13 +0100716 + "Can not perform update group (appCookie {}) operation",
717 deviceId, newAppCookie);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700718 //TODO: Send Group operation failure event
719 return;
720 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700721 GroupStoreMessage groupOp = GroupStoreMessage.
722 createGroupUpdateRequestMsg(deviceId,
723 oldAppCookie,
724 type,
725 newBuckets,
726 newAppCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700727
Madan Jampani175e8fd2015-05-20 14:10:45 -0700728 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700729 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
730 clusterMsgSerializer::serialize,
731 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
732 if (error != null) {
733 log.warn("Failed to send request to master: {} to {}",
pierventree3e577f2022-02-10 19:18:13 +0100734 groupOp, mastershipService.getMasterFor(deviceId), error);
alshabibb0285992016-03-28 23:30:37 -0700735 }
736 //TODO: Send Group operation failure event
737 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700738 return;
739 }
pierventree3e577f2022-02-10 19:18:13 +0100740 log.debug("updateGroupDescription for device {} is getting handled locally (appCookie {})",
741 deviceId, newAppCookie);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700742 updateGroupDescriptionInternal(deviceId,
743 oldAppCookie,
744 type,
745 newBuckets,
746 newAppCookie);
747 }
748
749 private void updateGroupDescriptionInternal(DeviceId deviceId,
alshabibb0285992016-03-28 23:30:37 -0700750 GroupKey oldAppCookie,
751 UpdateType type,
752 GroupBuckets newBuckets,
753 GroupKey newAppCookie) {
alshabib10580802015-02-18 18:30:33 -0800754 // Check if a group is existing with the provided key
755 Group oldGroup = getGroup(deviceId, oldAppCookie);
756 if (oldGroup == null) {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800757 log.warn("updateGroupDescriptionInternal: Group not found...strange. "
pierventree3e577f2022-02-10 19:18:13 +0100758 + "GroupKey:{} DeviceId:{} newGroupKey:{}",
759 oldAppCookie, deviceId, newAppCookie);
alshabib10580802015-02-18 18:30:33 -0800760 return;
761 }
762
763 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
764 type,
765 newBuckets);
766 if (newBucketList != null) {
767 // Create a new group object from the old group
768 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
769 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
770 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
771 oldGroup.deviceId(),
772 oldGroup.type(),
773 updatedBuckets,
774 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700775 oldGroup.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800776 oldGroup.appId());
777 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
778 updatedGroupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700779 log.debug("updateGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_UPDATE",
alshabibb0285992016-03-28 23:30:37 -0700780 oldGroup.id(),
781 oldGroup.deviceId(),
782 oldGroup.state());
alshabib10580802015-02-18 18:30:33 -0800783 newGroup.setState(GroupState.PENDING_UPDATE);
784 newGroup.setLife(oldGroup.life());
785 newGroup.setPackets(oldGroup.packets());
786 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700787 //Update the group entry in groupkey based map.
788 //Update to groupid based map will happen in the
789 //groupkey based map update listener
pierventree3e577f2022-02-10 19:18:13 +0100790 log.debug("updateGroupDescriptionInternal with type {}: Group {} updated with buckets",
791 type, newGroup.id());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700792 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700793 put(new GroupStoreKeyMapKey(newGroup.deviceId(),
794 newGroup.appCookie()), newGroup);
alshabib10580802015-02-18 18:30:33 -0800795 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700796 } else {
pierventree3e577f2022-02-10 19:18:13 +0100797 log.warn("updateGroupDescriptionInternal with type {}: Group {} No "
798 + "change in the buckets in update", type, oldGroup.id());
alshabib10580802015-02-18 18:30:33 -0800799 }
800 }
801
802 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
803 UpdateType type,
804 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300805 if (type == UpdateType.SET) {
806 return buckets.buckets();
807 }
808
Victor Silvadf1eeae2016-08-12 15:28:57 -0300809 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
810 List<GroupBucket> updatedBucketList = new ArrayList<>();
alshabib10580802015-02-18 18:30:33 -0800811 boolean groupDescUpdated = false;
812
813 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300814 List<GroupBucket> newBuckets = buckets.buckets();
815
816 // Add old buckets that will not be updated and check if any will be updated.
817 for (GroupBucket oldBucket : oldBuckets) {
818 int newBucketIndex = newBuckets.indexOf(oldBucket);
819
820 if (newBucketIndex != -1) {
821 GroupBucket newBucket = newBuckets.get(newBucketIndex);
822 if (!newBucket.hasSameParameters(oldBucket)) {
823 // Bucket will be updated
824 groupDescUpdated = true;
825 }
826 } else {
827 // Old bucket will remain the same - add it.
828 updatedBucketList.add(oldBucket);
alshabib10580802015-02-18 18:30:33 -0800829 }
830 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300831
832 // Add all new buckets
833 updatedBucketList.addAll(newBuckets);
834 if (!oldBuckets.containsAll(newBuckets)) {
835 groupDescUpdated = true;
836 }
837
alshabib10580802015-02-18 18:30:33 -0800838 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300839 List<GroupBucket> bucketsToRemove = buckets.buckets();
840
841 // Check which old buckets should remain
842 for (GroupBucket oldBucket : oldBuckets) {
843 if (!bucketsToRemove.contains(oldBucket)) {
844 updatedBucketList.add(oldBucket);
845 } else {
alshabib10580802015-02-18 18:30:33 -0800846 groupDescUpdated = true;
847 }
848 }
849 }
850
851 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300852 return updatedBucketList;
alshabib10580802015-02-18 18:30:33 -0800853 } else {
854 return null;
855 }
856 }
857
858 /**
859 * Triggers deleting the existing group entry.
860 *
alshabibb0285992016-03-28 23:30:37 -0700861 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800862 * @param appCookie the group key
863 */
864 @Override
865 public void deleteGroupDescription(DeviceId deviceId,
866 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700867 // Check if group to be deleted by a remote instance
868 if (mastershipService.
869 getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700870 log.debug("deleteGroupDescription: Device {} local role is not MASTER",
871 deviceId);
872 if (mastershipService.getMasterFor(deviceId) == null) {
873 log.error("No Master for device {}..."
pierventree3e577f2022-02-10 19:18:13 +0100874 + "Can not perform delete group (appCookie {}) operation",
875 deviceId, appCookie);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700876 //TODO: Send Group operation failure event
877 return;
878 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700879 GroupStoreMessage groupOp = GroupStoreMessage.
880 createGroupDeleteRequestMsg(deviceId,
881 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700882
Madan Jampani175e8fd2015-05-20 14:10:45 -0700883 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700884 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
885 clusterMsgSerializer::serialize,
886 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
887 if (error != null) {
888 log.warn("Failed to send request to master: {} to {}",
pierventree3e577f2022-02-10 19:18:13 +0100889 groupOp, mastershipService.getMasterFor(deviceId), error);
alshabibb0285992016-03-28 23:30:37 -0700890 }
891 //TODO: Send Group operation failure event
892 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700893 return;
894 }
pierventree3e577f2022-02-10 19:18:13 +0100895 log.debug("deleteGroupDescription in device {} is getting handled locally (appCookie {})",
896 deviceId, appCookie);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700897 deleteGroupDescriptionInternal(deviceId, appCookie);
898 }
899
900 private void deleteGroupDescriptionInternal(DeviceId deviceId,
901 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800902 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700903 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800904 if (existing == null) {
905 return;
906 }
907
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700908 log.debug("deleteGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_DELETE",
alshabibb0285992016-03-28 23:30:37 -0700909 existing.id(),
910 existing.deviceId(),
911 existing.state());
pier1e80f362020-04-03 12:44:21 +0200912 // TODO is this really safe ?
alshabib10580802015-02-18 18:30:33 -0800913 synchronized (existing) {
914 existing.setState(GroupState.PENDING_DELETE);
Saurav Das80980c72016-03-23 11:22:49 -0700915 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700916 put(new GroupStoreKeyMapKey(existing.deviceId(), existing.appCookie()),
917 existing);
alshabib10580802015-02-18 18:30:33 -0800918 }
pierventree3e577f2022-02-10 19:18:13 +0100919 log.debug("deleteGroupDescriptionInternal: in device {} issuing GROUP_REMOVE_REQUESTED for {}",
920 deviceId, existing.id());
alshabib10580802015-02-18 18:30:33 -0800921 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
922 }
923
924 /**
pier1e80f362020-04-03 12:44:21 +0200925 * Updates the stats of an existing group entry.
926 *
927 * @param group the new stats
928 * @param existing the existing group
929 */
930 private void updateGroupEntryStatsInternal(Group group, StoredGroupEntry existing) {
931 for (GroupBucket bucket : group.buckets().buckets()) {
932 Optional<GroupBucket> matchingBucket =
933 existing.buckets().buckets()
934 .stream()
935 .filter((existingBucket) -> (existingBucket.equals(bucket)))
936 .findFirst();
937 if (matchingBucket.isPresent()) {
938 ((StoredGroupBucketEntry) matchingBucket.
939 get()).setPackets(bucket.packets());
940 ((StoredGroupBucketEntry) matchingBucket.
941 get()).setBytes(bucket.bytes());
942 } else {
943 log.warn("updateGroupEntryStatsInternal: No matching bucket {}" +
pierventree3e577f2022-02-10 19:18:13 +0100944 " to update stats for group {}", bucket, group.id());
pier1e80f362020-04-03 12:44:21 +0200945 }
946 }
947 existing.setLife(group.life());
948 existing.setPackets(group.packets());
949 existing.setBytes(group.bytes());
950 existing.setReferenceCount(group.referenceCount());
951 existing.setFailedRetryCount(0);
952 }
953
954 /**
alshabib10580802015-02-18 18:30:33 -0800955 * Stores a new group entry, or updates an existing entry.
956 *
957 * @param group group entry
958 */
959 @Override
960 public void addOrUpdateGroupEntry(Group group) {
961 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700962 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
963 group.id());
alshabib10580802015-02-18 18:30:33 -0800964 GroupEvent event = null;
965
966 if (existing != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800967 log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700968 group.id(),
969 group.deviceId());
pier1e80f362020-04-03 12:44:21 +0200970 // TODO is this really safe ?
alshabib10580802015-02-18 18:30:33 -0800971 synchronized (existing) {
pier1e80f362020-04-03 12:44:21 +0200972 // Update stats
973 updateGroupEntryStatsInternal(group, existing);
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700974 if ((existing.state() == GroupState.PENDING_ADD) ||
alshabibb0285992016-03-28 23:30:37 -0700975 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800976 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700977 existing.id(),
978 existing.deviceId(),
979 existing.state());
alshabib10580802015-02-18 18:30:33 -0800980 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700981 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800982 event = new GroupEvent(Type.GROUP_ADDED, existing);
983 } else {
Saurav Das0fd79d92016-03-07 10:58:36 -0800984 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700985 existing.id(),
986 existing.deviceId(),
987 GroupState.PENDING_UPDATE);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700988 existing.setState(GroupState.ADDED);
989 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800990 event = new GroupEvent(Type.GROUP_UPDATED, existing);
991 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700992 //Re-PUT map entries to trigger map update events
993 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700994 put(new GroupStoreKeyMapKey(existing.deviceId(),
995 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800996 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700997 } else {
pier1e80f362020-04-03 12:44:21 +0200998 log.warn("addOrUpdateGroupEntry: Group update {} " +
999 "happening for a non-existing entry in the map", group);
alshabib10580802015-02-18 18:30:33 -08001000 }
1001
pier1e80f362020-04-03 12:44:21 +02001002 // TODO if map is going to trigger event, is this one needed?
alshabib10580802015-02-18 18:30:33 -08001003 if (event != null) {
1004 notifyDelegate(event);
1005 }
1006 }
1007
1008 /**
pier1e80f362020-04-03 12:44:21 +02001009 * Updates stats of an existing entry.
1010 *
1011 * @param group group entry
1012 */
1013 private void updateGroupEntryStats(Group group) {
1014 // check if this new entry is an update to an existing entry
1015 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
1016 group.id());
1017 if (existing != null) {
1018 log.trace("updateStatsGroupEntry: updating group entry {} in device {}",
1019 group.id(),
1020 group.deviceId());
1021 // TODO is this really safe ?
1022 synchronized (existing) {
1023 // We don't make further update - it will be gone after the next update
1024 if (existing.state() == GroupState.PENDING_DELETE) {
1025 log.trace("updateStatsGroupEntry: group entry {} in device {} is in {} not updated",
1026 existing.id(),
1027 existing.deviceId(),
1028 existing.state());
1029 return;
1030 }
1031 // Update stats
1032 updateGroupEntryStatsInternal(group, existing);
1033 if ((existing.state() == GroupState.PENDING_ADD) ||
1034 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
1035 log.trace("updateStatsGroupEntry: group entry {} in device {} moving from {} to ADDED",
1036 existing.id(),
1037 existing.deviceId(),
1038 existing.state());
1039 existing.setState(GroupState.ADDED);
1040 existing.setIsGroupStateAddedFirstTime(true);
1041 } else {
1042 log.trace("updateStatsGroupEntry: group entry {} in device {} moving from {} to ADDED",
1043 existing.id(),
1044 existing.deviceId(),
1045 GroupState.PENDING_UPDATE);
1046 existing.setState(GroupState.ADDED);
1047 existing.setIsGroupStateAddedFirstTime(false);
1048 }
1049 //Re-PUT map entries to trigger map update events
1050 getGroupStoreKeyMap().
1051 put(new GroupStoreKeyMapKey(existing.deviceId(),
1052 existing.appCookie()), existing);
1053 }
1054 } else {
1055 log.warn("updateStatsGroupEntry: Group update {} "
1056 + "happening for a non-existing entry in the map", group);
1057 }
1058 }
1059
1060 /**
alshabib10580802015-02-18 18:30:33 -08001061 * Removes the group entry from store.
1062 *
1063 * @param group group entry
1064 */
1065 @Override
1066 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001067 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
1068 group.id());
alshabib10580802015-02-18 18:30:33 -08001069
1070 if (existing != null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001071 log.debug("removeGroupEntry: removing group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001072 group.id(),
1073 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001074 //Removal from groupid based map will happen in the
1075 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001076 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1077 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001078 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001079 } else {
1080 log.warn("removeGroupEntry for {} in device{} is "
alshabibb0285992016-03-28 23:30:37 -07001081 + "not existing in our maps",
1082 group.id(),
1083 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001084 }
1085 }
1086
Victor Silva4e8b7832016-08-17 17:11:19 -03001087 private void purgeGroupEntries(Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entries) {
1088 entries.forEach(entry -> {
1089 groupStoreEntriesByKey.remove(entry.getKey());
1090 });
1091 }
1092
alshabib10580802015-02-18 18:30:33 -08001093 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -08001094 public void purgeGroupEntry(DeviceId deviceId) {
Victor Silva4e8b7832016-08-17 17:11:19 -03001095 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entriesPendingRemove =
Charles Chan0c7c43b2016-01-14 17:39:20 -08001096 new HashSet<>();
1097
Madan Jampani0b847532016-03-03 13:44:15 -08001098 getGroupStoreKeyMap().entrySet().stream()
Charles Chan0c7c43b2016-01-14 17:39:20 -08001099 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Victor Silva4e8b7832016-08-17 17:11:19 -03001100 .forEach(entriesPendingRemove::add);
Charles Chan0c7c43b2016-01-14 17:39:20 -08001101
Victor Silva4e8b7832016-08-17 17:11:19 -03001102 purgeGroupEntries(entriesPendingRemove);
1103 }
1104
1105 @Override
Daniele Morocfd77402021-07-15 17:02:59 +02001106 public void purgeGroupEntries(DeviceId deviceId, ApplicationId appId) {
1107 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entriesPendingRemove =
1108 new HashSet<>();
1109
1110 getGroupStoreKeyMap().entrySet().stream()
1111 .filter(entry -> entry.getKey().deviceId().equals(deviceId) && entry.getValue().appId().equals(appId))
1112 .forEach(entriesPendingRemove::add);
1113
1114 purgeGroupEntries(entriesPendingRemove);
1115 }
1116
1117 @Override
Victor Silva4e8b7832016-08-17 17:11:19 -03001118 public void purgeGroupEntries() {
1119 purgeGroupEntries(getGroupStoreKeyMap().entrySet());
Charles Chan0c7c43b2016-01-14 17:39:20 -08001120 }
1121
1122 @Override
alshabib10580802015-02-18 18:30:33 -08001123 public void deviceInitialAuditCompleted(DeviceId deviceId,
1124 boolean completed) {
1125 synchronized (deviceAuditStatus) {
1126 if (completed) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001127 log.debug("AUDIT completed for device {}",
1128 deviceId);
alshabib10580802015-02-18 18:30:33 -08001129 deviceAuditStatus.put(deviceId, true);
1130 // Execute all pending group requests
pierventree3e577f2022-02-10 19:18:13 +01001131 List<StoredGroupEntry> pendingGroupRequests = getPendingGroupKeyTable().values()
1132 .stream()
1133 .filter(g -> g.deviceId().equals(deviceId))
1134 .collect(Collectors.toList());
1135 if (log.isDebugEnabled()) {
1136 List<String> pendingIds = pendingGroupRequests.stream()
1137 .map(GroupDescription::givenGroupId)
1138 .map(id -> id != null ? "0x" + Integer.toHexString(id) : "N/A")
1139 .collect(Collectors.toList());
1140 log.debug("processing pending group add requests for device {}: {}",
1141 deviceId, pendingIds);
1142 }
alshabibb0285992016-03-28 23:30:37 -07001143 for (Group group : pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -08001144 GroupDescription tmp = new DefaultGroupDescription(
1145 group.deviceId(),
1146 group.type(),
1147 group.buckets(),
1148 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -07001149 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -08001150 group.appId());
1151 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001152 getPendingGroupKeyTable().
alshabibb0285992016-03-28 23:30:37 -07001153 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001154 }
alshabib10580802015-02-18 18:30:33 -08001155 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001156 Boolean audited = deviceAuditStatus.get(deviceId);
1157 if (audited != null && audited) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001158 log.debug("Clearing AUDIT status for device {}", deviceId);
alshabib10580802015-02-18 18:30:33 -08001159 deviceAuditStatus.put(deviceId, false);
1160 }
1161 }
1162 }
1163 }
1164
1165 @Override
1166 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
1167 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001168 Boolean audited = deviceAuditStatus.get(deviceId);
1169 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -08001170 }
1171 }
1172
1173 @Override
1174 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
1175
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001176 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
1177 operation.groupId());
alshabib10580802015-02-18 18:30:33 -08001178
1179 if (existing == null) {
1180 log.warn("No group entry with ID {} found ", operation.groupId());
1181 return;
1182 }
1183
Saurav Das137f27f2018-06-11 17:02:31 -07001184 log.warn("groupOperationFailed: group operation {} failed in state {} "
alshabibb0285992016-03-28 23:30:37 -07001185 + "for group {} in device {} with code {}",
1186 operation.opType(),
Saurav Das137f27f2018-06-11 17:02:31 -07001187 existing.state(),
alshabibb0285992016-03-28 23:30:37 -07001188 existing.id(),
1189 existing.deviceId(),
1190 operation.failureCode());
Saurav Das0fd79d92016-03-07 10:58:36 -08001191 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001192 if (operation.buckets().equals(existing.buckets())) {
Saurav Dasc88d4662017-05-15 15:34:25 -07001193 if (existing.state() == GroupState.PENDING_ADD ||
1194 existing.state() == GroupState.PENDING_ADD_RETRY) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001195 log.info("GROUP_EXISTS: GroupID and Buckets match for group in pending "
alshabibb0285992016-03-28 23:30:37 -07001196 + "add state - moving to ADDED for group {} in device {}",
1197 existing.id(), deviceId);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001198 addOrUpdateGroupEntry(existing);
1199 return;
1200 } else {
Saurav Dasc88d4662017-05-15 15:34:25 -07001201 log.warn("GROUP_EXISTS: GroupId and Buckets match but existing"
1202 + "group in state: {}", existing.state());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001203 }
Saurav Dasc88d4662017-05-15 15:34:25 -07001204 } else {
1205 log.warn("GROUP EXISTS: Group ID matched but buckets did not. "
1206 + "Operation: {} Existing: {}", operation.buckets(),
1207 existing.buckets());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001208 }
Saurav Das0fd79d92016-03-07 10:58:36 -08001209 }
Saurav Das137f27f2018-06-11 17:02:31 -07001210 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.INVALID_GROUP) {
1211 existing.incrFailedRetryCount();
1212 if (existing.failedRetryCount() < MAX_FAILED_ATTEMPTS) {
1213 log.warn("Group {} programming failed {} of {} times in dev {}, "
1214 + "retrying ..", existing.id(),
1215 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1216 deviceId);
1217 return;
1218 }
1219 log.warn("Group {} programming failed {} of {} times in dev {}, "
1220 + "removing group from store", existing.id(),
1221 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1222 deviceId);
1223 // fall through to case
1224 }
1225
alshabib10580802015-02-18 18:30:33 -08001226 switch (operation.opType()) {
1227 case ADD:
Saurav Das137f27f2018-06-11 17:02:31 -07001228 if (existing.state() == GroupState.PENDING_ADD
1229 || existing.state() == GroupState.PENDING_ADD_RETRY) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001230 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
pierventree3e577f2022-02-10 19:18:13 +01001231 log.warn("groupOperationFailed: cleaning up "
alshabibb0285992016-03-28 23:30:37 -07001232 + "group {} from store in device {}....",
1233 existing.id(),
1234 existing.deviceId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001235 //Removal from groupid based map will happen in the
1236 //map update listener
1237 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1238 existing.appCookie()));
1239 }
alshabib10580802015-02-18 18:30:33 -08001240 break;
1241 case MODIFY:
1242 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
1243 break;
1244 case DELETE:
1245 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
1246 break;
1247 default:
1248 log.warn("Unknown group operation type {}", operation.opType());
1249 }
alshabib10580802015-02-18 18:30:33 -08001250 }
1251
1252 @Override
1253 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001254 log.debug("add/update extraneous group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001255 group.id(),
1256 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001257 ConcurrentMap<GroupId, Group> extraneousIdTable =
1258 getExtraneousGroupIdTable(group.deviceId());
1259 extraneousIdTable.put(group.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -07001260 // Don't remove the extraneous groups, instead re-use it when
1261 // a group request comes with the same set of buckets
alshabib10580802015-02-18 18:30:33 -08001262 }
1263
1264 @Override
1265 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001266 log.debug("remove extraneous group entry {} of device {} from store",
alshabibb0285992016-03-28 23:30:37 -07001267 group.id(),
1268 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001269 ConcurrentMap<GroupId, Group> extraneousIdTable =
1270 getExtraneousGroupIdTable(group.deviceId());
1271 extraneousIdTable.remove(group.id());
1272 }
1273
1274 @Override
1275 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
1276 // flatten and make iterator unmodifiable
1277 return FluentIterable.from(
1278 getExtraneousGroupIdTable(deviceId).values());
1279 }
1280
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001281 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001282 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001283 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001284 private class GroupStoreKeyMapListener implements
Madan Jampani0b847532016-03-03 13:44:15 -08001285 MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001286
1287 @Override
Madan Jampani0b847532016-03-03 13:44:15 -08001288 public void event(MapEvent<GroupStoreKeyMapKey, StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001289 GroupEvent groupEvent = null;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001290 GroupStoreKeyMapKey key = mapEvent.key();
Madan Jampani0b847532016-03-03 13:44:15 -08001291 StoredGroupEntry group = Versioned.valueOrNull(mapEvent.newValue());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001292 if ((key == null) && (group == null)) {
1293 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001294 + "event {} with null entry", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001295 return;
1296 } else if (group == null) {
1297 group = getGroupIdTable(key.deviceId()).values()
1298 .stream()
1299 .filter((storedGroup) -> (storedGroup.appCookie().equals(key.appCookie)))
Yuta HIGUCHI6e5f4702016-11-21 11:42:11 -08001300 .findFirst().orElse(null);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001301 if (group == null) {
1302 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001303 + "event {} with null entry... can not process", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001304 return;
1305 }
1306 }
1307 log.trace("received groupid map event {} for id {} in device {}",
1308 mapEvent.type(),
1309 group.id(),
jaegonkim68e080c2016-12-01 22:31:01 +09001310 (key != null ? key.deviceId() : null));
Madan Jampani0b847532016-03-03 13:44:15 -08001311 if (mapEvent.type() == MapEvent.Type.INSERT || mapEvent.type() == MapEvent.Type.UPDATE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001312 // Update the group ID table
1313 getGroupIdTable(group.deviceId()).put(group.id(), group);
Madan Jampani0b847532016-03-03 13:44:15 -08001314 StoredGroupEntry value = Versioned.valueOrNull(mapEvent.newValue());
1315 if (value.state() == Group.GroupState.ADDED) {
1316 if (value.isGroupStateAddedFirstTime()) {
1317 groupEvent = new GroupEvent(Type.GROUP_ADDED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001318 log.trace("Received first time GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001319 group.id(),
1320 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001321 } else {
Madan Jampani0b847532016-03-03 13:44:15 -08001322 groupEvent = new GroupEvent(Type.GROUP_UPDATED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001323 log.trace("Received following GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001324 group.id(),
1325 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001326 }
1327 }
Madan Jampani0b847532016-03-03 13:44:15 -08001328 } else if (mapEvent.type() == MapEvent.Type.REMOVE) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001329 groupEvent = new GroupEvent(Type.GROUP_REMOVED, group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001330 // Remove the entry from the group ID table
1331 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001332 }
1333
1334 if (groupEvent != null) {
1335 notifyDelegate(groupEvent);
1336 }
1337 }
1338 }
Madan Jampani01e05fb2015-08-13 13:29:36 -07001339
helenyrwua1c41152016-08-18 16:16:14 -07001340 private void processGroupMessage(GroupStoreMessage message) {
1341 if (message.type() == GroupStoreMessage.Type.FAILOVER) {
1342 // FIXME: groupStoreEntriesByKey inaccessible here
1343 getGroupIdTable(message.deviceId()).values()
1344 .stream()
1345 .filter((storedGroup) -> (storedGroup.appCookie().equals(message.appCookie())))
1346 .findFirst().ifPresent(group -> notifyDelegate(new GroupEvent(Type.GROUP_BUCKET_FAILOVER, group)));
1347 }
1348 }
1349
Madan Jampani01e05fb2015-08-13 13:29:36 -07001350 private void process(GroupStoreMessage groupOp) {
1351 log.debug("Received remote group operation {} request for device {}",
alshabibb0285992016-03-28 23:30:37 -07001352 groupOp.type(),
1353 groupOp.deviceId());
1354 if (!mastershipService.isLocalMaster(groupOp.deviceId())) {
pierventree3e577f2022-02-10 19:18:13 +01001355 log.warn("This node is not MASTER for device {} discard {}",
1356 groupOp.deviceId(), groupOp);
alshabibb0285992016-03-28 23:30:37 -07001357 return;
1358 }
1359 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
1360 storeGroupDescriptionInternal(groupOp.groupDesc());
1361 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
1362 updateGroupDescriptionInternal(groupOp.deviceId(),
1363 groupOp.appCookie(),
1364 groupOp.updateType(),
1365 groupOp.updateBuckets(),
1366 groupOp.newAppCookie());
1367 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
1368 deleteGroupDescriptionInternal(groupOp.deviceId(),
1369 groupOp.appCookie());
1370 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001371 }
1372
1373 /**
1374 * Flattened map key to be used to store group entries.
1375 */
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001376 protected static class GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001377 private final DeviceId deviceId;
1378
1379 public GroupStoreMapKey(DeviceId deviceId) {
1380 this.deviceId = deviceId;
1381 }
1382
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001383 public DeviceId deviceId() {
1384 return deviceId;
1385 }
1386
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001387 @Override
1388 public boolean equals(Object o) {
1389 if (this == o) {
1390 return true;
1391 }
1392 if (!(o instanceof GroupStoreMapKey)) {
1393 return false;
1394 }
1395 GroupStoreMapKey that = (GroupStoreMapKey) o;
1396 return this.deviceId.equals(that.deviceId);
1397 }
1398
1399 @Override
1400 public int hashCode() {
1401 int result = 17;
1402
1403 result = 31 * result + Objects.hash(this.deviceId);
1404
1405 return result;
1406 }
1407 }
1408
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001409 protected static class GroupStoreKeyMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001410 private final GroupKey appCookie;
alshabibb0285992016-03-28 23:30:37 -07001411
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001412 public GroupStoreKeyMapKey(DeviceId deviceId,
1413 GroupKey appCookie) {
1414 super(deviceId);
1415 this.appCookie = appCookie;
1416 }
1417
1418 @Override
1419 public boolean equals(Object o) {
1420 if (this == o) {
1421 return true;
1422 }
1423 if (!(o instanceof GroupStoreKeyMapKey)) {
1424 return false;
1425 }
1426 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
1427 return (super.equals(that) &&
1428 this.appCookie.equals(that.appCookie));
1429 }
1430
1431 @Override
1432 public int hashCode() {
1433 int result = 17;
1434
1435 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
1436
1437 return result;
1438 }
1439 }
1440
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001441 protected static class GroupStoreIdMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001442 private final GroupId groupId;
alshabibb0285992016-03-28 23:30:37 -07001443
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001444 public GroupStoreIdMapKey(DeviceId deviceId,
1445 GroupId groupId) {
1446 super(deviceId);
1447 this.groupId = groupId;
1448 }
1449
1450 @Override
1451 public boolean equals(Object o) {
1452 if (this == o) {
1453 return true;
1454 }
1455 if (!(o instanceof GroupStoreIdMapKey)) {
1456 return false;
1457 }
1458 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1459 return (super.equals(that) &&
1460 this.groupId.equals(that.groupId));
1461 }
1462
1463 @Override
1464 public int hashCode() {
1465 int result = 17;
1466
1467 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1468
1469 return result;
1470 }
1471 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001472
1473 @Override
1474 public void pushGroupMetrics(DeviceId deviceId,
1475 Collection<Group> groupEntries) {
1476 boolean deviceInitialAuditStatus =
1477 deviceInitialAuditStatus(deviceId);
1478 Set<Group> southboundGroupEntries =
1479 Sets.newHashSet(groupEntries);
1480 Set<StoredGroupEntry> storedGroupEntries =
1481 Sets.newHashSet(getStoredGroups(deviceId));
1482 Set<Group> extraneousStoredEntries =
1483 Sets.newHashSet(getExtraneousGroups(deviceId));
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001484 NodeId master;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001485
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001486 if (log.isTraceEnabled()) {
1487 log.trace("pushGroupMetrics: Displaying all ({}) southboundGroupEntries for device {}",
1488 southboundGroupEntries.size(),
1489 deviceId);
1490 for (Group group : southboundGroupEntries) {
1491 log.trace("Group {} in device {}", group, deviceId);
1492 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001493
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001494 log.trace("Displaying all ({}) stored group entries for device {}",
1495 storedGroupEntries.size(),
1496 deviceId);
1497 for (StoredGroupEntry group : storedGroupEntries) {
1498 log.trace("Stored Group {} for device {}", group, deviceId);
1499 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001500 }
1501
alshabibb0285992016-03-28 23:30:37 -07001502 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1503
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001504 // update stats
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001505 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001506 // Mastership change can occur during this iteration
1507 master = mastershipService.getMasterFor(deviceId);
1508 if (!Objects.equals(local, master)) {
1509 log.warn("Tried to update the group stats while the node was not the master");
1510 return;
1511 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001512 Group group = it2.next();
1513 if (storedGroupEntries.remove(group)) {
1514 // we both have the group, let's update some info then.
1515 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001516 group.id(), deviceId);
1517
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001518 groupAdded(group);
1519 it2.remove();
1520 }
1521 }
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001522
1523 // extraneous groups in the dataplane
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001524 for (Group group : southboundGroupEntries) {
1525 if (getGroup(group.deviceId(), group.id()) != null) {
1526 // There is a group existing with the same id
1527 // It is possible that group update is
1528 // in progress while we got a stale info from switch
1529 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001530 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001531 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001532 + "Group exists in ID based table while "
1533 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001534 }
1535 } else {
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001536 // Mastership change can occur during this iteration
1537 master = mastershipService.getMasterFor(deviceId);
1538 if (!Objects.equals(local, master)) {
1539 log.warn("Tried to process extraneous groups while the node was not the master");
1540 return;
1541 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001542 // there are groups in the switch that aren't in the store
1543 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001544 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001545 extraneousStoredEntries.remove(group);
Kavitha Alagesanc56cded2017-01-13 10:48:18 +05301546 if (allowExtraneousGroups) {
1547 extraneousGroup(group);
1548 } else {
1549 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
1550 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001551 }
1552 }
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001553
1554 // missing groups in the dataplane
Charles Chan07f15f22018-05-08 21:35:50 -07001555 for (StoredGroupEntry group : storedGroupEntries) {
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001556 // Mastership change can occur during this iteration
1557 master = mastershipService.getMasterFor(deviceId);
1558 if (!Objects.equals(local, master)) {
1559 log.warn("Tried to process missing groups while the node was not the master");
1560 return;
1561 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001562 // there are groups in the store that aren't in the switch
1563 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001564 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001565 groupMissing(group);
1566 }
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001567
1568 // extraneous groups in the store
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001569 for (Group group : extraneousStoredEntries) {
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001570 // Mastership change can occur during this iteration
1571 master = mastershipService.getMasterFor(deviceId);
1572 if (!Objects.equals(local, master)) {
1573 log.warn("Tried to process node extraneous groups while the node was not the master");
1574 return;
1575 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001576 // there are groups in the extraneous store that
1577 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001578 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001579 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001580 removeExtraneousGroupEntry(group);
1581 }
1582
1583 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001584 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001585 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001586 deviceInitialAuditCompleted(deviceId, true);
1587 }
1588 }
1589
helenyrwu89470f12016-08-12 13:18:10 -07001590 @Override
1591 public void notifyOfFailovers(Collection<Group> failoverGroups) {
helenyrwu89470f12016-08-12 13:18:10 -07001592 failoverGroups.forEach(group -> {
1593 if (group.type() == Group.Type.FAILOVER) {
helenyrwua1c41152016-08-18 16:16:14 -07001594 groupTopic.publish(GroupStoreMessage.createGroupFailoverMsg(
1595 group.deviceId(), group));
helenyrwu89470f12016-08-12 13:18:10 -07001596 }
1597 });
helenyrwu89470f12016-08-12 13:18:10 -07001598 }
1599
alshabibb0285992016-03-28 23:30:37 -07001600 private void garbageCollect(DeviceId deviceId,
1601 Set<Group> southboundGroupEntries,
1602 Set<StoredGroupEntry> storedGroupEntries) {
1603 if (!garbageCollect) {
1604 return;
1605 }
1606
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001607 NodeId master;
alshabibb0285992016-03-28 23:30:37 -07001608 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1609 while (it.hasNext()) {
Pier Luigi Ventred8a923c2020-02-20 11:25:31 +00001610 // Mastership change can occur during this iteration
1611 master = mastershipService.getMasterFor(deviceId);
1612 if (!Objects.equals(local, master)) {
1613 log.warn("Tried to run garbage collector while the node was not the master");
1614 return;
1615 }
alshabibb0285992016-03-28 23:30:37 -07001616 StoredGroupEntry group = it.next();
1617 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1618 log.debug("Garbage collecting group {} on {}", group, deviceId);
1619 deleteGroupDescription(deviceId, group.appCookie());
1620 southboundGroupEntries.remove(group);
1621 it.remove();
1622 }
1623 }
1624 }
1625
1626 private boolean checkGroupRefCount(Group group) {
1627 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1628 }
1629
Charles Chan07f15f22018-05-08 21:35:50 -07001630 private void groupMissing(StoredGroupEntry group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001631 switch (group.state()) {
1632 case PENDING_DELETE:
1633 log.debug("Group {} delete confirmation from device {}",
1634 group, group.deviceId());
1635 removeGroupEntry(group);
1636 break;
1637 case ADDED:
1638 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001639 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001640 case PENDING_UPDATE:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001641 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
Charles Chan07f15f22018-05-08 21:35:50 -07001642 group.id(),
1643 group.deviceId(),
1644 group.state());
1645 group.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001646 //Re-PUT map entries to trigger map update events
Charles Chan07f15f22018-05-08 21:35:50 -07001647 getGroupStoreKeyMap().put(new GroupStoreKeyMapKey(group.deviceId(), group.appCookie()), group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001648 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1649 group));
1650 break;
1651 default:
1652 log.debug("Group {} has not been installed.", group);
1653 break;
1654 }
1655 }
1656
1657 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001658 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001659 group, group.deviceId());
1660 addOrUpdateExtraneousGroupEntry(group);
1661 }
1662
1663 private void groupAdded(Group group) {
1664 log.trace("Group {} Added or Updated in device {}",
1665 group, group.deviceId());
pier1e80f362020-04-03 12:44:21 +02001666 updateGroupEntryStats(group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001667 }
alshabib10580802015-02-18 18:30:33 -08001668}