blob: 98a3f027c60137688ddf4bec8d07d538bab7902b [file] [log] [blame]
alshabib10580802015-02-18 18:30:33 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
alshabib10580802015-02-18 18:30:33 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.store.group.impl;
17
Jonathan Hart6ec029a2015-03-24 17:12:35 -070018import com.google.common.collect.FluentIterable;
Charles Chanf4838a72015-12-07 18:13:45 -080019import com.google.common.collect.ImmutableSet;
Jonathan Hart6ec029a2015-03-24 17:12:35 -070020import com.google.common.collect.Iterables;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070021import com.google.common.collect.Sets;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070022import org.onlab.util.KryoNamespace;
alshabibb0285992016-03-28 23:30:37 -070023import org.onosproject.cfg.ComponentConfigService;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070024import org.onosproject.cluster.ClusterService;
Charles Chanf4838a72015-12-07 18:13:45 -080025import org.onosproject.cluster.NodeId;
alshabib10580802015-02-18 18:30:33 -080026import org.onosproject.core.GroupId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070027import org.onosproject.mastership.MastershipService;
alshabib10580802015-02-18 18:30:33 -080028import org.onosproject.net.DeviceId;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070029import org.onosproject.net.MastershipRole;
jaegonkim9477a9d2018-04-01 16:36:36 +090030import org.onosproject.net.driver.DriverService;
alshabib10580802015-02-18 18:30:33 -080031import org.onosproject.net.group.DefaultGroup;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070032import org.onosproject.net.group.DefaultGroupBucket;
alshabib10580802015-02-18 18:30:33 -080033import org.onosproject.net.group.DefaultGroupDescription;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070034import org.onosproject.net.group.DefaultGroupKey;
alshabib10580802015-02-18 18:30:33 -080035import 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;
96import static org.slf4j.LoggerFactory.getLogger;
alshabib10580802015-02-18 18:30:33 -080097
98/**
Saurav Das0fd79d92016-03-07 10:58:36 -080099 * Manages inventory of group entries using distributed group stores from the
100 * storage service.
alshabib10580802015-02-18 18:30:33 -0800101 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700102@Component(immediate = true, service = GroupStore.class)
alshabib10580802015-02-18 18:30:33 -0800103public class DistributedGroupStore
104 extends AbstractStore<GroupEvent, GroupStoreDelegate>
105 implements GroupStore {
106
107 private final Logger log = getLogger(getClass());
108
alshabibb0285992016-03-28 23:30:37 -0700109 private static final boolean GARBAGE_COLLECT = false;
110 private static final int GC_THRESH = 6;
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530111 private static final boolean ALLOW_EXTRANEOUS_GROUPS = true;
Saurav Das137f27f2018-06-11 17:02:31 -0700112 private static final int MAX_FAILED_ATTEMPTS = 3;
alshabibb0285992016-03-28 23:30:37 -0700113
alshabib10580802015-02-18 18:30:33 -0800114 private final int dummyId = 0xffffffff;
Yi Tsengfa394de2017-02-01 11:26:40 -0800115 private final GroupId dummyGroupId = new GroupId(dummyId);
alshabib10580802015-02-18 18:30:33 -0800116
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700118 protected ClusterCommunicationService clusterCommunicator;
119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700121 protected ClusterService clusterService;
122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700124 protected StorageService storageService;
125
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700127 protected MastershipService mastershipService;
128
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibb0285992016-03-28 23:30:37 -0700130 protected ComponentConfigService cfgService;
131
jaegonkim9477a9d2018-04-01 16:36:36 +0900132 // Guarantees enabling DriverService before enabling GroupStore
133 // (DriverService is used in serializing/de-serializing DefaultGroup)
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
jaegonkim9477a9d2018-04-01 16:36:36 +0900135 protected DriverService driverService;
136
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530137 private ScheduledExecutorService executor;
138 private Consumer<Status> statusChangeListener;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700139 // Per device group table with (device id + app cookie) as key
Madan Jampani0b847532016-03-03 13:44:15 -0800140 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700141 StoredGroupEntry> groupStoreEntriesByKey = null;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700142 // Per device group table with (device id + group id) as key
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700143 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
alshabibb0285992016-03-28 23:30:37 -0700144 groupEntriesById = new ConcurrentHashMap<>();
Madan Jampani0b847532016-03-03 13:44:15 -0800145 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700146 StoredGroupEntry> auditPendingReqQueue = null;
Frank Wange0eb5ce2016-07-01 18:21:25 +0800147 private MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry>
148 mapListener = new GroupStoreKeyMapListener();
alshabib10580802015-02-18 18:30:33 -0800149 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
150 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700151 private ExecutorService messageHandlingExecutor;
152 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
Saurav Das137f27f2018-06-11 17:02:31 -0700153
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700154 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
alshabib10580802015-02-18 18:30:33 -0800155
156 private final AtomicInteger groupIdGen = new AtomicInteger();
157
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700158 private KryoNamespace clusterMsgSerializer;
159
helenyrwua1c41152016-08-18 16:16:14 -0700160 private static Topic<GroupStoreMessage> groupTopic;
161
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700162 //@Property(name = "garbageCollect", boolValue = GARBAGE_COLLECT,
163 // label = "Enable group garbage collection")
alshabibb0285992016-03-28 23:30:37 -0700164 private boolean garbageCollect = GARBAGE_COLLECT;
165
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700166 //@Property(name = "gcThresh", intValue = GC_THRESH,
167 // label = "Number of rounds for group garbage collection")
alshabibb0285992016-03-28 23:30:37 -0700168 private int gcThresh = GC_THRESH;
169
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700170 //@Property(name = "allowExtraneousGroups", boolValue = ALLOW_EXTRANEOUS_GROUPS,
171 // label = "Allow groups in switches not installed by ONOS")
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530172 private boolean allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS;
alshabibb0285992016-03-28 23:30:37 -0700173
alshabib10580802015-02-18 18:30:33 -0800174 @Activate
sisubram4beea652017-08-09 10:38:14 +0000175 public void activate(ComponentContext context) {
alshabibb0285992016-03-28 23:30:37 -0700176 cfgService.registerProperties(getClass());
sisubram4beea652017-08-09 10:38:14 +0000177 modified(context);
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700178 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
alshabibb0285992016-03-28 23:30:37 -0700179 .register(KryoNamespaces.API)
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700180 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
alshabibb0285992016-03-28 23:30:37 -0700181 .register(DefaultGroup.class,
182 DefaultGroupBucket.class,
183 DefaultGroupDescription.class,
184 DefaultGroupKey.class,
185 GroupDescription.Type.class,
186 Group.GroupState.class,
187 GroupBuckets.class,
alshabibb0285992016-03-28 23:30:37 -0700188 GroupStoreMessage.class,
189 GroupStoreMessage.Type.class,
190 UpdateType.class,
191 GroupStoreMessageSubjects.class,
192 MultiValuedTimestamp.class,
193 GroupStoreKeyMapKey.class,
194 GroupStoreIdMapKey.class,
195 GroupStoreMapKey.class
196 );
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700197
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700198 clusterMsgSerializer = kryoBuilder.build("GroupStore");
199 Serializer serializer = Serializer.using(clusterMsgSerializer);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700200
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700201 messageHandlingExecutor = Executors.
202 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
203 groupedThreads("onos/store/group",
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700204 "message-handlers",
205 log));
Madan Jampani01e05fb2015-08-13 13:29:36 -0700206
207 clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
alshabibb0285992016-03-28 23:30:37 -0700208 clusterMsgSerializer::deserialize,
209 this::process,
210 messageHandlingExecutor);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700211
Madan Jampani0b847532016-03-03 13:44:15 -0800212 log.debug("Creating Consistent map onos-group-store-keymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700213
Madan Jampani0b847532016-03-03 13:44:15 -0800214 groupStoreEntriesByKey = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
215 .withName("onos-group-store-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700216 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700217 .build();
Frank Wange0eb5ce2016-07-01 18:21:25 +0800218 groupStoreEntriesByKey.addListener(mapListener);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700219 log.debug("Current size of groupstorekeymap:{}",
220 groupStoreEntriesByKey.size());
Thiago Santosfb73c502016-08-18 18:15:13 -0300221 synchronizeGroupStoreEntries();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700222
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530223 log.debug("Creating GroupStoreId Map From GroupStoreKey Map");
224 matchGroupEntries();
225 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/group", "store", log));
226 statusChangeListener = status -> {
227 if (status == Status.ACTIVE) {
228 executor.execute(this::matchGroupEntries);
229 }
230 };
231 groupStoreEntriesByKey.addStatusChangeListener(statusChangeListener);
232
Madan Jampani0b847532016-03-03 13:44:15 -0800233 log.debug("Creating Consistent map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700234
Madan Jampani0b847532016-03-03 13:44:15 -0800235 auditPendingReqQueue = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
236 .withName("onos-pending-group-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700237 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700238 .build();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700239 log.debug("Current size of pendinggroupkeymap:{}",
240 auditPendingReqQueue.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700241
helenyrwua1c41152016-08-18 16:16:14 -0700242 groupTopic = getOrCreateGroupTopic(serializer);
243 groupTopic.subscribe(this::processGroupMessage);
244
alshabib10580802015-02-18 18:30:33 -0800245 log.info("Started");
246 }
247
248 @Deactivate
249 public void deactivate() {
Frank Wange0eb5ce2016-07-01 18:21:25 +0800250 groupStoreEntriesByKey.removeListener(mapListener);
alshabibb0285992016-03-28 23:30:37 -0700251 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700252 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
alshabib10580802015-02-18 18:30:33 -0800253 log.info("Stopped");
254 }
255
alshabibb0285992016-03-28 23:30:37 -0700256 @Modified
257 public void modified(ComponentContext context) {
258 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
259
260 try {
261 String s = get(properties, "garbageCollect");
262 garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT : Boolean.parseBoolean(s.trim());
263
264 s = get(properties, "gcThresh");
265 gcThresh = isNullOrEmpty(s) ? GC_THRESH : Integer.parseInt(s.trim());
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530266
267 s = get(properties, "allowExtraneousGroups");
268 allowExtraneousGroups = isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_GROUPS : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700269 } catch (Exception e) {
270 gcThresh = GC_THRESH;
271 garbageCollect = GARBAGE_COLLECT;
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530272 allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS;
alshabibb0285992016-03-28 23:30:37 -0700273 }
274 }
275
helenyrwua1c41152016-08-18 16:16:14 -0700276 private Topic<GroupStoreMessage> getOrCreateGroupTopic(Serializer serializer) {
277 if (groupTopic == null) {
278 return storageService.getTopic("group-failover-notif", serializer);
279 } else {
280 return groupTopic;
281 }
Sho SHIMIZUa6285542017-01-12 15:08:24 -0800282 }
helenyrwua1c41152016-08-18 16:16:14 -0700283
alshabib10580802015-02-18 18:30:33 -0800284 /**
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530285 * Updating values of groupEntriesById.
286 */
287 private void matchGroupEntries() {
288 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupStoreEntriesByKey.asJavaMap().entrySet()) {
289 StoredGroupEntry group = entry.getValue();
290 getGroupIdTable(entry.getKey().deviceId()).put(group.id(), group);
291 }
292 }
293
Thiago Santosfb73c502016-08-18 18:15:13 -0300294
295 private void synchronizeGroupStoreEntries() {
296 Map<GroupStoreKeyMapKey, StoredGroupEntry> groupEntryMap = groupStoreEntriesByKey.asJavaMap();
297 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupEntryMap.entrySet()) {
Thiago Santosfb73c502016-08-18 18:15:13 -0300298 StoredGroupEntry value = entry.getValue();
Thiago Santosfb73c502016-08-18 18:15:13 -0300299 ConcurrentMap<GroupId, StoredGroupEntry> groupIdTable = getGroupIdTable(value.deviceId());
300 groupIdTable.put(value.id(), value);
301 }
302 }
303
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530304 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700305 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800306 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700307 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800308 */
Madan Jampani0b847532016-03-03 13:44:15 -0800309 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700310 getGroupStoreKeyMap() {
Madan Jampani0b847532016-03-03 13:44:15 -0800311 return groupStoreEntriesByKey.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800312 }
313
314 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700315 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800316 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700317 * @param deviceId identifier of the device
318 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800319 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700320 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700321 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800322 }
323
324 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700325 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800326 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700327 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800328 */
Madan Jampani0b847532016-03-03 13:44:15 -0800329 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700330 getPendingGroupKeyTable() {
Madan Jampani0b847532016-03-03 13:44:15 -0800331 return auditPendingReqQueue.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800332 }
333
334 /**
335 * Returns the extraneous group id table for specified device.
336 *
337 * @param deviceId identifier of the device
338 * @return Map representing group key table of given device.
339 */
340 private ConcurrentMap<GroupId, Group>
341 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700342 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800343 }
344
345 /**
346 * Returns the number of groups for the specified device in the store.
347 *
348 * @return number of groups for the specified device
349 */
350 @Override
351 public int getGroupCount(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700352 return (getGroups(deviceId) != null) ?
alshabibb0285992016-03-28 23:30:37 -0700353 Iterables.size(getGroups(deviceId)) : 0;
alshabib10580802015-02-18 18:30:33 -0800354 }
355
356 /**
357 * Returns the groups associated with a device.
358 *
359 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800360 * @return the group entries
361 */
362 @Override
363 public Iterable<Group> getGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800364 // Let ImmutableSet.copyOf do the type conversion
365 return ImmutableSet.copyOf(getStoredGroups(deviceId));
alshabib10580802015-02-18 18:30:33 -0800366 }
367
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700368 private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800369 NodeId master = mastershipService.getMasterFor(deviceId);
370 if (master == null) {
371 log.debug("Failed to getGroups: No master for {}", deviceId);
372 return Collections.emptySet();
373 }
374
375 Set<StoredGroupEntry> storedGroups = getGroupStoreKeyMap().values()
376 .stream()
377 .filter(input -> input.deviceId().equals(deviceId))
378 .collect(Collectors.toSet());
379 return ImmutableSet.copyOf(storedGroups);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700380 }
381
alshabib10580802015-02-18 18:30:33 -0800382 /**
383 * Returns the stored group entry.
384 *
alshabibb0285992016-03-28 23:30:37 -0700385 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800386 * @param appCookie the group key
alshabib10580802015-02-18 18:30:33 -0800387 * @return a group associated with the key
388 */
389 @Override
390 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700391 return getStoredGroupEntry(deviceId, appCookie);
392 }
393
394 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
395 GroupKey appCookie) {
396 return getGroupStoreKeyMap().get(new GroupStoreKeyMapKey(deviceId,
397 appCookie));
398 }
399
400 @Override
401 public Group getGroup(DeviceId deviceId, GroupId groupId) {
402 return getStoredGroupEntry(deviceId, groupId);
403 }
404
405 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
406 GroupId groupId) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700407 return getGroupIdTable(deviceId).get(groupId);
alshabib10580802015-02-18 18:30:33 -0800408 }
409
410 private int getFreeGroupIdValue(DeviceId deviceId) {
411 int freeId = groupIdGen.incrementAndGet();
412
413 while (true) {
Yi Tsengfa394de2017-02-01 11:26:40 -0800414 Group existing = getGroup(deviceId, new GroupId(freeId));
alshabib10580802015-02-18 18:30:33 -0800415 if (existing == null) {
416 existing = (
417 extraneousGroupEntriesById.get(deviceId) != null) ?
418 extraneousGroupEntriesById.get(deviceId).
Yi Tsengfa394de2017-02-01 11:26:40 -0800419 get(new GroupId(freeId)) :
alshabib10580802015-02-18 18:30:33 -0800420 null;
421 }
422 if (existing != null) {
423 freeId = groupIdGen.incrementAndGet();
424 } else {
425 break;
426 }
427 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700428 log.debug("getFreeGroupIdValue: Next Free ID is {}", freeId);
alshabib10580802015-02-18 18:30:33 -0800429 return freeId;
430 }
431
432 /**
433 * Stores a new group entry using the information from group description.
434 *
435 * @param groupDesc group description to be used to create group entry
436 */
437 @Override
438 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700439 log.debug("In storeGroupDescription");
alshabib10580802015-02-18 18:30:33 -0800440 // Check if a group is existing with the same key
Saurav Das8a0732e2015-11-20 15:27:53 -0800441 Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
442 if (existingGroup != null) {
Saurav Dasc568c342018-01-25 09:49:01 -0800443 log.debug("Group already exists with the same key {} in dev:{} with id:0x{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800444 groupDesc.appCookie(), groupDesc.deviceId(),
445 Integer.toHexString(existingGroup.id().id()));
alshabib10580802015-02-18 18:30:33 -0800446 return;
447 }
448
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700449 // Check if group to be created by a remote instance
Madan Jampani175e8fd2015-05-20 14:10:45 -0700450 if (mastershipService.getLocalRole(groupDesc.deviceId()) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700451 log.debug("storeGroupDescription: Device {} local role is not MASTER",
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700452 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700453 if (mastershipService.getMasterFor(groupDesc.deviceId()) == null) {
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530454 log.debug("No Master for device {}..."
455 + "Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700456 groupDesc.deviceId());
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530457 addToPendingAudit(groupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700458 return;
459 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700460 GroupStoreMessage groupOp = GroupStoreMessage.
461 createGroupAddRequestMsg(groupDesc.deviceId(),
462 groupDesc);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700463
Madan Jampani175e8fd2015-05-20 14:10:45 -0700464 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700465 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
466 clusterMsgSerializer::serialize,
467 mastershipService.getMasterFor(groupDesc.deviceId()))
468 .whenComplete((result, error) -> {
Madan Jampani175e8fd2015-05-20 14:10:45 -0700469 if (error != null) {
470 log.warn("Failed to send request to master: {} to {}",
alshabibb0285992016-03-28 23:30:37 -0700471 groupOp,
472 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700473 //TODO: Send Group operation failure event
474 } else {
475 log.debug("Sent Group operation request for device {} "
alshabibb0285992016-03-28 23:30:37 -0700476 + "to remote MASTER {}",
477 groupDesc.deviceId(),
478 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700479 }
480 });
alshabib10580802015-02-18 18:30:33 -0800481 return;
482 }
483
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700484 log.debug("Store group for device {} is getting handled locally",
485 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800486 storeGroupDescriptionInternal(groupDesc);
487 }
488
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530489 private void addToPendingAudit(GroupDescription groupDesc) {
490 Integer groupIdVal = groupDesc.givenGroupId();
491 GroupId groupId = (groupIdVal != null) ? new GroupId(groupIdVal) : dummyGroupId;
492 addToPendingKeyTable(new DefaultGroup(groupId, groupDesc));
493 }
494
495 private void addToPendingKeyTable(StoredGroupEntry group) {
496 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
497 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
498 getPendingGroupKeyTable();
499 pendingKeyTable.put(new GroupStoreKeyMapKey(group.deviceId(),
500 group.appCookie()),
501 group);
502 }
503
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700504 private Group getMatchingExtraneousGroupbyId(DeviceId deviceId, Integer groupId) {
505 ConcurrentMap<GroupId, Group> extraneousMap =
506 extraneousGroupEntriesById.get(deviceId);
507 if (extraneousMap == null) {
508 return null;
509 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800510 return extraneousMap.get(new GroupId(groupId));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700511 }
512
513 private Group getMatchingExtraneousGroupbyBuckets(DeviceId deviceId,
514 GroupBuckets buckets) {
515 ConcurrentMap<GroupId, Group> extraneousMap =
516 extraneousGroupEntriesById.get(deviceId);
517 if (extraneousMap == null) {
518 return null;
519 }
520
alshabibb0285992016-03-28 23:30:37 -0700521 for (Group extraneousGroup : extraneousMap.values()) {
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700522 if (extraneousGroup.buckets().equals(buckets)) {
523 return extraneousGroup;
524 }
525 }
526 return null;
527 }
528
alshabib10580802015-02-18 18:30:33 -0800529 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
530 // Check if a group is existing with the same key
531 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
532 return;
533 }
534
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700535 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
536 // Device group audit has not completed yet
537 // Add this group description to pending group key table
538 // Create a group entry object with Dummy Group ID
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700539 log.debug("storeGroupDescriptionInternal: Device {} AUDIT pending...Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700540 groupDesc.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700541 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
542 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
Madan Jampani0b847532016-03-03 13:44:15 -0800543 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700544 getPendingGroupKeyTable();
545 pendingKeyTable.put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
546 groupDesc.appCookie()),
547 group);
548 return;
549 }
550
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700551 Group matchingExtraneousGroup = null;
552 if (groupDesc.givenGroupId() != null) {
553 //Check if there is a extraneous group existing with the same Id
554 matchingExtraneousGroup = getMatchingExtraneousGroupbyId(
alshabibb0285992016-03-28 23:30:37 -0700555 groupDesc.deviceId(), groupDesc.givenGroupId());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700556 if (matchingExtraneousGroup != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800557 log.debug("storeGroupDescriptionInternal: Matching extraneous group "
alshabibb0285992016-03-28 23:30:37 -0700558 + "found in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700559 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800560 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700561 //Check if the group buckets matches with user provided buckets
562 if (matchingExtraneousGroup.buckets().equals(groupDesc.buckets())) {
563 //Group is already existing with the same buckets and Id
564 // Create a group entry object
Saurav Das0fd79d92016-03-07 10:58:36 -0800565 log.debug("storeGroupDescriptionInternal: Buckets also matching "
alshabibb0285992016-03-28 23:30:37 -0700566 + "in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700567 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800568 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700569 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700570 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700571 // Insert the newly created group entry into key and id maps
572 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700573 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
574 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700575 // Ensure it also inserted into group id based table to
576 // avoid any chances of duplication in group id generation
577 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700578 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700579 addOrUpdateGroupEntry(matchingExtraneousGroup);
580 removeExtraneousGroupEntry(matchingExtraneousGroup);
581 return;
582 } else {
583 //Group buckets are not matching. Update group
584 //with user provided buckets.
Saurav Das0fd79d92016-03-07 10:58:36 -0800585 log.debug("storeGroupDescriptionInternal: Buckets are not "
alshabibb0285992016-03-28 23:30:37 -0700586 + "matching in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700587 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800588 Integer.toHexString(groupDesc.givenGroupId()));
589 StoredGroupEntry modifiedGroup = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700590 matchingExtraneousGroup.id(), groupDesc);
Saurav Das0fd79d92016-03-07 10:58:36 -0800591 modifiedGroup.setState(GroupState.PENDING_UPDATE);
592 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700593 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
594 groupDesc.appCookie()), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800595 // Ensure it also inserted into group id based table to
596 // avoid any chances of duplication in group id generation
597 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700598 put(matchingExtraneousGroup.id(), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800599 removeExtraneousGroupEntry(matchingExtraneousGroup);
600 log.debug("storeGroupDescriptionInternal: Triggering Group "
alshabibb0285992016-03-28 23:30:37 -0700601 + "UPDATE request for {} in device {}",
Saurav Das0fd79d92016-03-07 10:58:36 -0800602 matchingExtraneousGroup.id(),
603 groupDesc.deviceId());
604 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, modifiedGroup));
605 return;
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700606 }
607 }
608 } else {
609 //Check if there is an extraneous group with user provided buckets
610 matchingExtraneousGroup = getMatchingExtraneousGroupbyBuckets(
alshabibb0285992016-03-28 23:30:37 -0700611 groupDesc.deviceId(), groupDesc.buckets());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700612 if (matchingExtraneousGroup != null) {
613 //Group is already existing with the same buckets.
614 //So reuse this group.
615 log.debug("storeGroupDescriptionInternal: Matching extraneous group found in Device {}",
616 groupDesc.deviceId());
617 //Create a group entry object
618 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700619 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700620 // Insert the newly created group entry into key and id maps
621 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700622 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
623 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700624 // Ensure it also inserted into group id based table to
625 // avoid any chances of duplication in group id generation
626 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700627 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700628 addOrUpdateGroupEntry(matchingExtraneousGroup);
629 removeExtraneousGroupEntry(matchingExtraneousGroup);
630 return;
631 } else {
632 //TODO: Check if there are any empty groups that can be used here
633 log.debug("storeGroupDescriptionInternal: No matching extraneous groups found in Device {}",
634 groupDesc.deviceId());
635 }
636 }
637
Saurav Das100e3b82015-04-30 11:12:10 -0700638 GroupId id = null;
639 if (groupDesc.givenGroupId() == null) {
640 // Get a new group identifier
Yi Tsengfa394de2017-02-01 11:26:40 -0800641 id = new GroupId(getFreeGroupIdValue(groupDesc.deviceId()));
Saurav Das100e3b82015-04-30 11:12:10 -0700642 } else {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800643 // we need to use the identifier passed in by caller, but check if
644 // already used
645 Group existing = getGroup(groupDesc.deviceId(),
Yi Tsengfa394de2017-02-01 11:26:40 -0800646 new GroupId(groupDesc.givenGroupId()));
Saurav Das8be4e3a2016-03-11 17:19:07 -0800647 if (existing != null) {
648 log.warn("Group already exists with the same id: 0x{} in dev:{} "
alshabibb0285992016-03-28 23:30:37 -0700649 + "but with different key: {} (request gkey: {})",
650 Integer.toHexString(groupDesc.givenGroupId()),
651 groupDesc.deviceId(),
652 existing.appCookie(),
653 groupDesc.appCookie());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800654 return;
655 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800656 id = new GroupId(groupDesc.givenGroupId());
Saurav Das100e3b82015-04-30 11:12:10 -0700657 }
alshabib10580802015-02-18 18:30:33 -0800658 // Create a group entry object
659 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700660 // Insert the newly created group entry into key and id maps
661 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700662 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
663 groupDesc.appCookie()), group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700664 // Ensure it also inserted into group id based table to
665 // avoid any chances of duplication in group id generation
666 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700667 put(id, group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700668 log.debug("storeGroupDescriptionInternal: Processing Group ADD request for Id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700669 id,
670 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800671 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
672 group));
673 }
674
675 /**
676 * Updates the existing group entry with the information
677 * from group description.
678 *
alshabibb0285992016-03-28 23:30:37 -0700679 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800680 * @param oldAppCookie the current group key
alshabibb0285992016-03-28 23:30:37 -0700681 * @param type update type
682 * @param newBuckets group buckets for updates
alshabib10580802015-02-18 18:30:33 -0800683 * @param newAppCookie optional new group key
684 */
685 @Override
686 public void updateGroupDescription(DeviceId deviceId,
687 GroupKey oldAppCookie,
688 UpdateType type,
689 GroupBuckets newBuckets,
690 GroupKey newAppCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700691 // Check if group update to be done by a remote instance
sangho52abe3a2015-05-05 14:13:34 -0700692 if (mastershipService.getMasterFor(deviceId) != null &&
693 mastershipService.getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700694 log.debug("updateGroupDescription: Device {} local role is not MASTER",
695 deviceId);
696 if (mastershipService.getMasterFor(deviceId) == null) {
697 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700698 + "Can not perform update group operation",
699 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700700 //TODO: Send Group operation failure event
701 return;
702 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700703 GroupStoreMessage groupOp = GroupStoreMessage.
704 createGroupUpdateRequestMsg(deviceId,
705 oldAppCookie,
706 type,
707 newBuckets,
708 newAppCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700709
Madan Jampani175e8fd2015-05-20 14:10:45 -0700710 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700711 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
712 clusterMsgSerializer::serialize,
713 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
714 if (error != null) {
715 log.warn("Failed to send request to master: {} to {}",
716 groupOp,
717 mastershipService.getMasterFor(deviceId), error);
718 }
719 //TODO: Send Group operation failure event
720 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700721 return;
722 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700723 log.debug("updateGroupDescription for device {} is getting handled locally",
724 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700725 updateGroupDescriptionInternal(deviceId,
726 oldAppCookie,
727 type,
728 newBuckets,
729 newAppCookie);
730 }
731
732 private void updateGroupDescriptionInternal(DeviceId deviceId,
alshabibb0285992016-03-28 23:30:37 -0700733 GroupKey oldAppCookie,
734 UpdateType type,
735 GroupBuckets newBuckets,
736 GroupKey newAppCookie) {
alshabib10580802015-02-18 18:30:33 -0800737 // Check if a group is existing with the provided key
738 Group oldGroup = getGroup(deviceId, oldAppCookie);
739 if (oldGroup == null) {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800740 log.warn("updateGroupDescriptionInternal: Group not found...strange. "
alshabibb0285992016-03-28 23:30:37 -0700741 + "GroupKey:{} DeviceId:{}", oldAppCookie, deviceId);
alshabib10580802015-02-18 18:30:33 -0800742 return;
743 }
744
745 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
746 type,
747 newBuckets);
748 if (newBucketList != null) {
749 // Create a new group object from the old group
750 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
751 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
752 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
753 oldGroup.deviceId(),
754 oldGroup.type(),
755 updatedBuckets,
756 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700757 oldGroup.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800758 oldGroup.appId());
759 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
760 updatedGroupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700761 log.debug("updateGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_UPDATE",
alshabibb0285992016-03-28 23:30:37 -0700762 oldGroup.id(),
763 oldGroup.deviceId(),
764 oldGroup.state());
alshabib10580802015-02-18 18:30:33 -0800765 newGroup.setState(GroupState.PENDING_UPDATE);
766 newGroup.setLife(oldGroup.life());
767 newGroup.setPackets(oldGroup.packets());
768 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700769 //Update the group entry in groupkey based map.
770 //Update to groupid based map will happen in the
771 //groupkey based map update listener
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700772 log.debug("updateGroupDescriptionInternal with type {}: Group updated with buckets",
773 type);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700774 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700775 put(new GroupStoreKeyMapKey(newGroup.deviceId(),
776 newGroup.appCookie()), newGroup);
alshabib10580802015-02-18 18:30:33 -0800777 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700778 } else {
779 log.warn("updateGroupDescriptionInternal with type {}: No "
alshabibb0285992016-03-28 23:30:37 -0700780 + "change in the buckets in update", type);
alshabib10580802015-02-18 18:30:33 -0800781 }
782 }
783
784 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
785 UpdateType type,
786 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300787 if (type == UpdateType.SET) {
788 return buckets.buckets();
789 }
790
Victor Silvadf1eeae2016-08-12 15:28:57 -0300791 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
792 List<GroupBucket> updatedBucketList = new ArrayList<>();
alshabib10580802015-02-18 18:30:33 -0800793 boolean groupDescUpdated = false;
794
795 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300796 List<GroupBucket> newBuckets = buckets.buckets();
797
798 // Add old buckets that will not be updated and check if any will be updated.
799 for (GroupBucket oldBucket : oldBuckets) {
800 int newBucketIndex = newBuckets.indexOf(oldBucket);
801
802 if (newBucketIndex != -1) {
803 GroupBucket newBucket = newBuckets.get(newBucketIndex);
804 if (!newBucket.hasSameParameters(oldBucket)) {
805 // Bucket will be updated
806 groupDescUpdated = true;
807 }
808 } else {
809 // Old bucket will remain the same - add it.
810 updatedBucketList.add(oldBucket);
alshabib10580802015-02-18 18:30:33 -0800811 }
812 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300813
814 // Add all new buckets
815 updatedBucketList.addAll(newBuckets);
816 if (!oldBuckets.containsAll(newBuckets)) {
817 groupDescUpdated = true;
818 }
819
alshabib10580802015-02-18 18:30:33 -0800820 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300821 List<GroupBucket> bucketsToRemove = buckets.buckets();
822
823 // Check which old buckets should remain
824 for (GroupBucket oldBucket : oldBuckets) {
825 if (!bucketsToRemove.contains(oldBucket)) {
826 updatedBucketList.add(oldBucket);
827 } else {
alshabib10580802015-02-18 18:30:33 -0800828 groupDescUpdated = true;
829 }
830 }
831 }
832
833 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300834 return updatedBucketList;
alshabib10580802015-02-18 18:30:33 -0800835 } else {
836 return null;
837 }
838 }
839
840 /**
841 * Triggers deleting the existing group entry.
842 *
alshabibb0285992016-03-28 23:30:37 -0700843 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800844 * @param appCookie the group key
845 */
846 @Override
847 public void deleteGroupDescription(DeviceId deviceId,
848 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700849 // Check if group to be deleted by a remote instance
850 if (mastershipService.
851 getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700852 log.debug("deleteGroupDescription: Device {} local role is not MASTER",
853 deviceId);
854 if (mastershipService.getMasterFor(deviceId) == null) {
855 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700856 + "Can not perform delete group operation",
857 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700858 //TODO: Send Group operation failure event
859 return;
860 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700861 GroupStoreMessage groupOp = GroupStoreMessage.
862 createGroupDeleteRequestMsg(deviceId,
863 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700864
Madan Jampani175e8fd2015-05-20 14:10:45 -0700865 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700866 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
867 clusterMsgSerializer::serialize,
868 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
869 if (error != null) {
870 log.warn("Failed to send request to master: {} to {}",
871 groupOp,
872 mastershipService.getMasterFor(deviceId), error);
873 }
874 //TODO: Send Group operation failure event
875 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700876 return;
877 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700878 log.debug("deleteGroupDescription in device {} is getting handled locally",
879 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700880 deleteGroupDescriptionInternal(deviceId, appCookie);
881 }
882
883 private void deleteGroupDescriptionInternal(DeviceId deviceId,
884 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800885 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700886 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800887 if (existing == null) {
888 return;
889 }
890
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700891 log.debug("deleteGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_DELETE",
alshabibb0285992016-03-28 23:30:37 -0700892 existing.id(),
893 existing.deviceId(),
894 existing.state());
alshabib10580802015-02-18 18:30:33 -0800895 synchronized (existing) {
896 existing.setState(GroupState.PENDING_DELETE);
Saurav Das80980c72016-03-23 11:22:49 -0700897 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700898 put(new GroupStoreKeyMapKey(existing.deviceId(), existing.appCookie()),
899 existing);
alshabib10580802015-02-18 18:30:33 -0800900 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700901 log.debug("deleteGroupDescriptionInternal: in device {} issuing GROUP_REMOVE_REQUESTED",
902 deviceId);
alshabib10580802015-02-18 18:30:33 -0800903 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
904 }
905
906 /**
907 * Stores a new group entry, or updates an existing entry.
908 *
909 * @param group group entry
910 */
911 @Override
912 public void addOrUpdateGroupEntry(Group group) {
913 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700914 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
915 group.id());
alshabib10580802015-02-18 18:30:33 -0800916 GroupEvent event = null;
917
918 if (existing != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800919 log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700920 group.id(),
921 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800922 synchronized (existing) {
alshabibb0285992016-03-28 23:30:37 -0700923 for (GroupBucket bucket : group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700924 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700925 existing.buckets().buckets()
alshabibb0285992016-03-28 23:30:37 -0700926 .stream()
927 .filter((existingBucket) -> (existingBucket.equals(bucket)))
928 .findFirst();
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700929 if (matchingBucket.isPresent()) {
930 ((StoredGroupBucketEntry) matchingBucket.
931 get()).setPackets(bucket.packets());
932 ((StoredGroupBucketEntry) matchingBucket.
933 get()).setBytes(bucket.bytes());
934 } else {
935 log.warn("addOrUpdateGroupEntry: No matching "
alshabibb0285992016-03-28 23:30:37 -0700936 + "buckets to update stats");
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700937 }
938 }
alshabib10580802015-02-18 18:30:33 -0800939 existing.setLife(group.life());
940 existing.setPackets(group.packets());
941 existing.setBytes(group.bytes());
alshabibb0285992016-03-28 23:30:37 -0700942 existing.setReferenceCount(group.referenceCount());
Saurav Das137f27f2018-06-11 17:02:31 -0700943 existing.setFailedRetryCount(0);
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700944 if ((existing.state() == GroupState.PENDING_ADD) ||
alshabibb0285992016-03-28 23:30:37 -0700945 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800946 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700947 existing.id(),
948 existing.deviceId(),
949 existing.state());
alshabib10580802015-02-18 18:30:33 -0800950 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700951 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800952 event = new GroupEvent(Type.GROUP_ADDED, existing);
953 } else {
Saurav Das0fd79d92016-03-07 10:58:36 -0800954 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700955 existing.id(),
956 existing.deviceId(),
957 GroupState.PENDING_UPDATE);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700958 existing.setState(GroupState.ADDED);
959 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800960 event = new GroupEvent(Type.GROUP_UPDATED, existing);
961 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700962 //Re-PUT map entries to trigger map update events
963 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700964 put(new GroupStoreKeyMapKey(existing.deviceId(),
965 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800966 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700967 } else {
968 log.warn("addOrUpdateGroupEntry: Group update "
alshabibb0285992016-03-28 23:30:37 -0700969 + "happening for a non-existing entry in the map");
alshabib10580802015-02-18 18:30:33 -0800970 }
971
Saurav Das137f27f2018-06-11 17:02:31 -0700972 // XXX if map is going to trigger event, is this one needed?
alshabib10580802015-02-18 18:30:33 -0800973 if (event != null) {
974 notifyDelegate(event);
975 }
976 }
977
978 /**
979 * Removes the group entry from store.
980 *
981 * @param group group entry
982 */
983 @Override
984 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700985 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
986 group.id());
alshabib10580802015-02-18 18:30:33 -0800987
988 if (existing != null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700989 log.debug("removeGroupEntry: removing group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700990 group.id(),
991 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700992 //Removal from groupid based map will happen in the
993 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700994 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
995 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -0800996 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700997 } else {
998 log.warn("removeGroupEntry for {} in device{} is "
alshabibb0285992016-03-28 23:30:37 -0700999 + "not existing in our maps",
1000 group.id(),
1001 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001002 }
1003 }
1004
Victor Silva4e8b7832016-08-17 17:11:19 -03001005 private void purgeGroupEntries(Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entries) {
1006 entries.forEach(entry -> {
1007 groupStoreEntriesByKey.remove(entry.getKey());
1008 });
1009 }
1010
alshabib10580802015-02-18 18:30:33 -08001011 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -08001012 public void purgeGroupEntry(DeviceId deviceId) {
Victor Silva4e8b7832016-08-17 17:11:19 -03001013 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entriesPendingRemove =
Charles Chan0c7c43b2016-01-14 17:39:20 -08001014 new HashSet<>();
1015
Madan Jampani0b847532016-03-03 13:44:15 -08001016 getGroupStoreKeyMap().entrySet().stream()
Charles Chan0c7c43b2016-01-14 17:39:20 -08001017 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Victor Silva4e8b7832016-08-17 17:11:19 -03001018 .forEach(entriesPendingRemove::add);
Charles Chan0c7c43b2016-01-14 17:39:20 -08001019
Victor Silva4e8b7832016-08-17 17:11:19 -03001020 purgeGroupEntries(entriesPendingRemove);
1021 }
1022
1023 @Override
1024 public void purgeGroupEntries() {
1025 purgeGroupEntries(getGroupStoreKeyMap().entrySet());
Charles Chan0c7c43b2016-01-14 17:39:20 -08001026 }
1027
1028 @Override
alshabib10580802015-02-18 18:30:33 -08001029 public void deviceInitialAuditCompleted(DeviceId deviceId,
1030 boolean completed) {
1031 synchronized (deviceAuditStatus) {
1032 if (completed) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001033 log.debug("AUDIT completed for device {}",
1034 deviceId);
alshabib10580802015-02-18 18:30:33 -08001035 deviceAuditStatus.put(deviceId, true);
1036 // Execute all pending group requests
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001037 List<StoredGroupEntry> pendingGroupRequests =
1038 getPendingGroupKeyTable().values()
alshabibb0285992016-03-28 23:30:37 -07001039 .stream()
1040 .filter(g -> g.deviceId().equals(deviceId))
1041 .collect(Collectors.toList());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001042 log.debug("processing pending group add requests for device {} and number of pending requests {}",
alshabibb0285992016-03-28 23:30:37 -07001043 deviceId,
1044 pendingGroupRequests.size());
1045 for (Group group : pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -08001046 GroupDescription tmp = new DefaultGroupDescription(
1047 group.deviceId(),
1048 group.type(),
1049 group.buckets(),
1050 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -07001051 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -08001052 group.appId());
1053 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001054 getPendingGroupKeyTable().
alshabibb0285992016-03-28 23:30:37 -07001055 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001056 }
alshabib10580802015-02-18 18:30:33 -08001057 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001058 Boolean audited = deviceAuditStatus.get(deviceId);
1059 if (audited != null && audited) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001060 log.debug("Clearing AUDIT status for device {}", deviceId);
alshabib10580802015-02-18 18:30:33 -08001061 deviceAuditStatus.put(deviceId, false);
1062 }
1063 }
1064 }
1065 }
1066
1067 @Override
1068 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
1069 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001070 Boolean audited = deviceAuditStatus.get(deviceId);
1071 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -08001072 }
1073 }
1074
1075 @Override
1076 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
1077
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001078 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
1079 operation.groupId());
alshabib10580802015-02-18 18:30:33 -08001080
1081 if (existing == null) {
1082 log.warn("No group entry with ID {} found ", operation.groupId());
1083 return;
1084 }
1085
Saurav Das137f27f2018-06-11 17:02:31 -07001086 log.warn("groupOperationFailed: group operation {} failed in state {} "
alshabibb0285992016-03-28 23:30:37 -07001087 + "for group {} in device {} with code {}",
1088 operation.opType(),
Saurav Das137f27f2018-06-11 17:02:31 -07001089 existing.state(),
alshabibb0285992016-03-28 23:30:37 -07001090 existing.id(),
1091 existing.deviceId(),
1092 operation.failureCode());
Saurav Das0fd79d92016-03-07 10:58:36 -08001093 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001094 if (operation.buckets().equals(existing.buckets())) {
Saurav Dasc88d4662017-05-15 15:34:25 -07001095 if (existing.state() == GroupState.PENDING_ADD ||
1096 existing.state() == GroupState.PENDING_ADD_RETRY) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001097 log.info("GROUP_EXISTS: GroupID and Buckets match for group in pending "
alshabibb0285992016-03-28 23:30:37 -07001098 + "add state - moving to ADDED for group {} in device {}",
1099 existing.id(), deviceId);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001100 addOrUpdateGroupEntry(existing);
1101 return;
1102 } else {
Saurav Dasc88d4662017-05-15 15:34:25 -07001103 log.warn("GROUP_EXISTS: GroupId and Buckets match but existing"
1104 + "group in state: {}", existing.state());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001105 }
Saurav Dasc88d4662017-05-15 15:34:25 -07001106 } else {
1107 log.warn("GROUP EXISTS: Group ID matched but buckets did not. "
1108 + "Operation: {} Existing: {}", operation.buckets(),
1109 existing.buckets());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001110 }
Saurav Das0fd79d92016-03-07 10:58:36 -08001111 }
Saurav Das137f27f2018-06-11 17:02:31 -07001112 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.INVALID_GROUP) {
1113 existing.incrFailedRetryCount();
1114 if (existing.failedRetryCount() < MAX_FAILED_ATTEMPTS) {
1115 log.warn("Group {} programming failed {} of {} times in dev {}, "
1116 + "retrying ..", existing.id(),
1117 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1118 deviceId);
1119 return;
1120 }
1121 log.warn("Group {} programming failed {} of {} times in dev {}, "
1122 + "removing group from store", existing.id(),
1123 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1124 deviceId);
1125 // fall through to case
1126 }
1127
alshabib10580802015-02-18 18:30:33 -08001128 switch (operation.opType()) {
1129 case ADD:
Saurav Das137f27f2018-06-11 17:02:31 -07001130 if (existing.state() == GroupState.PENDING_ADD
1131 || existing.state() == GroupState.PENDING_ADD_RETRY) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001132 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
1133 log.warn("groupOperationFailed: cleaningup "
alshabibb0285992016-03-28 23:30:37 -07001134 + "group {} from store in device {}....",
1135 existing.id(),
1136 existing.deviceId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001137 //Removal from groupid based map will happen in the
1138 //map update listener
1139 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1140 existing.appCookie()));
1141 }
alshabib10580802015-02-18 18:30:33 -08001142 break;
1143 case MODIFY:
1144 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
1145 break;
1146 case DELETE:
1147 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
1148 break;
1149 default:
1150 log.warn("Unknown group operation type {}", operation.opType());
1151 }
alshabib10580802015-02-18 18:30:33 -08001152 }
1153
1154 @Override
1155 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001156 log.debug("add/update extraneous group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001157 group.id(),
1158 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001159 ConcurrentMap<GroupId, Group> extraneousIdTable =
1160 getExtraneousGroupIdTable(group.deviceId());
1161 extraneousIdTable.put(group.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -07001162 // Don't remove the extraneous groups, instead re-use it when
1163 // a group request comes with the same set of buckets
alshabib10580802015-02-18 18:30:33 -08001164 }
1165
1166 @Override
1167 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001168 log.debug("remove extraneous group entry {} of device {} from store",
alshabibb0285992016-03-28 23:30:37 -07001169 group.id(),
1170 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001171 ConcurrentMap<GroupId, Group> extraneousIdTable =
1172 getExtraneousGroupIdTable(group.deviceId());
1173 extraneousIdTable.remove(group.id());
1174 }
1175
1176 @Override
1177 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
1178 // flatten and make iterator unmodifiable
1179 return FluentIterable.from(
1180 getExtraneousGroupIdTable(deviceId).values());
1181 }
1182
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001183 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001184 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001185 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001186 private class GroupStoreKeyMapListener implements
Madan Jampani0b847532016-03-03 13:44:15 -08001187 MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001188
1189 @Override
Madan Jampani0b847532016-03-03 13:44:15 -08001190 public void event(MapEvent<GroupStoreKeyMapKey, StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001191 GroupEvent groupEvent = null;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001192 GroupStoreKeyMapKey key = mapEvent.key();
Madan Jampani0b847532016-03-03 13:44:15 -08001193 StoredGroupEntry group = Versioned.valueOrNull(mapEvent.newValue());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001194 if ((key == null) && (group == null)) {
1195 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001196 + "event {} with null entry", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001197 return;
1198 } else if (group == null) {
1199 group = getGroupIdTable(key.deviceId()).values()
1200 .stream()
1201 .filter((storedGroup) -> (storedGroup.appCookie().equals(key.appCookie)))
Yuta HIGUCHI6e5f4702016-11-21 11:42:11 -08001202 .findFirst().orElse(null);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001203 if (group == null) {
1204 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001205 + "event {} with null entry... can not process", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001206 return;
1207 }
1208 }
1209 log.trace("received groupid map event {} for id {} in device {}",
1210 mapEvent.type(),
1211 group.id(),
jaegonkim68e080c2016-12-01 22:31:01 +09001212 (key != null ? key.deviceId() : null));
Madan Jampani0b847532016-03-03 13:44:15 -08001213 if (mapEvent.type() == MapEvent.Type.INSERT || mapEvent.type() == MapEvent.Type.UPDATE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001214 // Update the group ID table
1215 getGroupIdTable(group.deviceId()).put(group.id(), group);
Madan Jampani0b847532016-03-03 13:44:15 -08001216 StoredGroupEntry value = Versioned.valueOrNull(mapEvent.newValue());
1217 if (value.state() == Group.GroupState.ADDED) {
1218 if (value.isGroupStateAddedFirstTime()) {
1219 groupEvent = new GroupEvent(Type.GROUP_ADDED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001220 log.trace("Received first time GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001221 group.id(),
1222 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001223 } else {
Madan Jampani0b847532016-03-03 13:44:15 -08001224 groupEvent = new GroupEvent(Type.GROUP_UPDATED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001225 log.trace("Received following GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001226 group.id(),
1227 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001228 }
1229 }
Madan Jampani0b847532016-03-03 13:44:15 -08001230 } else if (mapEvent.type() == MapEvent.Type.REMOVE) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001231 groupEvent = new GroupEvent(Type.GROUP_REMOVED, group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001232 // Remove the entry from the group ID table
1233 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001234 }
1235
1236 if (groupEvent != null) {
1237 notifyDelegate(groupEvent);
1238 }
1239 }
1240 }
Madan Jampani01e05fb2015-08-13 13:29:36 -07001241
helenyrwua1c41152016-08-18 16:16:14 -07001242 private void processGroupMessage(GroupStoreMessage message) {
1243 if (message.type() == GroupStoreMessage.Type.FAILOVER) {
1244 // FIXME: groupStoreEntriesByKey inaccessible here
1245 getGroupIdTable(message.deviceId()).values()
1246 .stream()
1247 .filter((storedGroup) -> (storedGroup.appCookie().equals(message.appCookie())))
1248 .findFirst().ifPresent(group -> notifyDelegate(new GroupEvent(Type.GROUP_BUCKET_FAILOVER, group)));
1249 }
1250 }
1251
Madan Jampani01e05fb2015-08-13 13:29:36 -07001252 private void process(GroupStoreMessage groupOp) {
1253 log.debug("Received remote group operation {} request for device {}",
alshabibb0285992016-03-28 23:30:37 -07001254 groupOp.type(),
1255 groupOp.deviceId());
1256 if (!mastershipService.isLocalMaster(groupOp.deviceId())) {
1257 log.warn("This node is not MASTER for device {}", groupOp.deviceId());
1258 return;
1259 }
1260 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
1261 storeGroupDescriptionInternal(groupOp.groupDesc());
1262 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
1263 updateGroupDescriptionInternal(groupOp.deviceId(),
1264 groupOp.appCookie(),
1265 groupOp.updateType(),
1266 groupOp.updateBuckets(),
1267 groupOp.newAppCookie());
1268 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
1269 deleteGroupDescriptionInternal(groupOp.deviceId(),
1270 groupOp.appCookie());
1271 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001272 }
1273
1274 /**
1275 * Flattened map key to be used to store group entries.
1276 */
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001277 protected static class GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001278 private final DeviceId deviceId;
1279
1280 public GroupStoreMapKey(DeviceId deviceId) {
1281 this.deviceId = deviceId;
1282 }
1283
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001284 public DeviceId deviceId() {
1285 return deviceId;
1286 }
1287
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001288 @Override
1289 public boolean equals(Object o) {
1290 if (this == o) {
1291 return true;
1292 }
1293 if (!(o instanceof GroupStoreMapKey)) {
1294 return false;
1295 }
1296 GroupStoreMapKey that = (GroupStoreMapKey) o;
1297 return this.deviceId.equals(that.deviceId);
1298 }
1299
1300 @Override
1301 public int hashCode() {
1302 int result = 17;
1303
1304 result = 31 * result + Objects.hash(this.deviceId);
1305
1306 return result;
1307 }
1308 }
1309
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001310 protected static class GroupStoreKeyMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001311 private final GroupKey appCookie;
alshabibb0285992016-03-28 23:30:37 -07001312
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001313 public GroupStoreKeyMapKey(DeviceId deviceId,
1314 GroupKey appCookie) {
1315 super(deviceId);
1316 this.appCookie = appCookie;
1317 }
1318
1319 @Override
1320 public boolean equals(Object o) {
1321 if (this == o) {
1322 return true;
1323 }
1324 if (!(o instanceof GroupStoreKeyMapKey)) {
1325 return false;
1326 }
1327 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
1328 return (super.equals(that) &&
1329 this.appCookie.equals(that.appCookie));
1330 }
1331
1332 @Override
1333 public int hashCode() {
1334 int result = 17;
1335
1336 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
1337
1338 return result;
1339 }
1340 }
1341
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001342 protected static class GroupStoreIdMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001343 private final GroupId groupId;
alshabibb0285992016-03-28 23:30:37 -07001344
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001345 public GroupStoreIdMapKey(DeviceId deviceId,
1346 GroupId groupId) {
1347 super(deviceId);
1348 this.groupId = groupId;
1349 }
1350
1351 @Override
1352 public boolean equals(Object o) {
1353 if (this == o) {
1354 return true;
1355 }
1356 if (!(o instanceof GroupStoreIdMapKey)) {
1357 return false;
1358 }
1359 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1360 return (super.equals(that) &&
1361 this.groupId.equals(that.groupId));
1362 }
1363
1364 @Override
1365 public int hashCode() {
1366 int result = 17;
1367
1368 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1369
1370 return result;
1371 }
1372 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001373
1374 @Override
1375 public void pushGroupMetrics(DeviceId deviceId,
1376 Collection<Group> groupEntries) {
1377 boolean deviceInitialAuditStatus =
1378 deviceInitialAuditStatus(deviceId);
1379 Set<Group> southboundGroupEntries =
1380 Sets.newHashSet(groupEntries);
1381 Set<StoredGroupEntry> storedGroupEntries =
1382 Sets.newHashSet(getStoredGroups(deviceId));
1383 Set<Group> extraneousStoredEntries =
1384 Sets.newHashSet(getExtraneousGroups(deviceId));
1385
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001386 if (log.isTraceEnabled()) {
1387 log.trace("pushGroupMetrics: Displaying all ({}) southboundGroupEntries for device {}",
1388 southboundGroupEntries.size(),
1389 deviceId);
1390 for (Group group : southboundGroupEntries) {
1391 log.trace("Group {} in device {}", group, deviceId);
1392 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001393
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001394 log.trace("Displaying all ({}) stored group entries for device {}",
1395 storedGroupEntries.size(),
1396 deviceId);
1397 for (StoredGroupEntry group : storedGroupEntries) {
1398 log.trace("Stored Group {} for device {}", group, deviceId);
1399 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001400 }
1401
alshabibb0285992016-03-28 23:30:37 -07001402 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1403
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001404 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
1405 Group group = it2.next();
1406 if (storedGroupEntries.remove(group)) {
1407 // we both have the group, let's update some info then.
1408 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001409 group.id(), deviceId);
1410
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001411 groupAdded(group);
1412 it2.remove();
1413 }
1414 }
1415 for (Group group : southboundGroupEntries) {
1416 if (getGroup(group.deviceId(), group.id()) != null) {
1417 // There is a group existing with the same id
1418 // It is possible that group update is
1419 // in progress while we got a stale info from switch
1420 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001421 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001422 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001423 + "Group exists in ID based table while "
1424 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001425 }
1426 } else {
1427 // there are groups in the switch that aren't in the store
1428 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001429 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001430 extraneousStoredEntries.remove(group);
Kavitha Alagesanc56cded2017-01-13 10:48:18 +05301431 if (allowExtraneousGroups) {
1432 extraneousGroup(group);
1433 } else {
1434 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
1435 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001436 }
1437 }
Charles Chan07f15f22018-05-08 21:35:50 -07001438 for (StoredGroupEntry group : storedGroupEntries) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001439 // there are groups in the store that aren't in the switch
1440 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001441 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001442 groupMissing(group);
1443 }
1444 for (Group group : extraneousStoredEntries) {
1445 // there are groups in the extraneous store that
1446 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001447 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001448 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001449 removeExtraneousGroupEntry(group);
1450 }
1451
1452 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001453 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001454 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001455 deviceInitialAuditCompleted(deviceId, true);
1456 }
1457 }
1458
helenyrwu89470f12016-08-12 13:18:10 -07001459 @Override
1460 public void notifyOfFailovers(Collection<Group> failoverGroups) {
helenyrwu89470f12016-08-12 13:18:10 -07001461 failoverGroups.forEach(group -> {
1462 if (group.type() == Group.Type.FAILOVER) {
helenyrwua1c41152016-08-18 16:16:14 -07001463 groupTopic.publish(GroupStoreMessage.createGroupFailoverMsg(
1464 group.deviceId(), group));
helenyrwu89470f12016-08-12 13:18:10 -07001465 }
1466 });
helenyrwu89470f12016-08-12 13:18:10 -07001467 }
1468
alshabibb0285992016-03-28 23:30:37 -07001469 private void garbageCollect(DeviceId deviceId,
1470 Set<Group> southboundGroupEntries,
1471 Set<StoredGroupEntry> storedGroupEntries) {
1472 if (!garbageCollect) {
1473 return;
1474 }
1475
1476 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1477 while (it.hasNext()) {
1478 StoredGroupEntry group = it.next();
1479 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1480 log.debug("Garbage collecting group {} on {}", group, deviceId);
1481 deleteGroupDescription(deviceId, group.appCookie());
1482 southboundGroupEntries.remove(group);
1483 it.remove();
1484 }
1485 }
1486 }
1487
1488 private boolean checkGroupRefCount(Group group) {
1489 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1490 }
1491
Charles Chan07f15f22018-05-08 21:35:50 -07001492 private void groupMissing(StoredGroupEntry group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001493 switch (group.state()) {
1494 case PENDING_DELETE:
1495 log.debug("Group {} delete confirmation from device {}",
1496 group, group.deviceId());
1497 removeGroupEntry(group);
1498 break;
1499 case ADDED:
1500 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001501 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001502 case PENDING_UPDATE:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001503 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
Charles Chan07f15f22018-05-08 21:35:50 -07001504 group.id(),
1505 group.deviceId(),
1506 group.state());
1507 group.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001508 //Re-PUT map entries to trigger map update events
Charles Chan07f15f22018-05-08 21:35:50 -07001509 getGroupStoreKeyMap().put(new GroupStoreKeyMapKey(group.deviceId(), group.appCookie()), group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001510 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1511 group));
1512 break;
1513 default:
1514 log.debug("Group {} has not been installed.", group);
1515 break;
1516 }
1517 }
1518
1519 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001520 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001521 group, group.deviceId());
1522 addOrUpdateExtraneousGroupEntry(group);
1523 }
1524
1525 private void groupAdded(Group group) {
1526 log.trace("Group {} Added or Updated in device {}",
1527 group, group.deviceId());
1528 addOrUpdateGroupEntry(group);
1529 }
alshabib10580802015-02-18 18:30:33 -08001530}