blob: bf7c2fc5704fb24bcff7e8c44c257e1c8f799296 [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
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -070086 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080087
88 private final AtomicInteger groupIdGen = new AtomicInteger();
89
90 @Activate
91 public void activate() {
92 log.info("Started");
93 }
94
95 @Deactivate
96 public void deactivate() {
97 groupEntriesByKey.clear();
98 groupEntriesById.clear();
99 log.info("Stopped");
100 }
101
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800102 private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
103 lazyEmptyGroupKeyTable() {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800104 return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
105 }
106
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800107 private static NewConcurrentHashMap<GroupId, StoredGroupEntry>
108 lazyEmptyGroupIdTable() {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800109 return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded();
110 }
111
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800112 private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
113 lazyEmptyPendingGroupKeyTable() {
114 return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
115 }
116
117 private static NewConcurrentHashMap<GroupId, Group>
118 lazyEmptyExtraneousGroupIdTable() {
119 return NewConcurrentHashMap.<GroupId, Group>ifNeeded();
120 }
121
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800122 /**
123 * Returns the group key table for specified device.
124 *
125 * @param deviceId identifier of the device
126 * @return Map representing group key table of given device.
127 */
128 private ConcurrentMap<GroupKey, StoredGroupEntry> getGroupKeyTable(DeviceId deviceId) {
129 return createIfAbsentUnchecked(groupEntriesByKey,
130 deviceId, lazyEmptyGroupKeyTable());
131 }
132
133 /**
134 * Returns the group id table for specified device.
135 *
136 * @param deviceId identifier of the device
137 * @return Map representing group key table of given device.
138 */
139 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
140 return createIfAbsentUnchecked(groupEntriesById,
141 deviceId, lazyEmptyGroupIdTable());
142 }
143
144 /**
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800145 * Returns the pending group key table for specified device.
146 *
147 * @param deviceId identifier of the device
148 * @return Map representing group key table of given device.
149 */
150 private ConcurrentMap<GroupKey, StoredGroupEntry>
151 getPendingGroupKeyTable(DeviceId deviceId) {
152 return createIfAbsentUnchecked(pendingGroupEntriesByKey,
153 deviceId, lazyEmptyPendingGroupKeyTable());
154 }
155
156 /**
157 * Returns the extraneous group id table for specified device.
158 *
159 * @param deviceId identifier of the device
160 * @return Map representing group key table of given device.
161 */
162 private ConcurrentMap<GroupId, Group>
163 getExtraneousGroupIdTable(DeviceId deviceId) {
164 return createIfAbsentUnchecked(extraneousGroupEntriesById,
165 deviceId,
166 lazyEmptyExtraneousGroupIdTable());
167 }
168
169 /**
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800170 * Returns the number of groups for the specified device in the store.
171 *
172 * @return number of groups for the specified device
173 */
174 @Override
175 public int getGroupCount(DeviceId deviceId) {
176 return (groupEntriesByKey.get(deviceId) != null) ?
177 groupEntriesByKey.get(deviceId).size() : 0;
178 }
179
180 /**
181 * Returns the groups associated with a device.
182 *
183 * @param deviceId the device ID
184 *
185 * @return the group entries
186 */
187 @Override
188 public Iterable<Group> getGroups(DeviceId deviceId) {
189 // flatten and make iterator unmodifiable
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800190 return FluentIterable.from(getGroupKeyTable(deviceId).values())
191 .transform(
192 new Function<StoredGroupEntry, Group>() {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800193
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800194 @Override
195 public Group apply(
196 StoredGroupEntry input) {
197 return input;
198 }
199 });
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800200 }
201
202 /**
203 * Returns the stored group entry.
204 *
205 * @param deviceId the device ID
206 * @param appCookie the group key
207 *
208 * @return a group associated with the key
209 */
210 @Override
211 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
212 return (groupEntriesByKey.get(deviceId) != null) ?
213 groupEntriesByKey.get(deviceId).get(appCookie) :
214 null;
215 }
216
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700217 @Override
218 public Group getGroup(DeviceId deviceId, GroupId groupId) {
219 return (groupEntriesById.get(deviceId) != null) ?
220 groupEntriesById.get(deviceId).get(groupId) :
221 null;
222 }
223
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800224 private int getFreeGroupIdValue(DeviceId deviceId) {
225 int freeId = groupIdGen.incrementAndGet();
226
227 while (true) {
228 Group existing = (
229 groupEntriesById.get(deviceId) != null) ?
230 groupEntriesById.get(deviceId).get(new DefaultGroupId(freeId)) :
231 null;
232 if (existing == null) {
233 existing = (
234 extraneousGroupEntriesById.get(deviceId) != null) ?
235 extraneousGroupEntriesById.get(deviceId).
236 get(new DefaultGroupId(freeId)) :
237 null;
238 }
239 if (existing != null) {
240 freeId = groupIdGen.incrementAndGet();
241 } else {
242 break;
243 }
244 }
245 return freeId;
246 }
247
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800248 /**
249 * Stores a new group entry using the information from group description.
250 *
251 * @param groupDesc group description to be used to create group entry
252 */
253 @Override
254 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800255 // Check if a group is existing with the same key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800256 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
257 return;
258 }
259
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800260 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
261 // Device group audit has not completed yet
262 // Add this group description to pending group key table
263 // Create a group entry object with Dummy Group ID
264 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
265 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
266 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
267 getPendingGroupKeyTable(groupDesc.deviceId());
268 pendingKeyTable.put(groupDesc.appCookie(), group);
269 return;
270 }
271
272 storeGroupDescriptionInternal(groupDesc);
273 }
274
275 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800276 // Check if a group is existing with the same key
277 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
278 return;
279 }
280
Saurav Das100e3b82015-04-30 11:12:10 -0700281 GroupId id = null;
282 if (groupDesc.givenGroupId() == null) {
283 // Get a new group identifier
284 id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
285 } else {
286 id = new DefaultGroupId(groupDesc.givenGroupId());
287 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800288 // Create a group entry object
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800289 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800290 // Insert the newly created group entry into concurrent key and id maps
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800291 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
292 getGroupKeyTable(groupDesc.deviceId());
293 keyTable.put(groupDesc.appCookie(), group);
294 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
295 getGroupIdTable(groupDesc.deviceId());
296 idTable.put(id, group);
297 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
298 group));
299 }
300
301 /**
302 * Updates the existing group entry with the information
303 * from group description.
304 *
305 * @param deviceId the device ID
306 * @param oldAppCookie the current group key
307 * @param type update type
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800308 * @param newBuckets group buckets for updates
309 * @param newAppCookie optional new group key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800310 */
311 @Override
312 public void updateGroupDescription(DeviceId deviceId,
313 GroupKey oldAppCookie,
314 UpdateType type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800315 GroupBuckets newBuckets,
316 GroupKey newAppCookie) {
317 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800318 Group oldGroup = getGroup(deviceId, oldAppCookie);
319 if (oldGroup == null) {
320 return;
321 }
322
323 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
324 type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800325 newBuckets);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800326 if (newBucketList != null) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800327 // Create a new group object from the old group
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800328 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800329 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800330 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
331 oldGroup.deviceId(),
332 oldGroup.type(),
333 updatedBuckets,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800334 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700335 oldGroup.givenGroupId(),
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800336 oldGroup.appId());
337 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
338 updatedGroupDesc);
339 newGroup.setState(GroupState.PENDING_UPDATE);
340 newGroup.setLife(oldGroup.life());
341 newGroup.setPackets(oldGroup.packets());
342 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800343 // Remove the old entry from maps and add new entry using new key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800344 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
345 getGroupKeyTable(oldGroup.deviceId());
346 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
347 getGroupIdTable(oldGroup.deviceId());
348 keyTable.remove(oldGroup.appCookie());
349 idTable.remove(oldGroup.id());
350 keyTable.put(newGroup.appCookie(), newGroup);
351 idTable.put(newGroup.id(), newGroup);
352 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
353 }
354 }
355
356 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
357 UpdateType type,
358 GroupBuckets buckets) {
359 GroupBuckets oldBuckets = oldGroup.buckets();
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700360 List<GroupBucket> newBucketList = new ArrayList<>(oldBuckets.buckets());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800361 boolean groupDescUpdated = false;
362
363 if (type == UpdateType.ADD) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800364 // Check if the any of the new buckets are part of
365 // the old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800366 for (GroupBucket addBucket:buckets.buckets()) {
367 if (!newBucketList.contains(addBucket)) {
368 newBucketList.add(addBucket);
369 groupDescUpdated = true;
370 }
371 }
372 } else if (type == UpdateType.REMOVE) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800373 // Check if the to be removed buckets are part of the
374 // old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800375 for (GroupBucket removeBucket:buckets.buckets()) {
376 if (newBucketList.contains(removeBucket)) {
377 newBucketList.remove(removeBucket);
378 groupDescUpdated = true;
379 }
380 }
381 }
382
383 if (groupDescUpdated) {
384 return newBucketList;
385 } else {
386 return null;
387 }
388 }
389
390 /**
391 * Triggers deleting the existing group entry.
392 *
393 * @param deviceId the device ID
394 * @param appCookie the group key
395 */
396 @Override
397 public void deleteGroupDescription(DeviceId deviceId,
398 GroupKey appCookie) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800399 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800400 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
401 groupEntriesByKey.get(deviceId).get(appCookie) :
402 null;
403 if (existing == null) {
404 return;
405 }
406
407 synchronized (existing) {
408 existing.setState(GroupState.PENDING_DELETE);
409 }
410 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
411 }
412
413 /**
414 * Stores a new group entry, or updates an existing entry.
415 *
416 * @param group group entry
417 */
418 @Override
419 public void addOrUpdateGroupEntry(Group group) {
420 // check if this new entry is an update to an existing entry
421 StoredGroupEntry existing = (groupEntriesById.get(
422 group.deviceId()) != null) ?
423 groupEntriesById.get(group.deviceId()).get(group.id()) :
424 null;
425 GroupEvent event = null;
426
427 if (existing != null) {
428 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700429 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700430 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700431 existing.buckets().buckets()
432 .stream()
433 .filter((existingBucket)->(existingBucket.equals(bucket)))
434 .findFirst();
435 if (matchingBucket.isPresent()) {
436 ((StoredGroupBucketEntry) matchingBucket.
437 get()).setPackets(bucket.packets());
438 ((StoredGroupBucketEntry) matchingBucket.
439 get()).setBytes(bucket.bytes());
440 } else {
441 log.warn("addOrUpdateGroupEntry: No matching "
442 + "buckets to update stats");
443 }
444 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800445 existing.setLife(group.life());
446 existing.setPackets(group.packets());
447 existing.setBytes(group.bytes());
448 if (existing.state() == GroupState.PENDING_ADD) {
449 existing.setState(GroupState.ADDED);
450 event = new GroupEvent(Type.GROUP_ADDED, existing);
451 } else {
452 if (existing.state() == GroupState.PENDING_UPDATE) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700453 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800454 }
455 event = new GroupEvent(Type.GROUP_UPDATED, existing);
456 }
457 }
458 }
459
460 if (event != null) {
461 notifyDelegate(event);
462 }
463 }
464
465 /**
466 * Removes the group entry from store.
467 *
468 * @param group group entry
469 */
470 @Override
471 public void removeGroupEntry(Group group) {
472 StoredGroupEntry existing = (groupEntriesById.get(
473 group.deviceId()) != null) ?
474 groupEntriesById.get(group.deviceId()).get(group.id()) :
475 null;
476
477 if (existing != null) {
478 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
479 getGroupKeyTable(existing.deviceId());
480 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
481 getGroupIdTable(existing.deviceId());
482 idTable.remove(existing.id());
483 keyTable.remove(existing.appCookie());
484 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
485 }
486 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800487
488 @Override
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800489 public void deviceInitialAuditCompleted(DeviceId deviceId,
490 boolean completed) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800491 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800492 if (completed) {
493 log.debug("deviceInitialAuditCompleted: AUDIT "
494 + "completed for device {}", deviceId);
495 deviceAuditStatus.put(deviceId, true);
496 // Execute all pending group requests
497 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
498 getPendingGroupKeyTable(deviceId);
499 for (Group group:pendingGroupRequests.values()) {
500 GroupDescription tmp = new DefaultGroupDescription(
501 group.deviceId(),
502 group.type(),
503 group.buckets(),
504 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700505 group.givenGroupId(),
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800506 group.appId());
507 storeGroupDescriptionInternal(tmp);
508 }
509 getPendingGroupKeyTable(deviceId).clear();
510 } else {
511 if (deviceAuditStatus.get(deviceId)) {
512 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
513 + "status for device {}", deviceId);
514 deviceAuditStatus.put(deviceId, false);
515 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800516 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800517 }
518 }
519
520 @Override
521 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
522 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800523 return (deviceAuditStatus.get(deviceId) != null)
524 ? deviceAuditStatus.get(deviceId) : false;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800525 }
526 }
527
528 @Override
sangho7ff01812015-02-09 16:21:53 -0800529 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
530
531 StoredGroupEntry existing = (groupEntriesById.get(
532 deviceId) != null) ?
533 groupEntriesById.get(deviceId).get(operation.groupId()) :
534 null;
535
536 if (existing == null) {
537 log.warn("No group entry with ID {} found ", operation.groupId());
538 return;
539 }
540
541 switch (operation.opType()) {
542 case ADD:
543 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
544 break;
545 case MODIFY:
546 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
547 break;
548 case DELETE:
549 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
550 break;
551 default:
552 log.warn("Unknown group operation type {}", operation.opType());
553 }
554
555 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
556 getGroupKeyTable(existing.deviceId());
557 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
558 getGroupIdTable(existing.deviceId());
559 idTable.remove(existing.id());
560 keyTable.remove(existing.appCookie());
561 }
562
563 @Override
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800564 public void addOrUpdateExtraneousGroupEntry(Group group) {
565 ConcurrentMap<GroupId, Group> extraneousIdTable =
566 getExtraneousGroupIdTable(group.deviceId());
567 extraneousIdTable.put(group.id(), group);
568 // Check the reference counter
569 if (group.referenceCount() == 0) {
570 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
571 }
572 }
573
574 @Override
575 public void removeExtraneousGroupEntry(Group group) {
576 ConcurrentMap<GroupId, Group> extraneousIdTable =
577 getExtraneousGroupIdTable(group.deviceId());
578 extraneousIdTable.remove(group.id());
579 }
580
581 @Override
582 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
583 // flatten and make iterator unmodifiable
584 return FluentIterable.from(
585 getExtraneousGroupIdTable(deviceId).values());
586 }
sangho7ff01812015-02-09 16:21:53 -0800587
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700588 @Override
589 public void pushGroupMetrics(DeviceId deviceId,
590 Collection<Group> groupEntries) {
591 boolean deviceInitialAuditStatus =
592 deviceInitialAuditStatus(deviceId);
593 Set<Group> southboundGroupEntries =
594 Sets.newHashSet(groupEntries);
595 Set<Group> storedGroupEntries =
596 Sets.newHashSet(getGroups(deviceId));
597 Set<Group> extraneousStoredEntries =
598 Sets.newHashSet(getExtraneousGroups(deviceId));
599
600 log.trace("pushGroupMetrics: Displaying all ({}) "
601 + "southboundGroupEntries for device {}",
602 southboundGroupEntries.size(),
603 deviceId);
604 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
605 Group group = it.next();
606 log.trace("Group {} in device {}", group, deviceId);
607 }
608
609 log.trace("Displaying all ({}) stored group entries for device {}",
610 storedGroupEntries.size(),
611 deviceId);
612 for (Iterator<Group> it1 = storedGroupEntries.iterator();
613 it1.hasNext();) {
614 Group group = it1.next();
615 log.trace("Stored Group {} for device {}", group, deviceId);
616 }
617
618 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
619 Group group = it2.next();
620 if (storedGroupEntries.remove(group)) {
621 // we both have the group, let's update some info then.
622 log.trace("Group AUDIT: group {} exists "
623 + "in both planes for device {}",
624 group.id(), deviceId);
625 groupAdded(group);
626 it2.remove();
627 }
628 }
629 for (Group group : southboundGroupEntries) {
630 if (getGroup(group.deviceId(), group.id()) != null) {
631 // There is a group existing with the same id
632 // It is possible that group update is
633 // in progress while we got a stale info from switch
634 if (!storedGroupEntries.remove(getGroup(
635 group.deviceId(), group.id()))) {
636 log.warn("Group AUDIT: Inconsistent state:"
637 + "Group exists in ID based table while "
638 + "not present in key based table");
639 }
640 } else {
641 // there are groups in the switch that aren't in the store
642 log.trace("Group AUDIT: extraneous group {} exists "
643 + "in data plane for device {}",
644 group.id(), deviceId);
645 extraneousStoredEntries.remove(group);
646 extraneousGroup(group);
647 }
648 }
649 for (Group group : storedGroupEntries) {
650 // there are groups in the store that aren't in the switch
651 log.trace("Group AUDIT: group {} missing "
652 + "in data plane for device {}",
653 group.id(), deviceId);
654 groupMissing(group);
655 }
656 for (Group group : extraneousStoredEntries) {
657 // there are groups in the extraneous store that
658 // aren't in the switch
659 log.trace("Group AUDIT: clearing extransoeus group {} "
660 + "from store for device {}",
661 group.id(), deviceId);
662 removeExtraneousGroupEntry(group);
663 }
664
665 if (!deviceInitialAuditStatus) {
666 log.debug("Group AUDIT: Setting device {} initial "
667 + "AUDIT completed", deviceId);
668 deviceInitialAuditCompleted(deviceId, true);
669 }
670 }
671
672 private void groupMissing(Group group) {
673 switch (group.state()) {
674 case PENDING_DELETE:
675 log.debug("Group {} delete confirmation from device {}",
676 group, group.deviceId());
677 removeGroupEntry(group);
678 break;
679 case ADDED:
680 case PENDING_ADD:
681 case PENDING_UPDATE:
682 log.debug("Group {} is in store but not on device {}",
683 group, group.deviceId());
684 StoredGroupEntry existing = (groupEntriesById.get(
685 group.deviceId()) != null) ?
686 groupEntriesById.get(group.deviceId()).get(group.id()) :
687 null;
688 log.trace("groupMissing: group "
689 + "entry {} in device {} moving "
690 + "from {} to PENDING_ADD",
691 existing.id(),
692 existing.deviceId(),
693 existing.state());
694 existing.setState(Group.GroupState.PENDING_ADD);
695 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
696 group));
697 break;
698 default:
699 log.debug("Group {} has not been installed.", group);
700 break;
701 }
702 }
703
704 private void extraneousGroup(Group group) {
705 log.debug("Group {} is on device {} but not in store.",
706 group, group.deviceId());
707 addOrUpdateExtraneousGroupEntry(group);
708 }
709
710 private void groupAdded(Group group) {
711 log.trace("Group {} Added or Updated in device {}",
712 group, group.deviceId());
713 addOrUpdateGroupEntry(group);
714 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800715}