blob: 3c696ce2035db43f86889bebbb2a14affb85cd93 [file] [log] [blame]
/*
* 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);
}
}