blob: bf5337cf5160c2ebd94117db86124a4a8b0c25f5 [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;
27import java.util.Map.Entry;
Sho SHIMIZU30d639b2015-05-05 09:30:35 -070028import java.util.Optional;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070029import java.util.Set;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080030import java.util.concurrent.ConcurrentHashMap;
31import java.util.concurrent.ConcurrentMap;
32import java.util.concurrent.atomic.AtomicInteger;
33
34import org.apache.felix.scr.annotations.Activate;
35import org.apache.felix.scr.annotations.Component;
36import org.apache.felix.scr.annotations.Deactivate;
37import org.apache.felix.scr.annotations.Service;
38import org.onlab.util.NewConcurrentHashMap;
39import org.onosproject.core.DefaultGroupId;
40import org.onosproject.core.GroupId;
41import org.onosproject.net.DeviceId;
42import org.onosproject.net.group.DefaultGroup;
43import org.onosproject.net.group.DefaultGroupDescription;
44import org.onosproject.net.group.Group;
45import org.onosproject.net.group.Group.GroupState;
46import org.onosproject.net.group.GroupBucket;
47import org.onosproject.net.group.GroupBuckets;
48import org.onosproject.net.group.GroupDescription;
49import org.onosproject.net.group.GroupEvent;
50import org.onosproject.net.group.GroupEvent.Type;
51import org.onosproject.net.group.GroupKey;
sangho7ff01812015-02-09 16:21:53 -080052import org.onosproject.net.group.GroupOperation;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080053import org.onosproject.net.group.GroupStore;
54import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -070055import org.onosproject.net.group.StoredGroupBucketEntry;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080056import org.onosproject.net.group.StoredGroupEntry;
57import org.onosproject.store.AbstractStore;
58import org.slf4j.Logger;
59
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080060import com.google.common.collect.FluentIterable;
Srikanth Vavilapalli23181912015-05-04 09:48:09 -070061import com.google.common.collect.Sets;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080062
63/**
64 * Manages inventory of group entries using trivial in-memory implementation.
65 */
66@Component(immediate = true)
67@Service
68public class SimpleGroupStore
69 extends AbstractStore<GroupEvent, GroupStoreDelegate>
70 implements GroupStore {
71
72 private final Logger log = getLogger(getClass());
73
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080074 private final int dummyId = 0xffffffff;
75 private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
76
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080077 // inner Map is per device group table
78 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
79 groupEntriesByKey = new ConcurrentHashMap<>();
80 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>
81 groupEntriesById = new ConcurrentHashMap<>();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080082 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>
83 pendingGroupEntriesByKey = new ConcurrentHashMap<>();
84 private final ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>
85 extraneousGroupEntriesById = new ConcurrentHashMap<>();
86
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -070087 private final HashMap<DeviceId, Boolean> deviceAuditStatus = new HashMap<>();
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -080088
89 private final AtomicInteger groupIdGen = new AtomicInteger();
90
91 @Activate
92 public void activate() {
93 log.info("Started");
94 }
95
96 @Deactivate
97 public void deactivate() {
98 groupEntriesByKey.clear();
99 groupEntriesById.clear();
100 log.info("Stopped");
101 }
102
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800103 private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
104 lazyEmptyGroupKeyTable() {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800105 return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
106 }
107
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800108 private static NewConcurrentHashMap<GroupId, StoredGroupEntry>
109 lazyEmptyGroupIdTable() {
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800110 return NewConcurrentHashMap.<GroupId, StoredGroupEntry>ifNeeded();
111 }
112
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800113 private static NewConcurrentHashMap<GroupKey, StoredGroupEntry>
114 lazyEmptyPendingGroupKeyTable() {
115 return NewConcurrentHashMap.<GroupKey, StoredGroupEntry>ifNeeded();
116 }
117
118 private static NewConcurrentHashMap<GroupId, Group>
119 lazyEmptyExtraneousGroupIdTable() {
120 return NewConcurrentHashMap.<GroupId, Group>ifNeeded();
121 }
122
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800123 /**
124 * Returns the group key table for specified device.
125 *
126 * @param deviceId identifier of the device
127 * @return Map representing group key table of given device.
128 */
129 private ConcurrentMap<GroupKey, StoredGroupEntry> getGroupKeyTable(DeviceId deviceId) {
130 return createIfAbsentUnchecked(groupEntriesByKey,
131 deviceId, lazyEmptyGroupKeyTable());
132 }
133
134 /**
135 * Returns the group id table for specified device.
136 *
137 * @param deviceId identifier of the device
138 * @return Map representing group key table of given device.
139 */
140 private ConcurrentMap<GroupId, StoredGroupEntry> getGroupIdTable(DeviceId deviceId) {
141 return createIfAbsentUnchecked(groupEntriesById,
142 deviceId, lazyEmptyGroupIdTable());
143 }
144
145 /**
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800146 * Returns the pending group key table for specified device.
147 *
148 * @param deviceId identifier of the device
149 * @return Map representing group key table of given device.
150 */
151 private ConcurrentMap<GroupKey, StoredGroupEntry>
152 getPendingGroupKeyTable(DeviceId deviceId) {
153 return createIfAbsentUnchecked(pendingGroupEntriesByKey,
154 deviceId, lazyEmptyPendingGroupKeyTable());
155 }
156
157 /**
158 * Returns the extraneous group id table for specified device.
159 *
160 * @param deviceId identifier of the device
161 * @return Map representing group key table of given device.
162 */
163 private ConcurrentMap<GroupId, Group>
164 getExtraneousGroupIdTable(DeviceId deviceId) {
165 return createIfAbsentUnchecked(extraneousGroupEntriesById,
166 deviceId,
167 lazyEmptyExtraneousGroupIdTable());
168 }
169
170 /**
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800171 * Returns the number of groups for the specified device in the store.
172 *
173 * @return number of groups for the specified device
174 */
175 @Override
176 public int getGroupCount(DeviceId deviceId) {
177 return (groupEntriesByKey.get(deviceId) != null) ?
178 groupEntriesByKey.get(deviceId).size() : 0;
179 }
180
181 /**
182 * Returns the groups associated with a device.
183 *
184 * @param deviceId the device ID
185 *
186 * @return the group entries
187 */
188 @Override
189 public Iterable<Group> getGroups(DeviceId deviceId) {
190 // flatten and make iterator unmodifiable
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800191 return FluentIterable.from(getGroupKeyTable(deviceId).values())
Sho SHIMIZU74626412015-09-11 11:46:27 -0700192 .transform(input -> input);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800193 }
194
195 /**
196 * Returns the stored group entry.
197 *
198 * @param deviceId the device ID
199 * @param appCookie the group key
200 *
201 * @return a group associated with the key
202 */
203 @Override
204 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
205 return (groupEntriesByKey.get(deviceId) != null) ?
206 groupEntriesByKey.get(deviceId).get(appCookie) :
207 null;
208 }
209
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700210 @Override
211 public Group getGroup(DeviceId deviceId, GroupId groupId) {
212 return (groupEntriesById.get(deviceId) != null) ?
213 groupEntriesById.get(deviceId).get(groupId) :
214 null;
215 }
216
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800217 private int getFreeGroupIdValue(DeviceId deviceId) {
218 int freeId = groupIdGen.incrementAndGet();
219
220 while (true) {
221 Group existing = (
222 groupEntriesById.get(deviceId) != null) ?
223 groupEntriesById.get(deviceId).get(new DefaultGroupId(freeId)) :
224 null;
225 if (existing == null) {
226 existing = (
227 extraneousGroupEntriesById.get(deviceId) != null) ?
228 extraneousGroupEntriesById.get(deviceId).
229 get(new DefaultGroupId(freeId)) :
230 null;
231 }
232 if (existing != null) {
233 freeId = groupIdGen.incrementAndGet();
234 } else {
235 break;
236 }
237 }
238 return freeId;
239 }
240
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800241 /**
242 * Stores a new group entry using the information from group description.
243 *
244 * @param groupDesc group description to be used to create group entry
245 */
246 @Override
247 public void storeGroupDescription(GroupDescription groupDesc) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800248 // Check if a group is existing with the same key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800249 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
250 return;
251 }
252
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800253 if (deviceAuditStatus.get(groupDesc.deviceId()) == null) {
254 // Device group audit has not completed yet
255 // Add this group description to pending group key table
256 // Create a group entry object with Dummy Group ID
257 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
258 group.setState(GroupState.WAITING_AUDIT_COMPLETE);
259 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
260 getPendingGroupKeyTable(groupDesc.deviceId());
261 pendingKeyTable.put(groupDesc.appCookie(), group);
262 return;
263 }
264
265 storeGroupDescriptionInternal(groupDesc);
266 }
267
268 private void storeGroupDescriptionInternal(GroupDescription groupDesc) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800269 // Check if a group is existing with the same key
270 if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) {
271 return;
272 }
273
Saurav Das100e3b82015-04-30 11:12:10 -0700274 GroupId id = null;
275 if (groupDesc.givenGroupId() == null) {
276 // Get a new group identifier
277 id = new DefaultGroupId(getFreeGroupIdValue(groupDesc.deviceId()));
278 } else {
279 id = new DefaultGroupId(groupDesc.givenGroupId());
280 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800281 // Create a group entry object
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800282 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800283 // Insert the newly created group entry into concurrent key and id maps
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800284 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
285 getGroupKeyTable(groupDesc.deviceId());
286 keyTable.put(groupDesc.appCookie(), group);
287 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
288 getGroupIdTable(groupDesc.deviceId());
289 idTable.put(id, group);
290 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
291 group));
292 }
293
294 /**
295 * Updates the existing group entry with the information
296 * from group description.
297 *
298 * @param deviceId the device ID
299 * @param oldAppCookie the current group key
300 * @param type update type
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800301 * @param newBuckets group buckets for updates
302 * @param newAppCookie optional new group key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800303 */
304 @Override
305 public void updateGroupDescription(DeviceId deviceId,
306 GroupKey oldAppCookie,
307 UpdateType type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800308 GroupBuckets newBuckets,
309 GroupKey newAppCookie) {
310 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800311 Group oldGroup = getGroup(deviceId, oldAppCookie);
312 if (oldGroup == null) {
313 return;
314 }
315
316 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
317 type,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800318 newBuckets);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800319 if (newBucketList != null) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800320 // Create a new group object from the old group
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800321 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800322 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800323 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
324 oldGroup.deviceId(),
325 oldGroup.type(),
326 updatedBuckets,
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800327 newCookie,
Saurav Das100e3b82015-04-30 11:12:10 -0700328 oldGroup.givenGroupId(),
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800329 oldGroup.appId());
330 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
331 updatedGroupDesc);
332 newGroup.setState(GroupState.PENDING_UPDATE);
333 newGroup.setLife(oldGroup.life());
334 newGroup.setPackets(oldGroup.packets());
335 newGroup.setBytes(oldGroup.bytes());
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800336 // Remove the old entry from maps and add new entry using new key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800337 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
338 getGroupKeyTable(oldGroup.deviceId());
339 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
340 getGroupIdTable(oldGroup.deviceId());
341 keyTable.remove(oldGroup.appCookie());
342 idTable.remove(oldGroup.id());
343 keyTable.put(newGroup.appCookie(), newGroup);
344 idTable.put(newGroup.id(), newGroup);
345 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_REQUESTED, newGroup));
346 }
347 }
348
349 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
350 UpdateType type,
351 GroupBuckets buckets) {
352 GroupBuckets oldBuckets = oldGroup.buckets();
Sho SHIMIZU7a4087b2015-09-10 09:23:16 -0700353 List<GroupBucket> newBucketList = new ArrayList<>(oldBuckets.buckets());
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800354 boolean groupDescUpdated = false;
355
356 if (type == UpdateType.ADD) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800357 // Check if the any of the new buckets are part of
358 // the old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800359 for (GroupBucket addBucket:buckets.buckets()) {
360 if (!newBucketList.contains(addBucket)) {
361 newBucketList.add(addBucket);
362 groupDescUpdated = true;
363 }
364 }
365 } else if (type == UpdateType.REMOVE) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800366 // Check if the to be removed buckets are part of the
367 // old bucket list
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800368 for (GroupBucket removeBucket:buckets.buckets()) {
369 if (newBucketList.contains(removeBucket)) {
370 newBucketList.remove(removeBucket);
371 groupDescUpdated = true;
372 }
373 }
374 }
375
376 if (groupDescUpdated) {
377 return newBucketList;
378 } else {
379 return null;
380 }
381 }
382
383 /**
384 * Triggers deleting the existing group entry.
385 *
386 * @param deviceId the device ID
387 * @param appCookie the group key
388 */
389 @Override
390 public void deleteGroupDescription(DeviceId deviceId,
391 GroupKey appCookie) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800392 // Check if a group is existing with the provided key
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800393 StoredGroupEntry existing = (groupEntriesByKey.get(deviceId) != null) ?
394 groupEntriesByKey.get(deviceId).get(appCookie) :
395 null;
396 if (existing == null) {
397 return;
398 }
399
400 synchronized (existing) {
401 existing.setState(GroupState.PENDING_DELETE);
402 }
403 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, existing));
404 }
405
406 /**
407 * Stores a new group entry, or updates an existing entry.
408 *
409 * @param group group entry
410 */
411 @Override
412 public void addOrUpdateGroupEntry(Group group) {
413 // check if this new entry is an update to an existing entry
414 StoredGroupEntry existing = (groupEntriesById.get(
415 group.deviceId()) != null) ?
416 groupEntriesById.get(group.deviceId()).get(group.id()) :
417 null;
418 GroupEvent event = null;
419
420 if (existing != null) {
421 synchronized (existing) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700422 for (GroupBucket bucket:group.buckets().buckets()) {
Sho SHIMIZU30d639b2015-05-05 09:30:35 -0700423 Optional<GroupBucket> matchingBucket =
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700424 existing.buckets().buckets()
425 .stream()
426 .filter((existingBucket)->(existingBucket.equals(bucket)))
427 .findFirst();
428 if (matchingBucket.isPresent()) {
429 ((StoredGroupBucketEntry) matchingBucket.
430 get()).setPackets(bucket.packets());
431 ((StoredGroupBucketEntry) matchingBucket.
432 get()).setBytes(bucket.bytes());
433 } else {
434 log.warn("addOrUpdateGroupEntry: No matching "
435 + "buckets to update stats");
436 }
437 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800438 existing.setLife(group.life());
439 existing.setPackets(group.packets());
440 existing.setBytes(group.bytes());
441 if (existing.state() == GroupState.PENDING_ADD) {
442 existing.setState(GroupState.ADDED);
443 event = new GroupEvent(Type.GROUP_ADDED, existing);
444 } else {
445 if (existing.state() == GroupState.PENDING_UPDATE) {
Srikanth Vavilapalli10e75cd2015-04-13 16:21:24 -0700446 existing.setState(GroupState.ADDED);
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800447 }
448 event = new GroupEvent(Type.GROUP_UPDATED, existing);
449 }
450 }
451 }
452
453 if (event != null) {
454 notifyDelegate(event);
455 }
456 }
457
458 /**
459 * Removes the group entry from store.
460 *
461 * @param group group entry
462 */
463 @Override
464 public void removeGroupEntry(Group group) {
465 StoredGroupEntry existing = (groupEntriesById.get(
466 group.deviceId()) != null) ?
467 groupEntriesById.get(group.deviceId()).get(group.id()) :
468 null;
469
470 if (existing != null) {
471 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
472 getGroupKeyTable(existing.deviceId());
473 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
474 getGroupIdTable(existing.deviceId());
475 idTable.remove(existing.id());
476 keyTable.remove(existing.appCookie());
477 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, existing));
478 }
479 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800480
481 @Override
Charles Chan0c7c43b2016-01-14 17:39:20 -0800482 public void purgeGroupEntry(DeviceId deviceId) {
483 Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove =
484 groupEntriesById.get(deviceId).entrySet();
485
486 groupEntriesById.remove(deviceId);
487 groupEntriesByKey.remove(deviceId);
488
489 entryPendingRemove.forEach(entry -> {
490 notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue()));
491 });
492 }
493
494 @Override
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800495 public void deviceInitialAuditCompleted(DeviceId deviceId,
496 boolean completed) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800497 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800498 if (completed) {
499 log.debug("deviceInitialAuditCompleted: AUDIT "
500 + "completed for device {}", deviceId);
501 deviceAuditStatus.put(deviceId, true);
502 // Execute all pending group requests
503 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
504 getPendingGroupKeyTable(deviceId);
505 for (Group group:pendingGroupRequests.values()) {
506 GroupDescription tmp = new DefaultGroupDescription(
507 group.deviceId(),
508 group.type(),
509 group.buckets(),
510 group.appCookie(),
Saurav Das100e3b82015-04-30 11:12:10 -0700511 group.givenGroupId(),
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800512 group.appId());
513 storeGroupDescriptionInternal(tmp);
514 }
515 getPendingGroupKeyTable(deviceId).clear();
516 } else {
517 if (deviceAuditStatus.get(deviceId)) {
518 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
519 + "status for device {}", deviceId);
520 deviceAuditStatus.put(deviceId, false);
521 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800522 }
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800523 }
524 }
525
526 @Override
527 public boolean deviceInitialAuditStatus(DeviceId deviceId) {
528 synchronized (deviceAuditStatus) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800529 return (deviceAuditStatus.get(deviceId) != null)
530 ? deviceAuditStatus.get(deviceId) : false;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800531 }
532 }
533
534 @Override
sangho7ff01812015-02-09 16:21:53 -0800535 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
536
537 StoredGroupEntry existing = (groupEntriesById.get(
538 deviceId) != null) ?
539 groupEntriesById.get(deviceId).get(operation.groupId()) :
540 null;
541
542 if (existing == null) {
543 log.warn("No group entry with ID {} found ", operation.groupId());
544 return;
545 }
546
547 switch (operation.opType()) {
548 case ADD:
549 notifyDelegate(new GroupEvent(Type.GROUP_ADD_FAILED, existing));
550 break;
551 case MODIFY:
552 notifyDelegate(new GroupEvent(Type.GROUP_UPDATE_FAILED, existing));
553 break;
554 case DELETE:
555 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_FAILED, existing));
556 break;
557 default:
558 log.warn("Unknown group operation type {}", operation.opType());
559 }
560
561 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
562 getGroupKeyTable(existing.deviceId());
563 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
564 getGroupIdTable(existing.deviceId());
565 idTable.remove(existing.id());
566 keyTable.remove(existing.appCookie());
567 }
568
569 @Override
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800570 public void addOrUpdateExtraneousGroupEntry(Group group) {
571 ConcurrentMap<GroupId, Group> extraneousIdTable =
572 getExtraneousGroupIdTable(group.deviceId());
573 extraneousIdTable.put(group.id(), group);
574 // Check the reference counter
575 if (group.referenceCount() == 0) {
576 notifyDelegate(new GroupEvent(Type.GROUP_REMOVE_REQUESTED, group));
577 }
578 }
579
580 @Override
581 public void removeExtraneousGroupEntry(Group group) {
582 ConcurrentMap<GroupId, Group> extraneousIdTable =
583 getExtraneousGroupIdTable(group.deviceId());
584 extraneousIdTable.remove(group.id());
585 }
586
587 @Override
588 public Iterable<Group> getExtraneousGroups(DeviceId deviceId) {
589 // flatten and make iterator unmodifiable
590 return FluentIterable.from(
591 getExtraneousGroupIdTable(deviceId).values());
592 }
sangho7ff01812015-02-09 16:21:53 -0800593
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700594 @Override
595 public void pushGroupMetrics(DeviceId deviceId,
596 Collection<Group> groupEntries) {
597 boolean deviceInitialAuditStatus =
598 deviceInitialAuditStatus(deviceId);
599 Set<Group> southboundGroupEntries =
600 Sets.newHashSet(groupEntries);
601 Set<Group> storedGroupEntries =
602 Sets.newHashSet(getGroups(deviceId));
603 Set<Group> extraneousStoredEntries =
604 Sets.newHashSet(getExtraneousGroups(deviceId));
605
606 log.trace("pushGroupMetrics: Displaying all ({}) "
607 + "southboundGroupEntries for device {}",
608 southboundGroupEntries.size(),
609 deviceId);
610 for (Iterator<Group> it = southboundGroupEntries.iterator(); it.hasNext();) {
611 Group group = it.next();
612 log.trace("Group {} in device {}", group, deviceId);
613 }
614
615 log.trace("Displaying all ({}) stored group entries for device {}",
616 storedGroupEntries.size(),
617 deviceId);
618 for (Iterator<Group> it1 = storedGroupEntries.iterator();
619 it1.hasNext();) {
620 Group group = it1.next();
621 log.trace("Stored Group {} for device {}", group, deviceId);
622 }
623
624 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
625 Group group = it2.next();
626 if (storedGroupEntries.remove(group)) {
627 // we both have the group, let's update some info then.
628 log.trace("Group AUDIT: group {} exists "
629 + "in both planes for device {}",
630 group.id(), deviceId);
631 groupAdded(group);
632 it2.remove();
633 }
634 }
635 for (Group group : southboundGroupEntries) {
636 if (getGroup(group.deviceId(), group.id()) != null) {
637 // There is a group existing with the same id
638 // It is possible that group update is
639 // in progress while we got a stale info from switch
640 if (!storedGroupEntries.remove(getGroup(
641 group.deviceId(), group.id()))) {
642 log.warn("Group AUDIT: Inconsistent state:"
643 + "Group exists in ID based table while "
644 + "not present in key based table");
645 }
646 } else {
647 // there are groups in the switch that aren't in the store
648 log.trace("Group AUDIT: extraneous group {} exists "
649 + "in data plane for device {}",
650 group.id(), deviceId);
651 extraneousStoredEntries.remove(group);
652 extraneousGroup(group);
653 }
654 }
655 for (Group group : storedGroupEntries) {
656 // there are groups in the store that aren't in the switch
657 log.trace("Group AUDIT: group {} missing "
658 + "in data plane for device {}",
659 group.id(), deviceId);
660 groupMissing(group);
661 }
662 for (Group group : extraneousStoredEntries) {
663 // there are groups in the extraneous store that
664 // aren't in the switch
665 log.trace("Group AUDIT: clearing extransoeus group {} "
666 + "from store for device {}",
667 group.id(), deviceId);
668 removeExtraneousGroupEntry(group);
669 }
670
671 if (!deviceInitialAuditStatus) {
672 log.debug("Group AUDIT: Setting device {} initial "
673 + "AUDIT completed", deviceId);
674 deviceInitialAuditCompleted(deviceId, true);
675 }
676 }
677
678 private void groupMissing(Group group) {
679 switch (group.state()) {
680 case PENDING_DELETE:
681 log.debug("Group {} delete confirmation from device {}",
682 group, group.deviceId());
683 removeGroupEntry(group);
684 break;
685 case ADDED:
686 case PENDING_ADD:
687 case PENDING_UPDATE:
688 log.debug("Group {} is in store but not on device {}",
689 group, group.deviceId());
690 StoredGroupEntry existing = (groupEntriesById.get(
691 group.deviceId()) != null) ?
692 groupEntriesById.get(group.deviceId()).get(group.id()) :
693 null;
694 log.trace("groupMissing: group "
695 + "entry {} in device {} moving "
696 + "from {} to PENDING_ADD",
697 existing.id(),
698 existing.deviceId(),
699 existing.state());
700 existing.setState(Group.GroupState.PENDING_ADD);
701 notifyDelegate(new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
702 group));
703 break;
704 default:
705 log.debug("Group {} has not been installed.", group);
706 break;
707 }
708 }
709
710 private void extraneousGroup(Group group) {
711 log.debug("Group {} is on device {} but not in store.",
712 group, group.deviceId());
713 addOrUpdateExtraneousGroupEntry(group);
714 }
715
716 private void groupAdded(Group group) {
717 log.trace("Group {} Added or Updated in device {}",
718 group, group.deviceId());
719 addOrUpdateGroupEntry(group);
720 }
Srikanth Vavilapalli0599d512015-01-30 12:57:56 -0800721}