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