blob: f0ab09cb22f4528bf11bee262a9ebe7470bf03a0 [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
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;
Charles Chan0c7c43b2016-01-14 17:39:20 -080026import java.util.Map;
Sho SHIMIZU30d639b2015-05-05 09:30:35 -070027import java.util.Optional;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070028import java.util.Set;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080029import java.util.concurrent.ConcurrentHashMap;
30import java.util.concurrent.ConcurrentMap;
31import java.util.concurrent.atomic.AtomicInteger;
32
33import org.apache.felix.scr.annotations.Activate;
34import org.apache.felix.scr.annotations.Component;
35import org.apache.felix.scr.annotations.Deactivate;
36import org.apache.felix.scr.annotations.Service;
37import org.onlab.util.NewConcurrentHashMap;
38import org.onosproject.core.DefaultGroupId;
39import org.onosproject.core.GroupId;
40import org.onosproject.net.DeviceId;
41import org.onosproject.net.group.DefaultGroup;
42import org.onosproject.net.group.DefaultGroupDescription;
43import org.onosproject.net.group.Group;
44import org.onosproject.net.group.Group.GroupState;
45import org.onosproject.net.group.GroupBucket;
46import org.onosproject.net.group.GroupBuckets;
47import org.onosproject.net.group.GroupDescription;
48import org.onosproject.net.group.GroupEvent;
49import org.onosproject.net.group.GroupEvent.Type;
50import org.onosproject.net.group.GroupKey;
sangho7ff01812015-02-09 16:21:53 -080051import org.onosproject.net.group.GroupOperation;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080052import org.onosproject.net.group.GroupStore;
53import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070054import org.onosproject.net.group.StoredGroupBucketEntry;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080055import org.onosproject.net.group.StoredGroupEntry;
56import org.onosproject.store.AbstractStore;
57import org.slf4j.Logger;
58
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080059import 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())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700191 .transform(input -> input);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800192 }
193
194 /**
195 * Returns the stored group entry.
196 *
197 * @param deviceId the device ID
198 * @param appCookie the group key
199 *
200 * @return a group associated with the key
201 */
202 @Override
203 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
204 return (groupEntriesByKey.get(deviceId) != null) ?
205 groupEntriesByKey.get(deviceId).get(appCookie) :
206 null;
207 }
208
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700209 @Override
210 public Group getGroup(DeviceId deviceId, GroupId groupId) {
211 return (groupEntriesById.get(deviceId) != null) ?
212 groupEntriesById.get(deviceId).get(groupId) :
213 null;
214 }
215
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800216 private int getFreeGroupIdValue(DeviceId deviceId) {
217 int freeId = groupIdGen.incrementAndGet();
218
219 while (true) {
220 Group existing = (
221 groupEntriesById.get(deviceId) != null) ?
222 groupEntriesById.get(deviceId).get(new DefaultGroupId(freeId)) :
223 null;
224 if (existing == null) {
225 existing = (
226 extraneousGroupEntriesById.get(deviceId) != null) ?
227 extraneousGroupEntriesById.get(deviceId).
228 get(new DefaultGroupId(freeId)) :
229 null;
230 }
231 if (existing != null) {
232 freeId = groupIdGen.incrementAndGet();
233 } else {
234 break;
235 }
236 }
237 return freeId;
238 }
239
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800240 /**
241 * Stores a new group entry using the information from group description.
242 *
243 * @param groupDesc group description to be used to create group entry
244 */
245 @Override
246 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800247 // Check if a group is existing with the same key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800248 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
249 return;
250 }
251
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800252 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
253 // Device group audit has not completed yet
254 // Add this group description to pending group key table
255 // Create a group entry object with Dummy Group ID
256 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
257 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
258 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
259 getPendingGroupKeyTable(groupDesc.deviceId());
260 pendingKeyTable.put(groupDesc.appCookie(), group);
261 return;
262 }
263
264 storeGroupDescriptionInternal(groupDesc);
265 }
266
267 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800268 // Check if a group is existing with the same key
269 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
270 return;
271 }
272
Saurav Das100e3b82015-04-30 11:12:10 -0700273 GroupId id = null;
274 if (groupDesc.givenGroupId() == null) {
275 // Get a new group identifier
276 id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
277 } else {
278 id = new DefaultGroupId(groupDesc.givenGroupId());
279 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800280 // Create a group entry object
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800281 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800282 // Insert the newly created group entry into concurrent key and id maps
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800283 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
284 getGroupKeyTable(groupDesc.deviceId());
285 keyTable.put(groupDesc.appCookie(), group);
286 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
287 getGroupIdTable(groupDesc.deviceId());
288 idTable.put(id, group);
289 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
290 group));
291 }
292
293 /**
294 * Updates the existing group entry with the information
295 * from group description.
296 *
297 * @param deviceId the device ID
298 * @param oldAppCookie the current group key
299 * @param type update type
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800300 * @param newBuckets group buckets for updates
301 * @param newAppCookie optional new group key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800302 */
303 @Override
304 public void updateGroupDescription(DeviceId deviceId,
305 GroupKey oldAppCookie,
306 UpdateType type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800307 GroupBuckets newBuckets,
308 GroupKey newAppCookie) {
309 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800310 Group oldGroup = getGroup(deviceId, oldAppCookie);
311 if (oldGroup == null) {
312 return;
313 }
314
315 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
316 type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800317 newBuckets);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800318 if (newBucketList != null) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800319 // Create a new group object from the old group
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800320 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800321 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800322 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
323 oldGroup.deviceId(),
324 oldGroup.type(),
325 updatedBuckets,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800326 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700327 oldGroup.givenGroupId(),
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800328 oldGroup.appId());
329 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
330 updatedGroupDesc);
331 newGroup.setState(GroupState.PENDING_UPDATE);
332 newGroup.setLife(oldGroup.life());
333 newGroup.setPackets(oldGroup.packets());
334 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800335 // Remove the old entry from maps and add new entry using new key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800336 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
337 getGroupKeyTable(oldGroup.deviceId());
338 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
339 getGroupIdTable(oldGroup.deviceId());
340 keyTable.remove(oldGroup.appCookie());
341 idTable.remove(oldGroup.id());
342 keyTable.put(newGroup.appCookie(), newGroup);
343 idTable.put(newGroup.id(), newGroup);
344 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
345 }
346 }
347
348 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
349 UpdateType type,
350 GroupBuckets buckets) {
351 GroupBuckets oldBuckets = oldGroup.buckets();
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700352 List<GroupBucket> newBucketList = new ArrayList<>(oldBuckets.buckets());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800353 boolean groupDescUpdated = false;
354
355 if (type == UpdateType.ADD) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800356 // Check if the any of the new buckets are part of
357 // the old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800358 for (GroupBucket addBucket:buckets.buckets()) {
359 if (!newBucketList.contains(addBucket)) {
360 newBucketList.add(addBucket);
361 groupDescUpdated = true;
362 }
363 }
364 } else if (type == UpdateType.REMOVE) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800365 // Check if the to be removed buckets are part of the
366 // old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800367 for (GroupBucket removeBucket:buckets.buckets()) {
368 if (newBucketList.contains(removeBucket)) {
369 newBucketList.remove(removeBucket);
370 groupDescUpdated = true;
371 }
372 }
373 }
374
375 if (groupDescUpdated) {
376 return newBucketList;
377 } else {
378 return null;
379 }
380 }
381
382 /**
383 * Triggers deleting the existing group entry.
384 *
385 * @param deviceId the device ID
386 * @param appCookie the group key
387 */
388 @Override
389 public void deleteGroupDescription(DeviceId deviceId,
390 GroupKey appCookie) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800391 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800392 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
393 groupEntriesByKey.get(deviceId).get(appCookie) :
394 null;
395 if (existing == null) {
396 return;
397 }
398
399 synchronized (existing) {
400 existing.setState(GroupState.PENDING_DELETE);
401 }
402 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
403 }
404
405 /**
406 * Stores a new group entry, or updates an existing entry.
407 *
408 * @param group group entry
409 */
410 @Override
411 public void addOrUpdateGroupEntry(Group group) {
412 // check if this new entry is an update to an existing entry
413 StoredGroupEntry existing = (groupEntriesById.get(
414 group.deviceId()) != null) ?
415 groupEntriesById.get(group.deviceId()).get(group.id()) :
416 null;
417 GroupEvent event = null;
418
419 if (existing != null) {
420 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700421 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700422 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700423 existing.buckets().buckets()
424 .stream()
425 .filter((existingBucket)->(existingBucket.equals(bucket)))
426 .findFirst();
427 if (matchingBucket.isPresent()) {
428 ((StoredGroupBucketEntry) matchingBucket.
429 get()).setPackets(bucket.packets());
430 ((StoredGroupBucketEntry) matchingBucket.
431 get()).setBytes(bucket.bytes());
432 } else {
433 log.warn("addOrUpdateGroupEntry: No matching "
434 + "buckets to update stats");
435 }
436 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800437 existing.setLife(group.life());
438 existing.setPackets(group.packets());
439 existing.setBytes(group.bytes());
440 if (existing.state() == GroupState.PENDING_ADD) {
441 existing.setState(GroupState.ADDED);
442 event = new GroupEvent(Type.GROUP_ADDED, existing);
443 } else {
444 if (existing.state() == GroupState.PENDING_UPDATE) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700445 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800446 }
447 event = new GroupEvent(Type.GROUP_UPDATED, existing);
448 }
449 }
450 }
451
452 if (event != null) {
453 notifyDelegate(event);
454 }
455 }
456
457 /**
458 * Removes the group entry from store.
459 *
460 * @param group group entry
461 */
462 @Override
463 public void removeGroupEntry(Group group) {
464 StoredGroupEntry existing = (groupEntriesById.get(
465 group.deviceId()) != null) ?
466 groupEntriesById.get(group.deviceId()).get(group.id()) :
467 null;
468
469 if (existing != null) {
470 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
471 getGroupKeyTable(existing.deviceId());
472 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
473 getGroupIdTable(existing.deviceId());
474 idTable.remove(existing.id());
475 keyTable.remove(existing.appCookie());
476 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
477 }
478 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800479
480 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800481 public void purgeGroupEntry(DeviceId deviceId) {
482 Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove =
483 groupEntriesById.get(deviceId).entrySet();
484
485 groupEntriesById.remove(deviceId);
486 groupEntriesByKey.remove(deviceId);
487
488 entryPendingRemove.forEach(entry -> {
489 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
490 });
491 }
492
493 @Override
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800494 public void deviceInitialAuditCompleted(DeviceId deviceId,
495 boolean completed) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800496 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800497 if (completed) {
498 log.debug("deviceInitialAuditCompleted: AUDIT "
499 + "completed for device {}", deviceId);
500 deviceAuditStatus.put(deviceId, true);
501 // Execute all pending group requests
502 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
503 getPendingGroupKeyTable(deviceId);
504 for (Group group:pendingGroupRequests.values()) {
505 GroupDescription tmp = new DefaultGroupDescription(
506 group.deviceId(),
507 group.type(),
508 group.buckets(),
509 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700510 group.givenGroupId(),
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800511 group.appId());
512 storeGroupDescriptionInternal(tmp);
513 }
514 getPendingGroupKeyTable(deviceId).clear();
515 } else {
516 if (deviceAuditStatus.get(deviceId)) {
517 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
518 + "status for device {}", deviceId);
519 deviceAuditStatus.put(deviceId, false);
520 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800521 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800522 }
523 }
524
525 @Override
526 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
527 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800528 return (deviceAuditStatus.get(deviceId) != null)
529 ? deviceAuditStatus.get(deviceId) : false;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800530 }
531 }
532
533 @Override
sangho7ff01812015-02-09 16:21:53 -0800534 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
535
536 StoredGroupEntry existing = (groupEntriesById.get(
537 deviceId) != null) ?
538 groupEntriesById.get(deviceId).get(operation.groupId()) :
539 null;
540
541 if (existing == null) {
542 log.warn("No group entry with ID {} found ", operation.groupId());
543 return;
544 }
545
546 switch (operation.opType()) {
547 case ADD:
548 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
549 break;
550 case MODIFY:
551 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
552 break;
553 case DELETE:
554 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
555 break;
556 default:
557 log.warn("Unknown group operation type {}", operation.opType());
558 }
559
560 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
561 getGroupKeyTable(existing.deviceId());
562 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
563 getGroupIdTable(existing.deviceId());
564 idTable.remove(existing.id());
565 keyTable.remove(existing.appCookie());
566 }
567
568 @Override
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800569 public void addOrUpdateExtraneousGroupEntry(Group group) {
570 ConcurrentMap<GroupId, Group> extraneousIdTable =
571 getExtraneousGroupIdTable(group.deviceId());
572 extraneousIdTable.put(group.id(), group);
573 // Check the reference counter
574 if (group.referenceCount() == 0) {
575 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
576 }
577 }
578
579 @Override
580 public void removeExtraneousGroupEntry(Group group) {
581 ConcurrentMap<GroupId, Group> extraneousIdTable =
582 getExtraneousGroupIdTable(group.deviceId());
583 extraneousIdTable.remove(group.id());
584 }
585
586 @Override
587 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
588 // flatten and make iterator unmodifiable
589 return FluentIterable.from(
590 getExtraneousGroupIdTable(deviceId).values());
591 }
sangho7ff01812015-02-09 16:21:53 -0800592
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700593 @Override
594 public void pushGroupMetrics(DeviceId deviceId,
595 Collection<Group> groupEntries) {
596 boolean deviceInitialAuditStatus =
597 deviceInitialAuditStatus(deviceId);
598 Set<Group> southboundGroupEntries =
599 Sets.newHashSet(groupEntries);
600 Set<Group> storedGroupEntries =
601 Sets.newHashSet(getGroups(deviceId));
602 Set<Group> extraneousStoredEntries =
603 Sets.newHashSet(getExtraneousGroups(deviceId));
604
605 log.trace("pushGroupMetrics: Displaying all ({}) "
606 + "southboundGroupEntries for device {}",
607 southboundGroupEntries.size(),
608 deviceId);
609 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
610 Group group = it.next();
611 log.trace("Group {} in device {}", group, deviceId);
612 }
613
614 log.trace("Displaying all ({}) stored group entries for device {}",
615 storedGroupEntries.size(),
616 deviceId);
617 for (Iterator<Group> it1 = storedGroupEntries.iterator();
618 it1.hasNext();) {
619 Group group = it1.next();
620 log.trace("Stored Group {} for device {}", group, deviceId);
621 }
622
623 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
624 Group group = it2.next();
625 if (storedGroupEntries.remove(group)) {
626 // we both have the group, let's update some info then.
627 log.trace("Group AUDIT: group {} exists "
628 + "in both planes for device {}",
629 group.id(), deviceId);
630 groupAdded(group);
631 it2.remove();
632 }
633 }
634 for (Group group : southboundGroupEntries) {
635 if (getGroup(group.deviceId(), group.id()) != null) {
636 // There is a group existing with the same id
637 // It is possible that group update is
638 // in progress while we got a stale info from switch
639 if (!storedGroupEntries.remove(getGroup(
640 group.deviceId(), group.id()))) {
641 log.warn("Group AUDIT: Inconsistent state:"
642 + "Group exists in ID based table while "
643 + "not present in key based table");
644 }
645 } else {
646 // there are groups in the switch that aren't in the store
647 log.trace("Group AUDIT: extraneous group {} exists "
648 + "in data plane for device {}",
649 group.id(), deviceId);
650 extraneousStoredEntries.remove(group);
651 extraneousGroup(group);
652 }
653 }
654 for (Group group : storedGroupEntries) {
655 // there are groups in the store that aren't in the switch
656 log.trace("Group AUDIT: group {} missing "
657 + "in data plane for device {}",
658 group.id(), deviceId);
659 groupMissing(group);
660 }
661 for (Group group : extraneousStoredEntries) {
662 // there are groups in the extraneous store that
663 // aren't in the switch
664 log.trace("Group AUDIT: clearing extransoeus group {} "
665 + "from store for device {}",
666 group.id(), deviceId);
667 removeExtraneousGroupEntry(group);
668 }
669
670 if (!deviceInitialAuditStatus) {
671 log.debug("Group AUDIT: Setting device {} initial "
672 + "AUDIT completed", deviceId);
673 deviceInitialAuditCompleted(deviceId, true);
674 }
675 }
676
677 private void groupMissing(Group group) {
678 switch (group.state()) {
679 case PENDING_DELETE:
680 log.debug("Group {} delete confirmation from device {}",
681 group, group.deviceId());
682 removeGroupEntry(group);
683 break;
684 case ADDED:
685 case PENDING_ADD:
686 case PENDING_UPDATE:
687 log.debug("Group {} is in store but not on device {}",
688 group, group.deviceId());
689 StoredGroupEntry existing = (groupEntriesById.get(
690 group.deviceId()) != null) ?
691 groupEntriesById.get(group.deviceId()).get(group.id()) :
692 null;
693 log.trace("groupMissing: group "
694 + "entry {} in device {} moving "
695 + "from {} to PENDING_ADD",
696 existing.id(),
697 existing.deviceId(),
698 existing.state());
699 existing.setState(Group.GroupState.PENDING_ADD);
700 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
701 group));
702 break;
703 default:
704 log.debug("Group {} has not been installed.", group);
705 break;
706 }
707 }
708
709 private void extraneousGroup(Group group) {
710 log.debug("Group {} is on device {} but not in store.",
711 group, group.deviceId());
712 addOrUpdateExtraneousGroupEntry(group);
713 }
714
715 private void groupAdded(Group group) {
716 log.trace("Group {} Added or Updated in device {}",
717 group, group.deviceId());
718 addOrUpdateGroupEntry(group);
719 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800720}