blob: 8ece6483b41924e708eeb2f4957bdf77303a65ac [file] [log] [blame]
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -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 */
Thomas Vachuskac97aa612015-06-23 16:00:18 -070016package org.onosproject.store.trivial;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080017
Ray Milkeyd84f89b2018-08-17 14:54:17 -070018import com.google.common.collect.FluentIterable;
19import com.google.common.collect.Sets;
Daniele Moro43ac2892021-07-15 17:02:59 +020020import org.onosproject.core.ApplicationId;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080021import org.onosproject.core.GroupId;
22import org.onosproject.net.DeviceId;
23import org.onosproject.net.group.DefaultGroup;
24import org.onosproject.net.group.DefaultGroupDescription;
25import org.onosproject.net.group.Group;
26import org.onosproject.net.group.Group.GroupState;
27import org.onosproject.net.group.GroupBucket;
28import org.onosproject.net.group.GroupBuckets;
29import org.onosproject.net.group.GroupDescription;
30import org.onosproject.net.group.GroupEvent;
31import org.onosproject.net.group.GroupEvent.Type;
32import org.onosproject.net.group.GroupKey;
sangho7ff01812015-02-09 16:21:53 -080033import org.onosproject.net.group.GroupOperation;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080034import org.onosproject.net.group.GroupStore;
35import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070036import org.onosproject.net.group.StoredGroupBucketEntry;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080037import org.onosproject.net.group.StoredGroupEntry;
38import org.onosproject.store.AbstractStore;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070039import org.osgi.service.component.annotations.Activate;
40import org.osgi.service.component.annotations.Component;
41import org.osgi.service.component.annotations.Deactivate;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080042import org.slf4j.Logger;
43
Ray Milkeyd84f89b2018-08-17 14:54:17 -070044import java.util.ArrayList;
45import java.util.Collection;
46import java.util.HashMap;
47import java.util.Iterator;
48import java.util.List;
49import java.util.Map;
50import java.util.Optional;
51import java.util.Set;
52import java.util.concurrent.ConcurrentHashMap;
53import java.util.concurrent.ConcurrentMap;
54import java.util.concurrent.atomic.AtomicInteger;
Daniele Moro43ac2892021-07-15 17:02:59 +020055import java.util.stream.Collectors;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070056
57import static org.slf4j.LoggerFactory.getLogger;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080058
59/**
60 * Manages inventory of group entries using trivial in-memory implementation.
61 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062@Component(immediate = true, service = GroupStore.class)
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080063public class SimpleGroupStore
64 extends AbstractStore<GroupEvent, GroupStoreDelegate>
65 implements GroupStore {
66
67 private final Logger log = getLogger(getClass());
68
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080069 private final int dummyId = 0xffffffff;
Yi Tsengfa394de2017-02-01 11:26:40 -080070 private final GroupId dummyGroupId = new GroupId(dummyId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080071
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080072 // inner Map is per device group table
73 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
74 groupEntriesByKey = new ConcurrentHashMap<>();
75 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
76 groupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080077 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
78 pendingGroupEntriesByKey = new ConcurrentHashMap<>();
79 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
80 extraneousGroupEntriesById = new ConcurrentHashMap<>();
81
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -070082 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080083
84 private final AtomicInteger groupIdGen = new AtomicInteger();
85
86 @Activate
87 public void activate() {
88 log.info("Started");
89 }
90
91 @Deactivate
92 public void deactivate() {
93 groupEntriesByKey.clear();
94 groupEntriesById.clear();
95 log.info("Stopped");
96 }
97
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080098 /**
99 * Returns the group key table for specified device.
100 *
101 * @param deviceId identifier of the device
102 * @return Map representing group key table of given device.
103 */
104 private ConcurrentMap<GroupKey, StoredGroupEntry> getGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700105 return groupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800106 }
107
108 /**
109 * Returns the group id table for specified device.
110 *
111 * @param deviceId identifier of the device
112 * @return Map representing group key table of given device.
113 */
114 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700115 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800116 }
117
118 /**
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800119 * Returns the pending group key table for specified device.
120 *
121 * @param deviceId identifier of the device
122 * @return Map representing group key table of given device.
123 */
124 private ConcurrentMap<GroupKey, StoredGroupEntry>
125 getPendingGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700126 return pendingGroupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800127 }
128
129 /**
130 * Returns the extraneous group id table for specified device.
131 *
132 * @param deviceId identifier of the device
133 * @return Map representing group key table of given device.
134 */
135 private ConcurrentMap<GroupId, Group>
136 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700137 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800138 }
139
140 /**
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800141 * Returns the number of groups for the specified device in the store.
142 *
143 * @return number of groups for the specified device
144 */
145 @Override
146 public int getGroupCount(DeviceId deviceId) {
147 return (groupEntriesByKey.get(deviceId) != null) ?
148 groupEntriesByKey.get(deviceId).size() : 0;
149 }
150
151 /**
152 * Returns the groups associated with a device.
153 *
154 * @param deviceId the device ID
155 *
156 * @return the group entries
157 */
158 @Override
159 public Iterable<Group> getGroups(DeviceId deviceId) {
160 // flatten and make iterator unmodifiable
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800161 return FluentIterable.from(getGroupKeyTable(deviceId).values())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700162 .transform(input -> input);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800163 }
164
165 /**
166 * Returns the stored group entry.
167 *
168 * @param deviceId the device ID
169 * @param appCookie the group key
170 *
171 * @return a group associated with the key
172 */
173 @Override
174 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
175 return (groupEntriesByKey.get(deviceId) != null) ?
176 groupEntriesByKey.get(deviceId).get(appCookie) :
177 null;
178 }
179
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700180 @Override
181 public Group getGroup(DeviceId deviceId, GroupId groupId) {
182 return (groupEntriesById.get(deviceId) != null) ?
183 groupEntriesById.get(deviceId).get(groupId) :
184 null;
185 }
186
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800187 private int getFreeGroupIdValue(DeviceId deviceId) {
188 int freeId = groupIdGen.incrementAndGet();
189
190 while (true) {
191 Group existing = (
192 groupEntriesById.get(deviceId) != null) ?
Yi Tsengfa394de2017-02-01 11:26:40 -0800193 groupEntriesById.get(deviceId).get(new GroupId(freeId)) :
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800194 null;
195 if (existing == null) {
196 existing = (
197 extraneousGroupEntriesById.get(deviceId) != null) ?
198 extraneousGroupEntriesById.get(deviceId).
Yi Tsengfa394de2017-02-01 11:26:40 -0800199 get(new GroupId(freeId)) :
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800200 null;
201 }
202 if (existing != null) {
203 freeId = groupIdGen.incrementAndGet();
204 } else {
205 break;
206 }
207 }
208 return freeId;
209 }
210
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800211 /**
212 * Stores a new group entry using the information from group description.
213 *
214 * @param groupDesc group description to be used to create group entry
215 */
216 @Override
217 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800218 // Check if a group is existing with the same key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800219 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
220 return;
221 }
222
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800223 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
224 // Device group audit has not completed yet
225 // Add this group description to pending group key table
226 // Create a group entry object with Dummy Group ID
227 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
228 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
229 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
230 getPendingGroupKeyTable(groupDesc.deviceId());
231 pendingKeyTable.put(groupDesc.appCookie(), group);
232 return;
233 }
234
235 storeGroupDescriptionInternal(groupDesc);
236 }
237
238 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800239 // Check if a group is existing with the same key
240 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
241 return;
242 }
243
Saurav Das100e3b82015-04-30 11:12:10 -0700244 GroupId id = null;
245 if (groupDesc.givenGroupId() == null) {
246 // Get a new group identifier
Yi Tsengfa394de2017-02-01 11:26:40 -0800247 id = new GroupId(getFreeGroupIdValue(groupDesc.deviceId()));
Saurav Das100e3b82015-04-30 11:12:10 -0700248 } else {
Yi Tsengfa394de2017-02-01 11:26:40 -0800249 id = new GroupId(groupDesc.givenGroupId());
Saurav Das100e3b82015-04-30 11:12:10 -0700250 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800251 // Create a group entry object
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800252 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800253 // Insert the newly created group entry into concurrent key and id maps
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800254 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
255 getGroupKeyTable(groupDesc.deviceId());
256 keyTable.put(groupDesc.appCookie(), group);
257 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
258 getGroupIdTable(groupDesc.deviceId());
259 idTable.put(id, group);
260 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
261 group));
262 }
263
264 /**
265 * Updates the existing group entry with the information
266 * from group description.
267 *
268 * @param deviceId the device ID
269 * @param oldAppCookie the current group key
270 * @param type update type
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800271 * @param newBuckets group buckets for updates
272 * @param newAppCookie optional new group key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800273 */
274 @Override
275 public void updateGroupDescription(DeviceId deviceId,
276 GroupKey oldAppCookie,
277 UpdateType type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800278 GroupBuckets newBuckets,
279 GroupKey newAppCookie) {
280 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800281 Group oldGroup = getGroup(deviceId, oldAppCookie);
282 if (oldGroup == null) {
283 return;
284 }
285
286 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
287 type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800288 newBuckets);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800289 if (newBucketList != null) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800290 // Create a new group object from the old group
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800291 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800292 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800293 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
294 oldGroup.deviceId(),
295 oldGroup.type(),
296 updatedBuckets,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800297 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700298 oldGroup.givenGroupId(),
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800299 oldGroup.appId());
300 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
301 updatedGroupDesc);
302 newGroup.setState(GroupState.PENDING_UPDATE);
303 newGroup.setLife(oldGroup.life());
304 newGroup.setPackets(oldGroup.packets());
305 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800306 // Remove the old entry from maps and add new entry using new key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800307 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
308 getGroupKeyTable(oldGroup.deviceId());
309 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
310 getGroupIdTable(oldGroup.deviceId());
311 keyTable.remove(oldGroup.appCookie());
312 idTable.remove(oldGroup.id());
313 keyTable.put(newGroup.appCookie(), newGroup);
314 idTable.put(newGroup.id(), newGroup);
315 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
316 }
317 }
318
319 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
Victor Silvadf1eeae2016-08-12 15:28:57 -0300320 UpdateType type,
321 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300322 if (type == UpdateType.SET) {
323 return buckets.buckets();
324 }
325
Victor Silvadf1eeae2016-08-12 15:28:57 -0300326 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
327 List<GroupBucket> updatedBucketList = new ArrayList<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800328 boolean groupDescUpdated = false;
329
330 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300331 List<GroupBucket> newBuckets = buckets.buckets();
332
333 // Add old buckets that will not be updated and check if any will be updated.
334 for (GroupBucket oldBucket : oldBuckets) {
335 int newBucketIndex = newBuckets.indexOf(oldBucket);
336
337 if (newBucketIndex != -1) {
338 GroupBucket newBucket = newBuckets.get(newBucketIndex);
339 if (!newBucket.hasSameParameters(oldBucket)) {
340 // Bucket will be updated
341 groupDescUpdated = true;
342 }
343 } else {
344 // Old bucket will remain the same - add it.
345 updatedBucketList.add(oldBucket);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800346 }
347 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300348
349 // Add all new buckets
350 updatedBucketList.addAll(newBuckets);
351 if (!oldBuckets.containsAll(newBuckets)) {
352 groupDescUpdated = true;
353 }
354
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800355 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300356 List<GroupBucket> bucketsToRemove = buckets.buckets();
357
358 // Check which old buckets should remain
359 for (GroupBucket oldBucket : oldBuckets) {
360 if (!bucketsToRemove.contains(oldBucket)) {
361 updatedBucketList.add(oldBucket);
362 } else {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800363 groupDescUpdated = true;
364 }
365 }
366 }
367
368 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300369 return updatedBucketList;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800370 } else {
371 return null;
372 }
373 }
374
375 /**
376 * Triggers deleting the existing group entry.
377 *
378 * @param deviceId the device ID
379 * @param appCookie the group key
380 */
381 @Override
382 public void deleteGroupDescription(DeviceId deviceId,
383 GroupKey appCookie) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800384 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800385 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
386 groupEntriesByKey.get(deviceId).get(appCookie) :
387 null;
388 if (existing == null) {
389 return;
390 }
391
392 synchronized (existing) {
393 existing.setState(GroupState.PENDING_DELETE);
394 }
395 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
396 }
397
398 /**
399 * Stores a new group entry, or updates an existing entry.
400 *
401 * @param group group entry
402 */
403 @Override
404 public void addOrUpdateGroupEntry(Group group) {
405 // check if this new entry is an update to an existing entry
406 StoredGroupEntry existing = (groupEntriesById.get(
407 group.deviceId()) != null) ?
408 groupEntriesById.get(group.deviceId()).get(group.id()) :
409 null;
410 GroupEvent event = null;
411
412 if (existing != null) {
413 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700414 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700415 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700416 existing.buckets().buckets()
417 .stream()
Ray Milkey88cc3432017-03-30 17:19:08 -0700418 .filter((existingBucket) -> (existingBucket.equals(bucket)))
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700419 .findFirst();
420 if (matchingBucket.isPresent()) {
421 ((StoredGroupBucketEntry) matchingBucket.
422 get()).setPackets(bucket.packets());
423 ((StoredGroupBucketEntry) matchingBucket.
424 get()).setBytes(bucket.bytes());
425 } else {
426 log.warn("addOrUpdateGroupEntry: No matching "
427 + "buckets to update stats");
428 }
429 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800430 existing.setLife(group.life());
431 existing.setPackets(group.packets());
432 existing.setBytes(group.bytes());
433 if (existing.state() == GroupState.PENDING_ADD) {
434 existing.setState(GroupState.ADDED);
435 event = new GroupEvent(Type.GROUP_ADDED, existing);
436 } else {
437 if (existing.state() == GroupState.PENDING_UPDATE) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700438 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800439 }
440 event = new GroupEvent(Type.GROUP_UPDATED, existing);
441 }
442 }
443 }
444
445 if (event != null) {
446 notifyDelegate(event);
447 }
448 }
449
450 /**
451 * Removes the group entry from store.
452 *
453 * @param group group entry
454 */
455 @Override
456 public void removeGroupEntry(Group group) {
457 StoredGroupEntry existing = (groupEntriesById.get(
458 group.deviceId()) != null) ?
459 groupEntriesById.get(group.deviceId()).get(group.id()) :
460 null;
461
462 if (existing != null) {
463 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
464 getGroupKeyTable(existing.deviceId());
465 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
466 getGroupIdTable(existing.deviceId());
467 idTable.remove(existing.id());
468 keyTable.remove(existing.appCookie());
469 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
470 }
471 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800472
473 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800474 public void purgeGroupEntry(DeviceId deviceId) {
475 Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove =
476 groupEntriesById.get(deviceId).entrySet();
477
478 groupEntriesById.remove(deviceId);
479 groupEntriesByKey.remove(deviceId);
480
481 entryPendingRemove.forEach(entry -> {
482 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
483 });
484 }
485
486 @Override
Daniele Moro43ac2892021-07-15 17:02:59 +0200487 public void purgeGroupEntries(DeviceId deviceId, ApplicationId appId) {
488 List<StoredGroupEntry> entryPendingRemove =
489 groupEntriesById.get(deviceId).values().stream()
490 .filter(storedGroupEntry -> storedGroupEntry.appId().equals(appId))
491 .collect(Collectors.toList());
492
493 entryPendingRemove.forEach(storedGroupEntry -> {
494 groupEntriesById.computeIfPresent(deviceId, (k, value) -> {
495 value.remove(storedGroupEntry.id());
496 if (value.isEmpty()) {
497 return null;
498 }
499 return value;
500 });
501 groupEntriesByKey.computeIfPresent(deviceId, (k, value) -> {
502 value.remove(storedGroupEntry.appCookie());
503 if (value.isEmpty()) {
504 return null;
505 }
506 return value;
507 });
508 });
509 }
510
511 @Override
Victor Silva4e8b7832016-08-17 17:11:19 -0300512 public void purgeGroupEntries() {
513 groupEntriesById.values().forEach(groupEntries -> {
514 groupEntries.entrySet().forEach(entry -> {
515 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
516 });
517 });
518
519 groupEntriesById.clear();
520 groupEntriesByKey.clear();
521 }
522
523 @Override
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800524 public void deviceInitialAuditCompleted(DeviceId deviceId,
525 boolean completed) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800526 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800527 if (completed) {
528 log.debug("deviceInitialAuditCompleted: AUDIT "
529 + "completed for device {}", deviceId);
530 deviceAuditStatus.put(deviceId, true);
531 // Execute all pending group requests
532 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
533 getPendingGroupKeyTable(deviceId);
534 for (Group group:pendingGroupRequests.values()) {
535 GroupDescription tmp = new DefaultGroupDescription(
536 group.deviceId(),
537 group.type(),
538 group.buckets(),
539 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700540 group.givenGroupId(),
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800541 group.appId());
542 storeGroupDescriptionInternal(tmp);
543 }
544 getPendingGroupKeyTable(deviceId).clear();
545 } else {
Clement Cheung21039892019-09-03 16:31:16 -0700546 if (deviceAuditStatus.getOrDefault(deviceId, false)) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800547 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
548 + "status for device {}", deviceId);
549 deviceAuditStatus.put(deviceId, false);
550 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800551 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800552 }
553 }
554
555 @Override
556 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
557 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800558 return (deviceAuditStatus.get(deviceId) != null)
559 ? deviceAuditStatus.get(deviceId) : false;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800560 }
561 }
562
563 @Override
sangho7ff01812015-02-09 16:21:53 -0800564 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
565
566 StoredGroupEntry existing = (groupEntriesById.get(
567 deviceId) != null) ?
568 groupEntriesById.get(deviceId).get(operation.groupId()) :
569 null;
570
571 if (existing == null) {
572 log.warn("No group entry with ID {} found ", operation.groupId());
573 return;
574 }
575
576 switch (operation.opType()) {
577 case ADD:
578 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
579 break;
580 case MODIFY:
581 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
582 break;
583 case DELETE:
584 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
585 break;
586 default:
587 log.warn("Unknown group operation type {}", operation.opType());
588 }
589
590 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
591 getGroupKeyTable(existing.deviceId());
592 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
593 getGroupIdTable(existing.deviceId());
594 idTable.remove(existing.id());
595 keyTable.remove(existing.appCookie());
596 }
597
598 @Override
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800599 public void addOrUpdateExtraneousGroupEntry(Group group) {
600 ConcurrentMap<GroupId, Group> extraneousIdTable =
601 getExtraneousGroupIdTable(group.deviceId());
602 extraneousIdTable.put(group.id(), group);
603 // Check the reference counter
604 if (group.referenceCount() == 0) {
605 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
606 }
607 }
608
609 @Override
610 public void removeExtraneousGroupEntry(Group group) {
611 ConcurrentMap<GroupId, Group> extraneousIdTable =
612 getExtraneousGroupIdTable(group.deviceId());
613 extraneousIdTable.remove(group.id());
614 }
615
616 @Override
617 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
618 // flatten and make iterator unmodifiable
619 return FluentIterable.from(
620 getExtraneousGroupIdTable(deviceId).values());
621 }
sangho7ff01812015-02-09 16:21:53 -0800622
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700623 @Override
624 public void pushGroupMetrics(DeviceId deviceId,
625 Collection<Group> groupEntries) {
626 boolean deviceInitialAuditStatus =
627 deviceInitialAuditStatus(deviceId);
628 Set<Group> southboundGroupEntries =
629 Sets.newHashSet(groupEntries);
630 Set<Group> storedGroupEntries =
631 Sets.newHashSet(getGroups(deviceId));
632 Set<Group> extraneousStoredEntries =
633 Sets.newHashSet(getExtraneousGroups(deviceId));
634
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700635 if (log.isTraceEnabled()) {
636 log.trace("pushGroupMetrics: Displaying all ({}) "
637 + "southboundGroupEntries for device {}",
638 southboundGroupEntries.size(),
639 deviceId);
640 for (Group group : southboundGroupEntries) {
641 log.trace("Group {} in device {}", group, deviceId);
642 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700643
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700644 log.trace("Displaying all ({}) stored group entries for device {}",
645 storedGroupEntries.size(),
646 deviceId);
647 for (Group group : storedGroupEntries) {
648 log.trace("Stored Group {} for device {}", group, deviceId);
649 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700650 }
651
652 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
653 Group group = it2.next();
654 if (storedGroupEntries.remove(group)) {
655 // we both have the group, let's update some info then.
656 log.trace("Group AUDIT: group {} exists "
657 + "in both planes for device {}",
658 group.id(), deviceId);
659 groupAdded(group);
660 it2.remove();
661 }
662 }
663 for (Group group : southboundGroupEntries) {
664 if (getGroup(group.deviceId(), group.id()) != null) {
665 // There is a group existing with the same id
666 // It is possible that group update is
667 // in progress while we got a stale info from switch
668 if (!storedGroupEntries.remove(getGroup(
669 group.deviceId(), group.id()))) {
670 log.warn("Group AUDIT: Inconsistent state:"
671 + "Group exists in ID based table while "
672 + "not present in key based table");
673 }
674 } else {
675 // there are groups in the switch that aren't in the store
676 log.trace("Group AUDIT: extraneous group {} exists "
677 + "in data plane for device {}",
678 group.id(), deviceId);
679 extraneousStoredEntries.remove(group);
680 extraneousGroup(group);
681 }
682 }
683 for (Group group : storedGroupEntries) {
684 // there are groups in the store that aren't in the switch
685 log.trace("Group AUDIT: group {} missing "
686 + "in data plane for device {}",
687 group.id(), deviceId);
688 groupMissing(group);
689 }
690 for (Group group : extraneousStoredEntries) {
691 // there are groups in the extraneous store that
692 // aren't in the switch
693 log.trace("Group AUDIT: clearing extransoeus group {} "
694 + "from store for device {}",
695 group.id(), deviceId);
696 removeExtraneousGroupEntry(group);
697 }
698
699 if (!deviceInitialAuditStatus) {
700 log.debug("Group AUDIT: Setting device {} initial "
701 + "AUDIT completed", deviceId);
702 deviceInitialAuditCompleted(deviceId, true);
703 }
704 }
705
helenyrwu89470f12016-08-12 13:18:10 -0700706 @Override
707 public void notifyOfFailovers(Collection<Group> failoverGroups) {
708 List<GroupEvent> failoverEvents = new ArrayList<>();
709 failoverGroups.forEach(group -> {
710 if (group.type() == Group.Type.FAILOVER) {
711 failoverEvents.add(new GroupEvent(GroupEvent.Type.GROUP_BUCKET_FAILOVER, group));
712 }
713 });
714 notifyDelegate(failoverEvents);
715 }
716
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700717 private void groupMissing(Group group) {
718 switch (group.state()) {
719 case PENDING_DELETE:
720 log.debug("Group {} delete confirmation from device {}",
721 group, group.deviceId());
722 removeGroupEntry(group);
723 break;
724 case ADDED:
725 case PENDING_ADD:
726 case PENDING_UPDATE:
727 log.debug("Group {} is in store but not on device {}",
728 group, group.deviceId());
729 StoredGroupEntry existing = (groupEntriesById.get(
730 group.deviceId()) != null) ?
731 groupEntriesById.get(group.deviceId()).get(group.id()) :
732 null;
733 log.trace("groupMissing: group "
734 + "entry {} in device {} moving "
735 + "from {} to PENDING_ADD",
736 existing.id(),
737 existing.deviceId(),
738 existing.state());
739 existing.setState(Group.GroupState.PENDING_ADD);
740 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
741 group));
742 break;
743 default:
744 log.debug("Group {} has not been installed.", group);
745 break;
746 }
747 }
748
749 private void extraneousGroup(Group group) {
750 log.debug("Group {} is on device {} but not in store.",
751 group, group.deviceId());
752 addOrUpdateExtraneousGroupEntry(group);
753 }
754
755 private void groupAdded(Group group) {
756 log.trace("Group {} Added or Updated in device {}",
757 group, group.deviceId());
758 addOrUpdateGroupEntry(group);
759 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800760}