blob: 6e03b2b9d0442771954a3148f528c1e712cc43b7 [file] [log] [blame]
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Thomas Vachuskac97aa612015-06-23 16:00:18 -070016package org.onosproject.store.trivial;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080017
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080018import static org.slf4j.LoggerFactory.getLogger;
19
20import java.util.ArrayList;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070021import java.util.Collection;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080022import java.util.HashMap;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070023import java.util.Iterator;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080024import java.util.List;
Charles Chan0c7c43b2016-01-14 17:39:20 -080025import java.util.Map;
Sho SHIMIZU30d639b2015-05-05 09:30:35 -070026import java.util.Optional;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070027import java.util.Set;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080028import java.util.concurrent.ConcurrentHashMap;
29import java.util.concurrent.ConcurrentMap;
30import java.util.concurrent.atomic.AtomicInteger;
31
32import org.apache.felix.scr.annotations.Activate;
33import org.apache.felix.scr.annotations.Component;
34import org.apache.felix.scr.annotations.Deactivate;
35import org.apache.felix.scr.annotations.Service;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080036import org.onosproject.core.DefaultGroupId;
37import org.onosproject.core.GroupId;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.group.DefaultGroup;
40import org.onosproject.net.group.DefaultGroupDescription;
41import org.onosproject.net.group.Group;
42import org.onosproject.net.group.Group.GroupState;
43import org.onosproject.net.group.GroupBucket;
44import org.onosproject.net.group.GroupBuckets;
45import org.onosproject.net.group.GroupDescription;
46import org.onosproject.net.group.GroupEvent;
47import org.onosproject.net.group.GroupEvent.Type;
48import org.onosproject.net.group.GroupKey;
sangho7ff01812015-02-09 16:21:53 -080049import org.onosproject.net.group.GroupOperation;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080050import org.onosproject.net.group.GroupStore;
51import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070052import org.onosproject.net.group.StoredGroupBucketEntry;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080053import org.onosproject.net.group.StoredGroupEntry;
54import org.onosproject.store.AbstractStore;
55import org.slf4j.Logger;
56
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080057import com.google.common.collect.FluentIterable;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070058import com.google.common.collect.Sets;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080059
60/**
61 * Manages inventory of group entries using trivial in-memory implementation.
62 */
63@Component(immediate = true)
64@Service
65public class SimpleGroupStore
66 extends AbstractStore<GroupEvent, GroupStoreDelegate>
67 implements GroupStore {
68
69 private final Logger log = getLogger(getClass());
70
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080071 private final int dummyId = 0xffffffff;
72 private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
73
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080074 // inner Map is per device group table
75 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
76 groupEntriesByKey = new ConcurrentHashMap<>();
77 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
78 groupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080079 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
80 pendingGroupEntriesByKey = new ConcurrentHashMap<>();
81 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
82 extraneousGroupEntriesById = new ConcurrentHashMap<>();
83
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -070084 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080085
86 private final AtomicInteger groupIdGen = new AtomicInteger();
87
88 @Activate
89 public void activate() {
90 log.info("Started");
91 }
92
93 @Deactivate
94 public void deactivate() {
95 groupEntriesByKey.clear();
96 groupEntriesById.clear();
97 log.info("Stopped");
98 }
99
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800100 /**
101 * Returns the group key table for specified device.
102 *
103 * @param deviceId identifier of the device
104 * @return Map representing group key table of given device.
105 */
106 private ConcurrentMap<GroupKey, StoredGroupEntry> getGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700107 return groupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800108 }
109
110 /**
111 * Returns the group id table for specified device.
112 *
113 * @param deviceId identifier of the device
114 * @return Map representing group key table of given device.
115 */
116 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700117 return groupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800118 }
119
120 /**
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800121 * Returns the pending group key table for specified device.
122 *
123 * @param deviceId identifier of the device
124 * @return Map representing group key table of given device.
125 */
126 private ConcurrentMap<GroupKey, StoredGroupEntry>
127 getPendingGroupKeyTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700128 return pendingGroupEntriesByKey.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800129 }
130
131 /**
132 * Returns the extraneous group id table for specified device.
133 *
134 * @param deviceId identifier of the device
135 * @return Map representing group key table of given device.
136 */
137 private ConcurrentMap<GroupId, Group>
138 getExtraneousGroupIdTable(DeviceId deviceId) {
Yuta HIGUCHIc2e68152016-08-16 13:47:36 -0700139 return extraneousGroupEntriesById.computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800140 }
141
142 /**
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800143 * Returns the number of groups for the specified device in the store.
144 *
145 * @return number of groups for the specified device
146 */
147 @Override
148 public int getGroupCount(DeviceId deviceId) {
149 return (groupEntriesByKey.get(deviceId) != null) ?
150 groupEntriesByKey.get(deviceId).size() : 0;
151 }
152
153 /**
154 * Returns the groups associated with a device.
155 *
156 * @param deviceId the device ID
157 *
158 * @return the group entries
159 */
160 @Override
161 public Iterable<Group> getGroups(DeviceId deviceId) {
162 // flatten and make iterator unmodifiable
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800163 return FluentIterable.from(getGroupKeyTable(deviceId).values())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700164 .transform(input -> input);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800165 }
166
167 /**
168 * Returns the stored group entry.
169 *
170 * @param deviceId the device ID
171 * @param appCookie the group key
172 *
173 * @return a group associated with the key
174 */
175 @Override
176 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
177 return (groupEntriesByKey.get(deviceId) != null) ?
178 groupEntriesByKey.get(deviceId).get(appCookie) :
179 null;
180 }
181
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700182 @Override
183 public Group getGroup(DeviceId deviceId, GroupId groupId) {
184 return (groupEntriesById.get(deviceId) != null) ?
185 groupEntriesById.get(deviceId).get(groupId) :
186 null;
187 }
188
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800189 private int getFreeGroupIdValue(DeviceId deviceId) {
190 int freeId = groupIdGen.incrementAndGet();
191
192 while (true) {
193 Group existing = (
194 groupEntriesById.get(deviceId) != null) ?
195 groupEntriesById.get(deviceId).get(new DefaultGroupId(freeId)) :
196 null;
197 if (existing == null) {
198 existing = (
199 extraneousGroupEntriesById.get(deviceId) != null) ?
200 extraneousGroupEntriesById.get(deviceId).
201 get(new DefaultGroupId(freeId)) :
202 null;
203 }
204 if (existing != null) {
205 freeId = groupIdGen.incrementAndGet();
206 } else {
207 break;
208 }
209 }
210 return freeId;
211 }
212
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800213 /**
214 * Stores a new group entry using the information from group description.
215 *
216 * @param groupDesc group description to be used to create group entry
217 */
218 @Override
219 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800220 // Check if a group is existing with the same key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800221 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
222 return;
223 }
224
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800225 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
226 // Device group audit has not completed yet
227 // Add this group description to pending group key table
228 // Create a group entry object with Dummy Group ID
229 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
230 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
231 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
232 getPendingGroupKeyTable(groupDesc.deviceId());
233 pendingKeyTable.put(groupDesc.appCookie(), group);
234 return;
235 }
236
237 storeGroupDescriptionInternal(groupDesc);
238 }
239
240 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800241 // Check if a group is existing with the same key
242 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
243 return;
244 }
245
Saurav Das100e3b82015-04-30 11:12:10 -0700246 GroupId id = null;
247 if (groupDesc.givenGroupId() == null) {
248 // Get a new group identifier
249 id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
250 } else {
251 id = new DefaultGroupId(groupDesc.givenGroupId());
252 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800253 // Create a group entry object
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800254 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800255 // Insert the newly created group entry into concurrent key and id maps
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800256 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
257 getGroupKeyTable(groupDesc.deviceId());
258 keyTable.put(groupDesc.appCookie(), group);
259 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
260 getGroupIdTable(groupDesc.deviceId());
261 idTable.put(id, group);
262 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
263 group));
264 }
265
266 /**
267 * Updates the existing group entry with the information
268 * from group description.
269 *
270 * @param deviceId the device ID
271 * @param oldAppCookie the current group key
272 * @param type update type
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800273 * @param newBuckets group buckets for updates
274 * @param newAppCookie optional new group key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800275 */
276 @Override
277 public void updateGroupDescription(DeviceId deviceId,
278 GroupKey oldAppCookie,
279 UpdateType type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800280 GroupBuckets newBuckets,
281 GroupKey newAppCookie) {
282 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800283 Group oldGroup = getGroup(deviceId, oldAppCookie);
284 if (oldGroup == null) {
285 return;
286 }
287
288 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
289 type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800290 newBuckets);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800291 if (newBucketList != null) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800292 // Create a new group object from the old group
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800293 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800294 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800295 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
296 oldGroup.deviceId(),
297 oldGroup.type(),
298 updatedBuckets,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800299 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700300 oldGroup.givenGroupId(),
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800301 oldGroup.appId());
302 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
303 updatedGroupDesc);
304 newGroup.setState(GroupState.PENDING_UPDATE);
305 newGroup.setLife(oldGroup.life());
306 newGroup.setPackets(oldGroup.packets());
307 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800308 // Remove the old entry from maps and add new entry using new key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800309 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
310 getGroupKeyTable(oldGroup.deviceId());
311 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
312 getGroupIdTable(oldGroup.deviceId());
313 keyTable.remove(oldGroup.appCookie());
314 idTable.remove(oldGroup.id());
315 keyTable.put(newGroup.appCookie(), newGroup);
316 idTable.put(newGroup.id(), newGroup);
317 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
318 }
319 }
320
321 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
322 UpdateType type,
323 GroupBuckets buckets) {
324 GroupBuckets oldBuckets = oldGroup.buckets();
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700325 List<GroupBucket> newBucketList = new ArrayList<>(oldBuckets.buckets());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800326 boolean groupDescUpdated = false;
327
328 if (type == UpdateType.ADD) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800329 // Check if the any of the new buckets are part of
330 // the old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800331 for (GroupBucket addBucket:buckets.buckets()) {
332 if (!newBucketList.contains(addBucket)) {
333 newBucketList.add(addBucket);
334 groupDescUpdated = true;
335 }
336 }
337 } else if (type == UpdateType.REMOVE) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800338 // Check if the to be removed buckets are part of the
339 // old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800340 for (GroupBucket removeBucket:buckets.buckets()) {
341 if (newBucketList.contains(removeBucket)) {
342 newBucketList.remove(removeBucket);
343 groupDescUpdated = true;
344 }
345 }
346 }
347
348 if (groupDescUpdated) {
349 return newBucketList;
350 } else {
351 return null;
352 }
353 }
354
355 /**
356 * Triggers deleting the existing group entry.
357 *
358 * @param deviceId the device ID
359 * @param appCookie the group key
360 */
361 @Override
362 public void deleteGroupDescription(DeviceId deviceId,
363 GroupKey appCookie) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800364 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800365 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
366 groupEntriesByKey.get(deviceId).get(appCookie) :
367 null;
368 if (existing == null) {
369 return;
370 }
371
372 synchronized (existing) {
373 existing.setState(GroupState.PENDING_DELETE);
374 }
375 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
376 }
377
378 /**
379 * Stores a new group entry, or updates an existing entry.
380 *
381 * @param group group entry
382 */
383 @Override
384 public void addOrUpdateGroupEntry(Group group) {
385 // check if this new entry is an update to an existing entry
386 StoredGroupEntry existing = (groupEntriesById.get(
387 group.deviceId()) != null) ?
388 groupEntriesById.get(group.deviceId()).get(group.id()) :
389 null;
390 GroupEvent event = null;
391
392 if (existing != null) {
393 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700394 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700395 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700396 existing.buckets().buckets()
397 .stream()
398 .filter((existingBucket)->(existingBucket.equals(bucket)))
399 .findFirst();
400 if (matchingBucket.isPresent()) {
401 ((StoredGroupBucketEntry) matchingBucket.
402 get()).setPackets(bucket.packets());
403 ((StoredGroupBucketEntry) matchingBucket.
404 get()).setBytes(bucket.bytes());
405 } else {
406 log.warn("addOrUpdateGroupEntry: No matching "
407 + "buckets to update stats");
408 }
409 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800410 existing.setLife(group.life());
411 existing.setPackets(group.packets());
412 existing.setBytes(group.bytes());
413 if (existing.state() == GroupState.PENDING_ADD) {
414 existing.setState(GroupState.ADDED);
415 event = new GroupEvent(Type.GROUP_ADDED, existing);
416 } else {
417 if (existing.state() == GroupState.PENDING_UPDATE) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700418 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800419 }
420 event = new GroupEvent(Type.GROUP_UPDATED, existing);
421 }
422 }
423 }
424
425 if (event != null) {
426 notifyDelegate(event);
427 }
428 }
429
430 /**
431 * Removes the group entry from store.
432 *
433 * @param group group entry
434 */
435 @Override
436 public void removeGroupEntry(Group group) {
437 StoredGroupEntry existing = (groupEntriesById.get(
438 group.deviceId()) != null) ?
439 groupEntriesById.get(group.deviceId()).get(group.id()) :
440 null;
441
442 if (existing != null) {
443 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
444 getGroupKeyTable(existing.deviceId());
445 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
446 getGroupIdTable(existing.deviceId());
447 idTable.remove(existing.id());
448 keyTable.remove(existing.appCookie());
449 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
450 }
451 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800452
453 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800454 public void purgeGroupEntry(DeviceId deviceId) {
455 Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove =
456 groupEntriesById.get(deviceId).entrySet();
457
458 groupEntriesById.remove(deviceId);
459 groupEntriesByKey.remove(deviceId);
460
461 entryPendingRemove.forEach(entry -> {
462 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
463 });
464 }
465
466 @Override
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800467 public void deviceInitialAuditCompleted(DeviceId deviceId,
468 boolean completed) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800469 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800470 if (completed) {
471 log.debug("deviceInitialAuditCompleted: AUDIT "
472 + "completed for device {}", deviceId);
473 deviceAuditStatus.put(deviceId, true);
474 // Execute all pending group requests
475 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
476 getPendingGroupKeyTable(deviceId);
477 for (Group group:pendingGroupRequests.values()) {
478 GroupDescription tmp = new DefaultGroupDescription(
479 group.deviceId(),
480 group.type(),
481 group.buckets(),
482 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700483 group.givenGroupId(),
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800484 group.appId());
485 storeGroupDescriptionInternal(tmp);
486 }
487 getPendingGroupKeyTable(deviceId).clear();
488 } else {
489 if (deviceAuditStatus.get(deviceId)) {
490 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
491 + "status for device {}", deviceId);
492 deviceAuditStatus.put(deviceId, false);
493 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800494 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800495 }
496 }
497
498 @Override
499 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
500 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800501 return (deviceAuditStatus.get(deviceId) != null)
502 ? deviceAuditStatus.get(deviceId) : false;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800503 }
504 }
505
506 @Override
sangho7ff01812015-02-09 16:21:53 -0800507 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
508
509 StoredGroupEntry existing = (groupEntriesById.get(
510 deviceId) != null) ?
511 groupEntriesById.get(deviceId).get(operation.groupId()) :
512 null;
513
514 if (existing == null) {
515 log.warn("No group entry with ID {} found ", operation.groupId());
516 return;
517 }
518
519 switch (operation.opType()) {
520 case ADD:
521 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
522 break;
523 case MODIFY:
524 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
525 break;
526 case DELETE:
527 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
528 break;
529 default:
530 log.warn("Unknown group operation type {}", operation.opType());
531 }
532
533 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
534 getGroupKeyTable(existing.deviceId());
535 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
536 getGroupIdTable(existing.deviceId());
537 idTable.remove(existing.id());
538 keyTable.remove(existing.appCookie());
539 }
540
541 @Override
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800542 public void addOrUpdateExtraneousGroupEntry(Group group) {
543 ConcurrentMap<GroupId, Group> extraneousIdTable =
544 getExtraneousGroupIdTable(group.deviceId());
545 extraneousIdTable.put(group.id(), group);
546 // Check the reference counter
547 if (group.referenceCount() == 0) {
548 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
549 }
550 }
551
552 @Override
553 public void removeExtraneousGroupEntry(Group group) {
554 ConcurrentMap<GroupId, Group> extraneousIdTable =
555 getExtraneousGroupIdTable(group.deviceId());
556 extraneousIdTable.remove(group.id());
557 }
558
559 @Override
560 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
561 // flatten and make iterator unmodifiable
562 return FluentIterable.from(
563 getExtraneousGroupIdTable(deviceId).values());
564 }
sangho7ff01812015-02-09 16:21:53 -0800565
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700566 @Override
567 public void pushGroupMetrics(DeviceId deviceId,
568 Collection<Group> groupEntries) {
569 boolean deviceInitialAuditStatus =
570 deviceInitialAuditStatus(deviceId);
571 Set<Group> southboundGroupEntries =
572 Sets.newHashSet(groupEntries);
573 Set<Group> storedGroupEntries =
574 Sets.newHashSet(getGroups(deviceId));
575 Set<Group> extraneousStoredEntries =
576 Sets.newHashSet(getExtraneousGroups(deviceId));
577
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700578 if (log.isTraceEnabled()) {
579 log.trace("pushGroupMetrics: Displaying all ({}) "
580 + "southboundGroupEntries for device {}",
581 southboundGroupEntries.size(),
582 deviceId);
583 for (Group group : southboundGroupEntries) {
584 log.trace("Group {} in device {}", group, deviceId);
585 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700586
Sho SHIMIZU695bac62016-08-15 12:41:59 -0700587 log.trace("Displaying all ({}) stored group entries for device {}",
588 storedGroupEntries.size(),
589 deviceId);
590 for (Group group : storedGroupEntries) {
591 log.trace("Stored Group {} for device {}", group, deviceId);
592 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700593 }
594
595 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
596 Group group = it2.next();
597 if (storedGroupEntries.remove(group)) {
598 // we both have the group, let's update some info then.
599 log.trace("Group AUDIT: group {} exists "
600 + "in both planes for device {}",
601 group.id(), deviceId);
602 groupAdded(group);
603 it2.remove();
604 }
605 }
606 for (Group group : southboundGroupEntries) {
607 if (getGroup(group.deviceId(), group.id()) != null) {
608 // There is a group existing with the same id
609 // It is possible that group update is
610 // in progress while we got a stale info from switch
611 if (!storedGroupEntries.remove(getGroup(
612 group.deviceId(), group.id()))) {
613 log.warn("Group AUDIT: Inconsistent state:"
614 + "Group exists in ID based table while "
615 + "not present in key based table");
616 }
617 } else {
618 // there are groups in the switch that aren't in the store
619 log.trace("Group AUDIT: extraneous group {} exists "
620 + "in data plane for device {}",
621 group.id(), deviceId);
622 extraneousStoredEntries.remove(group);
623 extraneousGroup(group);
624 }
625 }
626 for (Group group : storedGroupEntries) {
627 // there are groups in the store that aren't in the switch
628 log.trace("Group AUDIT: group {} missing "
629 + "in data plane for device {}",
630 group.id(), deviceId);
631 groupMissing(group);
632 }
633 for (Group group : extraneousStoredEntries) {
634 // there are groups in the extraneous store that
635 // aren't in the switch
636 log.trace("Group AUDIT: clearing extransoeus group {} "
637 + "from store for device {}",
638 group.id(), deviceId);
639 removeExtraneousGroupEntry(group);
640 }
641
642 if (!deviceInitialAuditStatus) {
643 log.debug("Group AUDIT: Setting device {} initial "
644 + "AUDIT completed", deviceId);
645 deviceInitialAuditCompleted(deviceId, true);
646 }
647 }
648
649 private void groupMissing(Group group) {
650 switch (group.state()) {
651 case PENDING_DELETE:
652 log.debug("Group {} delete confirmation from device {}",
653 group, group.deviceId());
654 removeGroupEntry(group);
655 break;
656 case ADDED:
657 case PENDING_ADD:
658 case PENDING_UPDATE:
659 log.debug("Group {} is in store but not on device {}",
660 group, group.deviceId());
661 StoredGroupEntry existing = (groupEntriesById.get(
662 group.deviceId()) != null) ?
663 groupEntriesById.get(group.deviceId()).get(group.id()) :
664 null;
665 log.trace("groupMissing: group "
666 + "entry {} in device {} moving "
667 + "from {} to PENDING_ADD",
668 existing.id(),
669 existing.deviceId(),
670 existing.state());
671 existing.setState(Group.GroupState.PENDING_ADD);
672 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
673 group));
674 break;
675 default:
676 log.debug("Group {} has not been installed.", group);
677 break;
678 }
679 }
680
681 private void extraneousGroup(Group group) {
682 log.debug("Group {} is on device {} but not in store.",
683 group, group.deviceId());
684 addOrUpdateExtraneousGroupEntry(group);
685 }
686
687 private void groupAdded(Group group) {
688 log.trace("Group {} Added or Updated in device {}",
689 group, group.deviceId());
690 addOrUpdateGroupEntry(group);
691 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800692}