blob: 62c3e30c71a27b28e541457e8d94ba5881c9333d [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) {
324 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
325 List<GroupBucket> updatedBucketList = new ArrayList<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800326 boolean groupDescUpdated = false;
327
328 if (type == UpdateType.ADD) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300329 List<GroupBucket> newBuckets = buckets.buckets();
330
331 // Add old buckets that will not be updated and check if any will be updated.
332 for (GroupBucket oldBucket : oldBuckets) {
333 int newBucketIndex = newBuckets.indexOf(oldBucket);
334
335 if (newBucketIndex != -1) {
336 GroupBucket newBucket = newBuckets.get(newBucketIndex);
337 if (!newBucket.hasSameParameters(oldBucket)) {
338 // Bucket will be updated
339 groupDescUpdated = true;
340 }
341 } else {
342 // Old bucket will remain the same - add it.
343 updatedBucketList.add(oldBucket);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800344 }
345 }
Victor Silvadf1eeae2016-08-12 15:28:57 -0300346
347 // Add all new buckets
348 updatedBucketList.addAll(newBuckets);
349 if (!oldBuckets.containsAll(newBuckets)) {
350 groupDescUpdated = true;
351 }
352
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800353 } else if (type == UpdateType.REMOVE) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300354 List<GroupBucket> bucketsToRemove = buckets.buckets();
355
356 // Check which old buckets should remain
357 for (GroupBucket oldBucket : oldBuckets) {
358 if (!bucketsToRemove.contains(oldBucket)) {
359 updatedBucketList.add(oldBucket);
360 } else {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800361 groupDescUpdated = true;
362 }
363 }
364 }
365
366 if (groupDescUpdated) {
Victor Silvadf1eeae2016-08-12 15:28:57 -0300367 return updatedBucketList;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800368 } else {
369 return null;
370 }
371 }
372
373 /**
374 * Triggers deleting the existing group entry.
375 *
376 * @param deviceId the device ID
377 * @param appCookie the group key
378 */
379 @Override
380 public void deleteGroupDescription(DeviceId deviceId,
381 GroupKey appCookie) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800382 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800383 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
384 groupEntriesByKey.get(deviceId).get(appCookie) :
385 null;
386 if (existing == null) {
387 return;
388 }
389
390 synchronized (existing) {
391 existing.setState(GroupState.PENDING_DELETE);
392 }
393 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
394 }
395
396 /**
397 * Stores a new group entry, or updates an existing entry.
398 *
399 * @param group group entry
400 */
401 @Override
402 public void addOrUpdateGroupEntry(Group group) {
403 // check if this new entry is an update to an existing entry
404 StoredGroupEntry existing = (groupEntriesById.get(
405 group.deviceId()) != null) ?
406 groupEntriesById.get(group.deviceId()).get(group.id()) :
407 null;
408 GroupEvent event = null;
409
410 if (existing != null) {
411 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700412 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700413 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700414 existing.buckets().buckets()
415 .stream()
416 .filter((existingBucket)->(existingBucket.equals(bucket)))
417 .findFirst();
418 if (matchingBucket.isPresent()) {
419 ((StoredGroupBucketEntry) matchingBucket.
420 get()).setPackets(bucket.packets());
421 ((StoredGroupBucketEntry) matchingBucket.
422 get()).setBytes(bucket.bytes());
423 } else {
424 log.warn("addOrUpdateGroupEntry: No matching "
425 + "buckets to update stats");
426 }
427 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800428 existing.setLife(group.life());
429 existing.setPackets(group.packets());
430 existing.setBytes(group.bytes());
431 if (existing.state() == GroupState.PENDING_ADD) {
432 existing.setState(GroupState.ADDED);
433 event = new GroupEvent(Type.GROUP_ADDED, existing);
434 } else {
435 if (existing.state() == GroupState.PENDING_UPDATE) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700436 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800437 }
438 event = new GroupEvent(Type.GROUP_UPDATED, existing);
439 }
440 }
441 }
442
443 if (event != null) {
444 notifyDelegate(event);
445 }
446 }
447
448 /**
449 * Removes the group entry from store.
450 *
451 * @param group group entry
452 */
453 @Override
454 public void removeGroupEntry(Group group) {
455 StoredGroupEntry existing = (groupEntriesById.get(
456 group.deviceId()) != null) ?
457 groupEntriesById.get(group.deviceId()).get(group.id()) :
458 null;
459
460 if (existing != null) {
461 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
462 getGroupKeyTable(existing.deviceId());
463 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
464 getGroupIdTable(existing.deviceId());
465 idTable.remove(existing.id());
466 keyTable.remove(existing.appCookie());
467 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
468 }
469 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800470
471 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800472 public void purgeGroupEntry(DeviceId deviceId) {
473 Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove =
474 groupEntriesById.get(deviceId).entrySet();
475
476 groupEntriesById.remove(deviceId);
477 groupEntriesByKey.remove(deviceId);
478
479 entryPendingRemove.forEach(entry -> {
480 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
481 });
482 }
483
484 @Override
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800485 public void deviceInitialAuditCompleted(DeviceId deviceId,
486 boolean completed) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800487 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800488 if (completed) {
489 log.debug("deviceInitialAuditCompleted: AUDIT "
490 + "completed for device {}", deviceId);
491 deviceAuditStatus.put(deviceId, true);
492 // Execute all pending group requests
493 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
494 getPendingGroupKeyTable(deviceId);
495 for (Group group:pendingGroupRequests.values()) {
496 GroupDescription tmp = new DefaultGroupDescription(
497 group.deviceId(),
498 group.type(),
499 group.buckets(),
500 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700501 group.givenGroupId(),
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800502 group.appId());
503 storeGroupDescriptionInternal(tmp);
504 }
505 getPendingGroupKeyTable(deviceId).clear();
506 } else {
507 if (deviceAuditStatus.get(deviceId)) {
508 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
509 + "status for device {}", deviceId);
510 deviceAuditStatus.put(deviceId, false);
511 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800512 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800513 }
514 }
515
516 @Override
517 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
518 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800519 return (deviceAuditStatus.get(deviceId) != null)
520 ? deviceAuditStatus.get(deviceId) : false;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800521 }
522 }
523
524 @Override
sangho7ff01812015-02-09 16:21:53 -0800525 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
526
527 StoredGroupEntry existing = (groupEntriesById.get(
528 deviceId) != null) ?
529 groupEntriesById.get(deviceId).get(operation.groupId()) :
530 null;
531
532 if (existing == null) {
533 log.warn("No group entry with ID {} found ", operation.groupId());
534 return;
535 }
536
537 switch (operation.opType()) {
538 case ADD:
539 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
540 break;
541 case MODIFY:
542 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
543 break;
544 case DELETE:
545 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
546 break;
547 default:
548 log.warn("Unknown group operation type {}", operation.opType());
549 }
550
551 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
552 getGroupKeyTable(existing.deviceId());
553 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
554 getGroupIdTable(existing.deviceId());
555 idTable.remove(existing.id());
556 keyTable.remove(existing.appCookie());
557 }
558
559 @Override
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800560 public void addOrUpdateExtraneousGroupEntry(Group group) {
561 ConcurrentMap<GroupId, Group> extraneousIdTable =
562 getExtraneousGroupIdTable(group.deviceId());
563 extraneousIdTable.put(group.id(), group);
564 // Check the reference counter
565 if (group.referenceCount() == 0) {
566 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
567 }
568 }
569
570 @Override
571 public void removeExtraneousGroupEntry(Group group) {
572 ConcurrentMap<GroupId, Group> extraneousIdTable =
573 getExtraneousGroupIdTable(group.deviceId());
574 extraneousIdTable.remove(group.id());
575 }
576
577 @Override
578 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
579 // flatten and make iterator unmodifiable
580 return FluentIterable.from(
581 getExtraneousGroupIdTable(deviceId).values());
582 }
sangho7ff01812015-02-09 16:21:53 -0800583
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700584 @Override
585 public void pushGroupMetrics(DeviceId deviceId,
586 Collection<Group> groupEntries) {
587 boolean deviceInitialAuditStatus =
588 deviceInitialAuditStatus(deviceId);
589 Set<Group> southboundGroupEntries =
590 Sets.newHashSet(groupEntries);
591 Set<Group> storedGroupEntries =
592 Sets.newHashSet(getGroups(deviceId));
593 Set<Group> extraneousStoredEntries =
594 Sets.newHashSet(getExtraneousGroups(deviceId));
595
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700596 if (log.isTraceEnabled()) {
597 log.trace("pushGroupMetrics: Displaying all ({}) "
598 + "southboundGroupEntries for device {}",
599 southboundGroupEntries.size(),
600 deviceId);
601 for (Group group : southboundGroupEntries) {
602 log.trace("Group {} in device {}", group, deviceId);
603 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700604
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700605 log.trace("Displaying all ({}) stored group entries for device {}",
606 storedGroupEntries.size(),
607 deviceId);
608 for (Group group : storedGroupEntries) {
609 log.trace("Stored Group {} for device {}", group, deviceId);
610 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700611 }
612
613 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
614 Group group = it2.next();
615 if (storedGroupEntries.remove(group)) {
616 // we both have the group, let's update some info then.
617 log.trace("Group AUDIT: group {} exists "
618 + "in both planes for device {}",
619 group.id(), deviceId);
620 groupAdded(group);
621 it2.remove();
622 }
623 }
624 for (Group group : southboundGroupEntries) {
625 if (getGroup(group.deviceId(), group.id()) != null) {
626 // There is a group existing with the same id
627 // It is possible that group update is
628 // in progress while we got a stale info from switch
629 if (!storedGroupEntries.remove(getGroup(
630 group.deviceId(), group.id()))) {
631 log.warn("Group AUDIT: Inconsistent state:"
632 + "Group exists in ID based table while "
633 + "not present in key based table");
634 }
635 } else {
636 // there are groups in the switch that aren't in the store
637 log.trace("Group AUDIT: extraneous group {} exists "
638 + "in data plane for device {}",
639 group.id(), deviceId);
640 extraneousStoredEntries.remove(group);
641 extraneousGroup(group);
642 }
643 }
644 for (Group group : storedGroupEntries) {
645 // there are groups in the store that aren't in the switch
646 log.trace("Group AUDIT: group {} missing "
647 + "in data plane for device {}",
648 group.id(), deviceId);
649 groupMissing(group);
650 }
651 for (Group group : extraneousStoredEntries) {
652 // there are groups in the extraneous store that
653 // aren't in the switch
654 log.trace("Group AUDIT: clearing extransoeus group {} "
655 + "from store for device {}",
656 group.id(), deviceId);
657 removeExtraneousGroupEntry(group);
658 }
659
660 if (!deviceInitialAuditStatus) {
661 log.debug("Group AUDIT: Setting device {} initial "
662 + "AUDIT completed", deviceId);
663 deviceInitialAuditCompleted(deviceId, true);
664 }
665 }
666
helenyrwu89470f12016-08-12 13:18:10 -0700667 @Override
668 public void notifyOfFailovers(Collection<Group> failoverGroups) {
669 List<GroupEvent> failoverEvents = new ArrayList<>();
670 failoverGroups.forEach(group -> {
671 if (group.type() == Group.Type.FAILOVER) {
672 failoverEvents.add(new GroupEvent(GroupEvent.Type.GROUP_BUCKET_FAILOVER, group));
673 }
674 });
675 notifyDelegate(failoverEvents);
676 }
677
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700678 private void groupMissing(Group group) {
679 switch (group.state()) {
680 case PENDING_DELETE:
681 log.debug("Group {} delete confirmation from device {}",
682 group, group.deviceId());
683 removeGroupEntry(group);
684 break;
685 case ADDED:
686 case PENDING_ADD:
687 case PENDING_UPDATE:
688 log.debug("Group {} is in store but not on device {}",
689 group, group.deviceId());
690 StoredGroupEntry existing = (groupEntriesById.get(
691 group.deviceId()) != null) ?
692 groupEntriesById.get(group.deviceId()).get(group.id()) :
693 null;
694 log.trace("groupMissing: group "
695 + "entry {} in device {} moving "
696 + "from {} to PENDING_ADD",
697 existing.id(),
698 existing.deviceId(),
699 existing.state());
700 existing.setState(Group.GroupState.PENDING_ADD);
701 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
702 group));
703 break;
704 default:
705 log.debug("Group {} has not been installed.", group);
706 break;
707 }
708 }
709
710 private void extraneousGroup(Group group) {
711 log.debug("Group {} is on device {} but not in store.",
712 group, group.deviceId());
713 addOrUpdateExtraneousGroupEntry(group);
714 }
715
716 private void groupAdded(Group group) {
717 log.trace("Group {} Added or Updated in device {}",
718 group, group.deviceId());
719 addOrUpdateGroupEntry(group);
720 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800721}