Detangling incubator: virtual nets, tunnels, resource labels, oh my
- virtual networking moved to /apps/virtual; with CLI & REST API
- tunnels and labels moved to /apps/tunnel; with CLI & REST API; UI disabled for now
- protobuf/models moved to /core/protobuf/models
- defunct grpc/rpc registry stuff left under /graveyard
- compile dependencies on /incubator moved to respective modules for compilation
- run-time dependencies will need to be re-tested for dependent apps
- /graveyard will be removed in not-too-distant future
Change-Id: I0a0b995c635487edcf95a352f50dd162186b0b39
diff --git a/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualGroupStore.java b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualGroupStore.java
new file mode 100644
index 0000000..3c696ce
--- /dev/null
+++ b/apps/virtual/app/src/main/java/org/onosproject/incubator/net/virtual/store/impl/SimpleVirtualGroupStore.java
@@ -0,0 +1,764 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.incubator.net.virtual.store.impl;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Sets;
+import org.onosproject.core.GroupId;
+import org.onosproject.incubator.net.virtual.NetworkId;
+import org.onosproject.incubator.net.virtual.VirtualNetworkGroupStore;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupStoreDelegate;
+import org.onosproject.net.group.StoredGroupBucketEntry;
+import org.onosproject.net.group.StoredGroupEntry;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages inventory of virtual group entries using trivial in-memory implementation.
+ */
+@Component(immediate = true, service = VirtualNetworkGroupStore.class)
+public class SimpleVirtualGroupStore
+ extends AbstractVirtualStore<GroupEvent, GroupStoreDelegate>
+ implements VirtualNetworkGroupStore {
+
+ private final Logger log = getLogger(getClass());
+
+ private final int dummyId = 0xffffffff;
+ private final GroupId dummyGroupId = new GroupId(dummyId);
+
+ // inner Map is per device group table
+ private final ConcurrentMap<NetworkId,
+ ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>>
+ groupEntriesByKey = new ConcurrentHashMap<>();
+
+ private final ConcurrentMap<NetworkId,
+ ConcurrentMap<DeviceId, ConcurrentMap<GroupId, StoredGroupEntry>>>
+ groupEntriesById = new ConcurrentHashMap<>();
+
+ private final ConcurrentMap<NetworkId,
+ ConcurrentMap<DeviceId, ConcurrentMap<GroupKey, StoredGroupEntry>>>
+ pendingGroupEntriesByKey = new ConcurrentHashMap<>();
+
+ private final ConcurrentMap<NetworkId,
+ ConcurrentMap<DeviceId, ConcurrentMap<GroupId, Group>>>
+ extraneousGroupEntriesById = new ConcurrentHashMap<>();
+
+ private final ConcurrentMap<NetworkId, HashMap<DeviceId, Boolean>>
+ deviceAuditStatus = new ConcurrentHashMap<>();
+
+ private final AtomicInteger groupIdGen = new AtomicInteger();
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ groupEntriesByKey.clear();
+ groupEntriesById.clear();
+ log.info("Stopped");
+ }
+
+ /**
+ * Returns the group key table for specified device.
+ *
+ * @param networkId identifier of the virtual network
+ * @param deviceId identifier of the device
+ * @return Map representing group key table of given device.
+ */
+ private ConcurrentMap<GroupKey, StoredGroupEntry>
+ getGroupKeyTable(NetworkId networkId, DeviceId deviceId) {
+ groupEntriesByKey.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
+ return groupEntriesByKey.get(networkId)
+ .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
+ }
+
+ /**
+ * Returns the group id table for specified device.
+ *
+ * @param networkId identifier of the virtual network
+ * @param deviceId identifier of the device
+ * @return Map representing group key table of given device.
+ */
+ private ConcurrentMap<GroupId, StoredGroupEntry>
+ getGroupIdTable(NetworkId networkId, DeviceId deviceId) {
+ groupEntriesById.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
+ return groupEntriesById.get(networkId)
+ .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
+ }
+
+ /**
+ * Returns the pending group key table for specified device.
+ *
+ * @param networkId identifier of the virtual network
+ * @param deviceId identifier of the device
+ * @return Map representing group key table of given device.
+ */
+ private ConcurrentMap<GroupKey, StoredGroupEntry>
+ getPendingGroupKeyTable(NetworkId networkId, DeviceId deviceId) {
+ pendingGroupEntriesByKey.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
+ return pendingGroupEntriesByKey.get(networkId)
+ .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
+ }
+
+ /**
+ * Returns the extraneous group id table for specified device.
+ *
+ * @param networkId identifier of the virtual network
+ * @param deviceId identifier of the device
+ * @return Map representing group key table of given device.
+ */
+ private ConcurrentMap<GroupId, Group>
+ getExtraneousGroupIdTable(NetworkId networkId, DeviceId deviceId) {
+ extraneousGroupEntriesById.computeIfAbsent(networkId, n -> new ConcurrentHashMap<>());
+ return extraneousGroupEntriesById.get(networkId)
+ .computeIfAbsent(deviceId, k -> new ConcurrentHashMap<>());
+ }
+
+ @Override
+ public int getGroupCount(NetworkId networkId, DeviceId deviceId) {
+ return (groupEntriesByKey.get(networkId).get(deviceId) != null) ?
+ groupEntriesByKey.get(networkId).get(deviceId).size() : 0;
+ }
+
+ @Override
+ public Iterable<Group> getGroups(NetworkId networkId, DeviceId deviceId) {
+ // flatten and make iterator unmodifiable
+ return FluentIterable.from(getGroupKeyTable(networkId, deviceId).values())
+ .transform(input -> input);
+ }
+
+ @Override
+ public Group getGroup(NetworkId networkId, DeviceId deviceId, GroupKey appCookie) {
+ if (groupEntriesByKey.get(networkId) != null &&
+ groupEntriesByKey.get(networkId).get(deviceId) != null) {
+ return groupEntriesByKey.get(networkId).get(deviceId).get(appCookie);
+ }
+ return null;
+ }
+
+ @Override
+ public Group getGroup(NetworkId networkId, DeviceId deviceId, GroupId groupId) {
+ if (groupEntriesById.get(networkId) != null &&
+ groupEntriesById.get(networkId).get(deviceId) != null) {
+ return groupEntriesById.get(networkId).get(deviceId).get(groupId);
+ }
+ return null;
+ }
+
+ private int getFreeGroupIdValue(NetworkId networkId, DeviceId deviceId) {
+ int freeId = groupIdGen.incrementAndGet();
+
+ while (true) {
+ Group existing = null;
+ if (groupEntriesById.get(networkId) != null &&
+ groupEntriesById.get(networkId).get(deviceId) != null) {
+ existing = groupEntriesById.get(networkId).get(deviceId)
+ .get(new GroupId(freeId));
+ }
+
+ if (existing == null) {
+ if (extraneousGroupEntriesById.get(networkId) != null &&
+ extraneousGroupEntriesById.get(networkId).get(deviceId) != null) {
+ existing = extraneousGroupEntriesById.get(networkId).get(deviceId)
+ .get(new GroupId(freeId));
+ }
+ }
+
+ if (existing != null) {
+ freeId = groupIdGen.incrementAndGet();
+ } else {
+ break;
+ }
+ }
+ return freeId;
+ }
+
+ @Override
+ public void storeGroupDescription(NetworkId networkId, GroupDescription groupDesc) {
+ // Check if a group is existing with the same key
+ if (getGroup(networkId, groupDesc.deviceId(), groupDesc.appCookie()) != null) {
+ return;
+ }
+
+ if (deviceAuditStatus.get(networkId) == null ||
+ deviceAuditStatus.get(networkId).get(groupDesc.deviceId()) == null) {
+ // Device group audit has not completed yet
+ // Add this group description to pending group key table
+ // Create a group entry object with Dummy Group ID
+ StoredGroupEntry group = new DefaultGroup(dummyGroupId, groupDesc);
+ group.setState(Group.GroupState.WAITING_AUDIT_COMPLETE);
+ ConcurrentMap<GroupKey, StoredGroupEntry> pendingKeyTable =
+ getPendingGroupKeyTable(networkId, groupDesc.deviceId());
+ pendingKeyTable.put(groupDesc.appCookie(), group);
+ return;
+ }
+
+ storeGroupDescriptionInternal(networkId, groupDesc);
+ }
+
+ private void storeGroupDescriptionInternal(NetworkId networkId,
+ GroupDescription groupDesc) {
+ // Check if a group is existing with the same key
+ if (getGroup(networkId, groupDesc.deviceId(), groupDesc.appCookie()) != null) {
+ return;
+ }
+
+ GroupId id = null;
+ if (groupDesc.givenGroupId() == null) {
+ // Get a new group identifier
+ id = new GroupId(getFreeGroupIdValue(networkId, groupDesc.deviceId()));
+ } else {
+ id = new GroupId(groupDesc.givenGroupId());
+ }
+ // Create a group entry object
+ StoredGroupEntry group = new DefaultGroup(id, groupDesc);
+ // Insert the newly created group entry into concurrent key and id maps
+ ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
+ getGroupKeyTable(networkId, groupDesc.deviceId());
+ keyTable.put(groupDesc.appCookie(), group);
+ ConcurrentMap<GroupId, StoredGroupEntry> idTable =
+ getGroupIdTable(networkId, groupDesc.deviceId());
+ idTable.put(id, group);
+ notifyDelegate(networkId, new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
+ group));
+ }
+
+ @Override
+ public void updateGroupDescription(NetworkId networkId, DeviceId deviceId,
+ GroupKey oldAppCookie, UpdateType type,
+ GroupBuckets newBuckets, GroupKey newAppCookie) {
+ // Check if a group is existing with the provided key
+ Group oldGroup = getGroup(networkId, deviceId, oldAppCookie);
+ if (oldGroup == null) {
+ return;
+ }
+
+ List<GroupBucket> newBucketList = getUpdatedBucketList(oldGroup,
+ type,
+ newBuckets);
+ if (newBucketList != null) {
+ // Create a new group object from the old group
+ GroupBuckets updatedBuckets = new GroupBuckets(newBucketList);
+ GroupKey newCookie = (newAppCookie != null) ? newAppCookie : oldAppCookie;
+ GroupDescription updatedGroupDesc = new DefaultGroupDescription(
+ oldGroup.deviceId(),
+ oldGroup.type(),
+ updatedBuckets,
+ newCookie,
+ oldGroup.givenGroupId(),
+ oldGroup.appId());
+ StoredGroupEntry newGroup = new DefaultGroup(oldGroup.id(),
+ updatedGroupDesc);
+ newGroup.setState(Group.GroupState.PENDING_UPDATE);
+ newGroup.setLife(oldGroup.life());
+ newGroup.setPackets(oldGroup.packets());
+ newGroup.setBytes(oldGroup.bytes());
+
+ // Remove the old entry from maps and add new entry using new key
+ ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
+ getGroupKeyTable(networkId, oldGroup.deviceId());
+ ConcurrentMap<GroupId, StoredGroupEntry> idTable =
+ getGroupIdTable(networkId, oldGroup.deviceId());
+ keyTable.remove(oldGroup.appCookie());
+ idTable.remove(oldGroup.id());
+ keyTable.put(newGroup.appCookie(), newGroup);
+ idTable.put(newGroup.id(), newGroup);
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_UPDATE_REQUESTED,
+ newGroup));
+ }
+
+ }
+
+ private List<GroupBucket> getUpdatedBucketList(Group oldGroup,
+ UpdateType type,
+ GroupBuckets buckets) {
+ if (type == UpdateType.SET) {
+ return buckets.buckets();
+ }
+
+ List<GroupBucket> oldBuckets = oldGroup.buckets().buckets();
+ List<GroupBucket> updatedBucketList = new ArrayList<>();
+ boolean groupDescUpdated = false;
+
+ if (type == UpdateType.ADD) {
+ List<GroupBucket> newBuckets = buckets.buckets();
+
+ // Add old buckets that will not be updated and check if any will be updated.
+ for (GroupBucket oldBucket : oldBuckets) {
+ int newBucketIndex = newBuckets.indexOf(oldBucket);
+
+ if (newBucketIndex != -1) {
+ GroupBucket newBucket = newBuckets.get(newBucketIndex);
+ if (!newBucket.hasSameParameters(oldBucket)) {
+ // Bucket will be updated
+ groupDescUpdated = true;
+ }
+ } else {
+ // Old bucket will remain the same - add it.
+ updatedBucketList.add(oldBucket);
+ }
+ }
+
+ // Add all new buckets
+ updatedBucketList.addAll(newBuckets);
+ if (!oldBuckets.containsAll(newBuckets)) {
+ groupDescUpdated = true;
+ }
+
+ } else if (type == UpdateType.REMOVE) {
+ List<GroupBucket> bucketsToRemove = buckets.buckets();
+
+ // Check which old buckets should remain
+ for (GroupBucket oldBucket : oldBuckets) {
+ if (!bucketsToRemove.contains(oldBucket)) {
+ updatedBucketList.add(oldBucket);
+ } else {
+ groupDescUpdated = true;
+ }
+ }
+ }
+
+ if (groupDescUpdated) {
+ return updatedBucketList;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void deleteGroupDescription(NetworkId networkId, DeviceId deviceId,
+ GroupKey appCookie) {
+ // Check if a group is existing with the provided key
+ StoredGroupEntry existing = null;
+ if (groupEntriesByKey.get(networkId) != null &&
+ groupEntriesByKey.get(networkId).get(deviceId) != null) {
+ existing = groupEntriesByKey.get(networkId).get(deviceId).get(appCookie);
+ }
+
+ if (existing == null) {
+ return;
+ }
+
+ synchronized (existing) {
+ existing.setState(Group.GroupState.PENDING_DELETE);
+ }
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVE_REQUESTED, existing));
+ }
+
+ @Override
+ public void addOrUpdateGroupEntry(NetworkId networkId, Group group) {
+ // check if this new entry is an update to an existing entry
+ StoredGroupEntry existing = null;
+
+ if (groupEntriesById.get(networkId) != null &&
+ groupEntriesById.get(networkId).get(group.deviceId()) != null) {
+ existing = groupEntriesById
+ .get(networkId)
+ .get(group.deviceId())
+ .get(group.id());
+ }
+
+ GroupEvent event = null;
+
+ if (existing != null) {
+ synchronized (existing) {
+ for (GroupBucket bucket:group.buckets().buckets()) {
+ Optional<GroupBucket> matchingBucket =
+ existing.buckets().buckets()
+ .stream()
+ .filter((existingBucket) -> (existingBucket.equals(bucket)))
+ .findFirst();
+ if (matchingBucket.isPresent()) {
+ ((StoredGroupBucketEntry) matchingBucket.
+ get()).setPackets(bucket.packets());
+ ((StoredGroupBucketEntry) matchingBucket.
+ get()).setBytes(bucket.bytes());
+ } else {
+ log.warn("addOrUpdateGroupEntry: No matching "
+ + "buckets to update stats");
+ }
+ }
+ existing.setLife(group.life());
+ existing.setPackets(group.packets());
+ existing.setBytes(group.bytes());
+ if (existing.state() == Group.GroupState.PENDING_ADD) {
+ existing.setState(Group.GroupState.ADDED);
+ event = new GroupEvent(GroupEvent.Type.GROUP_ADDED, existing);
+ } else {
+ if (existing.state() == Group.GroupState.PENDING_UPDATE) {
+ existing.setState(Group.GroupState.ADDED);
+ }
+ event = new GroupEvent(GroupEvent.Type.GROUP_UPDATED, existing);
+ }
+ }
+ }
+
+ if (event != null) {
+ notifyDelegate(networkId, event);
+ }
+ }
+
+ @Override
+ public void removeGroupEntry(NetworkId networkId, Group group) {
+ StoredGroupEntry existing = null;
+ if (groupEntriesById.get(networkId) != null
+ && groupEntriesById.get(networkId).get(group.deviceId()) != null) {
+ existing = groupEntriesById
+ .get(networkId).get(group.deviceId()).get(group.id());
+ }
+
+ if (existing != null) {
+ ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
+ getGroupKeyTable(networkId, existing.deviceId());
+ ConcurrentMap<GroupId, StoredGroupEntry> idTable =
+ getGroupIdTable(networkId, existing.deviceId());
+ idTable.remove(existing.id());
+ keyTable.remove(existing.appCookie());
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVED, existing));
+ }
+ }
+
+ @Override
+ public void purgeGroupEntry(NetworkId networkId, DeviceId deviceId) {
+ if (groupEntriesById.get(networkId) != null) {
+ Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove =
+ groupEntriesById.get(networkId).get(deviceId).entrySet();
+ groupEntriesById.get(networkId).remove(deviceId);
+ groupEntriesByKey.get(networkId).remove(deviceId);
+
+ entryPendingRemove.forEach(entry -> {
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVED,
+ entry.getValue()));
+ });
+ }
+ }
+
+ @Override
+ public void purgeGroupEntries(NetworkId networkId) {
+ if (groupEntriesById.get(networkId) != null) {
+ groupEntriesById.get((networkId)).values().forEach(groupEntries -> {
+ groupEntries.entrySet().forEach(entry -> {
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVED,
+ entry.getValue()));
+ });
+ });
+
+ groupEntriesById.get(networkId).clear();
+ groupEntriesByKey.get(networkId).clear();
+ }
+ }
+
+ @Override
+ public void addOrUpdateExtraneousGroupEntry(NetworkId networkId, Group group) {
+ ConcurrentMap<GroupId, Group> extraneousIdTable =
+ getExtraneousGroupIdTable(networkId, group.deviceId());
+ extraneousIdTable.put(group.id(), group);
+ // Check the reference counter
+ if (group.referenceCount() == 0) {
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVE_REQUESTED, group));
+ }
+ }
+
+ @Override
+ public void removeExtraneousGroupEntry(NetworkId networkId, Group group) {
+ ConcurrentMap<GroupId, Group> extraneousIdTable =
+ getExtraneousGroupIdTable(networkId, group.deviceId());
+ extraneousIdTable.remove(group.id());
+ }
+
+ @Override
+ public Iterable<Group> getExtraneousGroups(NetworkId networkId, DeviceId deviceId) {
+ // flatten and make iterator unmodifiable
+ return FluentIterable.from(
+ getExtraneousGroupIdTable(networkId, deviceId).values());
+ }
+
+ @Override
+ public void deviceInitialAuditCompleted(NetworkId networkId, DeviceId deviceId,
+ boolean completed) {
+ deviceAuditStatus.computeIfAbsent(networkId, k -> new HashMap<>());
+
+ HashMap<DeviceId, Boolean> deviceAuditStatusByNetwork =
+ deviceAuditStatus.get(networkId);
+
+ synchronized (deviceAuditStatusByNetwork) {
+ if (completed) {
+ log.debug("deviceInitialAuditCompleted: AUDIT "
+ + "completed for device {}", deviceId);
+ deviceAuditStatusByNetwork.put(deviceId, true);
+ // Execute all pending group requests
+ ConcurrentMap<GroupKey, StoredGroupEntry> pendingGroupRequests =
+ getPendingGroupKeyTable(networkId, deviceId);
+ for (Group group:pendingGroupRequests.values()) {
+ GroupDescription tmp = new DefaultGroupDescription(
+ group.deviceId(),
+ group.type(),
+ group.buckets(),
+ group.appCookie(),
+ group.givenGroupId(),
+ group.appId());
+ storeGroupDescriptionInternal(networkId, tmp);
+ }
+ getPendingGroupKeyTable(networkId, deviceId).clear();
+ } else {
+ if (deviceAuditStatusByNetwork.get(deviceId)) {
+ log.debug("deviceInitialAuditCompleted: Clearing AUDIT "
+ + "status for device {}", deviceId);
+ deviceAuditStatusByNetwork.put(deviceId, false);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean deviceInitialAuditStatus(NetworkId networkId, DeviceId deviceId) {
+ deviceAuditStatus.computeIfAbsent(networkId, k -> new HashMap<>());
+
+ HashMap<DeviceId, Boolean> deviceAuditStatusByNetwork =
+ deviceAuditStatus.get(networkId);
+
+ synchronized (deviceAuditStatusByNetwork) {
+ return (deviceAuditStatusByNetwork.get(deviceId) != null)
+ ? deviceAuditStatusByNetwork.get(deviceId) : false;
+ }
+ }
+
+ @Override
+ public void groupOperationFailed(NetworkId networkId, DeviceId deviceId,
+ GroupOperation operation) {
+
+ StoredGroupEntry existing = null;
+ if (groupEntriesById.get(networkId) != null &&
+ groupEntriesById.get(networkId).get(deviceId) != null) {
+ existing = groupEntriesById.get(networkId).get(deviceId)
+ .get(operation.groupId());
+ }
+
+ if (existing == null) {
+ log.warn("No group entry with ID {} found ", operation.groupId());
+ return;
+ }
+
+ switch (operation.opType()) {
+ case ADD:
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_ADD_FAILED,
+ existing));
+ break;
+ case MODIFY:
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_UPDATE_FAILED,
+ existing));
+ break;
+ case DELETE:
+ notifyDelegate(networkId,
+ new GroupEvent(GroupEvent.Type.GROUP_REMOVE_FAILED,
+ existing));
+ break;
+ default:
+ log.warn("Unknown group operation type {}", operation.opType());
+ }
+
+ ConcurrentMap<GroupKey, StoredGroupEntry> keyTable =
+ getGroupKeyTable(networkId, existing.deviceId());
+ ConcurrentMap<GroupId, StoredGroupEntry> idTable =
+ getGroupIdTable(networkId, existing.deviceId());
+ idTable.remove(existing.id());
+ keyTable.remove(existing.appCookie());
+ }
+
+ @Override
+ public void pushGroupMetrics(NetworkId networkId, DeviceId deviceId,
+ Collection<Group> groupEntries) {
+ boolean deviceInitialAuditStatus =
+ deviceInitialAuditStatus(networkId, deviceId);
+ Set<Group> southboundGroupEntries =
+ Sets.newHashSet(groupEntries);
+ Set<Group> storedGroupEntries =
+ Sets.newHashSet(getGroups(networkId, deviceId));
+ Set<Group> extraneousStoredEntries =
+ Sets.newHashSet(getExtraneousGroups(networkId, deviceId));
+
+ if (log.isTraceEnabled()) {
+ log.trace("pushGroupMetrics: Displaying all ({}) "
+ + "southboundGroupEntries for device {}",
+ southboundGroupEntries.size(),
+ deviceId);
+ for (Group group : southboundGroupEntries) {
+ log.trace("Group {} in device {}", group, deviceId);
+ }
+
+ log.trace("Displaying all ({}) stored group entries for device {}",
+ storedGroupEntries.size(),
+ deviceId);
+ for (Group group : storedGroupEntries) {
+ log.trace("Stored Group {} for device {}", group, deviceId);
+ }
+ }
+
+ for (Iterator<Group> it2 = southboundGroupEntries.iterator(); it2.hasNext();) {
+ Group group = it2.next();
+ if (storedGroupEntries.remove(group)) {
+ // we both have the group, let's update some info then.
+ log.trace("Group AUDIT: group {} exists "
+ + "in both planes for device {}",
+ group.id(), deviceId);
+ groupAdded(networkId, group);
+ it2.remove();
+ }
+ }
+ for (Group group : southboundGroupEntries) {
+ if (getGroup(networkId, group.deviceId(), group.id()) != null) {
+ // There is a group existing with the same id
+ // It is possible that group update is
+ // in progress while we got a stale info from switch
+ if (!storedGroupEntries.remove(getGroup(
+ networkId, group.deviceId(), group.id()))) {
+ log.warn("Group AUDIT: Inconsistent state:"
+ + "Group exists in ID based table while "
+ + "not present in key based table");
+ }
+ } else {
+ // there are groups in the switch that aren't in the store
+ log.trace("Group AUDIT: extraneous group {} exists "
+ + "in data plane for device {}",
+ group.id(), deviceId);
+ extraneousStoredEntries.remove(group);
+ extraneousGroup(networkId, group);
+ }
+ }
+ for (Group group : storedGroupEntries) {
+ // there are groups in the store that aren't in the switch
+ log.trace("Group AUDIT: group {} missing "
+ + "in data plane for device {}",
+ group.id(), deviceId);
+ groupMissing(networkId, group);
+ }
+ for (Group group : extraneousStoredEntries) {
+ // there are groups in the extraneous store that
+ // aren't in the switch
+ log.trace("Group AUDIT: clearing extransoeus group {} "
+ + "from store for device {}",
+ group.id(), deviceId);
+ removeExtraneousGroupEntry(networkId, group);
+ }
+
+ if (!deviceInitialAuditStatus) {
+ log.debug("Group AUDIT: Setting device {} initial "
+ + "AUDIT completed", deviceId);
+ deviceInitialAuditCompleted(networkId, deviceId, true);
+ }
+ }
+
+ @Override
+ public void notifyOfFailovers(NetworkId networkId, Collection<Group> failoverGroups) {
+ List<GroupEvent> failoverEvents = new ArrayList<>();
+ failoverGroups.forEach(group -> {
+ if (group.type() == Group.Type.FAILOVER) {
+ failoverEvents.add(new GroupEvent(GroupEvent.Type.GROUP_BUCKET_FAILOVER, group));
+ }
+ });
+ notifyDelegate(networkId, failoverEvents);
+ }
+
+ private void groupMissing(NetworkId networkId, Group group) {
+ switch (group.state()) {
+ case PENDING_DELETE:
+ log.debug("Group {} delete confirmation from device {} " +
+ "of virtaual network {}",
+ group, group.deviceId(), networkId);
+ removeGroupEntry(networkId, group);
+ break;
+ case ADDED:
+ case PENDING_ADD:
+ case PENDING_UPDATE:
+ log.debug("Group {} is in store but not on device {}",
+ group, group.deviceId());
+ StoredGroupEntry existing = null;
+ if (groupEntriesById.get(networkId) != null &&
+ groupEntriesById.get(networkId).get(group.deviceId()) != null) {
+
+ existing = groupEntriesById.get(networkId)
+ .get(group.deviceId()).get(group.id());
+ }
+ if (existing == null) {
+ break;
+ }
+
+ log.trace("groupMissing: group "
+ + "entry {} in device {} moving "
+ + "from {} to PENDING_ADD",
+ existing.id(),
+ existing.deviceId(),
+ existing.state());
+ existing.setState(Group.GroupState.PENDING_ADD);
+ notifyDelegate(networkId, new GroupEvent(GroupEvent.Type.GROUP_ADD_REQUESTED,
+ group));
+ break;
+ default:
+ log.debug("Virtual network {} : Group {} has not been installed.",
+ networkId, group);
+ break;
+ }
+ }
+
+ private void extraneousGroup(NetworkId networkId, Group group) {
+ log.debug("Group {} is on device {} of virtual network{}, but not in store.",
+ group, group.deviceId(), networkId);
+ addOrUpdateExtraneousGroupEntry(networkId, group);
+ }
+
+ private void groupAdded(NetworkId networkId, Group group) {
+ log.trace("Group {} Added or Updated in device {} of virtual network {}",
+ group, group.deviceId(), networkId);
+ addOrUpdateGroupEntry(networkId, group);
+ }
+}