blob: 42a6d56311f0a97bd88db0968b8bcd9d15a28130 [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.DefaultGroupId;
37import org.onosproject.core.GroupId;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.group.DefaultGroup;
40import org.onosproject.net.group.DefaultGroupDescription;
41import org.onosproject.net.group.Group;
42import org.onosproject.net.group.Group.GroupState;
43import org.onosproject.net.group.GroupBucket;
44import org.onosproject.net.group.GroupBuckets;
45import org.onosproject.net.group.GroupDescription;
46import org.onosproject.net.group.GroupEvent;
47import org.onosproject.net.group.GroupEvent.Type;
48import org.onosproject.net.group.GroupKey;
sangho7ff01812015-02-09 16:21:53 -080049import org.onosproject.net.group.GroupOperation;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080050import org.onosproject.net.group.GroupStore;
51import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070052import org.onosproject.net.group.StoredGroupBucketEntry;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080053import org.onosproject.net.group.StoredGroupEntry;
54import org.onosproject.store.AbstractStore;
55import org.slf4j.Logger;
56
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080057import com.google.common.collect.FluentIterable;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070058import com.google.common.collect.Sets;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080059
60/**
61 * Manages inventory of group entries using trivial in-memory implementation.
62 */
63@Component(immediate = true)
64@Service
65public class SimpleGroupStore
66 extends AbstractStore<GroupEvent, GroupStoreDelegate>
67 implements GroupStore {
68
69 private final Logger log = getLogger(getClass());
70
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080071 private final int dummyId = 0xffffffff;
72 private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
73
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080074 // inner Map is per device group table
75 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
76 groupEntriesByKey = new ConcurrentHashMap<>();
77 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
78 groupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080079 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
80 pendingGroupEntriesByKey = new ConcurrentHashMap<>();
81 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
82 extraneousGroupEntriesById = new ConcurrentHashMap<>();
83
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -070084 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080085
86 private final AtomicInteger groupIdGen = new AtomicInteger();
87
88 @Activate
89 public void activate() {
90 log.info("Started");
91 }
92
93 @Deactivate
94 public void deactivate() {
95 groupEntriesByKey.clear();
96 groupEntriesById.clear();
97 log.info("Stopped");
98 }
99
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800100 /**
101 * Returns the group key table for specified device.
102 *
103 * @param deviceId identifier of the device
104 * @return Map representing group key table of given device.
105 */
106 private ConcurrentMap<GroupKey, StoredGroupEntry> getGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700107 return groupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800108 }
109
110 /**
111 * Returns the group id table for specified device.
112 *
113 * @param deviceId identifier of the device
114 * @return Map representing group key table of given device.
115 */
116 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700117 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800118 }
119
120 /**
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800121 * Returns the pending group key table for specified device.
122 *
123 * @param deviceId identifier of the device
124 * @return Map representing group key table of given device.
125 */
126 private ConcurrentMap<GroupKey, StoredGroupEntry>
127 getPendingGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700128 return pendingGroupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800129 }
130
131 /**
132 * Returns the extraneous group id table for specified device.
133 *
134 * @param deviceId identifier of the device
135 * @return Map representing group key table of given device.
136 */
137 private ConcurrentMap<GroupId, Group>
138 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700139 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800140 }
141
142 /**
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800143 * Returns the number of groups for the specified device in the store.
144 *
145 * @return number of groups for the specified device
146 */
147 @Override
148 public int getGroupCount(DeviceId deviceId) {
149 return (groupEntriesByKey.get(deviceId) != null) ?
150 groupEntriesByKey.get(deviceId).size() : 0;
151 }
152
153 /**
154 * Returns the groups associated with a device.
155 *
156 * @param deviceId the device ID
157 *
158 * @return the group entries
159 */
160 @Override
161 public Iterable<Group> getGroups(DeviceId deviceId) {
162 // flatten and make iterator unmodifiable
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800163 return FluentIterable.from(getGroupKeyTable(deviceId).values())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700164 .transform(input -> input);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800165 }
166
167 /**
168 * Returns the stored group entry.
169 *
170 * @param deviceId the device ID
171 * @param appCookie the group key
172 *
173 * @return a group associated with the key
174 */
175 @Override
176 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
177 return (groupEntriesByKey.get(deviceId) != null) ?
178 groupEntriesByKey.get(deviceId).get(appCookie) :
179 null;
180 }
181
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700182 @Override
183 public Group getGroup(DeviceId deviceId, GroupId groupId) {
184 return (groupEntriesById.get(deviceId) != null) ?
185 groupEntriesById.get(deviceId).get(groupId) :
186 null;
187 }
188
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800189 private int getFreeGroupIdValue(DeviceId deviceId) {
190 int freeId = groupIdGen.incrementAndGet();
191
192 while (true) {
193 Group existing = (
194 groupEntriesById.get(deviceId) != null) ?
195 groupEntriesById.get(deviceId).get(new DefaultGroupId(freeId)) :
196 null;
197 if (existing == null) {
198 existing = (
199 extraneousGroupEntriesById.get(deviceId) != null) ?
200 extraneousGroupEntriesById.get(deviceId).
201 get(new DefaultGroupId(freeId)) :
202 null;
203 }
204 if (existing != null) {
205 freeId = groupIdGen.incrementAndGet();
206 } else {
207 break;
208 }
209 }
210 return freeId;
211 }
212
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800213 /**
214 * Stores a new group entry using the information from group description.
215 *
216 * @param groupDesc group description to be used to create group entry
217 */
218 @Override
219 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800220 // Check if a group is existing with the same key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800221 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
222 return;
223 }
224
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800225 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
226 // Device group audit has not completed yet
227 // Add this group description to pending group key table
228 // Create a group entry object with Dummy Group ID
229 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
230 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
231 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
232 getPendingGroupKeyTable(groupDesc.deviceId());
233 pendingKeyTable.put(groupDesc.appCookie(), group);
234 return;
235 }
236
237 storeGroupDescriptionInternal(groupDesc);
238 }
239
240 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800241 // Check if a group is existing with the same key
242 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
243 return;
244 }
245
Saurav Das100e3b82015-04-30 11:12:10 -0700246 GroupId id = null;
247 if (groupDesc.givenGroupId() == null) {
248 // Get a new group identifier
249 id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
250 } else {
251 id = new DefaultGroupId(groupDesc.givenGroupId());
252 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800253 // Create a group entry object
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800254 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800255 // Insert the newly created group entry into concurrent key and id maps
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800256 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
257 getGroupKeyTable(groupDesc.deviceId());
258 keyTable.put(groupDesc.appCookie(), group);
259 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
260 getGroupIdTable(groupDesc.deviceId());
261 idTable.put(id, group);
262 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
263 group));
264 }
265
266 /**
267 * Updates the existing group entry with the information
268 * from group description.
269 *
270 * @param deviceId the device ID
271 * @param oldAppCookie the current group key
272 * @param type update type
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800273 * @param newBuckets group buckets for updates
274 * @param newAppCookie optional new group key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800275 */
276 @Override
277 public void updateGroupDescription(DeviceId deviceId,
278 GroupKey oldAppCookie,
279 UpdateType type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800280 GroupBuckets newBuckets,
281 GroupKey newAppCookie) {
282 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800283 Group oldGroup = getGroup(deviceId, oldAppCookie);
284 if (oldGroup == null) {
285 return;
286 }
287
288 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
289 type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800290 newBuckets);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800291 if (newBucketList != null) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800292 // Create a new group object from the old group
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800293 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800294 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800295 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
296 oldGroup.deviceId(),
297 oldGroup.type(),
298 updatedBuckets,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800299 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700300 oldGroup.givenGroupId(),
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800301 oldGroup.appId());
302 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
303 updatedGroupDesc);
304 newGroup.setState(GroupState.PENDING_UPDATE);
305 newGroup.setLife(oldGroup.life());
306 newGroup.setPackets(oldGroup.packets());
307 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800308 // Remove the old entry from maps and add new entry using new key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800309 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
310 getGroupKeyTable(oldGroup.deviceId());
311 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
312 getGroupIdTable(oldGroup.deviceId());
313 keyTable.remove(oldGroup.appCookie());
314 idTable.remove(oldGroup.id());
315 keyTable.put(newGroup.appCookie(), newGroup);
316 idTable.put(newGroup.id(), newGroup);
317 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
318 }
319 }
320
321 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
Victor Silvadf1eeae2016-08-12 15:28:57 -0300322 UpdateType type,
323 GroupBuckets buckets) {
Victor Silva0282ab82016-11-15 16:30:27 -0300324 if (type == UpdateType.SET) {
325 return buckets.buckets();
326 }
327
Victor Silvadf1eeae2016-08-12 15:28:57 -0300328 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
329 List<GroupBucket> updatedBucketList = new ArrayList<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800330 boolean groupDescUpdated = false;
331
332 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300333 List<GroupBucket> newBuckets = buckets.buckets();
334
335 // Add old buckets that will not be updated and check if any will be updated.
336 for (GroupBucket oldBucket : oldBuckets) {
337 int newBucketIndex = newBuckets.indexOf(oldBucket);
338
339 if (newBucketIndex != -1) {
340 GroupBucket newBucket = newBuckets.get(newBucketIndex);
341 if (!newBucket.hasSameParameters(oldBucket)) {
342 // Bucket will be updated
343 groupDescUpdated = true;
344 }
345 } else {
346 // Old bucket will remain the same - add it.
347 updatedBucketList.add(oldBucket);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800348 }
349 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300350
351 // Add all new buckets
352 updatedBucketList.addAll(newBuckets);
353 if (!oldBuckets.containsAll(newBuckets)) {
354 groupDescUpdated = true;
355 }
356
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800357 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300358 List<GroupBucket> bucketsToRemove = buckets.buckets();
359
360 // Check which old buckets should remain
361 for (GroupBucket oldBucket : oldBuckets) {
362 if (!bucketsToRemove.contains(oldBucket)) {
363 updatedBucketList.add(oldBucket);
364 } else {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800365 groupDescUpdated = true;
366 }
367 }
368 }
369
370 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300371 return updatedBucketList;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800372 } else {
373 return null;
374 }
375 }
376
377 /**
378 * Triggers deleting the existing group entry.
379 *
380 * @param deviceId the device ID
381 * @param appCookie the group key
382 */
383 @Override
384 public void deleteGroupDescription(DeviceId deviceId,
385 GroupKey appCookie) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800386 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800387 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
388 groupEntriesByKey.get(deviceId).get(appCookie) :
389 null;
390 if (existing == null) {
391 return;
392 }
393
394 synchronized (existing) {
395 existing.setState(GroupState.PENDING_DELETE);
396 }
397 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
398 }
399
400 /**
401 * Stores a new group entry, or updates an existing entry.
402 *
403 * @param group group entry
404 */
405 @Override
406 public void addOrUpdateGroupEntry(Group group) {
407 // check if this new entry is an update to an existing entry
408 StoredGroupEntry existing = (groupEntriesById.get(
409 group.deviceId()) != null) ?
410 groupEntriesById.get(group.deviceId()).get(group.id()) :
411 null;
412 GroupEvent event = null;
413
414 if (existing != null) {
415 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700416 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700417 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700418 existing.buckets().buckets()
419 .stream()
420 .filter((existingBucket)->(existingBucket.equals(bucket)))
421 .findFirst();
422 if (matchingBucket.isPresent()) {
423 ((StoredGroupBucketEntry) matchingBucket.
424 get()).setPackets(bucket.packets());
425 ((StoredGroupBucketEntry) matchingBucket.
426 get()).setBytes(bucket.bytes());
427 } else {
428 log.warn("addOrUpdateGroupEntry: No matching "
429 + "buckets to update stats");
430 }
431 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800432 existing.setLife(group.life());
433 existing.setPackets(group.packets());
434 existing.setBytes(group.bytes());
435 if (existing.state() == GroupState.PENDING_ADD) {
436 existing.setState(GroupState.ADDED);
437 event = new GroupEvent(Type.GROUP_ADDED, existing);
438 } else {
439 if (existing.state() == GroupState.PENDING_UPDATE) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700440 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800441 }
442 event = new GroupEvent(Type.GROUP_UPDATED, existing);
443 }
444 }
445 }
446
447 if (event != null) {
448 notifyDelegate(event);
449 }
450 }
451
452 /**
453 * Removes the group entry from store.
454 *
455 * @param group group entry
456 */
457 @Override
458 public void removeGroupEntry(Group group) {
459 StoredGroupEntry existing = (groupEntriesById.get(
460 group.deviceId()) != null) ?
461 groupEntriesById.get(group.deviceId()).get(group.id()) :
462 null;
463
464 if (existing != null) {
465 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
466 getGroupKeyTable(existing.deviceId());
467 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
468 getGroupIdTable(existing.deviceId());
469 idTable.remove(existing.id());
470 keyTable.remove(existing.appCookie());
471 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
472 }
473 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800474
475 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800476 public void purgeGroupEntry(DeviceId deviceId) {
477 Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove =
478 groupEntriesById.get(deviceId).entrySet();
479
480 groupEntriesById.remove(deviceId);
481 groupEntriesByKey.remove(deviceId);
482
483 entryPendingRemove.forEach(entry -> {
484 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
485 });
486 }
487
488 @Override
Victor Silva4e8b7832016-08-17 17:11:19 -0300489 public void purgeGroupEntries() {
490 groupEntriesById.values().forEach(groupEntries -> {
491 groupEntries.entrySet().forEach(entry -> {
492 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
493 });
494 });
495
496 groupEntriesById.clear();
497 groupEntriesByKey.clear();
498 }
499
500 @Override
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800501 public void deviceInitialAuditCompleted(DeviceId deviceId,
502 boolean completed) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800503 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800504 if (completed) {
505 log.debug("deviceInitialAuditCompleted: AUDIT "
506 + "completed for device {}", deviceId);
507 deviceAuditStatus.put(deviceId, true);
508 // Execute all pending group requests
509 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
510 getPendingGroupKeyTable(deviceId);
511 for (Group group:pendingGroupRequests.values()) {
512 GroupDescription tmp = new DefaultGroupDescription(
513 group.deviceId(),
514 group.type(),
515 group.buckets(),
516 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700517 group.givenGroupId(),
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800518 group.appId());
519 storeGroupDescriptionInternal(tmp);
520 }
521 getPendingGroupKeyTable(deviceId).clear();
522 } else {
523 if (deviceAuditStatus.get(deviceId)) {
524 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
525 + "status for device {}", deviceId);
526 deviceAuditStatus.put(deviceId, false);
527 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800528 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800529 }
530 }
531
532 @Override
533 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
534 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800535 return (deviceAuditStatus.get(deviceId) != null)
536 ? deviceAuditStatus.get(deviceId) : false;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800537 }
538 }
539
540 @Override
sangho7ff01812015-02-09 16:21:53 -0800541 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
542
543 StoredGroupEntry existing = (groupEntriesById.get(
544 deviceId) != null) ?
545 groupEntriesById.get(deviceId).get(operation.groupId()) :
546 null;
547
548 if (existing == null) {
549 log.warn("No group entry with ID {} found ", operation.groupId());
550 return;
551 }
552
553 switch (operation.opType()) {
554 case ADD:
555 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
556 break;
557 case MODIFY:
558 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
559 break;
560 case DELETE:
561 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
562 break;
563 default:
564 log.warn("Unknown group operation type {}", operation.opType());
565 }
566
567 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
568 getGroupKeyTable(existing.deviceId());
569 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
570 getGroupIdTable(existing.deviceId());
571 idTable.remove(existing.id());
572 keyTable.remove(existing.appCookie());
573 }
574
575 @Override
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800576 public void addOrUpdateExtraneousGroupEntry(Group group) {
577 ConcurrentMap<GroupId, Group> extraneousIdTable =
578 getExtraneousGroupIdTable(group.deviceId());
579 extraneousIdTable.put(group.id(), group);
580 // Check the reference counter
581 if (group.referenceCount() == 0) {
582 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
583 }
584 }
585
586 @Override
587 public void removeExtraneousGroupEntry(Group group) {
588 ConcurrentMap<GroupId, Group> extraneousIdTable =
589 getExtraneousGroupIdTable(group.deviceId());
590 extraneousIdTable.remove(group.id());
591 }
592
593 @Override
594 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
595 // flatten and make iterator unmodifiable
596 return FluentIterable.from(
597 getExtraneousGroupIdTable(deviceId).values());
598 }
sangho7ff01812015-02-09 16:21:53 -0800599
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700600 @Override
601 public void pushGroupMetrics(DeviceId deviceId,
602 Collection<Group> groupEntries) {
603 boolean deviceInitialAuditStatus =
604 deviceInitialAuditStatus(deviceId);
605 Set<Group> southboundGroupEntries =
606 Sets.newHashSet(groupEntries);
607 Set<Group> storedGroupEntries =
608 Sets.newHashSet(getGroups(deviceId));
609 Set<Group> extraneousStoredEntries =
610 Sets.newHashSet(getExtraneousGroups(deviceId));
611
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700612 if (log.isTraceEnabled()) {
613 log.trace("pushGroupMetrics: Displaying all ({}) "
614 + "southboundGroupEntries for device {}",
615 southboundGroupEntries.size(),
616 deviceId);
617 for (Group group : southboundGroupEntries) {
618 log.trace("Group {} in device {}", group, deviceId);
619 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700620
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700621 log.trace("Displaying all ({}) stored group entries for device {}",
622 storedGroupEntries.size(),
623 deviceId);
624 for (Group group : storedGroupEntries) {
625 log.trace("Stored Group {} for device {}", group, deviceId);
626 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700627 }
628
629 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
630 Group group = it2.next();
631 if (storedGroupEntries.remove(group)) {
632 // we both have the group, let's update some info then.
633 log.trace("Group AUDIT: group {} exists "
634 + "in both planes for device {}",
635 group.id(), deviceId);
636 groupAdded(group);
637 it2.remove();
638 }
639 }
640 for (Group group : southboundGroupEntries) {
641 if (getGroup(group.deviceId(), group.id()) != null) {
642 // There is a group existing with the same id
643 // It is possible that group update is
644 // in progress while we got a stale info from switch
645 if (!storedGroupEntries.remove(getGroup(
646 group.deviceId(), group.id()))) {
647 log.warn("Group AUDIT: Inconsistent state:"
648 + "Group exists in ID based table while "
649 + "not present in key based table");
650 }
651 } else {
652 // there are groups in the switch that aren't in the store
653 log.trace("Group AUDIT: extraneous group {} exists "
654 + "in data plane for device {}",
655 group.id(), deviceId);
656 extraneousStoredEntries.remove(group);
657 extraneousGroup(group);
658 }
659 }
660 for (Group group : storedGroupEntries) {
661 // there are groups in the store that aren't in the switch
662 log.trace("Group AUDIT: group {} missing "
663 + "in data plane for device {}",
664 group.id(), deviceId);
665 groupMissing(group);
666 }
667 for (Group group : extraneousStoredEntries) {
668 // there are groups in the extraneous store that
669 // aren't in the switch
670 log.trace("Group AUDIT: clearing extransoeus group {} "
671 + "from store for device {}",
672 group.id(), deviceId);
673 removeExtraneousGroupEntry(group);
674 }
675
676 if (!deviceInitialAuditStatus) {
677 log.debug("Group AUDIT: Setting device {} initial "
678 + "AUDIT completed", deviceId);
679 deviceInitialAuditCompleted(deviceId, true);
680 }
681 }
682
helenyrwu89470f12016-08-12 13:18:10 -0700683 @Override
684 public void notifyOfFailovers(Collection<Group> failoverGroups) {
685 List<GroupEvent> failoverEvents = new ArrayList<>();
686 failoverGroups.forEach(group -> {
687 if (group.type() == Group.Type.FAILOVER) {
688 failoverEvents.add(new GroupEvent(GroupEvent.Type.GROUP_BUCKET_FAILOVER, group));
689 }
690 });
691 notifyDelegate(failoverEvents);
692 }
693
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700694 private void groupMissing(Group group) {
695 switch (group.state()) {
696 case PENDING_DELETE:
697 log.debug("Group {} delete confirmation from device {}",
698 group, group.deviceId());
699 removeGroupEntry(group);
700 break;
701 case ADDED:
702 case PENDING_ADD:
703 case PENDING_UPDATE:
704 log.debug("Group {} is in store but not on device {}",
705 group, group.deviceId());
706 StoredGroupEntry existing = (groupEntriesById.get(
707 group.deviceId()) != null) ?
708 groupEntriesById.get(group.deviceId()).get(group.id()) :
709 null;
710 log.trace("groupMissing: group "
711 + "entry {} in device {} moving "
712 + "from {} to PENDING_ADD",
713 existing.id(),
714 existing.deviceId(),
715 existing.state());
716 existing.setState(Group.GroupState.PENDING_ADD);
717 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
718 group));
719 break;
720 default:
721 log.debug("Group {} has not been installed.", group);
722 break;
723 }
724 }
725
726 private void extraneousGroup(Group group) {
727 log.debug("Group {} is on device {} but not in store.",
728 group, group.deviceId());
729 addOrUpdateExtraneousGroupEntry(group);
730 }
731
732 private void groupAdded(Group group) {
733 log.trace("Group {} Added or Updated in device {}",
734 group, group.deviceId());
735 addOrUpdateGroupEntry(group);
736 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800737}