blob: 6e8b12663cb1fe137b485e8a1f850d31c89a7e22 [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;
alshabib10580802015-02-18 18:30:33 -080022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
alshabibb0285992016-03-28 23:30:37 -070025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
alshabib10580802015-02-18 18:30:33 -080029import org.apache.felix.scr.annotations.Service;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070030import org.onlab.util.KryoNamespace;
alshabibb0285992016-03-28 23:30:37 -070031import org.onosproject.cfg.ComponentConfigService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070032import org.onosproject.cluster.ClusterService;
Charles Chanf4838a72015-12-07 18:13:45 -080033import org.onosproject.cluster.NodeId;
alshabib10580802015-02-18 18:30:33 -080034import org.onosproject.core.GroupId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070035import org.onosproject.mastership.MastershipService;
alshabib10580802015-02-18 18:30:33 -080036import org.onosproject.net.DeviceId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070037import org.onosproject.net.MastershipRole;
jaegonkim9477a9d2018-04-01 16:36:36 +090038import org.onosproject.net.driver.DriverService;
alshabib10580802015-02-18 18:30:33 -080039import org.onosproject.net.group.DefaultGroup;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070040import org.onosproject.net.group.DefaultGroupBucket;
alshabib10580802015-02-18 18:30:33 -080041import org.onosproject.net.group.DefaultGroupDescription;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070042import org.onosproject.net.group.DefaultGroupKey;
alshabib10580802015-02-18 18:30:33 -080043import org.onosproject.net.group.Group;
44import org.onosproject.net.group.Group.GroupState;
45import org.onosproject.net.group.GroupBucket;
46import org.onosproject.net.group.GroupBuckets;
47import org.onosproject.net.group.GroupDescription;
48import org.onosproject.net.group.GroupEvent;
49import org.onosproject.net.group.GroupEvent.Type;
50import org.onosproject.net.group.GroupKey;
51import org.onosproject.net.group.GroupOperation;
52import org.onosproject.net.group.GroupStore;
53import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070054import org.onosproject.net.group.StoredGroupBucketEntry;
alshabib10580802015-02-18 18:30:33 -080055import org.onosproject.net.group.StoredGroupEntry;
56import org.onosproject.store.AbstractStore;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070057import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070058import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani0b847532016-03-03 13:44:15 -080059import org.onosproject.store.service.ConsistentMap;
60import org.onosproject.store.service.MapEvent;
61import org.onosproject.store.service.MapEventListener;
alshabibb0285992016-03-28 23:30:37 -070062import org.onosproject.store.service.MultiValuedTimestamp;
Madan Jampani0b847532016-03-03 13:44:15 -080063import org.onosproject.store.service.Serializer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070064import org.onosproject.store.service.StorageService;
helenyrwua1c41152016-08-18 16:16:14 -070065import org.onosproject.store.service.Topic;
Madan Jampani0b847532016-03-03 13:44:15 -080066import org.onosproject.store.service.Versioned;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053067import org.onosproject.store.service.DistributedPrimitive.Status;
alshabibb0285992016-03-28 23:30:37 -070068import org.osgi.service.component.ComponentContext;
alshabib10580802015-02-18 18:30:33 -080069import org.slf4j.Logger;
70
Jonathan Hart6ec029a2015-03-24 17:12:35 -070071import java.util.ArrayList;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070072import java.util.Collection;
Charles Chanf4838a72015-12-07 18:13:45 -080073import java.util.Collections;
alshabibb0285992016-03-28 23:30:37 -070074import java.util.Dictionary;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070075import java.util.HashMap;
Charles Chan0c7c43b2016-01-14 17:39:20 -080076import java.util.HashSet;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070077import java.util.Iterator;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070078import java.util.List;
Madan Jampani0b847532016-03-03 13:44:15 -080079import java.util.Map;
Charles Chan0c7c43b2016-01-14 17:39:20 -080080import java.util.Map.Entry;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070081import java.util.Objects;
Sho SHIMIZU30d639b2015-05-05 09:30:35 -070082import java.util.Optional;
alshabibb0285992016-03-28 23:30:37 -070083import java.util.Properties;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070084import java.util.Set;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070085import java.util.concurrent.ConcurrentHashMap;
86import java.util.concurrent.ConcurrentMap;
87import java.util.concurrent.ExecutorService;
88import java.util.concurrent.Executors;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053089import java.util.concurrent.ScheduledExecutorService;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070090import java.util.concurrent.atomic.AtomicInteger;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053091import java.util.function.Consumer;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070092import java.util.stream.Collectors;
93
alshabibb0285992016-03-28 23:30:37 -070094import static com.google.common.base.Strings.isNullOrEmpty;
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +053095import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
alshabibb0285992016-03-28 23:30:37 -070096import static org.onlab.util.Tools.get;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070097import static org.onlab.util.Tools.groupedThreads;
98import static org.slf4j.LoggerFactory.getLogger;
alshabib10580802015-02-18 18:30:33 -080099
100/**
Saurav Das0fd79d92016-03-07 10:58:36 -0800101 * Manages inventory of group entries using distributed group stores from the
102 * storage service.
alshabib10580802015-02-18 18:30:33 -0800103 */
104@Component(immediate = true)
105@Service
106public class DistributedGroupStore
107 extends AbstractStore<GroupEvent, GroupStoreDelegate>
108 implements GroupStore {
109
110 private final Logger log = getLogger(getClass());
111
alshabibb0285992016-03-28 23:30:37 -0700112 private static final boolean GARBAGE_COLLECT = false;
113 private static final int GC_THRESH = 6;
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530114 private static final boolean ALLOW_EXTRANEOUS_GROUPS = true;
Saurav Das137f27f2018-06-11 17:02:31 -0700115 private static final int MAX_FAILED_ATTEMPTS = 3;
alshabibb0285992016-03-28 23:30:37 -0700116
alshabib10580802015-02-18 18:30:33 -0800117 private final int dummyId = 0xffffffff;
Yi Tsengfa394de2017-02-01 11:26:40 -0800118 private final GroupId dummyGroupId = new GroupId(dummyId);
alshabib10580802015-02-18 18:30:33 -0800119
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected ClusterCommunicationService clusterCommunicator;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected ClusterService clusterService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700127 protected StorageService storageService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700130 protected MastershipService mastershipService;
131
alshabibb0285992016-03-28 23:30:37 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected ComponentConfigService cfgService;
134
jaegonkim9477a9d2018-04-01 16:36:36 +0900135 // Guarantees enabling DriverService before enabling GroupStore
136 // (DriverService is used in serializing/de-serializing DefaultGroup)
137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected DriverService driverService;
139
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530140 private ScheduledExecutorService executor;
141 private Consumer<Status> statusChangeListener;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700142 // Per device group table with (device id + app cookie) as key
Madan Jampani0b847532016-03-03 13:44:15 -0800143 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700144 StoredGroupEntry> groupStoreEntriesByKey = null;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700145 // Per device group table with (device id + group id) as key
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700146 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
alshabibb0285992016-03-28 23:30:37 -0700147 groupEntriesById = new ConcurrentHashMap<>();
Madan Jampani0b847532016-03-03 13:44:15 -0800148 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700149 StoredGroupEntry> auditPendingReqQueue = null;
Frank Wange0eb5ce2016-07-01 18:21:25 +0800150 private MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry>
151 mapListener = new GroupStoreKeyMapListener();
alshabib10580802015-02-18 18:30:33 -0800152 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
153 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700154 private ExecutorService messageHandlingExecutor;
155 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
Saurav Das137f27f2018-06-11 17:02:31 -0700156
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700157 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
alshabib10580802015-02-18 18:30:33 -0800158
159 private final AtomicInteger groupIdGen = new AtomicInteger();
160
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700161 private KryoNamespace clusterMsgSerializer;
162
helenyrwua1c41152016-08-18 16:16:14 -0700163 private static Topic<GroupStoreMessage> groupTopic;
164
alshabibb0285992016-03-28 23:30:37 -0700165 @Property(name = "garbageCollect", boolValue = GARBAGE_COLLECT,
166 label = "Enable group garbage collection")
167 private boolean garbageCollect = GARBAGE_COLLECT;
168
169 @Property(name = "gcThresh", intValue = GC_THRESH,
170 label = "Number of rounds for group garbage collection")
171 private int gcThresh = GC_THRESH;
172
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530173 @Property(name = "allowExtraneousGroups", boolValue = ALLOW_EXTRANEOUS_GROUPS,
174 label = "Allow groups in switches not installed by ONOS")
175 private boolean allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS;
alshabibb0285992016-03-28 23:30:37 -0700176
alshabib10580802015-02-18 18:30:33 -0800177 @Activate
sisubram4beea652017-08-09 10:38:14 +0000178 public void activate(ComponentContext context) {
alshabibb0285992016-03-28 23:30:37 -0700179 cfgService.registerProperties(getClass());
sisubram4beea652017-08-09 10:38:14 +0000180 modified(context);
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700181 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
alshabibb0285992016-03-28 23:30:37 -0700182 .register(KryoNamespaces.API)
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700183 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
alshabibb0285992016-03-28 23:30:37 -0700184 .register(DefaultGroup.class,
185 DefaultGroupBucket.class,
186 DefaultGroupDescription.class,
187 DefaultGroupKey.class,
188 GroupDescription.Type.class,
189 Group.GroupState.class,
190 GroupBuckets.class,
alshabibb0285992016-03-28 23:30:37 -0700191 GroupStoreMessage.class,
192 GroupStoreMessage.Type.class,
193 UpdateType.class,
194 GroupStoreMessageSubjects.class,
195 MultiValuedTimestamp.class,
196 GroupStoreKeyMapKey.class,
197 GroupStoreIdMapKey.class,
198 GroupStoreMapKey.class
199 );
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700200
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700201 clusterMsgSerializer = kryoBuilder.build("GroupStore");
202 Serializer serializer = Serializer.using(clusterMsgSerializer);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700203
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700204 messageHandlingExecutor = Executors.
205 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
206 groupedThreads("onos/store/group",
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700207 "message-handlers",
208 log));
Madan Jampani01e05fb2015-08-13 13:29:36 -0700209
210 clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
alshabibb0285992016-03-28 23:30:37 -0700211 clusterMsgSerializer::deserialize,
212 this::process,
213 messageHandlingExecutor);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700214
Madan Jampani0b847532016-03-03 13:44:15 -0800215 log.debug("Creating Consistent map onos-group-store-keymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700216
Madan Jampani0b847532016-03-03 13:44:15 -0800217 groupStoreEntriesByKey = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
218 .withName("onos-group-store-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700219 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700220 .build();
Frank Wange0eb5ce2016-07-01 18:21:25 +0800221 groupStoreEntriesByKey.addListener(mapListener);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700222 log.debug("Current size of groupstorekeymap:{}",
223 groupStoreEntriesByKey.size());
Thiago Santosfb73c502016-08-18 18:15:13 -0300224 synchronizeGroupStoreEntries();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700225
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530226 log.debug("Creating GroupStoreId Map From GroupStoreKey Map");
227 matchGroupEntries();
228 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/group", "store", log));
229 statusChangeListener = status -> {
230 if (status == Status.ACTIVE) {
231 executor.execute(this::matchGroupEntries);
232 }
233 };
234 groupStoreEntriesByKey.addStatusChangeListener(statusChangeListener);
235
Madan Jampani0b847532016-03-03 13:44:15 -0800236 log.debug("Creating Consistent map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700237
Madan Jampani0b847532016-03-03 13:44:15 -0800238 auditPendingReqQueue = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
239 .withName("onos-pending-group-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700240 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700241 .build();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700242 log.debug("Current size of pendinggroupkeymap:{}",
243 auditPendingReqQueue.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700244
helenyrwua1c41152016-08-18 16:16:14 -0700245 groupTopic = getOrCreateGroupTopic(serializer);
246 groupTopic.subscribe(this::processGroupMessage);
247
alshabib10580802015-02-18 18:30:33 -0800248 log.info("Started");
249 }
250
251 @Deactivate
252 public void deactivate() {
Frank Wange0eb5ce2016-07-01 18:21:25 +0800253 groupStoreEntriesByKey.removeListener(mapListener);
alshabibb0285992016-03-28 23:30:37 -0700254 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700255 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
alshabib10580802015-02-18 18:30:33 -0800256 log.info("Stopped");
257 }
258
alshabibb0285992016-03-28 23:30:37 -0700259 @Modified
260 public void modified(ComponentContext context) {
261 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
262
263 try {
264 String s = get(properties, "garbageCollect");
265 garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT : Boolean.parseBoolean(s.trim());
266
267 s = get(properties, "gcThresh");
268 gcThresh = isNullOrEmpty(s) ? GC_THRESH : Integer.parseInt(s.trim());
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530269
270 s = get(properties, "allowExtraneousGroups");
271 allowExtraneousGroups = isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_GROUPS : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700272 } catch (Exception e) {
273 gcThresh = GC_THRESH;
274 garbageCollect = GARBAGE_COLLECT;
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530275 allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS;
alshabibb0285992016-03-28 23:30:37 -0700276 }
277 }
278
helenyrwua1c41152016-08-18 16:16:14 -0700279 private Topic<GroupStoreMessage> getOrCreateGroupTopic(Serializer serializer) {
280 if (groupTopic == null) {
281 return storageService.getTopic("group-failover-notif", serializer);
282 } else {
283 return groupTopic;
284 }
Sho SHIMIZUa6285542017-01-12 15:08:24 -0800285 }
helenyrwua1c41152016-08-18 16:16:14 -0700286
alshabib10580802015-02-18 18:30:33 -0800287 /**
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530288 * Updating values of groupEntriesById.
289 */
290 private void matchGroupEntries() {
291 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupStoreEntriesByKey.asJavaMap().entrySet()) {
292 StoredGroupEntry group = entry.getValue();
293 getGroupIdTable(entry.getKey().deviceId()).put(group.id(), group);
294 }
295 }
296
Thiago Santosfb73c502016-08-18 18:15:13 -0300297
298 private void synchronizeGroupStoreEntries() {
299 Map<GroupStoreKeyMapKey, StoredGroupEntry> groupEntryMap = groupStoreEntriesByKey.asJavaMap();
300 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupEntryMap.entrySet()) {
Thiago Santosfb73c502016-08-18 18:15:13 -0300301 StoredGroupEntry value = entry.getValue();
Thiago Santosfb73c502016-08-18 18:15:13 -0300302 ConcurrentMap<GroupId, StoredGroupEntry> groupIdTable = getGroupIdTable(value.deviceId());
303 groupIdTable.put(value.id(), value);
304 }
305 }
306
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530307 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700308 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800309 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700310 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800311 */
Madan Jampani0b847532016-03-03 13:44:15 -0800312 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700313 getGroupStoreKeyMap() {
Madan Jampani0b847532016-03-03 13:44:15 -0800314 return groupStoreEntriesByKey.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800315 }
316
317 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700318 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800319 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700320 * @param deviceId identifier of the device
321 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800322 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700323 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700324 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800325 }
326
327 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700328 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800329 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700330 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800331 */
Madan Jampani0b847532016-03-03 13:44:15 -0800332 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700333 getPendingGroupKeyTable() {
Madan Jampani0b847532016-03-03 13:44:15 -0800334 return auditPendingReqQueue.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800335 }
336
337 /**
338 * Returns the extraneous group id table for specified device.
339 *
340 * @param deviceId identifier of the device
341 * @return Map representing group key table of given device.
342 */
343 private ConcurrentMap<GroupId, Group>
344 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700345 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800346 }
347
348 /**
349 * Returns the number of groups for the specified device in the store.
350 *
351 * @return number of groups for the specified device
352 */
353 @Override
354 public int getGroupCount(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700355 return (getGroups(deviceId) != null) ?
alshabibb0285992016-03-28 23:30:37 -0700356 Iterables.size(getGroups(deviceId)) : 0;
alshabib10580802015-02-18 18:30:33 -0800357 }
358
359 /**
360 * Returns the groups associated with a device.
361 *
362 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800363 * @return the group entries
364 */
365 @Override
366 public Iterable<Group> getGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800367 // Let ImmutableSet.copyOf do the type conversion
368 return ImmutableSet.copyOf(getStoredGroups(deviceId));
alshabib10580802015-02-18 18:30:33 -0800369 }
370
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700371 private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800372 NodeId master = mastershipService.getMasterFor(deviceId);
373 if (master == null) {
374 log.debug("Failed to getGroups: No master for {}", deviceId);
375 return Collections.emptySet();
376 }
377
378 Set<StoredGroupEntry> storedGroups = getGroupStoreKeyMap().values()
379 .stream()
380 .filter(input -> input.deviceId().equals(deviceId))
381 .collect(Collectors.toSet());
382 return ImmutableSet.copyOf(storedGroups);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700383 }
384
alshabib10580802015-02-18 18:30:33 -0800385 /**
386 * Returns the stored group entry.
387 *
alshabibb0285992016-03-28 23:30:37 -0700388 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800389 * @param appCookie the group key
alshabib10580802015-02-18 18:30:33 -0800390 * @return a group associated with the key
391 */
392 @Override
393 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700394 return getStoredGroupEntry(deviceId, appCookie);
395 }
396
397 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
398 GroupKey appCookie) {
399 return getGroupStoreKeyMap().get(new GroupStoreKeyMapKey(deviceId,
400 appCookie));
401 }
402
403 @Override
404 public Group getGroup(DeviceId deviceId, GroupId groupId) {
405 return getStoredGroupEntry(deviceId, groupId);
406 }
407
408 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
409 GroupId groupId) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700410 return getGroupIdTable(deviceId).get(groupId);
alshabib10580802015-02-18 18:30:33 -0800411 }
412
413 private int getFreeGroupIdValue(DeviceId deviceId) {
414 int freeId = groupIdGen.incrementAndGet();
415
416 while (true) {
Yi Tsengfa394de2017-02-01 11:26:40 -0800417 Group existing = getGroup(deviceId, new GroupId(freeId));
alshabib10580802015-02-18 18:30:33 -0800418 if (existing == null) {
419 existing = (
420 extraneousGroupEntriesById.get(deviceId) != null) ?
421 extraneousGroupEntriesById.get(deviceId).
Yi Tsengfa394de2017-02-01 11:26:40 -0800422 get(new GroupId(freeId)) :
alshabib10580802015-02-18 18:30:33 -0800423 null;
424 }
425 if (existing != null) {
426 freeId = groupIdGen.incrementAndGet();
427 } else {
428 break;
429 }
430 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700431 log.debug("getFreeGroupIdValue: Next Free ID is {}", freeId);
alshabib10580802015-02-18 18:30:33 -0800432 return freeId;
433 }
434
435 /**
436 * Stores a new group entry using the information from group description.
437 *
438 * @param groupDesc group description to be used to create group entry
439 */
440 @Override
441 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700442 log.debug("In storeGroupDescription");
alshabib10580802015-02-18 18:30:33 -0800443 // Check if a group is existing with the same key
Saurav Das8a0732e2015-11-20 15:27:53 -0800444 Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
445 if (existingGroup != null) {
Saurav Dasc568c342018-01-25 09:49:01 -0800446 log.debug("Group already exists with the same key {} in dev:{} with id:0x{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800447 groupDesc.appCookie(), groupDesc.deviceId(),
448 Integer.toHexString(existingGroup.id().id()));
alshabib10580802015-02-18 18:30:33 -0800449 return;
450 }
451
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700452 // Check if group to be created by a remote instance
Madan Jampani175e8fd2015-05-20 14:10:45 -0700453 if (mastershipService.getLocalRole(groupDesc.deviceId()) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700454 log.debug("storeGroupDescription: Device {} local role is not MASTER",
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700455 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700456 if (mastershipService.getMasterFor(groupDesc.deviceId()) == null) {
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530457 log.debug("No Master for device {}..."
458 + "Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700459 groupDesc.deviceId());
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530460 addToPendingAudit(groupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700461 return;
462 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700463 GroupStoreMessage groupOp = GroupStoreMessage.
464 createGroupAddRequestMsg(groupDesc.deviceId(),
465 groupDesc);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700466
Madan Jampani175e8fd2015-05-20 14:10:45 -0700467 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700468 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
469 clusterMsgSerializer::serialize,
470 mastershipService.getMasterFor(groupDesc.deviceId()))
471 .whenComplete((result, error) -> {
Madan Jampani175e8fd2015-05-20 14:10:45 -0700472 if (error != null) {
473 log.warn("Failed to send request to master: {} to {}",
alshabibb0285992016-03-28 23:30:37 -0700474 groupOp,
475 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700476 //TODO: Send Group operation failure event
477 } else {
478 log.debug("Sent Group operation request for device {} "
alshabibb0285992016-03-28 23:30:37 -0700479 + "to remote MASTER {}",
480 groupDesc.deviceId(),
481 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700482 }
483 });
alshabib10580802015-02-18 18:30:33 -0800484 return;
485 }
486
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700487 log.debug("Store group for device {} is getting handled locally",
488 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800489 storeGroupDescriptionInternal(groupDesc);
490 }
491
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530492 private void addToPendingAudit(GroupDescription groupDesc) {
493 Integer groupIdVal = groupDesc.givenGroupId();
494 GroupId groupId = (groupIdVal != null) ? new GroupId(groupIdVal) : dummyGroupId;
495 addToPendingKeyTable(new DefaultGroup(groupId, groupDesc));
496 }
497
498 private void addToPendingKeyTable(StoredGroupEntry group) {
499 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
500 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
501 getPendingGroupKeyTable();
502 pendingKeyTable.put(new GroupStoreKeyMapKey(group.deviceId(),
503 group.appCookie()),
504 group);
505 }
506
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700507 private Group getMatchingExtraneousGroupbyId(DeviceId deviceId, Integer groupId) {
508 ConcurrentMap<GroupId, Group> extraneousMap =
509 extraneousGroupEntriesById.get(deviceId);
510 if (extraneousMap == null) {
511 return null;
512 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800513 return extraneousMap.get(new GroupId(groupId));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700514 }
515
516 private Group getMatchingExtraneousGroupbyBuckets(DeviceId deviceId,
517 GroupBuckets buckets) {
518 ConcurrentMap<GroupId, Group> extraneousMap =
519 extraneousGroupEntriesById.get(deviceId);
520 if (extraneousMap == null) {
521 return null;
522 }
523
alshabibb0285992016-03-28 23:30:37 -0700524 for (Group extraneousGroup : extraneousMap.values()) {
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700525 if (extraneousGroup.buckets().equals(buckets)) {
526 return extraneousGroup;
527 }
528 }
529 return null;
530 }
531
alshabib10580802015-02-18 18:30:33 -0800532 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
533 // Check if a group is existing with the same key
534 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
535 return;
536 }
537
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700538 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
539 // Device group audit has not completed yet
540 // Add this group description to pending group key table
541 // Create a group entry object with Dummy Group ID
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700542 log.debug("storeGroupDescriptionInternal: Device {} AUDIT pending...Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700543 groupDesc.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700544 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
545 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
Madan Jampani0b847532016-03-03 13:44:15 -0800546 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700547 getPendingGroupKeyTable();
548 pendingKeyTable.put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
549 groupDesc.appCookie()),
550 group);
551 return;
552 }
553
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700554 Group matchingExtraneousGroup = null;
555 if (groupDesc.givenGroupId() != null) {
556 //Check if there is a extraneous group existing with the same Id
557 matchingExtraneousGroup = getMatchingExtraneousGroupbyId(
alshabibb0285992016-03-28 23:30:37 -0700558 groupDesc.deviceId(), groupDesc.givenGroupId());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700559 if (matchingExtraneousGroup != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800560 log.debug("storeGroupDescriptionInternal: Matching extraneous group "
alshabibb0285992016-03-28 23:30:37 -0700561 + "found in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700562 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800563 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700564 //Check if the group buckets matches with user provided buckets
565 if (matchingExtraneousGroup.buckets().equals(groupDesc.buckets())) {
566 //Group is already existing with the same buckets and Id
567 // Create a group entry object
Saurav Das0fd79d92016-03-07 10:58:36 -0800568 log.debug("storeGroupDescriptionInternal: Buckets also matching "
alshabibb0285992016-03-28 23:30:37 -0700569 + "in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700570 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800571 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700572 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700573 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700574 // Insert the newly created group entry into key and id maps
575 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700576 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
577 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700578 // Ensure it also inserted into group id based table to
579 // avoid any chances of duplication in group id generation
580 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700581 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700582 addOrUpdateGroupEntry(matchingExtraneousGroup);
583 removeExtraneousGroupEntry(matchingExtraneousGroup);
584 return;
585 } else {
586 //Group buckets are not matching. Update group
587 //with user provided buckets.
Saurav Das0fd79d92016-03-07 10:58:36 -0800588 log.debug("storeGroupDescriptionInternal: Buckets are not "
alshabibb0285992016-03-28 23:30:37 -0700589 + "matching in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700590 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800591 Integer.toHexString(groupDesc.givenGroupId()));
592 StoredGroupEntry modifiedGroup = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700593 matchingExtraneousGroup.id(), groupDesc);
Saurav Das0fd79d92016-03-07 10:58:36 -0800594 modifiedGroup.setState(GroupState.PENDING_UPDATE);
595 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700596 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
597 groupDesc.appCookie()), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800598 // Ensure it also inserted into group id based table to
599 // avoid any chances of duplication in group id generation
600 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700601 put(matchingExtraneousGroup.id(), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800602 removeExtraneousGroupEntry(matchingExtraneousGroup);
603 log.debug("storeGroupDescriptionInternal: Triggering Group "
alshabibb0285992016-03-28 23:30:37 -0700604 + "UPDATE request for {} in device {}",
Saurav Das0fd79d92016-03-07 10:58:36 -0800605 matchingExtraneousGroup.id(),
606 groupDesc.deviceId());
607 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, modifiedGroup));
608 return;
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700609 }
610 }
611 } else {
612 //Check if there is an extraneous group with user provided buckets
613 matchingExtraneousGroup = getMatchingExtraneousGroupbyBuckets(
alshabibb0285992016-03-28 23:30:37 -0700614 groupDesc.deviceId(), groupDesc.buckets());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700615 if (matchingExtraneousGroup != null) {
616 //Group is already existing with the same buckets.
617 //So reuse this group.
618 log.debug("storeGroupDescriptionInternal: Matching extraneous group found in Device {}",
619 groupDesc.deviceId());
620 //Create a group entry object
621 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700622 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700623 // Insert the newly created group entry into key and id maps
624 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700625 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
626 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700627 // Ensure it also inserted into group id based table to
628 // avoid any chances of duplication in group id generation
629 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700630 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700631 addOrUpdateGroupEntry(matchingExtraneousGroup);
632 removeExtraneousGroupEntry(matchingExtraneousGroup);
633 return;
634 } else {
635 //TODO: Check if there are any empty groups that can be used here
636 log.debug("storeGroupDescriptionInternal: No matching extraneous groups found in Device {}",
637 groupDesc.deviceId());
638 }
639 }
640
Saurav Das100e3b82015-04-30 11:12:10 -0700641 GroupId id = null;
642 if (groupDesc.givenGroupId() == null) {
643 // Get a new group identifier
Yi Tsengfa394de2017-02-01 11:26:40 -0800644 id = new GroupId(getFreeGroupIdValue(groupDesc.deviceId()));
Saurav Das100e3b82015-04-30 11:12:10 -0700645 } else {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800646 // we need to use the identifier passed in by caller, but check if
647 // already used
648 Group existing = getGroup(groupDesc.deviceId(),
Yi Tsengfa394de2017-02-01 11:26:40 -0800649 new GroupId(groupDesc.givenGroupId()));
Saurav Das8be4e3a2016-03-11 17:19:07 -0800650 if (existing != null) {
651 log.warn("Group already exists with the same id: 0x{} in dev:{} "
alshabibb0285992016-03-28 23:30:37 -0700652 + "but with different key: {} (request gkey: {})",
653 Integer.toHexString(groupDesc.givenGroupId()),
654 groupDesc.deviceId(),
655 existing.appCookie(),
656 groupDesc.appCookie());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800657 return;
658 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800659 id = new GroupId(groupDesc.givenGroupId());
Saurav Das100e3b82015-04-30 11:12:10 -0700660 }
alshabib10580802015-02-18 18:30:33 -0800661 // Create a group entry object
662 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700663 // Insert the newly created group entry into key and id maps
664 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700665 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
666 groupDesc.appCookie()), group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700667 // Ensure it also inserted into group id based table to
668 // avoid any chances of duplication in group id generation
669 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700670 put(id, group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700671 log.debug("storeGroupDescriptionInternal: Processing Group ADD request for Id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700672 id,
673 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800674 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
675 group));
676 }
677
678 /**
679 * Updates the existing group entry with the information
680 * from group description.
681 *
alshabibb0285992016-03-28 23:30:37 -0700682 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800683 * @param oldAppCookie the current group key
alshabibb0285992016-03-28 23:30:37 -0700684 * @param type update type
685 * @param newBuckets group buckets for updates
alshabib10580802015-02-18 18:30:33 -0800686 * @param newAppCookie optional new group key
687 */
688 @Override
689 public void updateGroupDescription(DeviceId deviceId,
690 GroupKey oldAppCookie,
691 UpdateType type,
692 GroupBuckets newBuckets,
693 GroupKey newAppCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700694 // Check if group update to be done by a remote instance
sangho52abe3a2015-05-05 14:13:34 -0700695 if (mastershipService.getMasterFor(deviceId) != null &&
696 mastershipService.getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700697 log.debug("updateGroupDescription: Device {} local role is not MASTER",
698 deviceId);
699 if (mastershipService.getMasterFor(deviceId) == null) {
700 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700701 + "Can not perform update group operation",
702 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700703 //TODO: Send Group operation failure event
704 return;
705 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700706 GroupStoreMessage groupOp = GroupStoreMessage.
707 createGroupUpdateRequestMsg(deviceId,
708 oldAppCookie,
709 type,
710 newBuckets,
711 newAppCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700712
Madan Jampani175e8fd2015-05-20 14:10:45 -0700713 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700714 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
715 clusterMsgSerializer::serialize,
716 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
717 if (error != null) {
718 log.warn("Failed to send request to master: {} to {}",
719 groupOp,
720 mastershipService.getMasterFor(deviceId), error);
721 }
722 //TODO: Send Group operation failure event
723 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700724 return;
725 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700726 log.debug("updateGroupDescription for device {} is getting handled locally",
727 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700728 updateGroupDescriptionInternal(deviceId,
729 oldAppCookie,
730 type,
731 newBuckets,
732 newAppCookie);
733 }
734
735 private void updateGroupDescriptionInternal(DeviceId deviceId,
alshabibb0285992016-03-28 23:30:37 -0700736 GroupKey oldAppCookie,
737 UpdateType type,
738 GroupBuckets newBuckets,
739 GroupKey newAppCookie) {
alshabib10580802015-02-18 18:30:33 -0800740 // Check if a group is existing with the provided key
741 Group oldGroup = getGroup(deviceId, oldAppCookie);
742 if (oldGroup == null) {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800743 log.warn("updateGroupDescriptionInternal: Group not found...strange. "
alshabibb0285992016-03-28 23:30:37 -0700744 + "GroupKey:{} DeviceId:{}", oldAppCookie, deviceId);
alshabib10580802015-02-18 18:30:33 -0800745 return;
746 }
747
748 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
749 type,
750 newBuckets);
751 if (newBucketList != null) {
752 // Create a new group object from the old group
753 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
754 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
755 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
756 oldGroup.deviceId(),
757 oldGroup.type(),
758 updatedBuckets,
759 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700760 oldGroup.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800761 oldGroup.appId());
762 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
763 updatedGroupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700764 log.debug("updateGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_UPDATE",
alshabibb0285992016-03-28 23:30:37 -0700765 oldGroup.id(),
766 oldGroup.deviceId(),
767 oldGroup.state());
alshabib10580802015-02-18 18:30:33 -0800768 newGroup.setState(GroupState.PENDING_UPDATE);
769 newGroup.setLife(oldGroup.life());
770 newGroup.setPackets(oldGroup.packets());
771 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700772 //Update the group entry in groupkey based map.
773 //Update to groupid based map will happen in the
774 //groupkey based map update listener
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700775 log.debug("updateGroupDescriptionInternal with type {}: Group updated with buckets",
776 type);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700777 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700778 put(new GroupStoreKeyMapKey(newGroup.deviceId(),
779 newGroup.appCookie()), newGroup);
alshabib10580802015-02-18 18:30:33 -0800780 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700781 } else {
782 log.warn("updateGroupDescriptionInternal with type {}: No "
alshabibb0285992016-03-28 23:30:37 -0700783 + "change in the buckets in update", type);
alshabib10580802015-02-18 18:30:33 -0800784 }
785 }
786
787 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
788 UpdateType type,
789 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300790 if (type == UpdateType.SET) {
791 return buckets.buckets();
792 }
793
Victor Silvadf1eeae2016-08-12 15:28:57 -0300794 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
795 List<GroupBucket> updatedBucketList = new ArrayList<>();
alshabib10580802015-02-18 18:30:33 -0800796 boolean groupDescUpdated = false;
797
798 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300799 List<GroupBucket> newBuckets = buckets.buckets();
800
801 // Add old buckets that will not be updated and check if any will be updated.
802 for (GroupBucket oldBucket : oldBuckets) {
803 int newBucketIndex = newBuckets.indexOf(oldBucket);
804
805 if (newBucketIndex != -1) {
806 GroupBucket newBucket = newBuckets.get(newBucketIndex);
807 if (!newBucket.hasSameParameters(oldBucket)) {
808 // Bucket will be updated
809 groupDescUpdated = true;
810 }
811 } else {
812 // Old bucket will remain the same - add it.
813 updatedBucketList.add(oldBucket);
alshabib10580802015-02-18 18:30:33 -0800814 }
815 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300816
817 // Add all new buckets
818 updatedBucketList.addAll(newBuckets);
819 if (!oldBuckets.containsAll(newBuckets)) {
820 groupDescUpdated = true;
821 }
822
alshabib10580802015-02-18 18:30:33 -0800823 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300824 List<GroupBucket> bucketsToRemove = buckets.buckets();
825
826 // Check which old buckets should remain
827 for (GroupBucket oldBucket : oldBuckets) {
828 if (!bucketsToRemove.contains(oldBucket)) {
829 updatedBucketList.add(oldBucket);
830 } else {
alshabib10580802015-02-18 18:30:33 -0800831 groupDescUpdated = true;
832 }
833 }
834 }
835
836 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300837 return updatedBucketList;
alshabib10580802015-02-18 18:30:33 -0800838 } else {
839 return null;
840 }
841 }
842
843 /**
844 * Triggers deleting the existing group entry.
845 *
alshabibb0285992016-03-28 23:30:37 -0700846 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800847 * @param appCookie the group key
848 */
849 @Override
850 public void deleteGroupDescription(DeviceId deviceId,
851 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700852 // Check if group to be deleted by a remote instance
853 if (mastershipService.
854 getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700855 log.debug("deleteGroupDescription: Device {} local role is not MASTER",
856 deviceId);
857 if (mastershipService.getMasterFor(deviceId) == null) {
858 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700859 + "Can not perform delete group operation",
860 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700861 //TODO: Send Group operation failure event
862 return;
863 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700864 GroupStoreMessage groupOp = GroupStoreMessage.
865 createGroupDeleteRequestMsg(deviceId,
866 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700867
Madan Jampani175e8fd2015-05-20 14:10:45 -0700868 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700869 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
870 clusterMsgSerializer::serialize,
871 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
872 if (error != null) {
873 log.warn("Failed to send request to master: {} to {}",
874 groupOp,
875 mastershipService.getMasterFor(deviceId), error);
876 }
877 //TODO: Send Group operation failure event
878 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700879 return;
880 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700881 log.debug("deleteGroupDescription in device {} is getting handled locally",
882 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700883 deleteGroupDescriptionInternal(deviceId, appCookie);
884 }
885
886 private void deleteGroupDescriptionInternal(DeviceId deviceId,
887 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800888 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700889 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800890 if (existing == null) {
891 return;
892 }
893
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700894 log.debug("deleteGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_DELETE",
alshabibb0285992016-03-28 23:30:37 -0700895 existing.id(),
896 existing.deviceId(),
897 existing.state());
alshabib10580802015-02-18 18:30:33 -0800898 synchronized (existing) {
899 existing.setState(GroupState.PENDING_DELETE);
Saurav Das80980c72016-03-23 11:22:49 -0700900 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700901 put(new GroupStoreKeyMapKey(existing.deviceId(), existing.appCookie()),
902 existing);
alshabib10580802015-02-18 18:30:33 -0800903 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700904 log.debug("deleteGroupDescriptionInternal: in device {} issuing GROUP_REMOVE_REQUESTED",
905 deviceId);
alshabib10580802015-02-18 18:30:33 -0800906 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
907 }
908
909 /**
910 * Stores a new group entry, or updates an existing entry.
911 *
912 * @param group group entry
913 */
914 @Override
915 public void addOrUpdateGroupEntry(Group group) {
916 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700917 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
918 group.id());
alshabib10580802015-02-18 18:30:33 -0800919 GroupEvent event = null;
920
921 if (existing != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800922 log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700923 group.id(),
924 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800925 synchronized (existing) {
alshabibb0285992016-03-28 23:30:37 -0700926 for (GroupBucket bucket : group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700927 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700928 existing.buckets().buckets()
alshabibb0285992016-03-28 23:30:37 -0700929 .stream()
930 .filter((existingBucket) -> (existingBucket.equals(bucket)))
931 .findFirst();
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700932 if (matchingBucket.isPresent()) {
933 ((StoredGroupBucketEntry) matchingBucket.
934 get()).setPackets(bucket.packets());
935 ((StoredGroupBucketEntry) matchingBucket.
936 get()).setBytes(bucket.bytes());
937 } else {
938 log.warn("addOrUpdateGroupEntry: No matching "
alshabibb0285992016-03-28 23:30:37 -0700939 + "buckets to update stats");
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700940 }
941 }
alshabib10580802015-02-18 18:30:33 -0800942 existing.setLife(group.life());
943 existing.setPackets(group.packets());
944 existing.setBytes(group.bytes());
alshabibb0285992016-03-28 23:30:37 -0700945 existing.setReferenceCount(group.referenceCount());
Saurav Das137f27f2018-06-11 17:02:31 -0700946 existing.setFailedRetryCount(0);
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700947 if ((existing.state() == GroupState.PENDING_ADD) ||
alshabibb0285992016-03-28 23:30:37 -0700948 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800949 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700950 existing.id(),
951 existing.deviceId(),
952 existing.state());
alshabib10580802015-02-18 18:30:33 -0800953 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700954 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800955 event = new GroupEvent(Type.GROUP_ADDED, existing);
956 } else {
Saurav Das0fd79d92016-03-07 10:58:36 -0800957 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700958 existing.id(),
959 existing.deviceId(),
960 GroupState.PENDING_UPDATE);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700961 existing.setState(GroupState.ADDED);
962 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800963 event = new GroupEvent(Type.GROUP_UPDATED, existing);
964 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700965 //Re-PUT map entries to trigger map update events
966 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700967 put(new GroupStoreKeyMapKey(existing.deviceId(),
968 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800969 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700970 } else {
971 log.warn("addOrUpdateGroupEntry: Group update "
alshabibb0285992016-03-28 23:30:37 -0700972 + "happening for a non-existing entry in the map");
alshabib10580802015-02-18 18:30:33 -0800973 }
974
Saurav Das137f27f2018-06-11 17:02:31 -0700975 // XXX if map is going to trigger event, is this one needed?
alshabib10580802015-02-18 18:30:33 -0800976 if (event != null) {
977 notifyDelegate(event);
978 }
979 }
980
981 /**
982 * Removes the group entry from store.
983 *
984 * @param group group entry
985 */
986 @Override
987 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700988 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
989 group.id());
alshabib10580802015-02-18 18:30:33 -0800990
991 if (existing != null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700992 log.debug("removeGroupEntry: removing group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700993 group.id(),
994 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700995 //Removal from groupid based map will happen in the
996 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700997 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
998 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800999 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001000 } else {
1001 log.warn("removeGroupEntry for {} in device{} is "
alshabibb0285992016-03-28 23:30:37 -07001002 + "not existing in our maps",
1003 group.id(),
1004 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001005 }
1006 }
1007
Victor Silva4e8b7832016-08-17 17:11:19 -03001008 private void purgeGroupEntries(Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entries) {
1009 entries.forEach(entry -> {
1010 groupStoreEntriesByKey.remove(entry.getKey());
1011 });
1012 }
1013
alshabib10580802015-02-18 18:30:33 -08001014 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -08001015 public void purgeGroupEntry(DeviceId deviceId) {
Victor Silva4e8b7832016-08-17 17:11:19 -03001016 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entriesPendingRemove =
Charles Chan0c7c43b2016-01-14 17:39:20 -08001017 new HashSet<>();
1018
Madan Jampani0b847532016-03-03 13:44:15 -08001019 getGroupStoreKeyMap().entrySet().stream()
Charles Chan0c7c43b2016-01-14 17:39:20 -08001020 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Victor Silva4e8b7832016-08-17 17:11:19 -03001021 .forEach(entriesPendingRemove::add);
Charles Chan0c7c43b2016-01-14 17:39:20 -08001022
Victor Silva4e8b7832016-08-17 17:11:19 -03001023 purgeGroupEntries(entriesPendingRemove);
1024 }
1025
1026 @Override
1027 public void purgeGroupEntries() {
1028 purgeGroupEntries(getGroupStoreKeyMap().entrySet());
Charles Chan0c7c43b2016-01-14 17:39:20 -08001029 }
1030
1031 @Override
alshabib10580802015-02-18 18:30:33 -08001032 public void deviceInitialAuditCompleted(DeviceId deviceId,
1033 boolean completed) {
1034 synchronized (deviceAuditStatus) {
1035 if (completed) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001036 log.debug("AUDIT completed for device {}",
1037 deviceId);
alshabib10580802015-02-18 18:30:33 -08001038 deviceAuditStatus.put(deviceId, true);
1039 // Execute all pending group requests
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001040 List<StoredGroupEntry> pendingGroupRequests =
1041 getPendingGroupKeyTable().values()
alshabibb0285992016-03-28 23:30:37 -07001042 .stream()
1043 .filter(g -> g.deviceId().equals(deviceId))
1044 .collect(Collectors.toList());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001045 log.debug("processing pending group add requests for device {} and number of pending requests {}",
alshabibb0285992016-03-28 23:30:37 -07001046 deviceId,
1047 pendingGroupRequests.size());
1048 for (Group group : pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -08001049 GroupDescription tmp = new DefaultGroupDescription(
1050 group.deviceId(),
1051 group.type(),
1052 group.buckets(),
1053 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -07001054 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -08001055 group.appId());
1056 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001057 getPendingGroupKeyTable().
alshabibb0285992016-03-28 23:30:37 -07001058 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001059 }
alshabib10580802015-02-18 18:30:33 -08001060 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001061 Boolean audited = deviceAuditStatus.get(deviceId);
1062 if (audited != null && audited) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001063 log.debug("Clearing AUDIT status for device {}", deviceId);
alshabib10580802015-02-18 18:30:33 -08001064 deviceAuditStatus.put(deviceId, false);
1065 }
1066 }
1067 }
1068 }
1069
1070 @Override
1071 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
1072 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001073 Boolean audited = deviceAuditStatus.get(deviceId);
1074 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -08001075 }
1076 }
1077
1078 @Override
1079 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
1080
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001081 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
1082 operation.groupId());
alshabib10580802015-02-18 18:30:33 -08001083
1084 if (existing == null) {
1085 log.warn("No group entry with ID {} found ", operation.groupId());
1086 return;
1087 }
1088
Saurav Das137f27f2018-06-11 17:02:31 -07001089 log.warn("groupOperationFailed: group operation {} failed in state {} "
alshabibb0285992016-03-28 23:30:37 -07001090 + "for group {} in device {} with code {}",
1091 operation.opType(),
Saurav Das137f27f2018-06-11 17:02:31 -07001092 existing.state(),
alshabibb0285992016-03-28 23:30:37 -07001093 existing.id(),
1094 existing.deviceId(),
1095 operation.failureCode());
Saurav Das0fd79d92016-03-07 10:58:36 -08001096 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001097 if (operation.buckets().equals(existing.buckets())) {
Saurav Dasc88d4662017-05-15 15:34:25 -07001098 if (existing.state() == GroupState.PENDING_ADD ||
1099 existing.state() == GroupState.PENDING_ADD_RETRY) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001100 log.info("GROUP_EXISTS: GroupID and Buckets match for group in pending "
alshabibb0285992016-03-28 23:30:37 -07001101 + "add state - moving to ADDED for group {} in device {}",
1102 existing.id(), deviceId);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001103 addOrUpdateGroupEntry(existing);
1104 return;
1105 } else {
Saurav Dasc88d4662017-05-15 15:34:25 -07001106 log.warn("GROUP_EXISTS: GroupId and Buckets match but existing"
1107 + "group in state: {}", existing.state());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001108 }
Saurav Dasc88d4662017-05-15 15:34:25 -07001109 } else {
1110 log.warn("GROUP EXISTS: Group ID matched but buckets did not. "
1111 + "Operation: {} Existing: {}", operation.buckets(),
1112 existing.buckets());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001113 }
Saurav Das0fd79d92016-03-07 10:58:36 -08001114 }
Saurav Das137f27f2018-06-11 17:02:31 -07001115 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.INVALID_GROUP) {
1116 existing.incrFailedRetryCount();
1117 if (existing.failedRetryCount() < MAX_FAILED_ATTEMPTS) {
1118 log.warn("Group {} programming failed {} of {} times in dev {}, "
1119 + "retrying ..", existing.id(),
1120 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1121 deviceId);
1122 return;
1123 }
1124 log.warn("Group {} programming failed {} of {} times in dev {}, "
1125 + "removing group from store", existing.id(),
1126 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1127 deviceId);
1128 // fall through to case
1129 }
1130
alshabib10580802015-02-18 18:30:33 -08001131 switch (operation.opType()) {
1132 case ADD:
Saurav Das137f27f2018-06-11 17:02:31 -07001133 if (existing.state() == GroupState.PENDING_ADD
1134 || existing.state() == GroupState.PENDING_ADD_RETRY) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001135 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
1136 log.warn("groupOperationFailed: cleaningup "
alshabibb0285992016-03-28 23:30:37 -07001137 + "group {} from store in device {}....",
1138 existing.id(),
1139 existing.deviceId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001140 //Removal from groupid based map will happen in the
1141 //map update listener
1142 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1143 existing.appCookie()));
1144 }
alshabib10580802015-02-18 18:30:33 -08001145 break;
1146 case MODIFY:
1147 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
1148 break;
1149 case DELETE:
1150 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
1151 break;
1152 default:
1153 log.warn("Unknown group operation type {}", operation.opType());
1154 }
alshabib10580802015-02-18 18:30:33 -08001155 }
1156
1157 @Override
1158 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001159 log.debug("add/update extraneous group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001160 group.id(),
1161 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001162 ConcurrentMap<GroupId, Group> extraneousIdTable =
1163 getExtraneousGroupIdTable(group.deviceId());
1164 extraneousIdTable.put(group.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -07001165 // Don't remove the extraneous groups, instead re-use it when
1166 // a group request comes with the same set of buckets
alshabib10580802015-02-18 18:30:33 -08001167 }
1168
1169 @Override
1170 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001171 log.debug("remove extraneous group entry {} of device {} from store",
alshabibb0285992016-03-28 23:30:37 -07001172 group.id(),
1173 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001174 ConcurrentMap<GroupId, Group> extraneousIdTable =
1175 getExtraneousGroupIdTable(group.deviceId());
1176 extraneousIdTable.remove(group.id());
1177 }
1178
1179 @Override
1180 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
1181 // flatten and make iterator unmodifiable
1182 return FluentIterable.from(
1183 getExtraneousGroupIdTable(deviceId).values());
1184 }
1185
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001186 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001187 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001188 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001189 private class GroupStoreKeyMapListener implements
Madan Jampani0b847532016-03-03 13:44:15 -08001190 MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001191
1192 @Override
Madan Jampani0b847532016-03-03 13:44:15 -08001193 public void event(MapEvent<GroupStoreKeyMapKey, StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001194 GroupEvent groupEvent = null;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001195 GroupStoreKeyMapKey key = mapEvent.key();
Madan Jampani0b847532016-03-03 13:44:15 -08001196 StoredGroupEntry group = Versioned.valueOrNull(mapEvent.newValue());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001197 if ((key == null) && (group == null)) {
1198 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001199 + "event {} with null entry", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001200 return;
1201 } else if (group == null) {
1202 group = getGroupIdTable(key.deviceId()).values()
1203 .stream()
1204 .filter((storedGroup) -> (storedGroup.appCookie().equals(key.appCookie)))
Yuta HIGUCHI6e5f4702016-11-21 11:42:11 -08001205 .findFirst().orElse(null);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001206 if (group == null) {
1207 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001208 + "event {} with null entry... can not process", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001209 return;
1210 }
1211 }
1212 log.trace("received groupid map event {} for id {} in device {}",
1213 mapEvent.type(),
1214 group.id(),
jaegonkim68e080c2016-12-01 22:31:01 +09001215 (key != null ? key.deviceId() : null));
Madan Jampani0b847532016-03-03 13:44:15 -08001216 if (mapEvent.type() == MapEvent.Type.INSERT || mapEvent.type() == MapEvent.Type.UPDATE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001217 // Update the group ID table
1218 getGroupIdTable(group.deviceId()).put(group.id(), group);
Madan Jampani0b847532016-03-03 13:44:15 -08001219 StoredGroupEntry value = Versioned.valueOrNull(mapEvent.newValue());
1220 if (value.state() == Group.GroupState.ADDED) {
1221 if (value.isGroupStateAddedFirstTime()) {
1222 groupEvent = new GroupEvent(Type.GROUP_ADDED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001223 log.trace("Received first time GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001224 group.id(),
1225 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001226 } else {
Madan Jampani0b847532016-03-03 13:44:15 -08001227 groupEvent = new GroupEvent(Type.GROUP_UPDATED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001228 log.trace("Received following GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001229 group.id(),
1230 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001231 }
1232 }
Madan Jampani0b847532016-03-03 13:44:15 -08001233 } else if (mapEvent.type() == MapEvent.Type.REMOVE) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001234 groupEvent = new GroupEvent(Type.GROUP_REMOVED, group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001235 // Remove the entry from the group ID table
1236 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001237 }
1238
1239 if (groupEvent != null) {
1240 notifyDelegate(groupEvent);
1241 }
1242 }
1243 }
Madan Jampani01e05fb2015-08-13 13:29:36 -07001244
helenyrwua1c41152016-08-18 16:16:14 -07001245 private void processGroupMessage(GroupStoreMessage message) {
1246 if (message.type() == GroupStoreMessage.Type.FAILOVER) {
1247 // FIXME: groupStoreEntriesByKey inaccessible here
1248 getGroupIdTable(message.deviceId()).values()
1249 .stream()
1250 .filter((storedGroup) -> (storedGroup.appCookie().equals(message.appCookie())))
1251 .findFirst().ifPresent(group -> notifyDelegate(new GroupEvent(Type.GROUP_BUCKET_FAILOVER, group)));
1252 }
1253 }
1254
Madan Jampani01e05fb2015-08-13 13:29:36 -07001255 private void process(GroupStoreMessage groupOp) {
1256 log.debug("Received remote group operation {} request for device {}",
alshabibb0285992016-03-28 23:30:37 -07001257 groupOp.type(),
1258 groupOp.deviceId());
1259 if (!mastershipService.isLocalMaster(groupOp.deviceId())) {
1260 log.warn("This node is not MASTER for device {}", groupOp.deviceId());
1261 return;
1262 }
1263 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
1264 storeGroupDescriptionInternal(groupOp.groupDesc());
1265 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
1266 updateGroupDescriptionInternal(groupOp.deviceId(),
1267 groupOp.appCookie(),
1268 groupOp.updateType(),
1269 groupOp.updateBuckets(),
1270 groupOp.newAppCookie());
1271 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
1272 deleteGroupDescriptionInternal(groupOp.deviceId(),
1273 groupOp.appCookie());
1274 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001275 }
1276
1277 /**
1278 * Flattened map key to be used to store group entries.
1279 */
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001280 protected static class GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001281 private final DeviceId deviceId;
1282
1283 public GroupStoreMapKey(DeviceId deviceId) {
1284 this.deviceId = deviceId;
1285 }
1286
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001287 public DeviceId deviceId() {
1288 return deviceId;
1289 }
1290
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001291 @Override
1292 public boolean equals(Object o) {
1293 if (this == o) {
1294 return true;
1295 }
1296 if (!(o instanceof GroupStoreMapKey)) {
1297 return false;
1298 }
1299 GroupStoreMapKey that = (GroupStoreMapKey) o;
1300 return this.deviceId.equals(that.deviceId);
1301 }
1302
1303 @Override
1304 public int hashCode() {
1305 int result = 17;
1306
1307 result = 31 * result + Objects.hash(this.deviceId);
1308
1309 return result;
1310 }
1311 }
1312
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001313 protected static class GroupStoreKeyMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001314 private final GroupKey appCookie;
alshabibb0285992016-03-28 23:30:37 -07001315
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001316 public GroupStoreKeyMapKey(DeviceId deviceId,
1317 GroupKey appCookie) {
1318 super(deviceId);
1319 this.appCookie = appCookie;
1320 }
1321
1322 @Override
1323 public boolean equals(Object o) {
1324 if (this == o) {
1325 return true;
1326 }
1327 if (!(o instanceof GroupStoreKeyMapKey)) {
1328 return false;
1329 }
1330 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
1331 return (super.equals(that) &&
1332 this.appCookie.equals(that.appCookie));
1333 }
1334
1335 @Override
1336 public int hashCode() {
1337 int result = 17;
1338
1339 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
1340
1341 return result;
1342 }
1343 }
1344
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001345 protected static class GroupStoreIdMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001346 private final GroupId groupId;
alshabibb0285992016-03-28 23:30:37 -07001347
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001348 public GroupStoreIdMapKey(DeviceId deviceId,
1349 GroupId groupId) {
1350 super(deviceId);
1351 this.groupId = groupId;
1352 }
1353
1354 @Override
1355 public boolean equals(Object o) {
1356 if (this == o) {
1357 return true;
1358 }
1359 if (!(o instanceof GroupStoreIdMapKey)) {
1360 return false;
1361 }
1362 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1363 return (super.equals(that) &&
1364 this.groupId.equals(that.groupId));
1365 }
1366
1367 @Override
1368 public int hashCode() {
1369 int result = 17;
1370
1371 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1372
1373 return result;
1374 }
1375 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001376
1377 @Override
1378 public void pushGroupMetrics(DeviceId deviceId,
1379 Collection<Group> groupEntries) {
1380 boolean deviceInitialAuditStatus =
1381 deviceInitialAuditStatus(deviceId);
1382 Set<Group> southboundGroupEntries =
1383 Sets.newHashSet(groupEntries);
1384 Set<StoredGroupEntry> storedGroupEntries =
1385 Sets.newHashSet(getStoredGroups(deviceId));
1386 Set<Group> extraneousStoredEntries =
1387 Sets.newHashSet(getExtraneousGroups(deviceId));
1388
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001389 if (log.isTraceEnabled()) {
1390 log.trace("pushGroupMetrics: Displaying all ({}) southboundGroupEntries for device {}",
1391 southboundGroupEntries.size(),
1392 deviceId);
1393 for (Group group : southboundGroupEntries) {
1394 log.trace("Group {} in device {}", group, deviceId);
1395 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001396
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001397 log.trace("Displaying all ({}) stored group entries for device {}",
1398 storedGroupEntries.size(),
1399 deviceId);
1400 for (StoredGroupEntry group : storedGroupEntries) {
1401 log.trace("Stored Group {} for device {}", group, deviceId);
1402 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001403 }
1404
alshabibb0285992016-03-28 23:30:37 -07001405 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1406
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001407 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
1408 Group group = it2.next();
1409 if (storedGroupEntries.remove(group)) {
1410 // we both have the group, let's update some info then.
1411 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001412 group.id(), deviceId);
1413
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001414 groupAdded(group);
1415 it2.remove();
1416 }
1417 }
1418 for (Group group : southboundGroupEntries) {
1419 if (getGroup(group.deviceId(), group.id()) != null) {
1420 // There is a group existing with the same id
1421 // It is possible that group update is
1422 // in progress while we got a stale info from switch
1423 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001424 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001425 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001426 + "Group exists in ID based table while "
1427 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001428 }
1429 } else {
1430 // there are groups in the switch that aren't in the store
1431 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001432 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001433 extraneousStoredEntries.remove(group);
Kavitha Alagesanc56cded2017-01-13 10:48:18 +05301434 if (allowExtraneousGroups) {
1435 extraneousGroup(group);
1436 } else {
1437 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
1438 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001439 }
1440 }
Charles Chan07f15f22018-05-08 21:35:50 -07001441 for (StoredGroupEntry group : storedGroupEntries) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001442 // there are groups in the store that aren't in the switch
1443 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001444 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001445 groupMissing(group);
1446 }
1447 for (Group group : extraneousStoredEntries) {
1448 // there are groups in the extraneous store that
1449 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001450 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001451 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001452 removeExtraneousGroupEntry(group);
1453 }
1454
1455 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001456 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001457 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001458 deviceInitialAuditCompleted(deviceId, true);
1459 }
1460 }
1461
helenyrwu89470f12016-08-12 13:18:10 -07001462 @Override
1463 public void notifyOfFailovers(Collection<Group> failoverGroups) {
helenyrwu89470f12016-08-12 13:18:10 -07001464 failoverGroups.forEach(group -> {
1465 if (group.type() == Group.Type.FAILOVER) {
helenyrwua1c41152016-08-18 16:16:14 -07001466 groupTopic.publish(GroupStoreMessage.createGroupFailoverMsg(
1467 group.deviceId(), group));
helenyrwu89470f12016-08-12 13:18:10 -07001468 }
1469 });
helenyrwu89470f12016-08-12 13:18:10 -07001470 }
1471
alshabibb0285992016-03-28 23:30:37 -07001472 private void garbageCollect(DeviceId deviceId,
1473 Set<Group> southboundGroupEntries,
1474 Set<StoredGroupEntry> storedGroupEntries) {
1475 if (!garbageCollect) {
1476 return;
1477 }
1478
1479 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1480 while (it.hasNext()) {
1481 StoredGroupEntry group = it.next();
1482 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1483 log.debug("Garbage collecting group {} on {}", group, deviceId);
1484 deleteGroupDescription(deviceId, group.appCookie());
1485 southboundGroupEntries.remove(group);
1486 it.remove();
1487 }
1488 }
1489 }
1490
1491 private boolean checkGroupRefCount(Group group) {
1492 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1493 }
1494
Charles Chan07f15f22018-05-08 21:35:50 -07001495 private void groupMissing(StoredGroupEntry group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001496 switch (group.state()) {
1497 case PENDING_DELETE:
1498 log.debug("Group {} delete confirmation from device {}",
1499 group, group.deviceId());
1500 removeGroupEntry(group);
1501 break;
1502 case ADDED:
1503 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001504 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001505 case PENDING_UPDATE:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001506 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
Charles Chan07f15f22018-05-08 21:35:50 -07001507 group.id(),
1508 group.deviceId(),
1509 group.state());
1510 group.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001511 //Re-PUT map entries to trigger map update events
Charles Chan07f15f22018-05-08 21:35:50 -07001512 getGroupStoreKeyMap().put(new GroupStoreKeyMapKey(group.deviceId(), group.appCookie()), group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001513 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1514 group));
1515 break;
1516 default:
1517 log.debug("Group {} has not been installed.", group);
1518 break;
1519 }
1520 }
1521
1522 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001523 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001524 group, group.deviceId());
1525 addOrUpdateExtraneousGroupEntry(group);
1526 }
1527
1528 private void groupAdded(Group group) {
1529 log.trace("Group {} Added or Updated in device {}",
1530 group, group.deviceId());
1531 addOrUpdateGroupEntry(group);
1532 }
alshabib10580802015-02-18 18:30:33 -08001533}