blob: 230fa3392686e034783cd69f7334f5fdce857229 [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
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080058import com.google.common.collect.FluentIterable;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070059import com.google.common.collect.Sets;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080060
61/**
62 * Manages inventory of group entries using trivial in-memory implementation.
63 */
64@Component(immediate = true)
65@Service
66public class SimpleGroupStore
67 extends AbstractStore<GroupEvent, GroupStoreDelegate>
68 implements GroupStore {
69
70 private final Logger log = getLogger(getClass());
71
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080072 private final int dummyId = 0xffffffff;
73 private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
74
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080075 // inner Map is per device group table
76 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
77 groupEntriesByKey = new ConcurrentHashMap<>();
78 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
79 groupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080080 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
81 pendingGroupEntriesByKey = new ConcurrentHashMap<>();
82 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
83 extraneousGroupEntriesById = new ConcurrentHashMap<>();
84
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -070085 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080086
87 private final AtomicInteger groupIdGen = new AtomicInteger();
88
89 @Activate
90 public void activate() {
91 log.info("Started");
92 }
93
94 @Deactivate
95 public void deactivate() {
96 groupEntriesByKey.clear();
97 groupEntriesById.clear();
98 log.info("Stopped");
99 }
100
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800101 private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
102 lazyEmptyGroupKeyTable() {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800103 return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
104 }
105
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800106 private static NewConcurrentHashMap<GroupId, StoredGroupEntry>
107 lazyEmptyGroupIdTable() {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800108 return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded();
109 }
110
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800111 private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
112 lazyEmptyPendingGroupKeyTable() {
113 return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
114 }
115
116 private static NewConcurrentHashMap<GroupId, Group>
117 lazyEmptyExtraneousGroupIdTable() {
118 return NewConcurrentHashMap.<GroupId, Group>ifNeeded();
119 }
120
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800121 /**
122 * Returns the group key table for specified device.
123 *
124 * @param deviceId identifier of the device
125 * @return Map representing group key table of given device.
126 */
127 private ConcurrentMap<GroupKey, StoredGroupEntry> getGroupKeyTable(DeviceId deviceId) {
128 return createIfAbsentUnchecked(groupEntriesByKey,
129 deviceId, lazyEmptyGroupKeyTable());
130 }
131
132 /**
133 * Returns the group id table for specified device.
134 *
135 * @param deviceId identifier of the device
136 * @return Map representing group key table of given device.
137 */
138 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
139 return createIfAbsentUnchecked(groupEntriesById,
140 deviceId, lazyEmptyGroupIdTable());
141 }
142
143 /**
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800144 * Returns the pending group key table for specified device.
145 *
146 * @param deviceId identifier of the device
147 * @return Map representing group key table of given device.
148 */
149 private ConcurrentMap<GroupKey, StoredGroupEntry>
150 getPendingGroupKeyTable(DeviceId deviceId) {
151 return createIfAbsentUnchecked(pendingGroupEntriesByKey,
152 deviceId, lazyEmptyPendingGroupKeyTable());
153 }
154
155 /**
156 * Returns the extraneous group id table for specified device.
157 *
158 * @param deviceId identifier of the device
159 * @return Map representing group key table of given device.
160 */
161 private ConcurrentMap<GroupId, Group>
162 getExtraneousGroupIdTable(DeviceId deviceId) {
163 return createIfAbsentUnchecked(extraneousGroupEntriesById,
164 deviceId,
165 lazyEmptyExtraneousGroupIdTable());
166 }
167
168 /**
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800169 * Returns the number of groups for the specified device in the store.
170 *
171 * @return number of groups for the specified device
172 */
173 @Override
174 public int getGroupCount(DeviceId deviceId) {
175 return (groupEntriesByKey.get(deviceId) != null) ?
176 groupEntriesByKey.get(deviceId).size() : 0;
177 }
178
179 /**
180 * Returns the groups associated with a device.
181 *
182 * @param deviceId the device ID
183 *
184 * @return the group entries
185 */
186 @Override
187 public Iterable<Group> getGroups(DeviceId deviceId) {
188 // flatten and make iterator unmodifiable
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800189 return FluentIterable.from(getGroupKeyTable(deviceId).values())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700190 .transform(input -> input);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800191 }
192
193 /**
194 * Returns the stored group entry.
195 *
196 * @param deviceId the device ID
197 * @param appCookie the group key
198 *
199 * @return a group associated with the key
200 */
201 @Override
202 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
203 return (groupEntriesByKey.get(deviceId) != null) ?
204 groupEntriesByKey.get(deviceId).get(appCookie) :
205 null;
206 }
207
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700208 @Override
209 public Group getGroup(DeviceId deviceId, GroupId groupId) {
210 return (groupEntriesById.get(deviceId) != null) ?
211 groupEntriesById.get(deviceId).get(groupId) :
212 null;
213 }
214
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800215 private int getFreeGroupIdValue(DeviceId deviceId) {
216 int freeId = groupIdGen.incrementAndGet();
217
218 while (true) {
219 Group existing = (
220 groupEntriesById.get(deviceId) != null) ?
221 groupEntriesById.get(deviceId).get(new DefaultGroupId(freeId)) :
222 null;
223 if (existing == null) {
224 existing = (
225 extraneousGroupEntriesById.get(deviceId) != null) ?
226 extraneousGroupEntriesById.get(deviceId).
227 get(new DefaultGroupId(freeId)) :
228 null;
229 }
230 if (existing != null) {
231 freeId = groupIdGen.incrementAndGet();
232 } else {
233 break;
234 }
235 }
236 return freeId;
237 }
238
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800239 /**
240 * Stores a new group entry using the information from group description.
241 *
242 * @param groupDesc group description to be used to create group entry
243 */
244 @Override
245 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800246 // Check if a group is existing with the same key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800247 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
248 return;
249 }
250
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800251 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
252 // Device group audit has not completed yet
253 // Add this group description to pending group key table
254 // Create a group entry object with Dummy Group ID
255 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
256 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
257 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
258 getPendingGroupKeyTable(groupDesc.deviceId());
259 pendingKeyTable.put(groupDesc.appCookie(), group);
260 return;
261 }
262
263 storeGroupDescriptionInternal(groupDesc);
264 }
265
266 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800267 // Check if a group is existing with the same key
268 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
269 return;
270 }
271
Saurav Das100e3b82015-04-30 11:12:10 -0700272 GroupId id = null;
273 if (groupDesc.givenGroupId() == null) {
274 // Get a new group identifier
275 id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
276 } else {
277 id = new DefaultGroupId(groupDesc.givenGroupId());
278 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800279 // Create a group entry object
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800280 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800281 // Insert the newly created group entry into concurrent key and id maps
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800282 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
283 getGroupKeyTable(groupDesc.deviceId());
284 keyTable.put(groupDesc.appCookie(), group);
285 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
286 getGroupIdTable(groupDesc.deviceId());
287 idTable.put(id, group);
288 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
289 group));
290 }
291
292 /**
293 * Updates the existing group entry with the information
294 * from group description.
295 *
296 * @param deviceId the device ID
297 * @param oldAppCookie the current group key
298 * @param type update type
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800299 * @param newBuckets group buckets for updates
300 * @param newAppCookie optional new group key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800301 */
302 @Override
303 public void updateGroupDescription(DeviceId deviceId,
304 GroupKey oldAppCookie,
305 UpdateType type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800306 GroupBuckets newBuckets,
307 GroupKey newAppCookie) {
308 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800309 Group oldGroup = getGroup(deviceId, oldAppCookie);
310 if (oldGroup == null) {
311 return;
312 }
313
314 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
315 type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800316 newBuckets);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800317 if (newBucketList != null) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800318 // Create a new group object from the old group
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800319 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800320 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800321 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
322 oldGroup.deviceId(),
323 oldGroup.type(),
324 updatedBuckets,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800325 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700326 oldGroup.givenGroupId(),
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800327 oldGroup.appId());
328 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
329 updatedGroupDesc);
330 newGroup.setState(GroupState.PENDING_UPDATE);
331 newGroup.setLife(oldGroup.life());
332 newGroup.setPackets(oldGroup.packets());
333 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800334 // Remove the old entry from maps and add new entry using new key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800335 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
336 getGroupKeyTable(oldGroup.deviceId());
337 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
338 getGroupIdTable(oldGroup.deviceId());
339 keyTable.remove(oldGroup.appCookie());
340 idTable.remove(oldGroup.id());
341 keyTable.put(newGroup.appCookie(), newGroup);
342 idTable.put(newGroup.id(), newGroup);
343 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
344 }
345 }
346
347 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
348 UpdateType type,
349 GroupBuckets buckets) {
350 GroupBuckets oldBuckets = oldGroup.buckets();
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700351 List<GroupBucket> newBucketList = new ArrayList<>(oldBuckets.buckets());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800352 boolean groupDescUpdated = false;
353
354 if (type == UpdateType.ADD) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800355 // Check if the any of the new buckets are part of
356 // the old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800357 for (GroupBucket addBucket:buckets.buckets()) {
358 if (!newBucketList.contains(addBucket)) {
359 newBucketList.add(addBucket);
360 groupDescUpdated = true;
361 }
362 }
363 } else if (type == UpdateType.REMOVE) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800364 // Check if the to be removed buckets are part of the
365 // old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800366 for (GroupBucket removeBucket:buckets.buckets()) {
367 if (newBucketList.contains(removeBucket)) {
368 newBucketList.remove(removeBucket);
369 groupDescUpdated = true;
370 }
371 }
372 }
373
374 if (groupDescUpdated) {
375 return newBucketList;
376 } else {
377 return null;
378 }
379 }
380
381 /**
382 * Triggers deleting the existing group entry.
383 *
384 * @param deviceId the device ID
385 * @param appCookie the group key
386 */
387 @Override
388 public void deleteGroupDescription(DeviceId deviceId,
389 GroupKey appCookie) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800390 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800391 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
392 groupEntriesByKey.get(deviceId).get(appCookie) :
393 null;
394 if (existing == null) {
395 return;
396 }
397
398 synchronized (existing) {
399 existing.setState(GroupState.PENDING_DELETE);
400 }
401 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
402 }
403
404 /**
405 * Stores a new group entry, or updates an existing entry.
406 *
407 * @param group group entry
408 */
409 @Override
410 public void addOrUpdateGroupEntry(Group group) {
411 // check if this new entry is an update to an existing entry
412 StoredGroupEntry existing = (groupEntriesById.get(
413 group.deviceId()) != null) ?
414 groupEntriesById.get(group.deviceId()).get(group.id()) :
415 null;
416 GroupEvent event = null;
417
418 if (existing != null) {
419 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700420 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700421 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700422 existing.buckets().buckets()
423 .stream()
424 .filter((existingBucket)->(existingBucket.equals(bucket)))
425 .findFirst();
426 if (matchingBucket.isPresent()) {
427 ((StoredGroupBucketEntry) matchingBucket.
428 get()).setPackets(bucket.packets());
429 ((StoredGroupBucketEntry) matchingBucket.
430 get()).setBytes(bucket.bytes());
431 } else {
432 log.warn("addOrUpdateGroupEntry: No matching "
433 + "buckets to update stats");
434 }
435 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800436 existing.setLife(group.life());
437 existing.setPackets(group.packets());
438 existing.setBytes(group.bytes());
439 if (existing.state() == GroupState.PENDING_ADD) {
440 existing.setState(GroupState.ADDED);
441 event = new GroupEvent(Type.GROUP_ADDED, existing);
442 } else {
443 if (existing.state() == GroupState.PENDING_UPDATE) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700444 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800445 }
446 event = new GroupEvent(Type.GROUP_UPDATED, existing);
447 }
448 }
449 }
450
451 if (event != null) {
452 notifyDelegate(event);
453 }
454 }
455
456 /**
457 * Removes the group entry from store.
458 *
459 * @param group group entry
460 */
461 @Override
462 public void removeGroupEntry(Group group) {
463 StoredGroupEntry existing = (groupEntriesById.get(
464 group.deviceId()) != null) ?
465 groupEntriesById.get(group.deviceId()).get(group.id()) :
466 null;
467
468 if (existing != null) {
469 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
470 getGroupKeyTable(existing.deviceId());
471 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
472 getGroupIdTable(existing.deviceId());
473 idTable.remove(existing.id());
474 keyTable.remove(existing.appCookie());
475 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
476 }
477 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800478
479 @Override
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800480 public void deviceInitialAuditCompleted(DeviceId deviceId,
481 boolean completed) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800482 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800483 if (completed) {
484 log.debug("deviceInitialAuditCompleted: AUDIT "
485 + "completed for device {}", deviceId);
486 deviceAuditStatus.put(deviceId, true);
487 // Execute all pending group requests
488 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
489 getPendingGroupKeyTable(deviceId);
490 for (Group group:pendingGroupRequests.values()) {
491 GroupDescription tmp = new DefaultGroupDescription(
492 group.deviceId(),
493 group.type(),
494 group.buckets(),
495 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700496 group.givenGroupId(),
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800497 group.appId());
498 storeGroupDescriptionInternal(tmp);
499 }
500 getPendingGroupKeyTable(deviceId).clear();
501 } else {
502 if (deviceAuditStatus.get(deviceId)) {
503 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
504 + "status for device {}", deviceId);
505 deviceAuditStatus.put(deviceId, false);
506 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800507 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800508 }
509 }
510
511 @Override
512 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
513 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800514 return (deviceAuditStatus.get(deviceId) != null)
515 ? deviceAuditStatus.get(deviceId) : false;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800516 }
517 }
518
519 @Override
sangho7ff01812015-02-09 16:21:53 -0800520 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
521
522 StoredGroupEntry existing = (groupEntriesById.get(
523 deviceId) != null) ?
524 groupEntriesById.get(deviceId).get(operation.groupId()) :
525 null;
526
527 if (existing == null) {
528 log.warn("No group entry with ID {} found ", operation.groupId());
529 return;
530 }
531
532 switch (operation.opType()) {
533 case ADD:
534 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
535 break;
536 case MODIFY:
537 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
538 break;
539 case DELETE:
540 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
541 break;
542 default:
543 log.warn("Unknown group operation type {}", operation.opType());
544 }
545
546 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
547 getGroupKeyTable(existing.deviceId());
548 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
549 getGroupIdTable(existing.deviceId());
550 idTable.remove(existing.id());
551 keyTable.remove(existing.appCookie());
552 }
553
554 @Override
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800555 public void addOrUpdateExtraneousGroupEntry(Group group) {
556 ConcurrentMap<GroupId, Group> extraneousIdTable =
557 getExtraneousGroupIdTable(group.deviceId());
558 extraneousIdTable.put(group.id(), group);
559 // Check the reference counter
560 if (group.referenceCount() == 0) {
561 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
562 }
563 }
564
565 @Override
566 public void removeExtraneousGroupEntry(Group group) {
567 ConcurrentMap<GroupId, Group> extraneousIdTable =
568 getExtraneousGroupIdTable(group.deviceId());
569 extraneousIdTable.remove(group.id());
570 }
571
572 @Override
573 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
574 // flatten and make iterator unmodifiable
575 return FluentIterable.from(
576 getExtraneousGroupIdTable(deviceId).values());
577 }
sangho7ff01812015-02-09 16:21:53 -0800578
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700579 @Override
580 public void pushGroupMetrics(DeviceId deviceId,
581 Collection<Group> groupEntries) {
582 boolean deviceInitialAuditStatus =
583 deviceInitialAuditStatus(deviceId);
584 Set<Group> southboundGroupEntries =
585 Sets.newHashSet(groupEntries);
586 Set<Group> storedGroupEntries =
587 Sets.newHashSet(getGroups(deviceId));
588 Set<Group> extraneousStoredEntries =
589 Sets.newHashSet(getExtraneousGroups(deviceId));
590
591 log.trace("pushGroupMetrics: Displaying all ({}) "
592 + "southboundGroupEntries for device {}",
593 southboundGroupEntries.size(),
594 deviceId);
595 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
596 Group group = it.next();
597 log.trace("Group {} in device {}", group, deviceId);
598 }
599
600 log.trace("Displaying all ({}) stored group entries for device {}",
601 storedGroupEntries.size(),
602 deviceId);
603 for (Iterator<Group> it1 = storedGroupEntries.iterator();
604 it1.hasNext();) {
605 Group group = it1.next();
606 log.trace("Stored Group {} for device {}", group, deviceId);
607 }
608
609 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
610 Group group = it2.next();
611 if (storedGroupEntries.remove(group)) {
612 // we both have the group, let's update some info then.
613 log.trace("Group AUDIT: group {} exists "
614 + "in both planes for device {}",
615 group.id(), deviceId);
616 groupAdded(group);
617 it2.remove();
618 }
619 }
620 for (Group group : southboundGroupEntries) {
621 if (getGroup(group.deviceId(), group.id()) != null) {
622 // There is a group existing with the same id
623 // It is possible that group update is
624 // in progress while we got a stale info from switch
625 if (!storedGroupEntries.remove(getGroup(
626 group.deviceId(), group.id()))) {
627 log.warn("Group AUDIT: Inconsistent state:"
628 + "Group exists in ID based table while "
629 + "not present in key based table");
630 }
631 } else {
632 // there are groups in the switch that aren't in the store
633 log.trace("Group AUDIT: extraneous group {} exists "
634 + "in data plane for device {}",
635 group.id(), deviceId);
636 extraneousStoredEntries.remove(group);
637 extraneousGroup(group);
638 }
639 }
640 for (Group group : storedGroupEntries) {
641 // there are groups in the store that aren't in the switch
642 log.trace("Group AUDIT: group {} missing "
643 + "in data plane for device {}",
644 group.id(), deviceId);
645 groupMissing(group);
646 }
647 for (Group group : extraneousStoredEntries) {
648 // there are groups in the extraneous store that
649 // aren't in the switch
650 log.trace("Group AUDIT: clearing extransoeus group {} "
651 + "from store for device {}",
652 group.id(), deviceId);
653 removeExtraneousGroupEntry(group);
654 }
655
656 if (!deviceInitialAuditStatus) {
657 log.debug("Group AUDIT: Setting device {} initial "
658 + "AUDIT completed", deviceId);
659 deviceInitialAuditCompleted(deviceId, true);
660 }
661 }
662
663 private void groupMissing(Group group) {
664 switch (group.state()) {
665 case PENDING_DELETE:
666 log.debug("Group {} delete confirmation from device {}",
667 group, group.deviceId());
668 removeGroupEntry(group);
669 break;
670 case ADDED:
671 case PENDING_ADD:
672 case PENDING_UPDATE:
673 log.debug("Group {} is in store but not on device {}",
674 group, group.deviceId());
675 StoredGroupEntry existing = (groupEntriesById.get(
676 group.deviceId()) != null) ?
677 groupEntriesById.get(group.deviceId()).get(group.id()) :
678 null;
679 log.trace("groupMissing: group "
680 + "entry {} in device {} moving "
681 + "from {} to PENDING_ADD",
682 existing.id(),
683 existing.deviceId(),
684 existing.state());
685 existing.setState(Group.GroupState.PENDING_ADD);
686 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
687 group));
688 break;
689 default:
690 log.debug("Group {} has not been installed.", group);
691 break;
692 }
693 }
694
695 private void extraneousGroup(Group group) {
696 log.debug("Group {} is on device {} but not in store.",
697 group, group.deviceId());
698 addOrUpdateExtraneousGroupEntry(group);
699 }
700
701 private void groupAdded(Group group) {
702 log.trace("Group {} Added or Updated in device {}",
703 group, group.deviceId());
704 addOrUpdateGroupEntry(group);
705 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800706}