blob: 71de3e13e185f225528ae08c6f0331ebbf2fc38f [file] [log] [blame]
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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
18import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
19import static org.slf4j.LoggerFactory.getLogger;
20
21import java.util.ArrayList;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070022import java.util.Collection;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080023import java.util.HashMap;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070024import java.util.Iterator;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080025import java.util.List;
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;
36import org.onlab.util.NewConcurrentHashMap;
37import org.onosproject.core.DefaultGroupId;
38import org.onosproject.core.GroupId;
39import org.onosproject.net.DeviceId;
40import org.onosproject.net.group.DefaultGroup;
41import org.onosproject.net.group.DefaultGroupDescription;
42import org.onosproject.net.group.Group;
43import org.onosproject.net.group.Group.GroupState;
44import org.onosproject.net.group.GroupBucket;
45import org.onosproject.net.group.GroupBuckets;
46import org.onosproject.net.group.GroupDescription;
47import org.onosproject.net.group.GroupEvent;
48import org.onosproject.net.group.GroupEvent.Type;
49import org.onosproject.net.group.GroupKey;
sangho7ff01812015-02-09 16:21:53 -080050import org.onosproject.net.group.GroupOperation;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080051import org.onosproject.net.group.GroupStore;
52import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070053import org.onosproject.net.group.StoredGroupBucketEntry;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080054import org.onosproject.net.group.StoredGroupEntry;
55import org.onosproject.store.AbstractStore;
56import org.slf4j.Logger;
57
58import com.google.common.base.Function;
59import com.google.common.collect.FluentIterable;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070060import com.google.common.collect.Sets;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080061
62/**
63 * Manages inventory of group entries using trivial in-memory implementation.
64 */
65@Component(immediate = true)
66@Service
67public class SimpleGroupStore
68 extends AbstractStore<GroupEvent, GroupStoreDelegate>
69 implements GroupStore {
70
71 private final Logger log = getLogger(getClass());
72
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080073 private final int dummyId = 0xffffffff;
74 private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
75
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080076 // inner Map is per device group table
77 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
78 groupEntriesByKey = new ConcurrentHashMap<>();
79 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
80 groupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080081 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
82 pendingGroupEntriesByKey = new ConcurrentHashMap<>();
83 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
84 extraneousGroupEntriesById = new ConcurrentHashMap<>();
85
86 private final HashMap<DeviceId, Boolean> deviceAuditStatus =
87 new HashMap<DeviceId, Boolean>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080088
89 private final AtomicInteger groupIdGen = new AtomicInteger();
90
91 @Activate
92 public void activate() {
93 log.info("Started");
94 }
95
96 @Deactivate
97 public void deactivate() {
98 groupEntriesByKey.clear();
99 groupEntriesById.clear();
100 log.info("Stopped");
101 }
102
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800103 private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
104 lazyEmptyGroupKeyTable() {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800105 return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
106 }
107
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800108 private static NewConcurrentHashMap<GroupId, StoredGroupEntry>
109 lazyEmptyGroupIdTable() {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800110 return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded();
111 }
112
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800113 private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
114 lazyEmptyPendingGroupKeyTable() {
115 return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
116 }
117
118 private static NewConcurrentHashMap<GroupId, Group>
119 lazyEmptyExtraneousGroupIdTable() {
120 return NewConcurrentHashMap.<GroupId, Group>ifNeeded();
121 }
122
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800123 /**
124 * Returns the group key table for specified device.
125 *
126 * @param deviceId identifier of the device
127 * @return Map representing group key table of given device.
128 */
129 private ConcurrentMap<GroupKey, StoredGroupEntry> getGroupKeyTable(DeviceId deviceId) {
130 return createIfAbsentUnchecked(groupEntriesByKey,
131 deviceId, lazyEmptyGroupKeyTable());
132 }
133
134 /**
135 * Returns the group id table for specified device.
136 *
137 * @param deviceId identifier of the device
138 * @return Map representing group key table of given device.
139 */
140 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
141 return createIfAbsentUnchecked(groupEntriesById,
142 deviceId, lazyEmptyGroupIdTable());
143 }
144
145 /**
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800146 * Returns the pending group key table for specified device.
147 *
148 * @param deviceId identifier of the device
149 * @return Map representing group key table of given device.
150 */
151 private ConcurrentMap<GroupKey, StoredGroupEntry>
152 getPendingGroupKeyTable(DeviceId deviceId) {
153 return createIfAbsentUnchecked(pendingGroupEntriesByKey,
154 deviceId, lazyEmptyPendingGroupKeyTable());
155 }
156
157 /**
158 * Returns the extraneous group id table for specified device.
159 *
160 * @param deviceId identifier of the device
161 * @return Map representing group key table of given device.
162 */
163 private ConcurrentMap<GroupId, Group>
164 getExtraneousGroupIdTable(DeviceId deviceId) {
165 return createIfAbsentUnchecked(extraneousGroupEntriesById,
166 deviceId,
167 lazyEmptyExtraneousGroupIdTable());
168 }
169
170 /**
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800171 * Returns the number of groups for the specified device in the store.
172 *
173 * @return number of groups for the specified device
174 */
175 @Override
176 public int getGroupCount(DeviceId deviceId) {
177 return (groupEntriesByKey.get(deviceId) != null) ?
178 groupEntriesByKey.get(deviceId).size() : 0;
179 }
180
181 /**
182 * Returns the groups associated with a device.
183 *
184 * @param deviceId the device ID
185 *
186 * @return the group entries
187 */
188 @Override
189 public Iterable<Group> getGroups(DeviceId deviceId) {
190 // flatten and make iterator unmodifiable
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800191 return FluentIterable.from(getGroupKeyTable(deviceId).values())
192 .transform(
193 new Function<StoredGroupEntry, Group>() {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800194
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800195 @Override
196 public Group apply(
197 StoredGroupEntry input) {
198 return input;
199 }
200 });
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800201 }
202
203 /**
204 * Returns the stored group entry.
205 *
206 * @param deviceId the device ID
207 * @param appCookie the group key
208 *
209 * @return a group associated with the key
210 */
211 @Override
212 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
213 return (groupEntriesByKey.get(deviceId) != null) ?
214 groupEntriesByKey.get(deviceId).get(appCookie) :
215 null;
216 }
217
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700218 @Override
219 public Group getGroup(DeviceId deviceId, GroupId groupId) {
220 return (groupEntriesById.get(deviceId) != null) ?
221 groupEntriesById.get(deviceId).get(groupId) :
222 null;
223 }
224
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800225 private int getFreeGroupIdValue(DeviceId deviceId) {
226 int freeId = groupIdGen.incrementAndGet();
227
228 while (true) {
229 Group existing = (
230 groupEntriesById.get(deviceId) != null) ?
231 groupEntriesById.get(deviceId).get(new DefaultGroupId(freeId)) :
232 null;
233 if (existing == null) {
234 existing = (
235 extraneousGroupEntriesById.get(deviceId) != null) ?
236 extraneousGroupEntriesById.get(deviceId).
237 get(new DefaultGroupId(freeId)) :
238 null;
239 }
240 if (existing != null) {
241 freeId = groupIdGen.incrementAndGet();
242 } else {
243 break;
244 }
245 }
246 return freeId;
247 }
248
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800249 /**
250 * Stores a new group entry using the information from group description.
251 *
252 * @param groupDesc group description to be used to create group entry
253 */
254 @Override
255 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800256 // Check if a group is existing with the same key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800257 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
258 return;
259 }
260
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800261 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
262 // Device group audit has not completed yet
263 // Add this group description to pending group key table
264 // Create a group entry object with Dummy Group ID
265 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
266 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
267 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
268 getPendingGroupKeyTable(groupDesc.deviceId());
269 pendingKeyTable.put(groupDesc.appCookie(), group);
270 return;
271 }
272
273 storeGroupDescriptionInternal(groupDesc);
274 }
275
276 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800277 // Check if a group is existing with the same key
278 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
279 return;
280 }
281
Saurav Das100e3b82015-04-30 11:12:10 -0700282 GroupId id = null;
283 if (groupDesc.givenGroupId() == null) {
284 // Get a new group identifier
285 id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
286 } else {
287 id = new DefaultGroupId(groupDesc.givenGroupId());
288 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800289 // Create a group entry object
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800290 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800291 // Insert the newly created group entry into concurrent key and id maps
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800292 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
293 getGroupKeyTable(groupDesc.deviceId());
294 keyTable.put(groupDesc.appCookie(), group);
295 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
296 getGroupIdTable(groupDesc.deviceId());
297 idTable.put(id, group);
298 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
299 group));
300 }
301
302 /**
303 * Updates the existing group entry with the information
304 * from group description.
305 *
306 * @param deviceId the device ID
307 * @param oldAppCookie the current group key
308 * @param type update type
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800309 * @param newBuckets group buckets for updates
310 * @param newAppCookie optional new group key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800311 */
312 @Override
313 public void updateGroupDescription(DeviceId deviceId,
314 GroupKey oldAppCookie,
315 UpdateType type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800316 GroupBuckets newBuckets,
317 GroupKey newAppCookie) {
318 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800319 Group oldGroup = getGroup(deviceId, oldAppCookie);
320 if (oldGroup == null) {
321 return;
322 }
323
324 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
325 type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800326 newBuckets);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800327 if (newBucketList != null) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800328 // Create a new group object from the old group
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800329 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800330 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800331 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
332 oldGroup.deviceId(),
333 oldGroup.type(),
334 updatedBuckets,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800335 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700336 oldGroup.givenGroupId(),
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800337 oldGroup.appId());
338 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
339 updatedGroupDesc);
340 newGroup.setState(GroupState.PENDING_UPDATE);
341 newGroup.setLife(oldGroup.life());
342 newGroup.setPackets(oldGroup.packets());
343 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800344 // Remove the old entry from maps and add new entry using new key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800345 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
346 getGroupKeyTable(oldGroup.deviceId());
347 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
348 getGroupIdTable(oldGroup.deviceId());
349 keyTable.remove(oldGroup.appCookie());
350 idTable.remove(oldGroup.id());
351 keyTable.put(newGroup.appCookie(), newGroup);
352 idTable.put(newGroup.id(), newGroup);
353 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
354 }
355 }
356
357 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
358 UpdateType type,
359 GroupBuckets buckets) {
360 GroupBuckets oldBuckets = oldGroup.buckets();
361 List<GroupBucket> newBucketList = new ArrayList<GroupBucket>(
362 oldBuckets.buckets());
363 boolean groupDescUpdated = false;
364
365 if (type == UpdateType.ADD) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800366 // Check if the any of the new buckets are part of
367 // the old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800368 for (GroupBucket addBucket:buckets.buckets()) {
369 if (!newBucketList.contains(addBucket)) {
370 newBucketList.add(addBucket);
371 groupDescUpdated = true;
372 }
373 }
374 } else if (type == UpdateType.REMOVE) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800375 // Check if the to be removed buckets are part of the
376 // old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800377 for (GroupBucket removeBucket:buckets.buckets()) {
378 if (newBucketList.contains(removeBucket)) {
379 newBucketList.remove(removeBucket);
380 groupDescUpdated = true;
381 }
382 }
383 }
384
385 if (groupDescUpdated) {
386 return newBucketList;
387 } else {
388 return null;
389 }
390 }
391
392 /**
393 * Triggers deleting the existing group entry.
394 *
395 * @param deviceId the device ID
396 * @param appCookie the group key
397 */
398 @Override
399 public void deleteGroupDescription(DeviceId deviceId,
400 GroupKey appCookie) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800401 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800402 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
403 groupEntriesByKey.get(deviceId).get(appCookie) :
404 null;
405 if (existing == null) {
406 return;
407 }
408
409 synchronized (existing) {
410 existing.setState(GroupState.PENDING_DELETE);
411 }
412 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
413 }
414
415 /**
416 * Stores a new group entry, or updates an existing entry.
417 *
418 * @param group group entry
419 */
420 @Override
421 public void addOrUpdateGroupEntry(Group group) {
422 // check if this new entry is an update to an existing entry
423 StoredGroupEntry existing = (groupEntriesById.get(
424 group.deviceId()) != null) ?
425 groupEntriesById.get(group.deviceId()).get(group.id()) :
426 null;
427 GroupEvent event = null;
428
429 if (existing != null) {
430 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700431 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700432 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700433 existing.buckets().buckets()
434 .stream()
435 .filter((existingBucket)->(existingBucket.equals(bucket)))
436 .findFirst();
437 if (matchingBucket.isPresent()) {
438 ((StoredGroupBucketEntry) matchingBucket.
439 get()).setPackets(bucket.packets());
440 ((StoredGroupBucketEntry) matchingBucket.
441 get()).setBytes(bucket.bytes());
442 } else {
443 log.warn("addOrUpdateGroupEntry: No matching "
444 + "buckets to update stats");
445 }
446 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800447 existing.setLife(group.life());
448 existing.setPackets(group.packets());
449 existing.setBytes(group.bytes());
450 if (existing.state() == GroupState.PENDING_ADD) {
451 existing.setState(GroupState.ADDED);
452 event = new GroupEvent(Type.GROUP_ADDED, existing);
453 } else {
454 if (existing.state() == GroupState.PENDING_UPDATE) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700455 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800456 }
457 event = new GroupEvent(Type.GROUP_UPDATED, existing);
458 }
459 }
460 }
461
462 if (event != null) {
463 notifyDelegate(event);
464 }
465 }
466
467 /**
468 * Removes the group entry from store.
469 *
470 * @param group group entry
471 */
472 @Override
473 public void removeGroupEntry(Group group) {
474 StoredGroupEntry existing = (groupEntriesById.get(
475 group.deviceId()) != null) ?
476 groupEntriesById.get(group.deviceId()).get(group.id()) :
477 null;
478
479 if (existing != null) {
480 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
481 getGroupKeyTable(existing.deviceId());
482 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
483 getGroupIdTable(existing.deviceId());
484 idTable.remove(existing.id());
485 keyTable.remove(existing.appCookie());
486 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
487 }
488 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800489
490 @Override
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800491 public void deviceInitialAuditCompleted(DeviceId deviceId,
492 boolean completed) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800493 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800494 if (completed) {
495 log.debug("deviceInitialAuditCompleted: AUDIT "
496 + "completed for device {}", deviceId);
497 deviceAuditStatus.put(deviceId, true);
498 // Execute all pending group requests
499 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
500 getPendingGroupKeyTable(deviceId);
501 for (Group group:pendingGroupRequests.values()) {
502 GroupDescription tmp = new DefaultGroupDescription(
503 group.deviceId(),
504 group.type(),
505 group.buckets(),
506 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700507 group.givenGroupId(),
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800508 group.appId());
509 storeGroupDescriptionInternal(tmp);
510 }
511 getPendingGroupKeyTable(deviceId).clear();
512 } else {
513 if (deviceAuditStatus.get(deviceId)) {
514 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
515 + "status for device {}", deviceId);
516 deviceAuditStatus.put(deviceId, false);
517 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800518 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800519 }
520 }
521
522 @Override
523 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
524 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800525 return (deviceAuditStatus.get(deviceId) != null)
526 ? deviceAuditStatus.get(deviceId) : false;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800527 }
528 }
529
530 @Override
sangho7ff01812015-02-09 16:21:53 -0800531 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
532
533 StoredGroupEntry existing = (groupEntriesById.get(
534 deviceId) != null) ?
535 groupEntriesById.get(deviceId).get(operation.groupId()) :
536 null;
537
538 if (existing == null) {
539 log.warn("No group entry with ID {} found ", operation.groupId());
540 return;
541 }
542
543 switch (operation.opType()) {
544 case ADD:
545 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
546 break;
547 case MODIFY:
548 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
549 break;
550 case DELETE:
551 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
552 break;
553 default:
554 log.warn("Unknown group operation type {}", operation.opType());
555 }
556
557 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
558 getGroupKeyTable(existing.deviceId());
559 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
560 getGroupIdTable(existing.deviceId());
561 idTable.remove(existing.id());
562 keyTable.remove(existing.appCookie());
563 }
564
565 @Override
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800566 public void addOrUpdateExtraneousGroupEntry(Group group) {
567 ConcurrentMap<GroupId, Group> extraneousIdTable =
568 getExtraneousGroupIdTable(group.deviceId());
569 extraneousIdTable.put(group.id(), group);
570 // Check the reference counter
571 if (group.referenceCount() == 0) {
572 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
573 }
574 }
575
576 @Override
577 public void removeExtraneousGroupEntry(Group group) {
578 ConcurrentMap<GroupId, Group> extraneousIdTable =
579 getExtraneousGroupIdTable(group.deviceId());
580 extraneousIdTable.remove(group.id());
581 }
582
583 @Override
584 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
585 // flatten and make iterator unmodifiable
586 return FluentIterable.from(
587 getExtraneousGroupIdTable(deviceId).values());
588 }
sangho7ff01812015-02-09 16:21:53 -0800589
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700590 @Override
591 public void pushGroupMetrics(DeviceId deviceId,
592 Collection<Group> groupEntries) {
593 boolean deviceInitialAuditStatus =
594 deviceInitialAuditStatus(deviceId);
595 Set<Group> southboundGroupEntries =
596 Sets.newHashSet(groupEntries);
597 Set<Group> storedGroupEntries =
598 Sets.newHashSet(getGroups(deviceId));
599 Set<Group> extraneousStoredEntries =
600 Sets.newHashSet(getExtraneousGroups(deviceId));
601
602 log.trace("pushGroupMetrics: Displaying all ({}) "
603 + "southboundGroupEntries for device {}",
604 southboundGroupEntries.size(),
605 deviceId);
606 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
607 Group group = it.next();
608 log.trace("Group {} in device {}", group, deviceId);
609 }
610
611 log.trace("Displaying all ({}) stored group entries for device {}",
612 storedGroupEntries.size(),
613 deviceId);
614 for (Iterator<Group> it1 = storedGroupEntries.iterator();
615 it1.hasNext();) {
616 Group group = it1.next();
617 log.trace("Stored Group {} for device {}", group, deviceId);
618 }
619
620 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
621 Group group = it2.next();
622 if (storedGroupEntries.remove(group)) {
623 // we both have the group, let's update some info then.
624 log.trace("Group AUDIT: group {} exists "
625 + "in both planes for device {}",
626 group.id(), deviceId);
627 groupAdded(group);
628 it2.remove();
629 }
630 }
631 for (Group group : southboundGroupEntries) {
632 if (getGroup(group.deviceId(), group.id()) != null) {
633 // There is a group existing with the same id
634 // It is possible that group update is
635 // in progress while we got a stale info from switch
636 if (!storedGroupEntries.remove(getGroup(
637 group.deviceId(), group.id()))) {
638 log.warn("Group AUDIT: Inconsistent state:"
639 + "Group exists in ID based table while "
640 + "not present in key based table");
641 }
642 } else {
643 // there are groups in the switch that aren't in the store
644 log.trace("Group AUDIT: extraneous group {} exists "
645 + "in data plane for device {}",
646 group.id(), deviceId);
647 extraneousStoredEntries.remove(group);
648 extraneousGroup(group);
649 }
650 }
651 for (Group group : storedGroupEntries) {
652 // there are groups in the store that aren't in the switch
653 log.trace("Group AUDIT: group {} missing "
654 + "in data plane for device {}",
655 group.id(), deviceId);
656 groupMissing(group);
657 }
658 for (Group group : extraneousStoredEntries) {
659 // there are groups in the extraneous store that
660 // aren't in the switch
661 log.trace("Group AUDIT: clearing extransoeus group {} "
662 + "from store for device {}",
663 group.id(), deviceId);
664 removeExtraneousGroupEntry(group);
665 }
666
667 if (!deviceInitialAuditStatus) {
668 log.debug("Group AUDIT: Setting device {} initial "
669 + "AUDIT completed", deviceId);
670 deviceInitialAuditCompleted(deviceId, true);
671 }
672 }
673
674 private void groupMissing(Group group) {
675 switch (group.state()) {
676 case PENDING_DELETE:
677 log.debug("Group {} delete confirmation from device {}",
678 group, group.deviceId());
679 removeGroupEntry(group);
680 break;
681 case ADDED:
682 case PENDING_ADD:
683 case PENDING_UPDATE:
684 log.debug("Group {} is in store but not on device {}",
685 group, group.deviceId());
686 StoredGroupEntry existing = (groupEntriesById.get(
687 group.deviceId()) != null) ?
688 groupEntriesById.get(group.deviceId()).get(group.id()) :
689 null;
690 log.trace("groupMissing: group "
691 + "entry {} in device {} moving "
692 + "from {} to PENDING_ADD",
693 existing.id(),
694 existing.deviceId(),
695 existing.state());
696 existing.setState(Group.GroupState.PENDING_ADD);
697 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
698 group));
699 break;
700 default:
701 log.debug("Group {} has not been installed.", group);
702 break;
703 }
704 }
705
706 private void extraneousGroup(Group group) {
707 log.debug("Group {} is on device {} but not in store.",
708 group, group.deviceId());
709 addOrUpdateExtraneousGroupEntry(group);
710 }
711
712 private void groupAdded(Group group) {
713 log.trace("Group {} Added or Updated in device {}",
714 group, group.deviceId());
715 addOrUpdateGroupEntry(group);
716 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800717}