blob: f3f0bd7cbf651fa40b914fa2cc9dd3a8cfdb1cbd [file] [log] [blame]
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080018import static org.slf4j.LoggerFactory.getLogger;
19
20import java.util.ArrayList;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070021import java.util.Collection;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080022import java.util.HashMap;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070023import java.util.Iterator;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080024import java.util.List;
Charles Chan0c7c43b2016-01-14 17:39:20 -080025import java.util.Map;
Sho SHIMIZU30d639b2015-05-05 09:30:35 -070026import java.util.Optional;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070027import java.util.Set;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080028import java.util.concurrent.ConcurrentHashMap;
29import java.util.concurrent.ConcurrentMap;
30import java.util.concurrent.atomic.AtomicInteger;
31
32import org.apache.felix.scr.annotations.Activate;
33import org.apache.felix.scr.annotations.Component;
34import org.apache.felix.scr.annotations.Deactivate;
35import org.apache.felix.scr.annotations.Service;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080036import org.onosproject.core.GroupId;
37import org.onosproject.net.DeviceId;
38import org.onosproject.net.group.DefaultGroup;
39import org.onosproject.net.group.DefaultGroupDescription;
40import org.onosproject.net.group.Group;
41import org.onosproject.net.group.Group.GroupState;
42import org.onosproject.net.group.GroupBucket;
43import org.onosproject.net.group.GroupBuckets;
44import org.onosproject.net.group.GroupDescription;
45import org.onosproject.net.group.GroupEvent;
46import org.onosproject.net.group.GroupEvent.Type;
47import org.onosproject.net.group.GroupKey;
sangho7ff01812015-02-09 16:21:53 -080048import org.onosproject.net.group.GroupOperation;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080049import org.onosproject.net.group.GroupStore;
50import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070051import org.onosproject.net.group.StoredGroupBucketEntry;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080052import org.onosproject.net.group.StoredGroupEntry;
53import org.onosproject.store.AbstractStore;
54import org.slf4j.Logger;
55
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080056import com.google.common.collect.FluentIterable;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070057import com.google.common.collect.Sets;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080058
59/**
60 * Manages inventory of group entries using trivial in-memory implementation.
61 */
62@Component(immediate = true)
63@Service
64public class SimpleGroupStore
65 extends AbstractStore<GroupEvent, GroupStoreDelegate>
66 implements GroupStore {
67
68 private final Logger log = getLogger(getClass());
69
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080070 private final int dummyId = 0xffffffff;
Yi Tsengfa394de2017-02-01 11:26:40 -080071 private final GroupId dummyGroupId = new GroupId(dummyId);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080072
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080073 // inner Map is per device group table
74 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
75 groupEntriesByKey = new ConcurrentHashMap<>();
76 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
77 groupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080078 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
79 pendingGroupEntriesByKey = new ConcurrentHashMap<>();
80 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
81 extraneousGroupEntriesById = new ConcurrentHashMap<>();
82
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -070083 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080084
85 private final AtomicInteger groupIdGen = new AtomicInteger();
86
87 @Activate
88 public void activate() {
89 log.info("Started");
90 }
91
92 @Deactivate
93 public void deactivate() {
94 groupEntriesByKey.clear();
95 groupEntriesById.clear();
96 log.info("Stopped");
97 }
98
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080099 /**
100 * Returns the group key table for specified device.
101 *
102 * @param deviceId identifier of the device
103 * @return Map representing group key table of given device.
104 */
105 private ConcurrentMap<GroupKey, StoredGroupEntry> getGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700106 return groupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800107 }
108
109 /**
110 * Returns the group id table for specified device.
111 *
112 * @param deviceId identifier of the device
113 * @return Map representing group key table of given device.
114 */
115 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700116 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800117 }
118
119 /**
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800120 * Returns the pending group key table for specified device.
121 *
122 * @param deviceId identifier of the device
123 * @return Map representing group key table of given device.
124 */
125 private ConcurrentMap<GroupKey, StoredGroupEntry>
126 getPendingGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700127 return pendingGroupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800128 }
129
130 /**
131 * Returns the extraneous group id table for specified device.
132 *
133 * @param deviceId identifier of the device
134 * @return Map representing group key table of given device.
135 */
136 private ConcurrentMap<GroupId, Group>
137 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700138 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800139 }
140
141 /**
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800142 * Returns the number of groups for the specified device in the store.
143 *
144 * @return number of groups for the specified device
145 */
146 @Override
147 public int getGroupCount(DeviceId deviceId) {
148 return (groupEntriesByKey.get(deviceId) != null) ?
149 groupEntriesByKey.get(deviceId).size() : 0;
150 }
151
152 /**
153 * Returns the groups associated with a device.
154 *
155 * @param deviceId the device ID
156 *
157 * @return the group entries
158 */
159 @Override
160 public Iterable<Group> getGroups(DeviceId deviceId) {
161 // flatten and make iterator unmodifiable
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800162 return FluentIterable.from(getGroupKeyTable(deviceId).values())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700163 .transform(input -> input);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800164 }
165
166 /**
167 * Returns the stored group entry.
168 *
169 * @param deviceId the device ID
170 * @param appCookie the group key
171 *
172 * @return a group associated with the key
173 */
174 @Override
175 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
176 return (groupEntriesByKey.get(deviceId) != null) ?
177 groupEntriesByKey.get(deviceId).get(appCookie) :
178 null;
179 }
180
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700181 @Override
182 public Group getGroup(DeviceId deviceId, GroupId groupId) {
183 return (groupEntriesById.get(deviceId) != null) ?
184 groupEntriesById.get(deviceId).get(groupId) :
185 null;
186 }
187
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800188 private int getFreeGroupIdValue(DeviceId deviceId) {
189 int freeId = groupIdGen.incrementAndGet();
190
191 while (true) {
192 Group existing = (
193 groupEntriesById.get(deviceId) != null) ?
Yi Tsengfa394de2017-02-01 11:26:40 -0800194 groupEntriesById.get(deviceId).get(new GroupId(freeId)) :
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800195 null;
196 if (existing == null) {
197 existing = (
198 extraneousGroupEntriesById.get(deviceId) != null) ?
199 extraneousGroupEntriesById.get(deviceId).
Yi Tsengfa394de2017-02-01 11:26:40 -0800200 get(new GroupId(freeId)) :
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800201 null;
202 }
203 if (existing != null) {
204 freeId = groupIdGen.incrementAndGet();
205 } else {
206 break;
207 }
208 }
209 return freeId;
210 }
211
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800212 /**
213 * Stores a new group entry using the information from group description.
214 *
215 * @param groupDesc group description to be used to create group entry
216 */
217 @Override
218 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800219 // Check if a group is existing with the same key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800220 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
221 return;
222 }
223
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800224 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
225 // Device group audit has not completed yet
226 // Add this group description to pending group key table
227 // Create a group entry object with Dummy Group ID
228 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
229 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
230 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
231 getPendingGroupKeyTable(groupDesc.deviceId());
232 pendingKeyTable.put(groupDesc.appCookie(), group);
233 return;
234 }
235
236 storeGroupDescriptionInternal(groupDesc);
237 }
238
239 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800240 // Check if a group is existing with the same key
241 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
242 return;
243 }
244
Saurav Das100e3b82015-04-30 11:12:10 -0700245 GroupId id = null;
246 if (groupDesc.givenGroupId() == null) {
247 // Get a new group identifier
Yi Tsengfa394de2017-02-01 11:26:40 -0800248 id = new GroupId(getFreeGroupIdValue(groupDesc.deviceId()));
Saurav Das100e3b82015-04-30 11:12:10 -0700249 } else {
Yi Tsengfa394de2017-02-01 11:26:40 -0800250 id = new GroupId(groupDesc.givenGroupId());
Saurav Das100e3b82015-04-30 11:12:10 -0700251 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800252 // Create a group entry object
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800253 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800254 // Insert the newly created group entry into concurrent key and id maps
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800255 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
256 getGroupKeyTable(groupDesc.deviceId());
257 keyTable.put(groupDesc.appCookie(), group);
258 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
259 getGroupIdTable(groupDesc.deviceId());
260 idTable.put(id, group);
261 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
262 group));
263 }
264
265 /**
266 * Updates the existing group entry with the information
267 * from group description.
268 *
269 * @param deviceId the device ID
270 * @param oldAppCookie the current group key
271 * @param type update type
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800272 * @param newBuckets group buckets for updates
273 * @param newAppCookie optional new group key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800274 */
275 @Override
276 public void updateGroupDescription(DeviceId deviceId,
277 GroupKey oldAppCookie,
278 UpdateType type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800279 GroupBuckets newBuckets,
280 GroupKey newAppCookie) {
281 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800282 Group oldGroup = getGroup(deviceId, oldAppCookie);
283 if (oldGroup == null) {
284 return;
285 }
286
287 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
288 type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800289 newBuckets);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800290 if (newBucketList != null) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800291 // Create a new group object from the old group
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800292 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800293 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800294 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
295 oldGroup.deviceId(),
296 oldGroup.type(),
297 updatedBuckets,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800298 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700299 oldGroup.givenGroupId(),
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800300 oldGroup.appId());
301 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
302 updatedGroupDesc);
303 newGroup.setState(GroupState.PENDING_UPDATE);
304 newGroup.setLife(oldGroup.life());
305 newGroup.setPackets(oldGroup.packets());
306 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800307 // Remove the old entry from maps and add new entry using new key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800308 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
309 getGroupKeyTable(oldGroup.deviceId());
310 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
311 getGroupIdTable(oldGroup.deviceId());
312 keyTable.remove(oldGroup.appCookie());
313 idTable.remove(oldGroup.id());
314 keyTable.put(newGroup.appCookie(), newGroup);
315 idTable.put(newGroup.id(), newGroup);
316 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
317 }
318 }
319
320 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
Victor Silvadf1eeae2016-08-12 15:28:57 -0300321 UpdateType type,
322 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300323 if (type == UpdateType.SET) {
324 return buckets.buckets();
325 }
326
Victor Silvadf1eeae2016-08-12 15:28:57 -0300327 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
328 List<GroupBucket> updatedBucketList = new ArrayList<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800329 boolean groupDescUpdated = false;
330
331 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300332 List<GroupBucket> newBuckets = buckets.buckets();
333
334 // Add old buckets that will not be updated and check if any will be updated.
335 for (GroupBucket oldBucket : oldBuckets) {
336 int newBucketIndex = newBuckets.indexOf(oldBucket);
337
338 if (newBucketIndex != -1) {
339 GroupBucket newBucket = newBuckets.get(newBucketIndex);
340 if (!newBucket.hasSameParameters(oldBucket)) {
341 // Bucket will be updated
342 groupDescUpdated = true;
343 }
344 } else {
345 // Old bucket will remain the same - add it.
346 updatedBucketList.add(oldBucket);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800347 }
348 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300349
350 // Add all new buckets
351 updatedBucketList.addAll(newBuckets);
352 if (!oldBuckets.containsAll(newBuckets)) {
353 groupDescUpdated = true;
354 }
355
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800356 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300357 List<GroupBucket> bucketsToRemove = buckets.buckets();
358
359 // Check which old buckets should remain
360 for (GroupBucket oldBucket : oldBuckets) {
361 if (!bucketsToRemove.contains(oldBucket)) {
362 updatedBucketList.add(oldBucket);
363 } else {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800364 groupDescUpdated = true;
365 }
366 }
367 }
368
369 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300370 return updatedBucketList;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800371 } else {
372 return null;
373 }
374 }
375
376 /**
377 * Triggers deleting the existing group entry.
378 *
379 * @param deviceId the device ID
380 * @param appCookie the group key
381 */
382 @Override
383 public void deleteGroupDescription(DeviceId deviceId,
384 GroupKey appCookie) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800385 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800386 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
387 groupEntriesByKey.get(deviceId).get(appCookie) :
388 null;
389 if (existing == null) {
390 return;
391 }
392
393 synchronized (existing) {
394 existing.setState(GroupState.PENDING_DELETE);
395 }
396 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
397 }
398
399 /**
400 * Stores a new group entry, or updates an existing entry.
401 *
402 * @param group group entry
403 */
404 @Override
405 public void addOrUpdateGroupEntry(Group group) {
406 // check if this new entry is an update to an existing entry
407 StoredGroupEntry existing = (groupEntriesById.get(
408 group.deviceId()) != null) ?
409 groupEntriesById.get(group.deviceId()).get(group.id()) :
410 null;
411 GroupEvent event = null;
412
413 if (existing != null) {
414 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700415 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700416 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700417 existing.buckets().buckets()
418 .stream()
419 .filter((existingBucket)->(existingBucket.equals(bucket)))
420 .findFirst();
421 if (matchingBucket.isPresent()) {
422 ((StoredGroupBucketEntry) matchingBucket.
423 get()).setPackets(bucket.packets());
424 ((StoredGroupBucketEntry) matchingBucket.
425 get()).setBytes(bucket.bytes());
426 } else {
427 log.warn("addOrUpdateGroupEntry: No matching "
428 + "buckets to update stats");
429 }
430 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800431 existing.setLife(group.life());
432 existing.setPackets(group.packets());
433 existing.setBytes(group.bytes());
434 if (existing.state() == GroupState.PENDING_ADD) {
435 existing.setState(GroupState.ADDED);
436 event = new GroupEvent(Type.GROUP_ADDED, existing);
437 } else {
438 if (existing.state() == GroupState.PENDING_UPDATE) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700439 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800440 }
441 event = new GroupEvent(Type.GROUP_UPDATED, existing);
442 }
443 }
444 }
445
446 if (event != null) {
447 notifyDelegate(event);
448 }
449 }
450
451 /**
452 * Removes the group entry from store.
453 *
454 * @param group group entry
455 */
456 @Override
457 public void removeGroupEntry(Group group) {
458 StoredGroupEntry existing = (groupEntriesById.get(
459 group.deviceId()) != null) ?
460 groupEntriesById.get(group.deviceId()).get(group.id()) :
461 null;
462
463 if (existing != null) {
464 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
465 getGroupKeyTable(existing.deviceId());
466 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
467 getGroupIdTable(existing.deviceId());
468 idTable.remove(existing.id());
469 keyTable.remove(existing.appCookie());
470 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
471 }
472 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800473
474 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800475 public void purgeGroupEntry(DeviceId deviceId) {
476 Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove =
477 groupEntriesById.get(deviceId).entrySet();
478
479 groupEntriesById.remove(deviceId);
480 groupEntriesByKey.remove(deviceId);
481
482 entryPendingRemove.forEach(entry -> {
483 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
484 });
485 }
486
487 @Override
Victor Silva4e8b7832016-08-17 17:11:19 -0300488 public void purgeGroupEntries() {
489 groupEntriesById.values().forEach(groupEntries -> {
490 groupEntries.entrySet().forEach(entry -> {
491 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
492 });
493 });
494
495 groupEntriesById.clear();
496 groupEntriesByKey.clear();
497 }
498
499 @Override
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800500 public void deviceInitialAuditCompleted(DeviceId deviceId,
501 boolean completed) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800502 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800503 if (completed) {
504 log.debug("deviceInitialAuditCompleted: AUDIT "
505 + "completed for device {}", deviceId);
506 deviceAuditStatus.put(deviceId, true);
507 // Execute all pending group requests
508 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
509 getPendingGroupKeyTable(deviceId);
510 for (Group group:pendingGroupRequests.values()) {
511 GroupDescription tmp = new DefaultGroupDescription(
512 group.deviceId(),
513 group.type(),
514 group.buckets(),
515 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700516 group.givenGroupId(),
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800517 group.appId());
518 storeGroupDescriptionInternal(tmp);
519 }
520 getPendingGroupKeyTable(deviceId).clear();
521 } else {
522 if (deviceAuditStatus.get(deviceId)) {
523 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
524 + "status for device {}", deviceId);
525 deviceAuditStatus.put(deviceId, false);
526 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800527 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800528 }
529 }
530
531 @Override
532 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
533 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800534 return (deviceAuditStatus.get(deviceId) != null)
535 ? deviceAuditStatus.get(deviceId) : false;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800536 }
537 }
538
539 @Override
sangho7ff01812015-02-09 16:21:53 -0800540 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
541
542 StoredGroupEntry existing = (groupEntriesById.get(
543 deviceId) != null) ?
544 groupEntriesById.get(deviceId).get(operation.groupId()) :
545 null;
546
547 if (existing == null) {
548 log.warn("No group entry with ID {} found ", operation.groupId());
549 return;
550 }
551
552 switch (operation.opType()) {
553 case ADD:
554 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
555 break;
556 case MODIFY:
557 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
558 break;
559 case DELETE:
560 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
561 break;
562 default:
563 log.warn("Unknown group operation type {}", operation.opType());
564 }
565
566 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
567 getGroupKeyTable(existing.deviceId());
568 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
569 getGroupIdTable(existing.deviceId());
570 idTable.remove(existing.id());
571 keyTable.remove(existing.appCookie());
572 }
573
574 @Override
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800575 public void addOrUpdateExtraneousGroupEntry(Group group) {
576 ConcurrentMap<GroupId, Group> extraneousIdTable =
577 getExtraneousGroupIdTable(group.deviceId());
578 extraneousIdTable.put(group.id(), group);
579 // Check the reference counter
580 if (group.referenceCount() == 0) {
581 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
582 }
583 }
584
585 @Override
586 public void removeExtraneousGroupEntry(Group group) {
587 ConcurrentMap<GroupId, Group> extraneousIdTable =
588 getExtraneousGroupIdTable(group.deviceId());
589 extraneousIdTable.remove(group.id());
590 }
591
592 @Override
593 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
594 // flatten and make iterator unmodifiable
595 return FluentIterable.from(
596 getExtraneousGroupIdTable(deviceId).values());
597 }
sangho7ff01812015-02-09 16:21:53 -0800598
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700599 @Override
600 public void pushGroupMetrics(DeviceId deviceId,
601 Collection<Group> groupEntries) {
602 boolean deviceInitialAuditStatus =
603 deviceInitialAuditStatus(deviceId);
604 Set<Group> southboundGroupEntries =
605 Sets.newHashSet(groupEntries);
606 Set<Group> storedGroupEntries =
607 Sets.newHashSet(getGroups(deviceId));
608 Set<Group> extraneousStoredEntries =
609 Sets.newHashSet(getExtraneousGroups(deviceId));
610
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700611 if (log.isTraceEnabled()) {
612 log.trace("pushGroupMetrics: Displaying all ({}) "
613 + "southboundGroupEntries for device {}",
614 southboundGroupEntries.size(),
615 deviceId);
616 for (Group group : southboundGroupEntries) {
617 log.trace("Group {} in device {}", group, deviceId);
618 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700619
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700620 log.trace("Displaying all ({}) stored group entries for device {}",
621 storedGroupEntries.size(),
622 deviceId);
623 for (Group group : storedGroupEntries) {
624 log.trace("Stored Group {} for device {}", group, deviceId);
625 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700626 }
627
628 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
629 Group group = it2.next();
630 if (storedGroupEntries.remove(group)) {
631 // we both have the group, let's update some info then.
632 log.trace("Group AUDIT: group {} exists "
633 + "in both planes for device {}",
634 group.id(), deviceId);
635 groupAdded(group);
636 it2.remove();
637 }
638 }
639 for (Group group : southboundGroupEntries) {
640 if (getGroup(group.deviceId(), group.id()) != null) {
641 // There is a group existing with the same id
642 // It is possible that group update is
643 // in progress while we got a stale info from switch
644 if (!storedGroupEntries.remove(getGroup(
645 group.deviceId(), group.id()))) {
646 log.warn("Group AUDIT: Inconsistent state:"
647 + "Group exists in ID based table while "
648 + "not present in key based table");
649 }
650 } else {
651 // there are groups in the switch that aren't in the store
652 log.trace("Group AUDIT: extraneous group {} exists "
653 + "in data plane for device {}",
654 group.id(), deviceId);
655 extraneousStoredEntries.remove(group);
656 extraneousGroup(group);
657 }
658 }
659 for (Group group : storedGroupEntries) {
660 // there are groups in the store that aren't in the switch
661 log.trace("Group AUDIT: group {} missing "
662 + "in data plane for device {}",
663 group.id(), deviceId);
664 groupMissing(group);
665 }
666 for (Group group : extraneousStoredEntries) {
667 // there are groups in the extraneous store that
668 // aren't in the switch
669 log.trace("Group AUDIT: clearing extransoeus group {} "
670 + "from store for device {}",
671 group.id(), deviceId);
672 removeExtraneousGroupEntry(group);
673 }
674
675 if (!deviceInitialAuditStatus) {
676 log.debug("Group AUDIT: Setting device {} initial "
677 + "AUDIT completed", deviceId);
678 deviceInitialAuditCompleted(deviceId, true);
679 }
680 }
681
helenyrwu89470f12016-08-12 13:18:10 -0700682 @Override
683 public void notifyOfFailovers(Collection<Group> failoverGroups) {
684 List<GroupEvent> failoverEvents = new ArrayList<>();
685 failoverGroups.forEach(group -> {
686 if (group.type() == Group.Type.FAILOVER) {
687 failoverEvents.add(new GroupEvent(GroupEvent.Type.GROUP_BUCKET_FAILOVER, group));
688 }
689 });
690 notifyDelegate(failoverEvents);
691 }
692
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700693 private void groupMissing(Group group) {
694 switch (group.state()) {
695 case PENDING_DELETE:
696 log.debug("Group {} delete confirmation from device {}",
697 group, group.deviceId());
698 removeGroupEntry(group);
699 break;
700 case ADDED:
701 case PENDING_ADD:
702 case PENDING_UPDATE:
703 log.debug("Group {} is in store but not on device {}",
704 group, group.deviceId());
705 StoredGroupEntry existing = (groupEntriesById.get(
706 group.deviceId()) != null) ?
707 groupEntriesById.get(group.deviceId()).get(group.id()) :
708 null;
709 log.trace("groupMissing: group "
710 + "entry {} in device {} moving "
711 + "from {} to PENDING_ADD",
712 existing.id(),
713 existing.deviceId(),
714 existing.state());
715 existing.setState(Group.GroupState.PENDING_ADD);
716 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
717 group));
718 break;
719 default:
720 log.debug("Group {} has not been installed.", group);
721 break;
722 }
723 }
724
725 private void extraneousGroup(Group group) {
726 log.debug("Group {} is on device {} but not in store.",
727 group, group.deviceId());
728 addOrUpdateExtraneousGroupEntry(group);
729 }
730
731 private void groupAdded(Group group) {
732 log.trace("Group {} Added or Updated in device {}",
733 group, group.deviceId());
734 addOrUpdateGroupEntry(group);
735 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800736}