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