blob: d1ba60711d6eb1ec5791bfb83d67d817dab8472c [file] [log] [blame]
yoonseondc3210d2017-01-25 16:03:10 -08001/*
2 * Copyright 2017-present 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 */
16
17package org.onosproject.incubator.store.virtual.impl;
18
19import com.google.common.collect.FluentIterable;
20import com.google.common.collect.Sets;
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Service;
25import org.onosproject.core.DefaultGroupId;
26import org.onosproject.core.GroupId;
27import org.onosproject.incubator.net.virtual.NetworkId;
28import org.onosproject.incubator.net.virtual.VirtualNetworkGroupStore;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.group.DefaultGroup;
31import org.onosproject.net.group.DefaultGroupDescription;
32import org.onosproject.net.group.Group;
33import org.onosproject.net.group.GroupBucket;
34import org.onosproject.net.group.GroupBuckets;
35import org.onosproject.net.group.GroupDescription;
36import org.onosproject.net.group.GroupEvent;
37import org.onosproject.net.group.GroupKey;
38import org.onosproject.net.group.GroupOperation;
39import org.onosproject.net.group.GroupStoreDelegate;
40import org.onosproject.net.group.StoredGroupBucketEntry;
41import org.onosproject.net.group.StoredGroupEntry;
42import org.slf4j.Logger;
43
44import java.util.ArrayList;
45import java.util.Collection;
46import java.util.HashMap;
47import java.util.Iterator;
48import java.util.List;
49import java.util.Map;
50import java.util.Optional;
51import java.util.Set;
52import java.util.concurrent.ConcurrentHashMap;
53import java.util.concurrent.ConcurrentMap;
54import java.util.concurrent.atomic.AtomicInteger;
55
56import static org.slf4j.LoggerFactory.getLogger;
57
58/**
59 * Manages inventory of virtual group entries using trivial in-memory implementation.
60 */
61@Component(immediate = true)
62@Service
63public class SimpleVirtualGroupStore
64 extends AbstractVirtualStore<GroupEvent, GroupStoreDelegate>
65 implements VirtualNetworkGroupStore {
66
67 private final Logger log = getLogger(getClass());
68
69 private final int dummyId = 0xffffffff;
70 private final GroupId dummyGroupId = new DefaultGroupId(dummyId);
71
72 // inner Map is per device group table
73 private final ConcurrentMap<NetworkId,
74 ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>>
75 groupEntriesByKey = new ConcurrentHashMap<>();
76
77 private final ConcurrentMap<NetworkId,
78 ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>>
79 groupEntriesById = new ConcurrentHashMap<>();
80
81 private final ConcurrentMap<NetworkId,
82 ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>>
83 pendingGroupEntriesByKey = new ConcurrentHashMap<>();
84
85 private final ConcurrentMap<NetworkId,
86 ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>>
87 extraneousGroupEntriesById = new ConcurrentHashMap<>();
88
89 private final ConcurrentMap<NetworkId, HashMap<DeviceId, Boolean>>
90 deviceAuditStatus = new ConcurrentHashMap<>();
91
92 private final AtomicInteger groupIdGen = new AtomicInteger();
93
94 @Activate
95 public void activate() {
96 log.info("Started");
97 }
98
99 @Deactivate
100 public void deactivate() {
101 groupEntriesByKey.clear();
102 groupEntriesById.clear();
103 log.info("Stopped");
104 }
105
106 /**
107 * Returns the group key table for specified device.
108 *
109 * @param networkId identifier of the virtual network
110 * @param deviceId identifier of the device
111 * @return Map representing group key table of given device.
112 */
113 private ConcurrentMap<GroupKey, StoredGroupEntry>
114 getGroupKeyTable(NetworkId networkId, DeviceId deviceId) {
115 groupEntriesByKey.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
116 return groupEntriesByKey.get(networkId)
117 .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
118 }
119
120 /**
121 * Returns the group id table for specified device.
122 *
123 * @param networkId identifier of the virtual network
124 * @param deviceId identifier of the device
125 * @return Map representing group key table of given device.
126 */
127 private ConcurrentMap<GroupId, StoredGroupEntry>
128 getGroupIdTable(NetworkId networkId, DeviceId deviceId) {
129 groupEntriesById.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
130 return groupEntriesById.get(networkId)
131 .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
132 }
133
134 /**
135 * Returns the pending group key table for specified device.
136 *
137 * @param networkId identifier of the virtual network
138 * @param deviceId identifier of the device
139 * @return Map representing group key table of given device.
140 */
141 private ConcurrentMap<GroupKey, StoredGroupEntry>
142 getPendingGroupKeyTable(NetworkId networkId, DeviceId deviceId) {
143 pendingGroupEntriesByKey.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
144 return pendingGroupEntriesByKey.get(networkId)
145 .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
146 }
147
148 /**
149 * Returns the extraneous group id table for specified device.
150 *
151 * @param networkId identifier of the virtual network
152 * @param deviceId identifier of the device
153 * @return Map representing group key table of given device.
154 */
155 private ConcurrentMap<GroupId, Group>
156 getExtraneousGroupIdTable(NetworkId networkId, DeviceId deviceId) {
157 extraneousGroupEntriesById.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
158 return extraneousGroupEntriesById.get(networkId)
159 .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
160 }
161
162 @Override
163 public int getGroupCount(NetworkId networkId, DeviceId deviceId) {
164 return (groupEntriesByKey.get(networkId).get(deviceId) != null) ?
165 groupEntriesByKey.get(networkId).get(deviceId).size() : 0;
166 }
167
168 @Override
169 public Iterable<Group> getGroups(NetworkId networkId, DeviceId deviceId) {
170 // flatten and make iterator unmodifiable
171 return FluentIterable.from(getGroupKeyTable(networkId, deviceId).values())
172 .transform(input -> input);
173 }
174
175 @Override
176 public Group getGroup(NetworkId networkId, DeviceId deviceId, GroupKey appCookie) {
177 if (groupEntriesByKey.get(networkId) != null &&
178 groupEntriesByKey.get(networkId).get(deviceId) != null) {
179 return groupEntriesByKey.get(networkId).get(deviceId).get(appCookie);
180 }
181 return null;
182 }
183
184 @Override
185 public Group getGroup(NetworkId networkId, DeviceId deviceId, GroupId groupId) {
186 if (groupEntriesById.get(networkId) != null &&
187 groupEntriesById.get(networkId).get(deviceId) != null) {
188 return groupEntriesById.get(networkId).get(deviceId).get(groupId);
189 }
190 return null;
191 }
192
193 private int getFreeGroupIdValue(NetworkId networkId, DeviceId deviceId) {
194 int freeId = groupIdGen.incrementAndGet();
195
196 while (true) {
197 Group existing = null;
198 if (groupEntriesById.get(networkId) != null &&
199 groupEntriesById.get(networkId).get(deviceId) != null) {
200 existing = groupEntriesById.get(networkId).get(deviceId)
201 .get(new DefaultGroupId(freeId));
202 }
203
204 if (existing == null) {
205 if (extraneousGroupEntriesById.get(networkId) != null &&
206 extraneousGroupEntriesById.get(networkId).get(deviceId) != null) {
207 existing = extraneousGroupEntriesById.get(networkId).get(deviceId)
208 .get(new DefaultGroupId(freeId));
209 }
210 }
211
212 if (existing != null) {
213 freeId = groupIdGen.incrementAndGet();
214 } else {
215 break;
216 }
217 }
218 return freeId;
219 }
220
221 @Override
222 public void storeGroupDescription(NetworkId networkId, GroupDescription groupDesc) {
223 // Check if a group is existing with the same key
224 if (getGroup(networkId, groupDesc.deviceId(), groupDesc.appCookie()) != null) {
225 return;
226 }
227
228 if (deviceAuditStatus.get(networkId) == null ||
229 deviceAuditStatus.get(networkId).get(groupDesc.deviceId()) == null) {
230 // Device group audit has not completed yet
231 // Add this group description to pending group key table
232 // Create a group entry object with Dummy Group ID
233 StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
234 group.setState(Group.GroupState.WAITING_AUDIT_COMPLETE);
235 ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
236 getPendingGroupKeyTable(networkId, groupDesc.deviceId());
237 pendingKeyTable.put(groupDesc.appCookie(), group);
238 return;
239 }
240
241 storeGroupDescriptionInternal(networkId, groupDesc);
242 }
243
244 private void storeGroupDescriptionInternal(NetworkId networkId,
245 GroupDescription groupDesc) {
246 // Check if a group is existing with the same key
247 if (getGroup(networkId, groupDesc.deviceId(), groupDesc.appCookie()) != null) {
248 return;
249 }
250
251 GroupId id = null;
252 if (groupDesc.givenGroupId() == null) {
253 // Get a new group identifier
254 id = new DefaultGroupId(getFreeGroupIdValue(networkId, groupDesc.deviceId()));
255 } else {
256 id = new DefaultGroupId(groupDesc.givenGroupId());
257 }
258 // Create a group entry object
259 StoredGroupEntry group = new DefaultGroup(id, groupDesc);
260 // Insert the newly created group entry into concurrent key and id maps
261 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
262 getGroupKeyTable(networkId, groupDesc.deviceId());
263 keyTable.put(groupDesc.appCookie(), group);
264 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
265 getGroupIdTable(networkId, groupDesc.deviceId());
266 idTable.put(id, group);
267 notifyDelegate(networkId, new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
268 group));
269 }
270
271 @Override
272 public void updateGroupDescription(NetworkId networkId, DeviceId deviceId,
273 GroupKey oldAppCookie, UpdateType type,
274 GroupBuckets newBuckets, GroupKey newAppCookie) {
275 // Check if a group is existing with the provided key
276 Group oldGroup = getGroup(networkId, deviceId, oldAppCookie);
277 if (oldGroup == null) {
278 return;
279 }
280
281 List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
282 type,
283 newBuckets);
284 if (newBucketList != null) {
285 // Create a new group object from the old group
286 GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
287 GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
288 GroupDescription updatedGroupDesc = new DefaultGroupDescription(
289 oldGroup.deviceId(),
290 oldGroup.type(),
291 updatedBuckets,
292 newCookie,
293 oldGroup.givenGroupId(),
294 oldGroup.appId());
295 StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
296 updatedGroupDesc);
297 newGroup.setState(Group.GroupState.PENDING_UPDATE);
298 newGroup.setLife(oldGroup.life());
299 newGroup.setPackets(oldGroup.packets());
300 newGroup.setBytes(oldGroup.bytes());
301
302 // Remove the old entry from maps and add new entry using new key
303 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
304 getGroupKeyTable(networkId, oldGroup.deviceId());
305 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
306 getGroupIdTable(networkId, oldGroup.deviceId());
307 keyTable.remove(oldGroup.appCookie());
308 idTable.remove(oldGroup.id());
309 keyTable.put(newGroup.appCookie(), newGroup);
310 idTable.put(newGroup.id(), newGroup);
311 notifyDelegate(networkId,
312 new GroupEvent(GroupEvent.Type.GROUP_UPDATE_REQUESTED,
313 newGroup));
314 }
315
316 }
317
318 private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
319 UpdateType type,
320 GroupBuckets buckets) {
321 if (type == UpdateType.SET) {
322 return buckets.buckets();
323 }
324
325 List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
326 List<GroupBucket> updatedBucketList = new ArrayList<>();
327 boolean groupDescUpdated = false;
328
329 if (type == UpdateType.ADD) {
330 List<GroupBucket> newBuckets = buckets.buckets();
331
332 // Add old buckets that will not be updated and check if any will be updated.
333 for (GroupBucket oldBucket : oldBuckets) {
334 int newBucketIndex = newBuckets.indexOf(oldBucket);
335
336 if (newBucketIndex != -1) {
337 GroupBucket newBucket = newBuckets.get(newBucketIndex);
338 if (!newBucket.hasSameParameters(oldBucket)) {
339 // Bucket will be updated
340 groupDescUpdated = true;
341 }
342 } else {
343 // Old bucket will remain the same - add it.
344 updatedBucketList.add(oldBucket);
345 }
346 }
347
348 // Add all new buckets
349 updatedBucketList.addAll(newBuckets);
350 if (!oldBuckets.containsAll(newBuckets)) {
351 groupDescUpdated = true;
352 }
353
354 } else if (type == UpdateType.REMOVE) {
355 List<GroupBucket> bucketsToRemove = buckets.buckets();
356
357 // Check which old buckets should remain
358 for (GroupBucket oldBucket : oldBuckets) {
359 if (!bucketsToRemove.contains(oldBucket)) {
360 updatedBucketList.add(oldBucket);
361 } else {
362 groupDescUpdated = true;
363 }
364 }
365 }
366
367 if (groupDescUpdated) {
368 return updatedBucketList;
369 } else {
370 return null;
371 }
372 }
373
374 @Override
375 public void deleteGroupDescription(NetworkId networkId, DeviceId deviceId,
376 GroupKey appCookie) {
377 // Check if a group is existing with the provided key
378 StoredGroupEntry existing = null;
379 if (groupEntriesByKey.get(networkId) != null &&
380 groupEntriesByKey.get(networkId).get(deviceId) != null) {
381 existing = groupEntriesByKey.get(networkId).get(deviceId).get(appCookie);
382 }
383
384 if (existing == null) {
385 return;
386 }
387
388 synchronized (existing) {
389 existing.setState(Group.GroupState.PENDING_DELETE);
390 }
391 notifyDelegate(networkId,
392 new GroupEvent(GroupEvent.Type.GROUP_REMOVE_REQUESTED, existing));
393 }
394
395 @Override
396 public void addOrUpdateGroupEntry(NetworkId networkId, Group group) {
397 // check if this new entry is an update to an existing entry
398 StoredGroupEntry existing = null;
399
400 if (groupEntriesById.get(networkId) != null &&
401 groupEntriesById.get(networkId).get(group.deviceId()) != null) {
402 existing = groupEntriesById
403 .get(networkId)
404 .get(group.deviceId())
405 .get(group.id());
406 }
407
408 GroupEvent event = null;
409
410 if (existing != null) {
411 synchronized (existing) {
412 for (GroupBucket bucket:group.buckets().buckets()) {
413 Optional<GroupBucket> matchingBucket =
414 existing.buckets().buckets()
415 .stream()
416 .filter((existingBucket)->(existingBucket.equals(bucket)))
417 .findFirst();
418 if (matchingBucket.isPresent()) {
419 ((StoredGroupBucketEntry) matchingBucket.
420 get()).setPackets(bucket.packets());
421 ((StoredGroupBucketEntry) matchingBucket.
422 get()).setBytes(bucket.bytes());
423 } else {
424 log.warn("addOrUpdateGroupEntry: No matching "
425 + "buckets to update stats");
426 }
427 }
428 existing.setLife(group.life());
429 existing.setPackets(group.packets());
430 existing.setBytes(group.bytes());
431 if (existing.state() == Group.GroupState.PENDING_ADD) {
432 existing.setState(Group.GroupState.ADDED);
433 event = new GroupEvent(GroupEvent.Type.GROUP_ADDED, existing);
434 } else {
435 if (existing.state() == Group.GroupState.PENDING_UPDATE) {
436 existing.setState(Group.GroupState.ADDED);
437 }
438 event = new GroupEvent(GroupEvent.Type.GROUP_UPDATED, existing);
439 }
440 }
441 }
442
443 if (event != null) {
444 notifyDelegate(networkId, event);
445 }
446 }
447
448 @Override
449 public void removeGroupEntry(NetworkId networkId, Group group) {
450 StoredGroupEntry existing = null;
451 if (groupEntriesById.get(networkId) != null
452 && groupEntriesById.get(networkId).get(group.deviceId()) != null) {
453 existing = groupEntriesById
454 .get(networkId).get(group.deviceId()).get(group.id());
455 }
456
457 if (existing != null) {
458 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
459 getGroupKeyTable(networkId, existing.deviceId());
460 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
461 getGroupIdTable(networkId, existing.deviceId());
462 idTable.remove(existing.id());
463 keyTable.remove(existing.appCookie());
464 notifyDelegate(networkId,
465 new GroupEvent(GroupEvent.Type.GROUP_REMOVED, existing));
466 }
467 }
468
469 @Override
470 public void purgeGroupEntry(NetworkId networkId, DeviceId deviceId) {
471 if (groupEntriesById.get(networkId) != null) {
472 Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove =
473 groupEntriesById.get(networkId).get(deviceId).entrySet();
474 groupEntriesById.get(networkId).remove(deviceId);
475 groupEntriesByKey.get(networkId).remove(deviceId);
476
477 entryPendingRemove.forEach(entry -> {
478 notifyDelegate(networkId,
479 new GroupEvent(GroupEvent.Type.GROUP_REMOVED,
480 entry.getValue()));
481 });
482 }
483 }
484
485 @Override
486 public void purgeGroupEntries(NetworkId networkId) {
487 if (groupEntriesById.get(networkId) != null) {
488 groupEntriesById.get((networkId)).values().forEach(groupEntries -> {
489 groupEntries.entrySet().forEach(entry -> {
490 notifyDelegate(networkId,
491 new GroupEvent(GroupEvent.Type.GROUP_REMOVED,
492 entry.getValue()));
493 });
494 });
495
496 groupEntriesById.get(networkId).clear();
497 groupEntriesByKey.get(networkId).clear();
498 }
499 }
500
501 @Override
502 public void addOrUpdateExtraneousGroupEntry(NetworkId networkId, Group group) {
503 ConcurrentMap<GroupId, Group> extraneousIdTable =
504 getExtraneousGroupIdTable(networkId, group.deviceId());
505 extraneousIdTable.put(group.id(), group);
506 // Check the reference counter
507 if (group.referenceCount() == 0) {
508 notifyDelegate(networkId,
509 new GroupEvent(GroupEvent.Type.GROUP_REMOVE_REQUESTED, group));
510 }
511 }
512
513 @Override
514 public void removeExtraneousGroupEntry(NetworkId networkId, Group group) {
515 ConcurrentMap<GroupId, Group> extraneousIdTable =
516 getExtraneousGroupIdTable(networkId, group.deviceId());
517 extraneousIdTable.remove(group.id());
518 }
519
520 @Override
521 public Iterable<Group> getExtraneousGroups(NetworkId networkId, DeviceId deviceId) {
522 // flatten and make iterator unmodifiable
523 return FluentIterable.from(
524 getExtraneousGroupIdTable(networkId, deviceId).values());
525 }
526
527 @Override
528 public void deviceInitialAuditCompleted(NetworkId networkId, DeviceId deviceId,
529 boolean completed) {
530 deviceAuditStatus.computeIfAbsent(networkId, k -> new HashMap<>());
531
532 HashMap<DeviceId, Boolean> deviceAuditStatusByNetwork =
533 deviceAuditStatus.get(networkId);
534
535 synchronized (deviceAuditStatusByNetwork) {
536 if (completed) {
537 log.debug("deviceInitialAuditCompleted: AUDIT "
538 + "completed for device {}", deviceId);
539 deviceAuditStatusByNetwork.put(deviceId, true);
540 // Execute all pending group requests
541 ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
542 getPendingGroupKeyTable(networkId, deviceId);
543 for (Group group:pendingGroupRequests.values()) {
544 GroupDescription tmp = new DefaultGroupDescription(
545 group.deviceId(),
546 group.type(),
547 group.buckets(),
548 group.appCookie(),
549 group.givenGroupId(),
550 group.appId());
551 storeGroupDescriptionInternal(networkId, tmp);
552 }
553 getPendingGroupKeyTable(networkId, deviceId).clear();
554 } else {
555 if (deviceAuditStatusByNetwork.get(deviceId)) {
556 log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
557 + "status for device {}", deviceId);
558 deviceAuditStatusByNetwork.put(deviceId, false);
559 }
560 }
561 }
562 }
563
564 @Override
565 public boolean deviceInitialAuditStatus(NetworkId networkId, DeviceId deviceId) {
566 deviceAuditStatus.computeIfAbsent(networkId, k -> new HashMap<>());
567
568 HashMap<DeviceId, Boolean> deviceAuditStatusByNetwork =
569 deviceAuditStatus.get(networkId);
570
571 synchronized (deviceAuditStatusByNetwork) {
572 return (deviceAuditStatusByNetwork.get(deviceId) != null)
573 ? deviceAuditStatusByNetwork.get(deviceId) : false;
574 }
575 }
576
577 @Override
578 public void groupOperationFailed(NetworkId networkId, DeviceId deviceId,
579 GroupOperation operation) {
580
581 StoredGroupEntry existing = null;
582 if (groupEntriesById.get(networkId) != null &&
583 groupEntriesById.get(networkId).get(deviceId) != null) {
584 existing = groupEntriesById.get(networkId).get(deviceId)
585 .get(operation.groupId());
586 }
587
588 if (existing == null) {
589 log.warn("No group entry with ID {} found ", operation.groupId());
590 return;
591 }
592
593 switch (operation.opType()) {
594 case ADD:
595 notifyDelegate(networkId,
596 new GroupEvent(GroupEvent.Type.GROUP_ADD_FAILED,
597 existing));
598 break;
599 case MODIFY:
600 notifyDelegate(networkId,
601 new GroupEvent(GroupEvent.Type.GROUP_UPDATE_FAILED,
602 existing));
603 break;
604 case DELETE:
605 notifyDelegate(networkId,
606 new GroupEvent(GroupEvent.Type.GROUP_REMOVE_FAILED,
607 existing));
608 break;
609 default:
610 log.warn("Unknown group operation type {}", operation.opType());
611 }
612
613 ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
614 getGroupKeyTable(networkId, existing.deviceId());
615 ConcurrentMap<GroupId, StoredGroupEntry> idTable =
616 getGroupIdTable(networkId, existing.deviceId());
617 idTable.remove(existing.id());
618 keyTable.remove(existing.appCookie());
619 }
620
621 @Override
622 public void pushGroupMetrics(NetworkId networkId, DeviceId deviceId,
623 Collection<Group> groupEntries) {
624 boolean deviceInitialAuditStatus =
625 deviceInitialAuditStatus(networkId, deviceId);
626 Set<Group> southboundGroupEntries =
627 Sets.newHashSet(groupEntries);
628 Set<Group> storedGroupEntries =
629 Sets.newHashSet(getGroups(networkId, deviceId));
630 Set<Group> extraneousStoredEntries =
631 Sets.newHashSet(getExtraneousGroups(networkId, deviceId));
632
633 if (log.isTraceEnabled()) {
634 log.trace("pushGroupMetrics: Displaying all ({}) "
635 + "southboundGroupEntries for device {}",
636 southboundGroupEntries.size(),
637 deviceId);
638 for (Group group : southboundGroupEntries) {
639 log.trace("Group {} in device {}", group, deviceId);
640 }
641
642 log.trace("Displaying all ({}) stored group entries for device {}",
643 storedGroupEntries.size(),
644 deviceId);
645 for (Group group : storedGroupEntries) {
646 log.trace("Stored Group {} for device {}", group, deviceId);
647 }
648 }
649
650 for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
651 Group group = it2.next();
652 if (storedGroupEntries.remove(group)) {
653 // we both have the group, let's update some info then.
654 log.trace("Group AUDIT: group {} exists "
655 + "in both planes for device {}",
656 group.id(), deviceId);
657 groupAdded(networkId, group);
658 it2.remove();
659 }
660 }
661 for (Group group : southboundGroupEntries) {
662 if (getGroup(networkId, group.deviceId(), group.id()) != null) {
663 // There is a group existing with the same id
664 // It is possible that group update is
665 // in progress while we got a stale info from switch
666 if (!storedGroupEntries.remove(getGroup(
667 networkId, group.deviceId(), group.id()))) {
668 log.warn("Group AUDIT: Inconsistent state:"
669 + "Group exists in ID based table while "
670 + "not present in key based table");
671 }
672 } else {
673 // there are groups in the switch that aren't in the store
674 log.trace("Group AUDIT: extraneous group {} exists "
675 + "in data plane for device {}",
676 group.id(), deviceId);
677 extraneousStoredEntries.remove(group);
678 extraneousGroup(networkId, group);
679 }
680 }
681 for (Group group : storedGroupEntries) {
682 // there are groups in the store that aren't in the switch
683 log.trace("Group AUDIT: group {} missing "
684 + "in data plane for device {}",
685 group.id(), deviceId);
686 groupMissing(networkId, group);
687 }
688 for (Group group : extraneousStoredEntries) {
689 // there are groups in the extraneous store that
690 // aren't in the switch
691 log.trace("Group AUDIT: clearing extransoeus group {} "
692 + "from store for device {}",
693 group.id(), deviceId);
694 removeExtraneousGroupEntry(networkId, group);
695 }
696
697 if (!deviceInitialAuditStatus) {
698 log.debug("Group AUDIT: Setting device {} initial "
699 + "AUDIT completed", deviceId);
700 deviceInitialAuditCompleted(networkId, deviceId, true);
701 }
702 }
703
704 @Override
705 public void notifyOfFailovers(NetworkId networkId, Collection<Group> failoverGroups) {
706 List<GroupEvent> failoverEvents = new ArrayList<>();
707 failoverGroups.forEach(group -> {
708 if (group.type() == Group.Type.FAILOVER) {
709 failoverEvents.add(new GroupEvent(GroupEvent.Type.GROUP_BUCKET_FAILOVER, group));
710 }
711 });
712 notifyDelegate(networkId, failoverEvents);
713 }
714
715 private void groupMissing(NetworkId networkId, Group group) {
716 switch (group.state()) {
717 case PENDING_DELETE:
718 log.debug("Group {} delete confirmation from device {} " +
719 "of virtaual network {}",
720 group, group.deviceId(), networkId);
721 removeGroupEntry(networkId, group);
722 break;
723 case ADDED:
724 case PENDING_ADD:
725 case PENDING_UPDATE:
726 log.debug("Group {} is in store but not on device {}",
727 group, group.deviceId());
728 StoredGroupEntry existing = null;
729 if (groupEntriesById.get(networkId) != null &&
730 groupEntriesById.get(networkId).get(group.deviceId()) != null) {
731
732 existing = groupEntriesById.get(networkId)
733 .get(group.deviceId()).get(group.id());
734 }
735
736 log.trace("groupMissing: group "
737 + "entry {} in device {} moving "
738 + "from {} to PENDING_ADD",
739 existing.id(),
740 existing.deviceId(),
741 existing.state());
742 existing.setState(Group.GroupState.PENDING_ADD);
743 notifyDelegate(networkId, new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
744 group));
745 break;
746 default:
747 log.debug("Virtual network {} : Group {} has not been installed.",
748 networkId, group);
749 break;
750 }
751 }
752
753 private void extraneousGroup(NetworkId networkId, Group group) {
754 log.debug("Group {} is on device {} of virtual network{}, but not in store.",
755 group, group.deviceId(), networkId);
756 addOrUpdateExtraneousGroupEntry(networkId, group);
757 }
758
759 private void groupAdded(NetworkId networkId, Group group) {
760 log.trace("Group {} Added or Updated in device {} of virtual network {}",
761 group, group.deviceId(), networkId);
762 addOrUpdateGroupEntry(networkId, group);
763 }
764}