blob: 23c1c9fceb9fd0ebeb1c3d95db26c2d9e36c974d [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;
Ray Milkeyb5646e62018-10-16 11:42:18 -070096import static org.onosproject.store.OsgiPropertyConstants.ALLOW_EXTRANEOUS_GROUPS;
97import static org.onosproject.store.OsgiPropertyConstants.ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
98import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT;
99import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_DEFAULT;
100import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_THRESH;
101import static org.onosproject.store.OsgiPropertyConstants.GARBAGE_COLLECT_THRESH_DEFAULT;
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700102import static org.slf4j.LoggerFactory.getLogger;
alshabib10580802015-02-18 18:30:33 -0800103
104/**
Saurav Das0fd79d92016-03-07 10:58:36 -0800105 * Manages inventory of group entries using distributed group stores from the
106 * storage service.
alshabib10580802015-02-18 18:30:33 -0800107 */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700108@Component(
109 immediate = true,
110 service = GroupStore.class,
111 property = {
Ray Milkey2d7bca12018-10-17 14:51:52 -0700112 GARBAGE_COLLECT + ":Boolean=" + GARBAGE_COLLECT_DEFAULT,
113 GARBAGE_COLLECT_THRESH + ":Integer=" + GARBAGE_COLLECT_THRESH_DEFAULT,
114 ALLOW_EXTRANEOUS_GROUPS + ":Boolean=" + ALLOW_EXTRANEOUS_GROUPS_DEFAULT
Ray Milkeyb5646e62018-10-16 11:42:18 -0700115 }
116)
alshabib10580802015-02-18 18:30:33 -0800117public class DistributedGroupStore
118 extends AbstractStore<GroupEvent, GroupStoreDelegate>
119 implements GroupStore {
120
121 private final Logger log = getLogger(getClass());
122
Saurav Das137f27f2018-06-11 17:02:31 -0700123 private static final int MAX_FAILED_ATTEMPTS = 3;
alshabibb0285992016-03-28 23:30:37 -0700124
alshabib10580802015-02-18 18:30:33 -0800125 private final int dummyId = 0xffffffff;
Yi Tsengfa394de2017-02-01 11:26:40 -0800126 private final GroupId dummyGroupId = new GroupId(dummyId);
alshabib10580802015-02-18 18:30:33 -0800127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700129 protected ClusterCommunicationService clusterCommunicator;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700132 protected ClusterService clusterService;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700135 protected StorageService storageService;
136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700138 protected MastershipService mastershipService;
139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabibb0285992016-03-28 23:30:37 -0700141 protected ComponentConfigService cfgService;
142
jaegonkim9477a9d2018-04-01 16:36:36 +0900143 // Guarantees enabling DriverService before enabling GroupStore
144 // (DriverService is used in serializing/de-serializing DefaultGroup)
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
jaegonkim9477a9d2018-04-01 16:36:36 +0900146 protected DriverService driverService;
147
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530148 private ScheduledExecutorService executor;
149 private Consumer<Status> statusChangeListener;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700150 // Per device group table with (device id + app cookie) as key
Madan Jampani0b847532016-03-03 13:44:15 -0800151 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700152 StoredGroupEntry> groupStoreEntriesByKey = null;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700153 // Per device group table with (device id + group id) as key
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700154 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
alshabibb0285992016-03-28 23:30:37 -0700155 groupEntriesById = new ConcurrentHashMap<>();
Madan Jampani0b847532016-03-03 13:44:15 -0800156 private ConsistentMap<GroupStoreKeyMapKey,
alshabibb0285992016-03-28 23:30:37 -0700157 StoredGroupEntry> auditPendingReqQueue = null;
Frank Wange0eb5ce2016-07-01 18:21:25 +0800158 private MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry>
159 mapListener = new GroupStoreKeyMapListener();
alshabib10580802015-02-18 18:30:33 -0800160 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
161 extraneousGroupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700162 private ExecutorService messageHandlingExecutor;
163 private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 1;
Saurav Das137f27f2018-06-11 17:02:31 -0700164
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700165 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
alshabib10580802015-02-18 18:30:33 -0800166
167 private final AtomicInteger groupIdGen = new AtomicInteger();
168
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700169 private KryoNamespace clusterMsgSerializer;
170
helenyrwua1c41152016-08-18 16:16:14 -0700171 private static Topic<GroupStoreMessage> groupTopic;
172
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700173 /** Enable group garbage collection. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700174 private boolean garbageCollect = GARBAGE_COLLECT_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700175
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700176 /** Number of rounds for group garbage collection. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700177 private int gcThresh = GARBAGE_COLLECT_THRESH_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700178
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700179 /** Allow groups in switches not installed by ONOS. */
Ray Milkeyb5646e62018-10-16 11:42:18 -0700180 private boolean allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700181
alshabib10580802015-02-18 18:30:33 -0800182 @Activate
sisubram4beea652017-08-09 10:38:14 +0000183 public void activate(ComponentContext context) {
alshabibb0285992016-03-28 23:30:37 -0700184 cfgService.registerProperties(getClass());
sisubram4beea652017-08-09 10:38:14 +0000185 modified(context);
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700186 KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
alshabibb0285992016-03-28 23:30:37 -0700187 .register(KryoNamespaces.API)
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700188 .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
alshabibb0285992016-03-28 23:30:37 -0700189 .register(DefaultGroup.class,
190 DefaultGroupBucket.class,
191 DefaultGroupDescription.class,
192 DefaultGroupKey.class,
193 GroupDescription.Type.class,
194 Group.GroupState.class,
195 GroupBuckets.class,
alshabibb0285992016-03-28 23:30:37 -0700196 GroupStoreMessage.class,
197 GroupStoreMessage.Type.class,
198 UpdateType.class,
199 GroupStoreMessageSubjects.class,
200 MultiValuedTimestamp.class,
201 GroupStoreKeyMapKey.class,
202 GroupStoreIdMapKey.class,
203 GroupStoreMapKey.class
204 );
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700205
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700206 clusterMsgSerializer = kryoBuilder.build("GroupStore");
207 Serializer serializer = Serializer.using(clusterMsgSerializer);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700208
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700209 messageHandlingExecutor = Executors.
210 newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE,
211 groupedThreads("onos/store/group",
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700212 "message-handlers",
213 log));
Madan Jampani01e05fb2015-08-13 13:29:36 -0700214
215 clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
alshabibb0285992016-03-28 23:30:37 -0700216 clusterMsgSerializer::deserialize,
217 this::process,
218 messageHandlingExecutor);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700219
Madan Jampani0b847532016-03-03 13:44:15 -0800220 log.debug("Creating Consistent map onos-group-store-keymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700221
Madan Jampani0b847532016-03-03 13:44:15 -0800222 groupStoreEntriesByKey = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
223 .withName("onos-group-store-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700224 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700225 .build();
Frank Wange0eb5ce2016-07-01 18:21:25 +0800226 groupStoreEntriesByKey.addListener(mapListener);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700227 log.debug("Current size of groupstorekeymap:{}",
228 groupStoreEntriesByKey.size());
Thiago Santosfb73c502016-08-18 18:15:13 -0300229 synchronizeGroupStoreEntries();
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700230
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530231 log.debug("Creating GroupStoreId Map From GroupStoreKey Map");
232 matchGroupEntries();
233 executor = newSingleThreadScheduledExecutor(groupedThreads("onos/group", "store", log));
234 statusChangeListener = status -> {
235 if (status == Status.ACTIVE) {
236 executor.execute(this::matchGroupEntries);
237 }
238 };
239 groupStoreEntriesByKey.addStatusChangeListener(statusChangeListener);
240
Madan Jampani0b847532016-03-03 13:44:15 -0800241 log.debug("Creating Consistent map pendinggroupkeymap");
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700242
Madan Jampani0b847532016-03-03 13:44:15 -0800243 auditPendingReqQueue = storageService.<GroupStoreKeyMapKey, StoredGroupEntry>consistentMapBuilder()
244 .withName("onos-pending-group-keymap")
HIGUCHI Yuta3a84b322016-05-18 13:38:07 -0700245 .withSerializer(serializer)
Jonathan Hart6ec029a2015-03-24 17:12:35 -0700246 .build();
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700247 log.debug("Current size of pendinggroupkeymap:{}",
248 auditPendingReqQueue.size());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700249
helenyrwua1c41152016-08-18 16:16:14 -0700250 groupTopic = getOrCreateGroupTopic(serializer);
251 groupTopic.subscribe(this::processGroupMessage);
252
alshabib10580802015-02-18 18:30:33 -0800253 log.info("Started");
254 }
255
256 @Deactivate
257 public void deactivate() {
Frank Wange0eb5ce2016-07-01 18:21:25 +0800258 groupStoreEntriesByKey.removeListener(mapListener);
alshabibb0285992016-03-28 23:30:37 -0700259 cfgService.unregisterProperties(getClass(), false);
HIGUCHI Yuta180d70f2015-10-01 16:13:56 -0700260 clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST);
alshabib10580802015-02-18 18:30:33 -0800261 log.info("Stopped");
262 }
263
alshabibb0285992016-03-28 23:30:37 -0700264 @Modified
265 public void modified(ComponentContext context) {
266 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
267
268 try {
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700269 String s = get(properties, GARBAGE_COLLECT);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700270 garbageCollect = isNullOrEmpty(s) ? GARBAGE_COLLECT_DEFAULT : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700271
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700272 s = get(properties, GARBAGE_COLLECT_THRESH);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700273 gcThresh = isNullOrEmpty(s) ? GARBAGE_COLLECT_THRESH_DEFAULT : Integer.parseInt(s.trim());
Kavitha Alagesanc56cded2017-01-13 10:48:18 +0530274
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700275 s = get(properties, ALLOW_EXTRANEOUS_GROUPS);
Ray Milkeyb5646e62018-10-16 11:42:18 -0700276 allowExtraneousGroups = isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_GROUPS_DEFAULT : Boolean.parseBoolean(s.trim());
alshabibb0285992016-03-28 23:30:37 -0700277 } catch (Exception e) {
Ray Milkeyb5646e62018-10-16 11:42:18 -0700278 gcThresh = GARBAGE_COLLECT_THRESH_DEFAULT;
279 garbageCollect = GARBAGE_COLLECT_DEFAULT;
280 allowExtraneousGroups = ALLOW_EXTRANEOUS_GROUPS_DEFAULT;
alshabibb0285992016-03-28 23:30:37 -0700281 }
282 }
283
helenyrwua1c41152016-08-18 16:16:14 -0700284 private Topic<GroupStoreMessage> getOrCreateGroupTopic(Serializer serializer) {
285 if (groupTopic == null) {
286 return storageService.getTopic("group-failover-notif", serializer);
287 } else {
288 return groupTopic;
289 }
Sho SHIMIZUa6285542017-01-12 15:08:24 -0800290 }
helenyrwua1c41152016-08-18 16:16:14 -0700291
alshabib10580802015-02-18 18:30:33 -0800292 /**
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530293 * Updating values of groupEntriesById.
294 */
295 private void matchGroupEntries() {
296 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupStoreEntriesByKey.asJavaMap().entrySet()) {
297 StoredGroupEntry group = entry.getValue();
298 getGroupIdTable(entry.getKey().deviceId()).put(group.id(), group);
299 }
300 }
301
Thiago Santosfb73c502016-08-18 18:15:13 -0300302
303 private void synchronizeGroupStoreEntries() {
304 Map<GroupStoreKeyMapKey, StoredGroupEntry> groupEntryMap = groupStoreEntriesByKey.asJavaMap();
305 for (Entry<GroupStoreKeyMapKey, StoredGroupEntry> entry : groupEntryMap.entrySet()) {
Thiago Santosfb73c502016-08-18 18:15:13 -0300306 StoredGroupEntry value = entry.getValue();
Thiago Santosfb73c502016-08-18 18:15:13 -0300307 ConcurrentMap<GroupId, StoredGroupEntry> groupIdTable = getGroupIdTable(value.deviceId());
308 groupIdTable.put(value.id(), value);
309 }
310 }
311
Kavitha Alagesanc884c3ef2017-01-19 12:32:26 +0530312 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700313 * Returns the group store eventual consistent key map.
alshabib10580802015-02-18 18:30:33 -0800314 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700315 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800316 */
Madan Jampani0b847532016-03-03 13:44:15 -0800317 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700318 getGroupStoreKeyMap() {
Madan Jampani0b847532016-03-03 13:44:15 -0800319 return groupStoreEntriesByKey.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800320 }
321
322 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700323 * Returns the group id table for specified device.
alshabib10580802015-02-18 18:30:33 -0800324 *
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700325 * @param deviceId identifier of the device
326 * @return Map representing group key table of given device.
alshabib10580802015-02-18 18:30:33 -0800327 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700328 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700329 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800330 }
331
332 /**
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700333 * Returns the pending group request table.
alshabib10580802015-02-18 18:30:33 -0800334 *
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700335 * @return Map representing group key table.
alshabib10580802015-02-18 18:30:33 -0800336 */
Madan Jampani0b847532016-03-03 13:44:15 -0800337 private Map<GroupStoreKeyMapKey, StoredGroupEntry>
alshabibb0285992016-03-28 23:30:37 -0700338 getPendingGroupKeyTable() {
Madan Jampani0b847532016-03-03 13:44:15 -0800339 return auditPendingReqQueue.asJavaMap();
alshabib10580802015-02-18 18:30:33 -0800340 }
341
342 /**
343 * Returns the extraneous group id table for specified device.
344 *
345 * @param deviceId identifier of the device
346 * @return Map representing group key table of given device.
347 */
348 private ConcurrentMap<GroupId, Group>
349 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700350 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
alshabib10580802015-02-18 18:30:33 -0800351 }
352
353 /**
354 * Returns the number of groups for the specified device in the store.
355 *
356 * @return number of groups for the specified device
357 */
358 @Override
359 public int getGroupCount(DeviceId deviceId) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700360 return (getGroups(deviceId) != null) ?
alshabibb0285992016-03-28 23:30:37 -0700361 Iterables.size(getGroups(deviceId)) : 0;
alshabib10580802015-02-18 18:30:33 -0800362 }
363
364 /**
365 * Returns the groups associated with a device.
366 *
367 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800368 * @return the group entries
369 */
370 @Override
371 public Iterable<Group> getGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800372 // Let ImmutableSet.copyOf do the type conversion
373 return ImmutableSet.copyOf(getStoredGroups(deviceId));
alshabib10580802015-02-18 18:30:33 -0800374 }
375
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700376 private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) {
Charles Chanf4838a72015-12-07 18:13:45 -0800377 NodeId master = mastershipService.getMasterFor(deviceId);
378 if (master == null) {
379 log.debug("Failed to getGroups: No master for {}", deviceId);
380 return Collections.emptySet();
381 }
382
383 Set<StoredGroupEntry> storedGroups = getGroupStoreKeyMap().values()
384 .stream()
385 .filter(input -> input.deviceId().equals(deviceId))
386 .collect(Collectors.toSet());
387 return ImmutableSet.copyOf(storedGroups);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700388 }
389
alshabib10580802015-02-18 18:30:33 -0800390 /**
391 * Returns the stored group entry.
392 *
alshabibb0285992016-03-28 23:30:37 -0700393 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800394 * @param appCookie the group key
alshabib10580802015-02-18 18:30:33 -0800395 * @return a group associated with the key
396 */
397 @Override
398 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700399 return getStoredGroupEntry(deviceId, appCookie);
400 }
401
402 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
403 GroupKey appCookie) {
404 return getGroupStoreKeyMap().get(new GroupStoreKeyMapKey(deviceId,
405 appCookie));
406 }
407
408 @Override
409 public Group getGroup(DeviceId deviceId, GroupId groupId) {
410 return getStoredGroupEntry(deviceId, groupId);
411 }
412
413 private StoredGroupEntry getStoredGroupEntry(DeviceId deviceId,
414 GroupId groupId) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700415 return getGroupIdTable(deviceId).get(groupId);
alshabib10580802015-02-18 18:30:33 -0800416 }
417
418 private int getFreeGroupIdValue(DeviceId deviceId) {
419 int freeId = groupIdGen.incrementAndGet();
420
421 while (true) {
Yi Tsengfa394de2017-02-01 11:26:40 -0800422 Group existing = getGroup(deviceId, new GroupId(freeId));
alshabib10580802015-02-18 18:30:33 -0800423 if (existing == null) {
424 existing = (
425 extraneousGroupEntriesById.get(deviceId) != null) ?
426 extraneousGroupEntriesById.get(deviceId).
Yi Tsengfa394de2017-02-01 11:26:40 -0800427 get(new GroupId(freeId)) :
alshabib10580802015-02-18 18:30:33 -0800428 null;
429 }
430 if (existing != null) {
431 freeId = groupIdGen.incrementAndGet();
432 } else {
433 break;
434 }
435 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700436 log.debug("getFreeGroupIdValue: Next Free ID is {}", freeId);
alshabib10580802015-02-18 18:30:33 -0800437 return freeId;
438 }
439
440 /**
441 * Stores a new group entry using the information from group description.
442 *
443 * @param groupDesc group description to be used to create group entry
444 */
445 @Override
446 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700447 log.debug("In storeGroupDescription");
alshabib10580802015-02-18 18:30:33 -0800448 // Check if a group is existing with the same key
Saurav Das8a0732e2015-11-20 15:27:53 -0800449 Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
450 if (existingGroup != null) {
Saurav Dasc568c342018-01-25 09:49:01 -0800451 log.debug("Group already exists with the same key {} in dev:{} with id:0x{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800452 groupDesc.appCookie(), groupDesc.deviceId(),
453 Integer.toHexString(existingGroup.id().id()));
alshabib10580802015-02-18 18:30:33 -0800454 return;
455 }
456
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700457 // Check if group to be created by a remote instance
Madan Jampani175e8fd2015-05-20 14:10:45 -0700458 if (mastershipService.getLocalRole(groupDesc.deviceId()) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700459 log.debug("storeGroupDescription: Device {} local role is not MASTER",
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700460 groupDesc.deviceId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700461 if (mastershipService.getMasterFor(groupDesc.deviceId()) == null) {
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530462 log.debug("No Master for device {}..."
463 + "Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700464 groupDesc.deviceId());
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530465 addToPendingAudit(groupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700466 return;
467 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700468 GroupStoreMessage groupOp = GroupStoreMessage.
469 createGroupAddRequestMsg(groupDesc.deviceId(),
470 groupDesc);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700471
Madan Jampani175e8fd2015-05-20 14:10:45 -0700472 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700473 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
474 clusterMsgSerializer::serialize,
475 mastershipService.getMasterFor(groupDesc.deviceId()))
476 .whenComplete((result, error) -> {
Madan Jampani175e8fd2015-05-20 14:10:45 -0700477 if (error != null) {
478 log.warn("Failed to send request to master: {} to {}",
alshabibb0285992016-03-28 23:30:37 -0700479 groupOp,
480 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700481 //TODO: Send Group operation failure event
482 } else {
483 log.debug("Sent Group operation request for device {} "
alshabibb0285992016-03-28 23:30:37 -0700484 + "to remote MASTER {}",
485 groupDesc.deviceId(),
486 mastershipService.getMasterFor(groupDesc.deviceId()));
Madan Jampani175e8fd2015-05-20 14:10:45 -0700487 }
488 });
alshabib10580802015-02-18 18:30:33 -0800489 return;
490 }
491
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700492 log.debug("Store group for device {} is getting handled locally",
493 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800494 storeGroupDescriptionInternal(groupDesc);
495 }
496
Sivachidambaram Subramanian9f816de2017-06-13 07:16:54 +0530497 private void addToPendingAudit(GroupDescription groupDesc) {
498 Integer groupIdVal = groupDesc.givenGroupId();
499 GroupId groupId = (groupIdVal != null) ? new GroupId(groupIdVal) : dummyGroupId;
500 addToPendingKeyTable(new DefaultGroup(groupId, groupDesc));
501 }
502
503 private void addToPendingKeyTable(StoredGroupEntry group) {
504 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
505 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
506 getPendingGroupKeyTable();
507 pendingKeyTable.put(new GroupStoreKeyMapKey(group.deviceId(),
508 group.appCookie()),
509 group);
510 }
511
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700512 private Group getMatchingExtraneousGroupbyId(DeviceId deviceId, Integer groupId) {
513 ConcurrentMap<GroupId, Group> extraneousMap =
514 extraneousGroupEntriesById.get(deviceId);
515 if (extraneousMap == null) {
516 return null;
517 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800518 return extraneousMap.get(new GroupId(groupId));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700519 }
520
521 private Group getMatchingExtraneousGroupbyBuckets(DeviceId deviceId,
522 GroupBuckets buckets) {
523 ConcurrentMap<GroupId, Group> extraneousMap =
524 extraneousGroupEntriesById.get(deviceId);
525 if (extraneousMap == null) {
526 return null;
527 }
528
alshabibb0285992016-03-28 23:30:37 -0700529 for (Group extraneousGroup : extraneousMap.values()) {
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700530 if (extraneousGroup.buckets().equals(buckets)) {
531 return extraneousGroup;
532 }
533 }
534 return null;
535 }
536
alshabib10580802015-02-18 18:30:33 -0800537 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
538 // Check if a group is existing with the same key
539 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
540 return;
541 }
542
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700543 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
544 // Device group audit has not completed yet
545 // Add this group description to pending group key table
546 // Create a group entry object with Dummy Group ID
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700547 log.debug("storeGroupDescriptionInternal: Device {} AUDIT pending...Queuing Group ADD request",
alshabibb0285992016-03-28 23:30:37 -0700548 groupDesc.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700549 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
550 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
Madan Jampani0b847532016-03-03 13:44:15 -0800551 Map<GroupStoreKeyMapKey, StoredGroupEntry> pendingKeyTable =
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700552 getPendingGroupKeyTable();
553 pendingKeyTable.put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
554 groupDesc.appCookie()),
555 group);
556 return;
557 }
558
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700559 Group matchingExtraneousGroup = null;
560 if (groupDesc.givenGroupId() != null) {
561 //Check if there is a extraneous group existing with the same Id
562 matchingExtraneousGroup = getMatchingExtraneousGroupbyId(
alshabibb0285992016-03-28 23:30:37 -0700563 groupDesc.deviceId(), groupDesc.givenGroupId());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700564 if (matchingExtraneousGroup != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800565 log.debug("storeGroupDescriptionInternal: Matching extraneous group "
alshabibb0285992016-03-28 23:30:37 -0700566 + "found 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 //Check if the group buckets matches with user provided buckets
570 if (matchingExtraneousGroup.buckets().equals(groupDesc.buckets())) {
571 //Group is already existing with the same buckets and Id
572 // Create a group entry object
Saurav Das0fd79d92016-03-07 10:58:36 -0800573 log.debug("storeGroupDescriptionInternal: Buckets also matching "
alshabibb0285992016-03-28 23:30:37 -0700574 + "in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700575 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800576 Integer.toHexString(groupDesc.givenGroupId()));
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700577 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700578 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700579 // Insert the newly created group entry into key and id maps
580 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700581 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
582 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700583 // Ensure it also inserted into group id based table to
584 // avoid any chances of duplication in group id generation
585 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700586 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700587 addOrUpdateGroupEntry(matchingExtraneousGroup);
588 removeExtraneousGroupEntry(matchingExtraneousGroup);
589 return;
590 } else {
591 //Group buckets are not matching. Update group
592 //with user provided buckets.
Saurav Das0fd79d92016-03-07 10:58:36 -0800593 log.debug("storeGroupDescriptionInternal: Buckets are not "
alshabibb0285992016-03-28 23:30:37 -0700594 + "matching in Device {} for group id 0x{}",
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700595 groupDesc.deviceId(),
Saurav Das0fd79d92016-03-07 10:58:36 -0800596 Integer.toHexString(groupDesc.givenGroupId()));
597 StoredGroupEntry modifiedGroup = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700598 matchingExtraneousGroup.id(), groupDesc);
Saurav Das0fd79d92016-03-07 10:58:36 -0800599 modifiedGroup.setState(GroupState.PENDING_UPDATE);
600 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700601 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
602 groupDesc.appCookie()), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800603 // Ensure it also inserted into group id based table to
604 // avoid any chances of duplication in group id generation
605 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700606 put(matchingExtraneousGroup.id(), modifiedGroup);
Saurav Das0fd79d92016-03-07 10:58:36 -0800607 removeExtraneousGroupEntry(matchingExtraneousGroup);
608 log.debug("storeGroupDescriptionInternal: Triggering Group "
alshabibb0285992016-03-28 23:30:37 -0700609 + "UPDATE request for {} in device {}",
Saurav Das0fd79d92016-03-07 10:58:36 -0800610 matchingExtraneousGroup.id(),
611 groupDesc.deviceId());
612 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, modifiedGroup));
613 return;
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700614 }
615 }
616 } else {
617 //Check if there is an extraneous group with user provided buckets
618 matchingExtraneousGroup = getMatchingExtraneousGroupbyBuckets(
alshabibb0285992016-03-28 23:30:37 -0700619 groupDesc.deviceId(), groupDesc.buckets());
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700620 if (matchingExtraneousGroup != null) {
621 //Group is already existing with the same buckets.
622 //So reuse this group.
623 log.debug("storeGroupDescriptionInternal: Matching extraneous group found in Device {}",
624 groupDesc.deviceId());
625 //Create a group entry object
626 StoredGroupEntry group = new DefaultGroup(
alshabibb0285992016-03-28 23:30:37 -0700627 matchingExtraneousGroup.id(), groupDesc);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700628 // Insert the newly created group entry into key and id maps
629 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700630 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
631 groupDesc.appCookie()), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700632 // Ensure it also inserted into group id based table to
633 // avoid any chances of duplication in group id generation
634 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700635 put(matchingExtraneousGroup.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -0700636 addOrUpdateGroupEntry(matchingExtraneousGroup);
637 removeExtraneousGroupEntry(matchingExtraneousGroup);
638 return;
639 } else {
640 //TODO: Check if there are any empty groups that can be used here
641 log.debug("storeGroupDescriptionInternal: No matching extraneous groups found in Device {}",
642 groupDesc.deviceId());
643 }
644 }
645
Saurav Das100e3b82015-04-30 11:12:10 -0700646 GroupId id = null;
647 if (groupDesc.givenGroupId() == null) {
648 // Get a new group identifier
Yi Tsengfa394de2017-02-01 11:26:40 -0800649 id = new GroupId(getFreeGroupIdValue(groupDesc.deviceId()));
Saurav Das100e3b82015-04-30 11:12:10 -0700650 } else {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800651 // we need to use the identifier passed in by caller, but check if
652 // already used
653 Group existing = getGroup(groupDesc.deviceId(),
Yi Tsengfa394de2017-02-01 11:26:40 -0800654 new GroupId(groupDesc.givenGroupId()));
Saurav Das8be4e3a2016-03-11 17:19:07 -0800655 if (existing != null) {
656 log.warn("Group already exists with the same id: 0x{} in dev:{} "
alshabibb0285992016-03-28 23:30:37 -0700657 + "but with different key: {} (request gkey: {})",
658 Integer.toHexString(groupDesc.givenGroupId()),
659 groupDesc.deviceId(),
660 existing.appCookie(),
661 groupDesc.appCookie());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800662 return;
663 }
Yi Tsengfa394de2017-02-01 11:26:40 -0800664 id = new GroupId(groupDesc.givenGroupId());
Saurav Das100e3b82015-04-30 11:12:10 -0700665 }
alshabib10580802015-02-18 18:30:33 -0800666 // Create a group entry object
667 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700668 // Insert the newly created group entry into key and id maps
669 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700670 put(new GroupStoreKeyMapKey(groupDesc.deviceId(),
671 groupDesc.appCookie()), group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700672 // Ensure it also inserted into group id based table to
673 // avoid any chances of duplication in group id generation
674 getGroupIdTable(groupDesc.deviceId()).
alshabibb0285992016-03-28 23:30:37 -0700675 put(id, group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700676 log.debug("storeGroupDescriptionInternal: Processing Group ADD request for Id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700677 id,
678 groupDesc.deviceId());
alshabib10580802015-02-18 18:30:33 -0800679 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
680 group));
681 }
682
683 /**
684 * Updates the existing group entry with the information
685 * from group description.
686 *
alshabibb0285992016-03-28 23:30:37 -0700687 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800688 * @param oldAppCookie the current group key
alshabibb0285992016-03-28 23:30:37 -0700689 * @param type update type
690 * @param newBuckets group buckets for updates
alshabib10580802015-02-18 18:30:33 -0800691 * @param newAppCookie optional new group key
692 */
693 @Override
694 public void updateGroupDescription(DeviceId deviceId,
695 GroupKey oldAppCookie,
696 UpdateType type,
697 GroupBuckets newBuckets,
698 GroupKey newAppCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700699 // Check if group update to be done by a remote instance
sangho52abe3a2015-05-05 14:13:34 -0700700 if (mastershipService.getMasterFor(deviceId) != null &&
701 mastershipService.getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700702 log.debug("updateGroupDescription: Device {} local role is not MASTER",
703 deviceId);
704 if (mastershipService.getMasterFor(deviceId) == null) {
705 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700706 + "Can not perform update group operation",
707 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700708 //TODO: Send Group operation failure event
709 return;
710 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700711 GroupStoreMessage groupOp = GroupStoreMessage.
712 createGroupUpdateRequestMsg(deviceId,
713 oldAppCookie,
714 type,
715 newBuckets,
716 newAppCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700717
Madan Jampani175e8fd2015-05-20 14:10:45 -0700718 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700719 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
720 clusterMsgSerializer::serialize,
721 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
722 if (error != null) {
723 log.warn("Failed to send request to master: {} to {}",
724 groupOp,
725 mastershipService.getMasterFor(deviceId), error);
726 }
727 //TODO: Send Group operation failure event
728 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700729 return;
730 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700731 log.debug("updateGroupDescription for device {} is getting handled locally",
732 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700733 updateGroupDescriptionInternal(deviceId,
734 oldAppCookie,
735 type,
736 newBuckets,
737 newAppCookie);
738 }
739
740 private void updateGroupDescriptionInternal(DeviceId deviceId,
alshabibb0285992016-03-28 23:30:37 -0700741 GroupKey oldAppCookie,
742 UpdateType type,
743 GroupBuckets newBuckets,
744 GroupKey newAppCookie) {
alshabib10580802015-02-18 18:30:33 -0800745 // Check if a group is existing with the provided key
746 Group oldGroup = getGroup(deviceId, oldAppCookie);
747 if (oldGroup == null) {
Saurav Das8be4e3a2016-03-11 17:19:07 -0800748 log.warn("updateGroupDescriptionInternal: Group not found...strange. "
alshabibb0285992016-03-28 23:30:37 -0700749 + "GroupKey:{} DeviceId:{}", oldAppCookie, deviceId);
alshabib10580802015-02-18 18:30:33 -0800750 return;
751 }
752
753 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
754 type,
755 newBuckets);
756 if (newBucketList != null) {
757 // Create a new group object from the old group
758 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
759 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
760 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
761 oldGroup.deviceId(),
762 oldGroup.type(),
763 updatedBuckets,
764 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700765 oldGroup.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -0800766 oldGroup.appId());
767 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
768 updatedGroupDesc);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700769 log.debug("updateGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_UPDATE",
alshabibb0285992016-03-28 23:30:37 -0700770 oldGroup.id(),
771 oldGroup.deviceId(),
772 oldGroup.state());
alshabib10580802015-02-18 18:30:33 -0800773 newGroup.setState(GroupState.PENDING_UPDATE);
774 newGroup.setLife(oldGroup.life());
775 newGroup.setPackets(oldGroup.packets());
776 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700777 //Update the group entry in groupkey based map.
778 //Update to groupid based map will happen in the
779 //groupkey based map update listener
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700780 log.debug("updateGroupDescriptionInternal with type {}: Group updated with buckets",
781 type);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700782 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700783 put(new GroupStoreKeyMapKey(newGroup.deviceId(),
784 newGroup.appCookie()), newGroup);
alshabib10580802015-02-18 18:30:33 -0800785 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700786 } else {
787 log.warn("updateGroupDescriptionInternal with type {}: No "
alshabibb0285992016-03-28 23:30:37 -0700788 + "change in the buckets in update", type);
alshabib10580802015-02-18 18:30:33 -0800789 }
790 }
791
792 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
793 UpdateType type,
794 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300795 if (type == UpdateType.SET) {
796 return buckets.buckets();
797 }
798
Victor Silvadf1eeae2016-08-12 15:28:57 -0300799 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
800 List<GroupBucket> updatedBucketList = new ArrayList<>();
alshabib10580802015-02-18 18:30:33 -0800801 boolean groupDescUpdated = false;
802
803 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300804 List<GroupBucket> newBuckets = buckets.buckets();
805
806 // Add old buckets that will not be updated and check if any will be updated.
807 for (GroupBucket oldBucket : oldBuckets) {
808 int newBucketIndex = newBuckets.indexOf(oldBucket);
809
810 if (newBucketIndex != -1) {
811 GroupBucket newBucket = newBuckets.get(newBucketIndex);
812 if (!newBucket.hasSameParameters(oldBucket)) {
813 // Bucket will be updated
814 groupDescUpdated = true;
815 }
816 } else {
817 // Old bucket will remain the same - add it.
818 updatedBucketList.add(oldBucket);
alshabib10580802015-02-18 18:30:33 -0800819 }
820 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300821
822 // Add all new buckets
823 updatedBucketList.addAll(newBuckets);
824 if (!oldBuckets.containsAll(newBuckets)) {
825 groupDescUpdated = true;
826 }
827
alshabib10580802015-02-18 18:30:33 -0800828 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300829 List<GroupBucket> bucketsToRemove = buckets.buckets();
830
831 // Check which old buckets should remain
832 for (GroupBucket oldBucket : oldBuckets) {
833 if (!bucketsToRemove.contains(oldBucket)) {
834 updatedBucketList.add(oldBucket);
835 } else {
alshabib10580802015-02-18 18:30:33 -0800836 groupDescUpdated = true;
837 }
838 }
839 }
840
841 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300842 return updatedBucketList;
alshabib10580802015-02-18 18:30:33 -0800843 } else {
844 return null;
845 }
846 }
847
848 /**
849 * Triggers deleting the existing group entry.
850 *
alshabibb0285992016-03-28 23:30:37 -0700851 * @param deviceId the device ID
alshabib10580802015-02-18 18:30:33 -0800852 * @param appCookie the group key
853 */
854 @Override
855 public void deleteGroupDescription(DeviceId deviceId,
856 GroupKey appCookie) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700857 // Check if group to be deleted by a remote instance
858 if (mastershipService.
859 getLocalRole(deviceId) != MastershipRole.MASTER) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700860 log.debug("deleteGroupDescription: Device {} local role is not MASTER",
861 deviceId);
862 if (mastershipService.getMasterFor(deviceId) == null) {
863 log.error("No Master for device {}..."
alshabibb0285992016-03-28 23:30:37 -0700864 + "Can not perform delete group operation",
865 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700866 //TODO: Send Group operation failure event
867 return;
868 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700869 GroupStoreMessage groupOp = GroupStoreMessage.
870 createGroupDeleteRequestMsg(deviceId,
871 appCookie);
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700872
Madan Jampani175e8fd2015-05-20 14:10:45 -0700873 clusterCommunicator.unicast(groupOp,
alshabibb0285992016-03-28 23:30:37 -0700874 GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST,
875 clusterMsgSerializer::serialize,
876 mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> {
877 if (error != null) {
878 log.warn("Failed to send request to master: {} to {}",
879 groupOp,
880 mastershipService.getMasterFor(deviceId), error);
881 }
882 //TODO: Send Group operation failure event
883 });
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700884 return;
885 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700886 log.debug("deleteGroupDescription in device {} is getting handled locally",
887 deviceId);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700888 deleteGroupDescriptionInternal(deviceId, appCookie);
889 }
890
891 private void deleteGroupDescriptionInternal(DeviceId deviceId,
892 GroupKey appCookie) {
alshabib10580802015-02-18 18:30:33 -0800893 // Check if a group is existing with the provided key
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700894 StoredGroupEntry existing = getStoredGroupEntry(deviceId, appCookie);
alshabib10580802015-02-18 18:30:33 -0800895 if (existing == null) {
896 return;
897 }
898
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700899 log.debug("deleteGroupDescriptionInternal: group entry {} in device {} moving from {} to PENDING_DELETE",
alshabibb0285992016-03-28 23:30:37 -0700900 existing.id(),
901 existing.deviceId(),
902 existing.state());
alshabib10580802015-02-18 18:30:33 -0800903 synchronized (existing) {
904 existing.setState(GroupState.PENDING_DELETE);
Saurav Das80980c72016-03-23 11:22:49 -0700905 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700906 put(new GroupStoreKeyMapKey(existing.deviceId(), existing.appCookie()),
907 existing);
alshabib10580802015-02-18 18:30:33 -0800908 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700909 log.debug("deleteGroupDescriptionInternal: in device {} issuing GROUP_REMOVE_REQUESTED",
910 deviceId);
alshabib10580802015-02-18 18:30:33 -0800911 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
912 }
913
914 /**
915 * Stores a new group entry, or updates an existing entry.
916 *
917 * @param group group entry
918 */
919 @Override
920 public void addOrUpdateGroupEntry(Group group) {
921 // check if this new entry is an update to an existing entry
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700922 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
923 group.id());
alshabib10580802015-02-18 18:30:33 -0800924 GroupEvent event = null;
925
926 if (existing != null) {
Saurav Das0fd79d92016-03-07 10:58:36 -0800927 log.trace("addOrUpdateGroupEntry: updating group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700928 group.id(),
929 group.deviceId());
alshabib10580802015-02-18 18:30:33 -0800930 synchronized (existing) {
alshabibb0285992016-03-28 23:30:37 -0700931 for (GroupBucket bucket : group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700932 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700933 existing.buckets().buckets()
alshabibb0285992016-03-28 23:30:37 -0700934 .stream()
935 .filter((existingBucket) -> (existingBucket.equals(bucket)))
936 .findFirst();
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700937 if (matchingBucket.isPresent()) {
938 ((StoredGroupBucketEntry) matchingBucket.
939 get()).setPackets(bucket.packets());
940 ((StoredGroupBucketEntry) matchingBucket.
941 get()).setBytes(bucket.bytes());
942 } else {
943 log.warn("addOrUpdateGroupEntry: No matching "
alshabibb0285992016-03-28 23:30:37 -0700944 + "buckets to update stats");
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700945 }
946 }
alshabib10580802015-02-18 18:30:33 -0800947 existing.setLife(group.life());
948 existing.setPackets(group.packets());
949 existing.setBytes(group.bytes());
alshabibb0285992016-03-28 23:30:37 -0700950 existing.setReferenceCount(group.referenceCount());
Saurav Das137f27f2018-06-11 17:02:31 -0700951 existing.setFailedRetryCount(0);
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700952 if ((existing.state() == GroupState.PENDING_ADD) ||
alshabibb0285992016-03-28 23:30:37 -0700953 (existing.state() == GroupState.PENDING_ADD_RETRY)) {
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 existing.state());
alshabib10580802015-02-18 18:30:33 -0800958 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700959 existing.setIsGroupStateAddedFirstTime(true);
alshabib10580802015-02-18 18:30:33 -0800960 event = new GroupEvent(Type.GROUP_ADDED, existing);
961 } else {
Saurav Das0fd79d92016-03-07 10:58:36 -0800962 log.trace("addOrUpdateGroupEntry: group entry {} in device {} moving from {} to ADDED",
alshabibb0285992016-03-28 23:30:37 -0700963 existing.id(),
964 existing.deviceId(),
965 GroupState.PENDING_UPDATE);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700966 existing.setState(GroupState.ADDED);
967 existing.setIsGroupStateAddedFirstTime(false);
alshabib10580802015-02-18 18:30:33 -0800968 event = new GroupEvent(Type.GROUP_UPDATED, existing);
969 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700970 //Re-PUT map entries to trigger map update events
971 getGroupStoreKeyMap().
alshabibb0285992016-03-28 23:30:37 -0700972 put(new GroupStoreKeyMapKey(existing.deviceId(),
973 existing.appCookie()), existing);
alshabib10580802015-02-18 18:30:33 -0800974 }
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -0700975 } else {
976 log.warn("addOrUpdateGroupEntry: Group update "
alshabibb0285992016-03-28 23:30:37 -0700977 + "happening for a non-existing entry in the map");
alshabib10580802015-02-18 18:30:33 -0800978 }
979
Saurav Das137f27f2018-06-11 17:02:31 -0700980 // XXX if map is going to trigger event, is this one needed?
alshabib10580802015-02-18 18:30:33 -0800981 if (event != null) {
982 notifyDelegate(event);
983 }
984 }
985
986 /**
987 * Removes the group entry from store.
988 *
989 * @param group group entry
990 */
991 @Override
992 public void removeGroupEntry(Group group) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700993 StoredGroupEntry existing = getStoredGroupEntry(group.deviceId(),
994 group.id());
alshabib10580802015-02-18 18:30:33 -0800995
996 if (existing != null) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700997 log.debug("removeGroupEntry: removing group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -0700998 group.id(),
999 group.deviceId());
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001000 //Removal from groupid based map will happen in the
1001 //map update listener
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001002 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1003 existing.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001004 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001005 } else {
1006 log.warn("removeGroupEntry for {} in device{} is "
alshabibb0285992016-03-28 23:30:37 -07001007 + "not existing in our maps",
1008 group.id(),
1009 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001010 }
1011 }
1012
Victor Silva4e8b7832016-08-17 17:11:19 -03001013 private void purgeGroupEntries(Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entries) {
1014 entries.forEach(entry -> {
1015 groupStoreEntriesByKey.remove(entry.getKey());
1016 });
1017 }
1018
alshabib10580802015-02-18 18:30:33 -08001019 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -08001020 public void purgeGroupEntry(DeviceId deviceId) {
Victor Silva4e8b7832016-08-17 17:11:19 -03001021 Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entriesPendingRemove =
Charles Chan0c7c43b2016-01-14 17:39:20 -08001022 new HashSet<>();
1023
Madan Jampani0b847532016-03-03 13:44:15 -08001024 getGroupStoreKeyMap().entrySet().stream()
Charles Chan0c7c43b2016-01-14 17:39:20 -08001025 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
Victor Silva4e8b7832016-08-17 17:11:19 -03001026 .forEach(entriesPendingRemove::add);
Charles Chan0c7c43b2016-01-14 17:39:20 -08001027
Victor Silva4e8b7832016-08-17 17:11:19 -03001028 purgeGroupEntries(entriesPendingRemove);
1029 }
1030
1031 @Override
1032 public void purgeGroupEntries() {
1033 purgeGroupEntries(getGroupStoreKeyMap().entrySet());
Charles Chan0c7c43b2016-01-14 17:39:20 -08001034 }
1035
1036 @Override
alshabib10580802015-02-18 18:30:33 -08001037 public void deviceInitialAuditCompleted(DeviceId deviceId,
1038 boolean completed) {
1039 synchronized (deviceAuditStatus) {
1040 if (completed) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001041 log.debug("AUDIT completed for device {}",
1042 deviceId);
alshabib10580802015-02-18 18:30:33 -08001043 deviceAuditStatus.put(deviceId, true);
1044 // Execute all pending group requests
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001045 List<StoredGroupEntry> pendingGroupRequests =
1046 getPendingGroupKeyTable().values()
alshabibb0285992016-03-28 23:30:37 -07001047 .stream()
1048 .filter(g -> g.deviceId().equals(deviceId))
1049 .collect(Collectors.toList());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001050 log.debug("processing pending group add requests for device {} and number of pending requests {}",
alshabibb0285992016-03-28 23:30:37 -07001051 deviceId,
1052 pendingGroupRequests.size());
1053 for (Group group : pendingGroupRequests) {
alshabib10580802015-02-18 18:30:33 -08001054 GroupDescription tmp = new DefaultGroupDescription(
1055 group.deviceId(),
1056 group.type(),
1057 group.buckets(),
1058 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -07001059 group.givenGroupId(),
alshabib10580802015-02-18 18:30:33 -08001060 group.appId());
1061 storeGroupDescriptionInternal(tmp);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001062 getPendingGroupKeyTable().
alshabibb0285992016-03-28 23:30:37 -07001063 remove(new GroupStoreKeyMapKey(deviceId, group.appCookie()));
alshabib10580802015-02-18 18:30:33 -08001064 }
alshabib10580802015-02-18 18:30:33 -08001065 } else {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001066 Boolean audited = deviceAuditStatus.get(deviceId);
1067 if (audited != null && audited) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001068 log.debug("Clearing AUDIT status for device {}", deviceId);
alshabib10580802015-02-18 18:30:33 -08001069 deviceAuditStatus.put(deviceId, false);
1070 }
1071 }
1072 }
1073 }
1074
1075 @Override
1076 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
1077 synchronized (deviceAuditStatus) {
Thomas Vachuskac40d4632015-04-09 16:55:03 -07001078 Boolean audited = deviceAuditStatus.get(deviceId);
1079 return audited != null && audited;
alshabib10580802015-02-18 18:30:33 -08001080 }
1081 }
1082
1083 @Override
1084 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
1085
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001086 StoredGroupEntry existing = getStoredGroupEntry(deviceId,
1087 operation.groupId());
alshabib10580802015-02-18 18:30:33 -08001088
1089 if (existing == null) {
1090 log.warn("No group entry with ID {} found ", operation.groupId());
1091 return;
1092 }
1093
Saurav Das137f27f2018-06-11 17:02:31 -07001094 log.warn("groupOperationFailed: group operation {} failed in state {} "
alshabibb0285992016-03-28 23:30:37 -07001095 + "for group {} in device {} with code {}",
1096 operation.opType(),
Saurav Das137f27f2018-06-11 17:02:31 -07001097 existing.state(),
alshabibb0285992016-03-28 23:30:37 -07001098 existing.id(),
1099 existing.deviceId(),
1100 operation.failureCode());
Saurav Das0fd79d92016-03-07 10:58:36 -08001101 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.GROUP_EXISTS) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001102 if (operation.buckets().equals(existing.buckets())) {
Saurav Dasc88d4662017-05-15 15:34:25 -07001103 if (existing.state() == GroupState.PENDING_ADD ||
1104 existing.state() == GroupState.PENDING_ADD_RETRY) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001105 log.info("GROUP_EXISTS: GroupID and Buckets match for group in pending "
alshabibb0285992016-03-28 23:30:37 -07001106 + "add state - moving to ADDED for group {} in device {}",
1107 existing.id(), deviceId);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001108 addOrUpdateGroupEntry(existing);
1109 return;
1110 } else {
Saurav Dasc88d4662017-05-15 15:34:25 -07001111 log.warn("GROUP_EXISTS: GroupId and Buckets match but existing"
1112 + "group in state: {}", existing.state());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001113 }
Saurav Dasc88d4662017-05-15 15:34:25 -07001114 } else {
1115 log.warn("GROUP EXISTS: Group ID matched but buckets did not. "
1116 + "Operation: {} Existing: {}", operation.buckets(),
1117 existing.buckets());
Saurav Das8be4e3a2016-03-11 17:19:07 -08001118 }
Saurav Das0fd79d92016-03-07 10:58:36 -08001119 }
Saurav Das137f27f2018-06-11 17:02:31 -07001120 if (operation.failureCode() == GroupOperation.GroupMsgErrorCode.INVALID_GROUP) {
1121 existing.incrFailedRetryCount();
1122 if (existing.failedRetryCount() < MAX_FAILED_ATTEMPTS) {
1123 log.warn("Group {} programming failed {} of {} times in dev {}, "
1124 + "retrying ..", existing.id(),
1125 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1126 deviceId);
1127 return;
1128 }
1129 log.warn("Group {} programming failed {} of {} times in dev {}, "
1130 + "removing group from store", existing.id(),
1131 existing.failedRetryCount(), MAX_FAILED_ATTEMPTS,
1132 deviceId);
1133 // fall through to case
1134 }
1135
alshabib10580802015-02-18 18:30:33 -08001136 switch (operation.opType()) {
1137 case ADD:
Saurav Das137f27f2018-06-11 17:02:31 -07001138 if (existing.state() == GroupState.PENDING_ADD
1139 || existing.state() == GroupState.PENDING_ADD_RETRY) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001140 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
1141 log.warn("groupOperationFailed: cleaningup "
alshabibb0285992016-03-28 23:30:37 -07001142 + "group {} from store in device {}....",
1143 existing.id(),
1144 existing.deviceId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001145 //Removal from groupid based map will happen in the
1146 //map update listener
1147 getGroupStoreKeyMap().remove(new GroupStoreKeyMapKey(existing.deviceId(),
1148 existing.appCookie()));
1149 }
alshabib10580802015-02-18 18:30:33 -08001150 break;
1151 case MODIFY:
1152 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
1153 break;
1154 case DELETE:
1155 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
1156 break;
1157 default:
1158 log.warn("Unknown group operation type {}", operation.opType());
1159 }
alshabib10580802015-02-18 18:30:33 -08001160 }
1161
1162 @Override
1163 public void addOrUpdateExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001164 log.debug("add/update extraneous group entry {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001165 group.id(),
1166 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001167 ConcurrentMap<GroupId, Group> extraneousIdTable =
1168 getExtraneousGroupIdTable(group.deviceId());
1169 extraneousIdTable.put(group.id(), group);
Srikanth Vavilapallie48b3cf2015-07-06 11:43:07 -07001170 // Don't remove the extraneous groups, instead re-use it when
1171 // a group request comes with the same set of buckets
alshabib10580802015-02-18 18:30:33 -08001172 }
1173
1174 @Override
1175 public void removeExtraneousGroupEntry(Group group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001176 log.debug("remove extraneous group entry {} of device {} from store",
alshabibb0285992016-03-28 23:30:37 -07001177 group.id(),
1178 group.deviceId());
alshabib10580802015-02-18 18:30:33 -08001179 ConcurrentMap<GroupId, Group> extraneousIdTable =
1180 getExtraneousGroupIdTable(group.deviceId());
1181 extraneousIdTable.remove(group.id());
1182 }
1183
1184 @Override
1185 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
1186 // flatten and make iterator unmodifiable
1187 return FluentIterable.from(
1188 getExtraneousGroupIdTable(deviceId).values());
1189 }
1190
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001191 /**
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001192 * Map handler to receive any events when the group key map is updated.
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001193 */
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001194 private class GroupStoreKeyMapListener implements
Madan Jampani0b847532016-03-03 13:44:15 -08001195 MapEventListener<GroupStoreKeyMapKey, StoredGroupEntry> {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001196
1197 @Override
Madan Jampani0b847532016-03-03 13:44:15 -08001198 public void event(MapEvent<GroupStoreKeyMapKey, StoredGroupEntry> mapEvent) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001199 GroupEvent groupEvent = null;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001200 GroupStoreKeyMapKey key = mapEvent.key();
Madan Jampani0b847532016-03-03 13:44:15 -08001201 StoredGroupEntry group = Versioned.valueOrNull(mapEvent.newValue());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001202 if ((key == null) && (group == null)) {
1203 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001204 + "event {} with null entry", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001205 return;
1206 } else if (group == null) {
1207 group = getGroupIdTable(key.deviceId()).values()
1208 .stream()
1209 .filter((storedGroup) -> (storedGroup.appCookie().equals(key.appCookie)))
Yuta HIGUCHI6e5f4702016-11-21 11:42:11 -08001210 .findFirst().orElse(null);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001211 if (group == null) {
1212 log.error("GroupStoreKeyMapListener: Received "
alshabibb0285992016-03-28 23:30:37 -07001213 + "event {} with null entry... can not process", mapEvent.type());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001214 return;
1215 }
1216 }
1217 log.trace("received groupid map event {} for id {} in device {}",
1218 mapEvent.type(),
1219 group.id(),
jaegonkim68e080c2016-12-01 22:31:01 +09001220 (key != null ? key.deviceId() : null));
Madan Jampani0b847532016-03-03 13:44:15 -08001221 if (mapEvent.type() == MapEvent.Type.INSERT || mapEvent.type() == MapEvent.Type.UPDATE) {
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001222 // Update the group ID table
1223 getGroupIdTable(group.deviceId()).put(group.id(), group);
Madan Jampani0b847532016-03-03 13:44:15 -08001224 StoredGroupEntry value = Versioned.valueOrNull(mapEvent.newValue());
1225 if (value.state() == Group.GroupState.ADDED) {
1226 if (value.isGroupStateAddedFirstTime()) {
1227 groupEvent = new GroupEvent(Type.GROUP_ADDED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001228 log.trace("Received first time 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 } else {
Madan Jampani0b847532016-03-03 13:44:15 -08001232 groupEvent = new GroupEvent(Type.GROUP_UPDATED, value);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001233 log.trace("Received following GROUP_ADDED state update for id {} in device {}",
alshabibb0285992016-03-28 23:30:37 -07001234 group.id(),
1235 group.deviceId());
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001236 }
1237 }
Madan Jampani0b847532016-03-03 13:44:15 -08001238 } else if (mapEvent.type() == MapEvent.Type.REMOVE) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001239 groupEvent = new GroupEvent(Type.GROUP_REMOVED, group);
Srikanth Vavilapalli6a9d4e42015-03-30 19:41:56 -07001240 // Remove the entry from the group ID table
1241 getGroupIdTable(group.deviceId()).remove(group.id(), group);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001242 }
1243
1244 if (groupEvent != null) {
1245 notifyDelegate(groupEvent);
1246 }
1247 }
1248 }
Madan Jampani01e05fb2015-08-13 13:29:36 -07001249
helenyrwua1c41152016-08-18 16:16:14 -07001250 private void processGroupMessage(GroupStoreMessage message) {
1251 if (message.type() == GroupStoreMessage.Type.FAILOVER) {
1252 // FIXME: groupStoreEntriesByKey inaccessible here
1253 getGroupIdTable(message.deviceId()).values()
1254 .stream()
1255 .filter((storedGroup) -> (storedGroup.appCookie().equals(message.appCookie())))
1256 .findFirst().ifPresent(group -> notifyDelegate(new GroupEvent(Type.GROUP_BUCKET_FAILOVER, group)));
1257 }
1258 }
1259
Madan Jampani01e05fb2015-08-13 13:29:36 -07001260 private void process(GroupStoreMessage groupOp) {
1261 log.debug("Received remote group operation {} request for device {}",
alshabibb0285992016-03-28 23:30:37 -07001262 groupOp.type(),
1263 groupOp.deviceId());
1264 if (!mastershipService.isLocalMaster(groupOp.deviceId())) {
1265 log.warn("This node is not MASTER for device {}", groupOp.deviceId());
1266 return;
1267 }
1268 if (groupOp.type() == GroupStoreMessage.Type.ADD) {
1269 storeGroupDescriptionInternal(groupOp.groupDesc());
1270 } else if (groupOp.type() == GroupStoreMessage.Type.UPDATE) {
1271 updateGroupDescriptionInternal(groupOp.deviceId(),
1272 groupOp.appCookie(),
1273 groupOp.updateType(),
1274 groupOp.updateBuckets(),
1275 groupOp.newAppCookie());
1276 } else if (groupOp.type() == GroupStoreMessage.Type.DELETE) {
1277 deleteGroupDescriptionInternal(groupOp.deviceId(),
1278 groupOp.appCookie());
1279 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001280 }
1281
1282 /**
1283 * Flattened map key to be used to store group entries.
1284 */
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001285 protected static class GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001286 private final DeviceId deviceId;
1287
1288 public GroupStoreMapKey(DeviceId deviceId) {
1289 this.deviceId = deviceId;
1290 }
1291
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001292 public DeviceId deviceId() {
1293 return deviceId;
1294 }
1295
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001296 @Override
1297 public boolean equals(Object o) {
1298 if (this == o) {
1299 return true;
1300 }
1301 if (!(o instanceof GroupStoreMapKey)) {
1302 return false;
1303 }
1304 GroupStoreMapKey that = (GroupStoreMapKey) o;
1305 return this.deviceId.equals(that.deviceId);
1306 }
1307
1308 @Override
1309 public int hashCode() {
1310 int result = 17;
1311
1312 result = 31 * result + Objects.hash(this.deviceId);
1313
1314 return result;
1315 }
1316 }
1317
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001318 protected static class GroupStoreKeyMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001319 private final GroupKey appCookie;
alshabibb0285992016-03-28 23:30:37 -07001320
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001321 public GroupStoreKeyMapKey(DeviceId deviceId,
1322 GroupKey appCookie) {
1323 super(deviceId);
1324 this.appCookie = appCookie;
1325 }
1326
1327 @Override
1328 public boolean equals(Object o) {
1329 if (this == o) {
1330 return true;
1331 }
1332 if (!(o instanceof GroupStoreKeyMapKey)) {
1333 return false;
1334 }
1335 GroupStoreKeyMapKey that = (GroupStoreKeyMapKey) o;
1336 return (super.equals(that) &&
1337 this.appCookie.equals(that.appCookie));
1338 }
1339
1340 @Override
1341 public int hashCode() {
1342 int result = 17;
1343
1344 result = 31 * result + super.hashCode() + Objects.hash(this.appCookie);
1345
1346 return result;
1347 }
1348 }
1349
Ray Milkeyb3c5ce22015-08-10 09:07:36 -07001350 protected static class GroupStoreIdMapKey extends GroupStoreMapKey {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001351 private final GroupId groupId;
alshabibb0285992016-03-28 23:30:37 -07001352
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -07001353 public GroupStoreIdMapKey(DeviceId deviceId,
1354 GroupId groupId) {
1355 super(deviceId);
1356 this.groupId = groupId;
1357 }
1358
1359 @Override
1360 public boolean equals(Object o) {
1361 if (this == o) {
1362 return true;
1363 }
1364 if (!(o instanceof GroupStoreIdMapKey)) {
1365 return false;
1366 }
1367 GroupStoreIdMapKey that = (GroupStoreIdMapKey) o;
1368 return (super.equals(that) &&
1369 this.groupId.equals(that.groupId));
1370 }
1371
1372 @Override
1373 public int hashCode() {
1374 int result = 17;
1375
1376 result = 31 * result + super.hashCode() + Objects.hash(this.groupId);
1377
1378 return result;
1379 }
1380 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001381
1382 @Override
1383 public void pushGroupMetrics(DeviceId deviceId,
1384 Collection<Group> groupEntries) {
1385 boolean deviceInitialAuditStatus =
1386 deviceInitialAuditStatus(deviceId);
1387 Set<Group> southboundGroupEntries =
1388 Sets.newHashSet(groupEntries);
1389 Set<StoredGroupEntry> storedGroupEntries =
1390 Sets.newHashSet(getStoredGroups(deviceId));
1391 Set<Group> extraneousStoredEntries =
1392 Sets.newHashSet(getExtraneousGroups(deviceId));
1393
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001394 if (log.isTraceEnabled()) {
1395 log.trace("pushGroupMetrics: Displaying all ({}) southboundGroupEntries for device {}",
1396 southboundGroupEntries.size(),
1397 deviceId);
1398 for (Group group : southboundGroupEntries) {
1399 log.trace("Group {} in device {}", group, deviceId);
1400 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001401
Sho SHIMIZU695bac62016-08-15 12:41:59 -07001402 log.trace("Displaying all ({}) stored group entries for device {}",
1403 storedGroupEntries.size(),
1404 deviceId);
1405 for (StoredGroupEntry group : storedGroupEntries) {
1406 log.trace("Stored Group {} for device {}", group, deviceId);
1407 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001408 }
1409
alshabibb0285992016-03-28 23:30:37 -07001410 garbageCollect(deviceId, southboundGroupEntries, storedGroupEntries);
1411
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001412 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
1413 Group group = it2.next();
1414 if (storedGroupEntries.remove(group)) {
1415 // we both have the group, let's update some info then.
1416 log.trace("Group AUDIT: group {} exists in both planes for device {}",
alshabibb0285992016-03-28 23:30:37 -07001417 group.id(), deviceId);
1418
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001419 groupAdded(group);
1420 it2.remove();
1421 }
1422 }
1423 for (Group group : southboundGroupEntries) {
1424 if (getGroup(group.deviceId(), group.id()) != null) {
1425 // There is a group existing with the same id
1426 // It is possible that group update is
1427 // in progress while we got a stale info from switch
1428 if (!storedGroupEntries.remove(getGroup(
alshabibb0285992016-03-28 23:30:37 -07001429 group.deviceId(), group.id()))) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001430 log.warn("Group AUDIT: Inconsistent state:"
alshabibb0285992016-03-28 23:30:37 -07001431 + "Group exists in ID based table while "
1432 + "not present in key based table");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001433 }
1434 } else {
1435 // there are groups in the switch that aren't in the store
1436 log.debug("Group AUDIT: extraneous group {} exists in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001437 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001438 extraneousStoredEntries.remove(group);
Kavitha Alagesanc56cded2017-01-13 10:48:18 +05301439 if (allowExtraneousGroups) {
1440 extraneousGroup(group);
1441 } else {
1442 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
1443 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001444 }
1445 }
Charles Chan07f15f22018-05-08 21:35:50 -07001446 for (StoredGroupEntry group : storedGroupEntries) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001447 // there are groups in the store that aren't in the switch
1448 log.debug("Group AUDIT: group {} missing in data plane for device {}",
alshabibb0285992016-03-28 23:30:37 -07001449 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001450 groupMissing(group);
1451 }
1452 for (Group group : extraneousStoredEntries) {
1453 // there are groups in the extraneous store that
1454 // aren't in the switch
Saurav Das0fd79d92016-03-07 10:58:36 -08001455 log.debug("Group AUDIT: clearing extraneous group {} from store for device {}",
alshabibb0285992016-03-28 23:30:37 -07001456 group.id(), deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001457 removeExtraneousGroupEntry(group);
1458 }
1459
1460 if (!deviceInitialAuditStatus) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001461 log.info("Group AUDIT: Setting device {} initial AUDIT completed",
alshabibb0285992016-03-28 23:30:37 -07001462 deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001463 deviceInitialAuditCompleted(deviceId, true);
1464 }
1465 }
1466
helenyrwu89470f12016-08-12 13:18:10 -07001467 @Override
1468 public void notifyOfFailovers(Collection<Group> failoverGroups) {
helenyrwu89470f12016-08-12 13:18:10 -07001469 failoverGroups.forEach(group -> {
1470 if (group.type() == Group.Type.FAILOVER) {
helenyrwua1c41152016-08-18 16:16:14 -07001471 groupTopic.publish(GroupStoreMessage.createGroupFailoverMsg(
1472 group.deviceId(), group));
helenyrwu89470f12016-08-12 13:18:10 -07001473 }
1474 });
helenyrwu89470f12016-08-12 13:18:10 -07001475 }
1476
alshabibb0285992016-03-28 23:30:37 -07001477 private void garbageCollect(DeviceId deviceId,
1478 Set<Group> southboundGroupEntries,
1479 Set<StoredGroupEntry> storedGroupEntries) {
1480 if (!garbageCollect) {
1481 return;
1482 }
1483
1484 Iterator<StoredGroupEntry> it = storedGroupEntries.iterator();
1485 while (it.hasNext()) {
1486 StoredGroupEntry group = it.next();
1487 if (group.state() != GroupState.PENDING_DELETE && checkGroupRefCount(group)) {
1488 log.debug("Garbage collecting group {} on {}", group, deviceId);
1489 deleteGroupDescription(deviceId, group.appCookie());
1490 southboundGroupEntries.remove(group);
1491 it.remove();
1492 }
1493 }
1494 }
1495
1496 private boolean checkGroupRefCount(Group group) {
1497 return (group.referenceCount() == 0 && group.age() >= gcThresh);
1498 }
1499
Charles Chan07f15f22018-05-08 21:35:50 -07001500 private void groupMissing(StoredGroupEntry group) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001501 switch (group.state()) {
1502 case PENDING_DELETE:
1503 log.debug("Group {} delete confirmation from device {}",
1504 group, group.deviceId());
1505 removeGroupEntry(group);
1506 break;
1507 case ADDED:
1508 case PENDING_ADD:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001509 case PENDING_ADD_RETRY:
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001510 case PENDING_UPDATE:
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -07001511 log.debug("groupMissing: group entry {} in device {} moving from {} to PENDING_ADD_RETRY",
Charles Chan07f15f22018-05-08 21:35:50 -07001512 group.id(),
1513 group.deviceId(),
1514 group.state());
1515 group.setState(Group.GroupState.PENDING_ADD_RETRY);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001516 //Re-PUT map entries to trigger map update events
Charles Chan07f15f22018-05-08 21:35:50 -07001517 getGroupStoreKeyMap().put(new GroupStoreKeyMapKey(group.deviceId(), group.appCookie()), group);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001518 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
1519 group));
1520 break;
1521 default:
1522 log.debug("Group {} has not been installed.", group);
1523 break;
1524 }
1525 }
1526
1527 private void extraneousGroup(Group group) {
Saurav Das0fd79d92016-03-07 10:58:36 -08001528 log.trace("Group {} is on device {} but not in store.",
Srikanth Vavilapalli23181912015-05-04 09:48:09 -07001529 group, group.deviceId());
1530 addOrUpdateExtraneousGroupEntry(group);
1531 }
1532
1533 private void groupAdded(Group group) {
1534 log.trace("Group {} Added or Updated in device {}",
1535 group, group.deviceId());
1536 addOrUpdateGroupEntry(group);
1537 }
alshabib10580802015-02-18 18:30:33 -08001538}