Committed by
Gerrit Code Review
CORD-394 Purge group/flow store when device goes offline
Stage 1: (this commit) Add a component config purgeOnDisconnection, which is false by default. When set to true, GroupManager and FlowManager will purge groups/flows associated with a device when the device goes offline. Stage 2: (upcoming commit) Enable these configs in SegmentRoutingManager Clean up group related information in SegmentRountingManager Change-Id: I46d047d690d4641e030f6cdd084ce16ac02d8919
Showing
11 changed files
with
256 additions
and
16 deletions
| ... | @@ -107,6 +107,13 @@ public interface FlowRuleStore extends Store<FlowRuleBatchEvent, FlowRuleStoreDe | ... | @@ -107,6 +107,13 @@ public interface FlowRuleStore extends Store<FlowRuleBatchEvent, FlowRuleStoreDe |
| 107 | FlowRuleEvent pendingFlowRule(FlowEntry rule); | 107 | FlowRuleEvent pendingFlowRule(FlowEntry rule); |
| 108 | 108 | ||
| 109 | /** | 109 | /** |
| 110 | + * Removes all flow entries of given device from store. | ||
| 111 | + * | ||
| 112 | + * @param deviceId device id | ||
| 113 | + */ | ||
| 114 | + void purgeFlowRule(DeviceId deviceId); | ||
| 115 | + | ||
| 116 | + /** | ||
| 110 | * Updates the flow table statistics of the specified device using | 117 | * Updates the flow table statistics of the specified device using |
| 111 | * the given statistics. | 118 | * the given statistics. |
| 112 | * | 119 | * | ... | ... |
| ... | @@ -118,6 +118,13 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> { | ... | @@ -118,6 +118,13 @@ public interface GroupStore extends Store<GroupEvent, GroupStoreDelegate> { |
| 118 | void removeGroupEntry(Group group); | 118 | void removeGroupEntry(Group group); |
| 119 | 119 | ||
| 120 | /** | 120 | /** |
| 121 | + * Removes all group entries of given device from store. | ||
| 122 | + * | ||
| 123 | + * @param deviceId device id | ||
| 124 | + */ | ||
| 125 | + void purgeGroupEntry(DeviceId deviceId); | ||
| 126 | + | ||
| 127 | + /** | ||
| 121 | * A group entry that is present in switch but not in the store. | 128 | * A group entry that is present in switch but not in the store. |
| 122 | * | 129 | * |
| 123 | * @param group group entry | 130 | * @param group group entry | ... | ... |
| ... | @@ -274,6 +274,10 @@ public class SimpleFlowRuleStore | ... | @@ -274,6 +274,10 @@ public class SimpleFlowRuleStore |
| 274 | return null; | 274 | return null; |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | + public void purgeFlowRule(DeviceId deviceId) { | ||
| 278 | + flowEntries.remove(deviceId); | ||
| 279 | + } | ||
| 280 | + | ||
| 277 | @Override | 281 | @Override |
| 278 | public void storeBatch( | 282 | public void storeBatch( |
| 279 | FlowRuleBatchOperation operation) { | 283 | FlowRuleBatchOperation operation) { | ... | ... |
| ... | @@ -23,6 +23,8 @@ import java.util.Collection; | ... | @@ -23,6 +23,8 @@ import java.util.Collection; |
| 23 | import java.util.HashMap; | 23 | import java.util.HashMap; |
| 24 | import java.util.Iterator; | 24 | import java.util.Iterator; |
| 25 | import java.util.List; | 25 | import java.util.List; |
| 26 | +import java.util.Map; | ||
| 27 | +import java.util.Map.Entry; | ||
| 26 | import java.util.Optional; | 28 | import java.util.Optional; |
| 27 | import java.util.Set; | 29 | import java.util.Set; |
| 28 | import java.util.concurrent.ConcurrentHashMap; | 30 | import java.util.concurrent.ConcurrentHashMap; |
| ... | @@ -477,6 +479,19 @@ public class SimpleGroupStore | ... | @@ -477,6 +479,19 @@ public class SimpleGroupStore |
| 477 | } | 479 | } |
| 478 | 480 | ||
| 479 | @Override | 481 | @Override |
| 482 | + public void purgeGroupEntry(DeviceId deviceId) { | ||
| 483 | + Set<Map.Entry<GroupId, StoredGroupEntry>> entryPendingRemove = | ||
| 484 | + groupEntriesById.get(deviceId).entrySet(); | ||
| 485 | + | ||
| 486 | + groupEntriesById.remove(deviceId); | ||
| 487 | + groupEntriesByKey.remove(deviceId); | ||
| 488 | + | ||
| 489 | + entryPendingRemove.forEach(entry -> { | ||
| 490 | + notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue())); | ||
| 491 | + }); | ||
| 492 | + } | ||
| 493 | + | ||
| 494 | + @Override | ||
| 480 | public void deviceInitialAuditCompleted(DeviceId deviceId, | 495 | public void deviceInitialAuditCompleted(DeviceId deviceId, |
| 481 | boolean completed) { | 496 | boolean completed) { |
| 482 | synchronized (deviceAuditStatus) { | 497 | synchronized (deviceAuditStatus) { | ... | ... |
| ... | @@ -16,6 +16,8 @@ | ... | @@ -16,6 +16,8 @@ |
| 16 | package org.onosproject.store.trivial; | 16 | package org.onosproject.store.trivial; |
| 17 | 17 | ||
| 18 | import static org.junit.Assert.assertEquals; | 18 | import static org.junit.Assert.assertEquals; |
| 19 | +import static org.junit.Assert.assertThat; | ||
| 20 | +import static org.hamcrest.Matchers.is; | ||
| 19 | import static org.onosproject.net.DeviceId.deviceId; | 21 | import static org.onosproject.net.DeviceId.deviceId; |
| 20 | 22 | ||
| 21 | import java.util.ArrayList; | 23 | import java.util.ArrayList; |
| ... | @@ -199,6 +201,11 @@ public class SimpleGroupStoreTest { | ... | @@ -199,6 +201,11 @@ public class SimpleGroupStoreTest { |
| 199 | 201 | ||
| 200 | // Testing removeGroupEntry operation from southbound | 202 | // Testing removeGroupEntry operation from southbound |
| 201 | testRemoveGroupFromSB(currKey); | 203 | testRemoveGroupFromSB(currKey); |
| 204 | + | ||
| 205 | + // Testing removing all groups on the given device | ||
| 206 | + newKey = new DefaultGroupKey("group1".getBytes()); | ||
| 207 | + testStoreAndGetGroup(newKey); | ||
| 208 | + testDeleteGroupOnDevice(newKey); | ||
| 202 | } | 209 | } |
| 203 | 210 | ||
| 204 | // Testing storeGroup operation | 211 | // Testing storeGroup operation |
| ... | @@ -376,6 +383,13 @@ public class SimpleGroupStoreTest { | ... | @@ -376,6 +383,13 @@ public class SimpleGroupStoreTest { |
| 376 | simpleGroupStore.unsetDelegate(deleteGroupDescDelegate); | 383 | simpleGroupStore.unsetDelegate(deleteGroupDescDelegate); |
| 377 | } | 384 | } |
| 378 | 385 | ||
| 386 | + // Testing deleteGroupDescription operation from northbound | ||
| 387 | + private void testDeleteGroupOnDevice(GroupKey currKey) { | ||
| 388 | + assertThat(simpleGroupStore.getGroupCount(D1), is(1)); | ||
| 389 | + simpleGroupStore.purgeGroupEntry(D1); | ||
| 390 | + assertThat(simpleGroupStore.getGroupCount(D1), is(0)); | ||
| 391 | + } | ||
| 392 | + | ||
| 379 | // Testing removeGroupEntry operation from southbound | 393 | // Testing removeGroupEntry operation from southbound |
| 380 | private void testRemoveGroupFromSB(GroupKey currKey) { | 394 | private void testRemoveGroupFromSB(GroupKey currKey) { |
| 381 | Group existingGroup = simpleGroupStore.getGroup(D1, currKey); | 395 | Group existingGroup = simpleGroupStore.getGroup(D1, currKey); | ... | ... |
| ... | @@ -15,7 +15,6 @@ | ... | @@ -15,7 +15,6 @@ |
| 15 | */ | 15 | */ |
| 16 | package org.onosproject.net.flow.impl; | 16 | package org.onosproject.net.flow.impl; |
| 17 | 17 | ||
| 18 | -import com.google.common.base.Strings; | ||
| 19 | import com.google.common.collect.ArrayListMultimap; | 18 | import com.google.common.collect.ArrayListMultimap; |
| 20 | import com.google.common.collect.Iterables; | 19 | import com.google.common.collect.Iterables; |
| 21 | import com.google.common.collect.Lists; | 20 | import com.google.common.collect.Lists; |
| ... | @@ -31,8 +30,9 @@ import org.apache.felix.scr.annotations.Property; | ... | @@ -31,8 +30,9 @@ import org.apache.felix.scr.annotations.Property; |
| 31 | import org.apache.felix.scr.annotations.Reference; | 30 | import org.apache.felix.scr.annotations.Reference; |
| 32 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 31 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
| 33 | import org.apache.felix.scr.annotations.Service; | 32 | import org.apache.felix.scr.annotations.Service; |
| 34 | -import org.onlab.util.Tools; | ||
| 35 | import org.onosproject.cfg.ComponentConfigService; | 33 | import org.onosproject.cfg.ComponentConfigService; |
| 34 | +import org.onosproject.net.device.DeviceEvent; | ||
| 35 | +import org.onosproject.net.device.DeviceListener; | ||
| 36 | import org.onosproject.net.provider.AbstractListenerProviderRegistry; | 36 | import org.onosproject.net.provider.AbstractListenerProviderRegistry; |
| 37 | import org.onosproject.core.ApplicationId; | 37 | import org.onosproject.core.ApplicationId; |
| 38 | import org.onosproject.core.CoreService; | 38 | import org.onosproject.core.CoreService; |
| ... | @@ -75,6 +75,7 @@ import java.util.concurrent.Executors; | ... | @@ -75,6 +75,7 @@ import java.util.concurrent.Executors; |
| 75 | import java.util.concurrent.atomic.AtomicBoolean; | 75 | import java.util.concurrent.atomic.AtomicBoolean; |
| 76 | 76 | ||
| 77 | import static com.google.common.base.Preconditions.checkNotNull; | 77 | import static com.google.common.base.Preconditions.checkNotNull; |
| 78 | +import static com.google.common.base.Strings.isNullOrEmpty; | ||
| 78 | import static org.onlab.util.Tools.groupedThreads; | 79 | import static org.onlab.util.Tools.groupedThreads; |
| 79 | import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_ADD_REQUESTED; | 80 | import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_ADD_REQUESTED; |
| 80 | import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVE_REQUESTED; | 81 | import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVE_REQUESTED; |
| ... | @@ -101,9 +102,14 @@ public class FlowRuleManager | ... | @@ -101,9 +102,14 @@ public class FlowRuleManager |
| 101 | label = "Allow flow rules in switch not installed by ONOS") | 102 | label = "Allow flow rules in switch not installed by ONOS") |
| 102 | private boolean allowExtraneousRules = ALLOW_EXTRANEOUS_RULES; | 103 | private boolean allowExtraneousRules = ALLOW_EXTRANEOUS_RULES; |
| 103 | 104 | ||
| 105 | + @Property(name = "purgeOnDisconnection", boolValue = false, | ||
| 106 | + label = "Purge entries associated with a device when the device goes offline") | ||
| 107 | + private boolean purgeOnDisconnection = false; | ||
| 108 | + | ||
| 104 | private final Logger log = getLogger(getClass()); | 109 | private final Logger log = getLogger(getClass()); |
| 105 | 110 | ||
| 106 | private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate(); | 111 | private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate(); |
| 112 | + private final DeviceListener deviceListener = new InternalDeviceListener(); | ||
| 107 | 113 | ||
| 108 | protected ExecutorService deviceInstallers = | 114 | protected ExecutorService deviceInstallers = |
| 109 | Executors.newFixedThreadPool(32, groupedThreads("onos/flowservice", "device-installer-%d")); | 115 | Executors.newFixedThreadPool(32, groupedThreads("onos/flowservice", "device-installer-%d")); |
| ... | @@ -130,13 +136,12 @@ public class FlowRuleManager | ... | @@ -130,13 +136,12 @@ public class FlowRuleManager |
| 130 | 136 | ||
| 131 | @Activate | 137 | @Activate |
| 132 | public void activate(ComponentContext context) { | 138 | public void activate(ComponentContext context) { |
| 139 | + store.setDelegate(delegate); | ||
| 140 | + eventDispatcher.addSink(FlowRuleEvent.class, listenerRegistry); | ||
| 141 | + deviceService.addListener(deviceListener); | ||
| 133 | cfgService.registerProperties(getClass()); | 142 | cfgService.registerProperties(getClass()); |
| 134 | idGenerator = coreService.getIdGenerator(FLOW_OP_TOPIC); | 143 | idGenerator = coreService.getIdGenerator(FLOW_OP_TOPIC); |
| 135 | - | ||
| 136 | modified(context); | 144 | modified(context); |
| 137 | - | ||
| 138 | - store.setDelegate(delegate); | ||
| 139 | - eventDispatcher.addSink(FlowRuleEvent.class, listenerRegistry); | ||
| 140 | log.info("Started"); | 145 | log.info("Started"); |
| 141 | } | 146 | } |
| 142 | 147 | ||
| ... | @@ -152,18 +157,59 @@ public class FlowRuleManager | ... | @@ -152,18 +157,59 @@ public class FlowRuleManager |
| 152 | 157 | ||
| 153 | @Modified | 158 | @Modified |
| 154 | public void modified(ComponentContext context) { | 159 | public void modified(ComponentContext context) { |
| 155 | - if (context == null) { | 160 | + if (context != null) { |
| 156 | - return; | 161 | + readComponentConfiguration(context); |
| 162 | + } | ||
| 157 | } | 163 | } |
| 158 | 164 | ||
| 165 | + /** | ||
| 166 | + * Extracts properties from the component configuration context. | ||
| 167 | + * | ||
| 168 | + * @param context the component context | ||
| 169 | + */ | ||
| 170 | + private void readComponentConfiguration(ComponentContext context) { | ||
| 159 | Dictionary<?, ?> properties = context.getProperties(); | 171 | Dictionary<?, ?> properties = context.getProperties(); |
| 172 | + Boolean flag; | ||
| 160 | 173 | ||
| 161 | - String s = Tools.get(properties, "allowExtraneousRules"); | 174 | + flag = isPropertyEnabled(properties, "allowExtraneousRules"); |
| 162 | - allowExtraneousRules = Strings.isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_RULES : Boolean.valueOf(s); | 175 | + if (flag == null) { |
| 176 | + log.info("AllowExtraneousRules is not configured, " + | ||
| 177 | + "using current value of {}", allowExtraneousRules); | ||
| 178 | + } else { | ||
| 179 | + allowExtraneousRules = flag; | ||
| 180 | + log.info("Configured. AllowExtraneousRules is {}", | ||
| 181 | + allowExtraneousRules ? "enabled" : "disabled"); | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + flag = isPropertyEnabled(properties, "purgeOnDisconnection"); | ||
| 185 | + if (flag == null) { | ||
| 186 | + log.info("PurgeOnDisconnection is not configured, " + | ||
| 187 | + "using current value of {}", purgeOnDisconnection); | ||
| 188 | + } else { | ||
| 189 | + purgeOnDisconnection = flag; | ||
| 190 | + log.info("Configured. PurgeOnDisconnection is {}", | ||
| 191 | + purgeOnDisconnection ? "enabled" : "disabled"); | ||
| 192 | + } | ||
| 193 | + } | ||
| 163 | 194 | ||
| 164 | - if (allowExtraneousRules) { | 195 | + /** |
| 165 | - log.info("Allowing flow rules not installed by ONOS"); | 196 | + * Check property name is defined and set to true. |
| 197 | + * | ||
| 198 | + * @param properties properties to be looked up | ||
| 199 | + * @param propertyName the name of the property to look up | ||
| 200 | + * @return value when the propertyName is defined or return null | ||
| 201 | + */ | ||
| 202 | + private static Boolean isPropertyEnabled(Dictionary<?, ?> properties, | ||
| 203 | + String propertyName) { | ||
| 204 | + Boolean value = null; | ||
| 205 | + try { | ||
| 206 | + String s = (String) properties.get(propertyName); | ||
| 207 | + value = isNullOrEmpty(s) ? null : s.trim().equals("true"); | ||
| 208 | + } catch (ClassCastException e) { | ||
| 209 | + // No propertyName defined. | ||
| 210 | + value = null; | ||
| 166 | } | 211 | } |
| 212 | + return value; | ||
| 167 | } | 213 | } |
| 168 | 214 | ||
| 169 | @Override | 215 | @Override |
| ... | @@ -613,4 +659,23 @@ public class FlowRuleManager | ... | @@ -613,4 +659,23 @@ public class FlowRuleManager |
| 613 | checkPermission(FLOWRULE_READ); | 659 | checkPermission(FLOWRULE_READ); |
| 614 | return store.getTableStatistics(deviceId); | 660 | return store.getTableStatistics(deviceId); |
| 615 | } | 661 | } |
| 662 | + | ||
| 663 | + private class InternalDeviceListener implements DeviceListener { | ||
| 664 | + @Override | ||
| 665 | + public void event(DeviceEvent event) { | ||
| 666 | + switch (event.type()) { | ||
| 667 | + case DEVICE_REMOVED: | ||
| 668 | + case DEVICE_AVAILABILITY_CHANGED: | ||
| 669 | + DeviceId deviceId = event.subject().id(); | ||
| 670 | + if (!deviceService.isAvailable(deviceId)) { | ||
| 671 | + if (purgeOnDisconnection) { | ||
| 672 | + store.purgeFlowRule(deviceId); | ||
| 673 | + } | ||
| 674 | + } | ||
| 675 | + break; | ||
| 676 | + default: | ||
| 677 | + break; | ||
| 678 | + } | ||
| 679 | + } | ||
| 680 | + } | ||
| 616 | } | 681 | } | ... | ... |
| ... | @@ -18,9 +18,12 @@ package org.onosproject.net.group.impl; | ... | @@ -18,9 +18,12 @@ package org.onosproject.net.group.impl; |
| 18 | import org.apache.felix.scr.annotations.Activate; | 18 | import org.apache.felix.scr.annotations.Activate; |
| 19 | import org.apache.felix.scr.annotations.Component; | 19 | import org.apache.felix.scr.annotations.Component; |
| 20 | import org.apache.felix.scr.annotations.Deactivate; | 20 | import org.apache.felix.scr.annotations.Deactivate; |
| 21 | +import org.apache.felix.scr.annotations.Modified; | ||
| 22 | +import org.apache.felix.scr.annotations.Property; | ||
| 21 | import org.apache.felix.scr.annotations.Reference; | 23 | import org.apache.felix.scr.annotations.Reference; |
| 22 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 24 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
| 23 | import org.apache.felix.scr.annotations.Service; | 25 | import org.apache.felix.scr.annotations.Service; |
| 26 | +import org.onosproject.cfg.ComponentConfigService; | ||
| 24 | import org.onosproject.net.provider.AbstractListenerProviderRegistry; | 27 | import org.onosproject.net.provider.AbstractListenerProviderRegistry; |
| 25 | import org.onosproject.core.ApplicationId; | 28 | import org.onosproject.core.ApplicationId; |
| 26 | import org.onosproject.net.DeviceId; | 29 | import org.onosproject.net.DeviceId; |
| ... | @@ -43,11 +46,14 @@ import org.onosproject.net.group.GroupStore; | ... | @@ -43,11 +46,14 @@ import org.onosproject.net.group.GroupStore; |
| 43 | import org.onosproject.net.group.GroupStore.UpdateType; | 46 | import org.onosproject.net.group.GroupStore.UpdateType; |
| 44 | import org.onosproject.net.group.GroupStoreDelegate; | 47 | import org.onosproject.net.group.GroupStoreDelegate; |
| 45 | import org.onosproject.net.provider.AbstractProviderService; | 48 | import org.onosproject.net.provider.AbstractProviderService; |
| 49 | +import org.osgi.service.component.ComponentContext; | ||
| 46 | import org.slf4j.Logger; | 50 | import org.slf4j.Logger; |
| 47 | 51 | ||
| 48 | import java.util.Collection; | 52 | import java.util.Collection; |
| 49 | import java.util.Collections; | 53 | import java.util.Collections; |
| 54 | +import java.util.Dictionary; | ||
| 50 | 55 | ||
| 56 | +import static com.google.common.base.Strings.isNullOrEmpty; | ||
| 51 | import static org.onosproject.security.AppGuard.checkPermission; | 57 | import static org.onosproject.security.AppGuard.checkPermission; |
| 52 | import static org.slf4j.LoggerFactory.getLogger; | 58 | import static org.slf4j.LoggerFactory.getLogger; |
| 53 | import static org.onosproject.security.AppPermission.Type.*; | 59 | import static org.onosproject.security.AppPermission.Type.*; |
| ... | @@ -75,21 +81,78 @@ public class GroupManager | ... | @@ -75,21 +81,78 @@ public class GroupManager |
| 75 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 81 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 76 | protected DeviceService deviceService; | 82 | protected DeviceService deviceService; |
| 77 | 83 | ||
| 84 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 85 | + protected ComponentConfigService cfgService; | ||
| 86 | + | ||
| 87 | + @Property(name = "purgeOnDisconnection", boolValue = false, | ||
| 88 | + label = "Purge entries associated with a device when the device goes offline") | ||
| 89 | + private boolean purgeOnDisconnection = false; | ||
| 90 | + | ||
| 78 | @Activate | 91 | @Activate |
| 79 | - public void activate() { | 92 | + public void activate(ComponentContext context) { |
| 80 | store.setDelegate(delegate); | 93 | store.setDelegate(delegate); |
| 81 | eventDispatcher.addSink(GroupEvent.class, listenerRegistry); | 94 | eventDispatcher.addSink(GroupEvent.class, listenerRegistry); |
| 82 | deviceService.addListener(deviceListener); | 95 | deviceService.addListener(deviceListener); |
| 96 | + cfgService.registerProperties(getClass()); | ||
| 97 | + modified(context); | ||
| 83 | log.info("Started"); | 98 | log.info("Started"); |
| 84 | } | 99 | } |
| 85 | 100 | ||
| 86 | @Deactivate | 101 | @Deactivate |
| 87 | public void deactivate() { | 102 | public void deactivate() { |
| 103 | + cfgService.unregisterProperties(getClass(), false); | ||
| 88 | store.unsetDelegate(delegate); | 104 | store.unsetDelegate(delegate); |
| 89 | eventDispatcher.removeSink(GroupEvent.class); | 105 | eventDispatcher.removeSink(GroupEvent.class); |
| 90 | log.info("Stopped"); | 106 | log.info("Stopped"); |
| 91 | } | 107 | } |
| 92 | 108 | ||
| 109 | + @Modified | ||
| 110 | + public void modified(ComponentContext context) { | ||
| 111 | + if (context != null) { | ||
| 112 | + readComponentConfiguration(context); | ||
| 113 | + } | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + /** | ||
| 117 | + * Extracts properties from the component configuration context. | ||
| 118 | + * | ||
| 119 | + * @param context the component context | ||
| 120 | + */ | ||
| 121 | + private void readComponentConfiguration(ComponentContext context) { | ||
| 122 | + Dictionary<?, ?> properties = context.getProperties(); | ||
| 123 | + Boolean flag; | ||
| 124 | + | ||
| 125 | + flag = isPropertyEnabled(properties, "purgeOnDisconnection"); | ||
| 126 | + if (flag == null) { | ||
| 127 | + log.info("PurgeOnDisconnection is not configured, " + | ||
| 128 | + "using current value of {}", purgeOnDisconnection); | ||
| 129 | + } else { | ||
| 130 | + purgeOnDisconnection = flag; | ||
| 131 | + log.info("Configured. PurgeOnDisconnection is {}", | ||
| 132 | + purgeOnDisconnection ? "enabled" : "disabled"); | ||
| 133 | + } | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + /** | ||
| 137 | + * Check property name is defined and set to true. | ||
| 138 | + * | ||
| 139 | + * @param properties properties to be looked up | ||
| 140 | + * @param propertyName the name of the property to look up | ||
| 141 | + * @return value when the propertyName is defined or return null | ||
| 142 | + */ | ||
| 143 | + private static Boolean isPropertyEnabled(Dictionary<?, ?> properties, | ||
| 144 | + String propertyName) { | ||
| 145 | + Boolean value = null; | ||
| 146 | + try { | ||
| 147 | + String s = (String) properties.get(propertyName); | ||
| 148 | + value = isNullOrEmpty(s) ? null : s.trim().equals("true"); | ||
| 149 | + } catch (ClassCastException e) { | ||
| 150 | + // No propertyName defined. | ||
| 151 | + value = null; | ||
| 152 | + } | ||
| 153 | + return value; | ||
| 154 | + } | ||
| 155 | + | ||
| 93 | /** | 156 | /** |
| 94 | * Create a group in the specified device with the provided parameters. | 157 | * Create a group in the specified device with the provided parameters. |
| 95 | * | 158 | * |
| ... | @@ -303,13 +366,17 @@ public class GroupManager | ... | @@ -303,13 +366,17 @@ public class GroupManager |
| 303 | switch (event.type()) { | 366 | switch (event.type()) { |
| 304 | case DEVICE_REMOVED: | 367 | case DEVICE_REMOVED: |
| 305 | case DEVICE_AVAILABILITY_CHANGED: | 368 | case DEVICE_AVAILABILITY_CHANGED: |
| 306 | - if (!deviceService.isAvailable(event.subject().id())) { | 369 | + DeviceId deviceId = event.subject().id(); |
| 370 | + if (!deviceService.isAvailable(deviceId)) { | ||
| 307 | log.debug("Device {} became un available; clearing initial audit status", | 371 | log.debug("Device {} became un available; clearing initial audit status", |
| 308 | event.type(), event.subject().id()); | 372 | event.type(), event.subject().id()); |
| 309 | store.deviceInitialAuditCompleted(event.subject().id(), false); | 373 | store.deviceInitialAuditCompleted(event.subject().id(), false); |
| 374 | + | ||
| 375 | + if (purgeOnDisconnection) { | ||
| 376 | + store.purgeGroupEntry(deviceId); | ||
| 377 | + } | ||
| 310 | } | 378 | } |
| 311 | break; | 379 | break; |
| 312 | - | ||
| 313 | default: | 380 | default: |
| 314 | break; | 381 | break; |
| 315 | } | 382 | } | ... | ... |
| ... | @@ -32,6 +32,7 @@ import org.junit.Before; | ... | @@ -32,6 +32,7 @@ import org.junit.Before; |
| 32 | import org.junit.Test; | 32 | import org.junit.Test; |
| 33 | import org.onlab.packet.MacAddress; | 33 | import org.onlab.packet.MacAddress; |
| 34 | import org.onlab.packet.MplsLabel; | 34 | import org.onlab.packet.MplsLabel; |
| 35 | +import org.onosproject.cfg.ComponentConfigAdapter; | ||
| 35 | import org.onosproject.core.ApplicationId; | 36 | import org.onosproject.core.ApplicationId; |
| 36 | import org.onosproject.core.DefaultApplicationId; | 37 | import org.onosproject.core.DefaultApplicationId; |
| 37 | import org.onosproject.core.DefaultGroupId; | 38 | import org.onosproject.core.DefaultGroupId; |
| ... | @@ -89,11 +90,12 @@ public class GroupManagerTest { | ... | @@ -89,11 +90,12 @@ public class GroupManagerTest { |
| 89 | mgr = new GroupManager(); | 90 | mgr = new GroupManager(); |
| 90 | groupService = mgr; | 91 | groupService = mgr; |
| 91 | mgr.deviceService = new DeviceManager(); | 92 | mgr.deviceService = new DeviceManager(); |
| 93 | + mgr.cfgService = new ComponentConfigAdapter(); | ||
| 92 | mgr.store = new SimpleGroupStore(); | 94 | mgr.store = new SimpleGroupStore(); |
| 93 | injectEventDispatcher(mgr, new TestEventDispatcher()); | 95 | injectEventDispatcher(mgr, new TestEventDispatcher()); |
| 94 | providerRegistry = mgr; | 96 | providerRegistry = mgr; |
| 95 | 97 | ||
| 96 | - mgr.activate(); | 98 | + mgr.activate(null); |
| 97 | mgr.addListener(listener); | 99 | mgr.addListener(listener); |
| 98 | 100 | ||
| 99 | internalProvider = new TestGroupProvider(PID); | 101 | internalProvider = new TestGroupProvider(PID); | ... | ... |
| ... | @@ -611,6 +611,11 @@ public class NewDistributedFlowRuleStore | ... | @@ -611,6 +611,11 @@ public class NewDistributedFlowRuleStore |
| 611 | } | 611 | } |
| 612 | 612 | ||
| 613 | @Override | 613 | @Override |
| 614 | + public void purgeFlowRule(DeviceId deviceId) { | ||
| 615 | + flowTable.purgeFlowRule(deviceId); | ||
| 616 | + } | ||
| 617 | + | ||
| 618 | + @Override | ||
| 614 | public void batchOperationComplete(FlowRuleBatchEvent event) { | 619 | public void batchOperationComplete(FlowRuleBatchEvent event) { |
| 615 | //FIXME: need a per device pending response | 620 | //FIXME: need a per device pending response |
| 616 | NodeId nodeId = pendingResponses.remove(event.subject().batchId()); | 621 | NodeId nodeId = pendingResponses.remove(event.subject().batchId()); |
| ... | @@ -827,6 +832,10 @@ public class NewDistributedFlowRuleStore | ... | @@ -827,6 +832,10 @@ public class NewDistributedFlowRuleStore |
| 827 | } | 832 | } |
| 828 | } | 833 | } |
| 829 | 834 | ||
| 835 | + public void purgeFlowRule(DeviceId deviceId) { | ||
| 836 | + flowEntries.remove(deviceId); | ||
| 837 | + } | ||
| 838 | + | ||
| 830 | private NodeId getBackupNode(DeviceId deviceId) { | 839 | private NodeId getBackupNode(DeviceId deviceId) { |
| 831 | List<NodeId> deviceStandbys = replicaInfoManager.getReplicaInfoFor(deviceId).backups(); | 840 | List<NodeId> deviceStandbys = replicaInfoManager.getReplicaInfoFor(deviceId).backups(); |
| 832 | // pick the standby which is most likely to become next master | 841 | // pick the standby which is most likely to become next master | ... | ... |
| ... | @@ -67,8 +67,10 @@ import java.util.ArrayList; | ... | @@ -67,8 +67,10 @@ import java.util.ArrayList; |
| 67 | import java.util.Collection; | 67 | import java.util.Collection; |
| 68 | import java.util.Collections; | 68 | import java.util.Collections; |
| 69 | import java.util.HashMap; | 69 | import java.util.HashMap; |
| 70 | +import java.util.HashSet; | ||
| 70 | import java.util.Iterator; | 71 | import java.util.Iterator; |
| 71 | import java.util.List; | 72 | import java.util.List; |
| 73 | +import java.util.Map.Entry; | ||
| 72 | import java.util.Objects; | 74 | import java.util.Objects; |
| 73 | import java.util.Optional; | 75 | import java.util.Optional; |
| 74 | import java.util.Set; | 76 | import java.util.Set; |
| ... | @@ -845,6 +847,21 @@ public class DistributedGroupStore | ... | @@ -845,6 +847,21 @@ public class DistributedGroupStore |
| 845 | } | 847 | } |
| 846 | 848 | ||
| 847 | @Override | 849 | @Override |
| 850 | + public void purgeGroupEntry(DeviceId deviceId) { | ||
| 851 | + Set<Entry<GroupStoreKeyMapKey, StoredGroupEntry>> entryPendingRemove = | ||
| 852 | + new HashSet<>(); | ||
| 853 | + | ||
| 854 | + groupStoreEntriesByKey.entrySet().stream() | ||
| 855 | + .filter(entry -> entry.getKey().deviceId().equals(deviceId)) | ||
| 856 | + .forEach(entryPendingRemove::add); | ||
| 857 | + | ||
| 858 | + entryPendingRemove.forEach(entry -> { | ||
| 859 | + groupStoreEntriesByKey.remove(entry.getKey()); | ||
| 860 | + notifyDelegate(new GroupEvent(Type.GROUP_REMOVED, entry.getValue())); | ||
| 861 | + }); | ||
| 862 | + } | ||
| 863 | + | ||
| 864 | + @Override | ||
| 848 | public void deviceInitialAuditCompleted(DeviceId deviceId, | 865 | public void deviceInitialAuditCompleted(DeviceId deviceId, |
| 849 | boolean completed) { | 866 | boolean completed) { |
| 850 | synchronized (deviceAuditStatus) { | 867 | synchronized (deviceAuditStatus) { | ... | ... |
| ... | @@ -71,8 +71,10 @@ public class DistributedGroupStoreTest { | ... | @@ -71,8 +71,10 @@ public class DistributedGroupStoreTest { |
| 71 | DeviceId deviceId2 = did("dev2"); | 71 | DeviceId deviceId2 = did("dev2"); |
| 72 | GroupId groupId1 = new DefaultGroupId(1); | 72 | GroupId groupId1 = new DefaultGroupId(1); |
| 73 | GroupId groupId2 = new DefaultGroupId(2); | 73 | GroupId groupId2 = new DefaultGroupId(2); |
| 74 | + GroupId groupId3 = new DefaultGroupId(3); | ||
| 74 | GroupKey groupKey1 = new DefaultGroupKey("abc".getBytes()); | 75 | GroupKey groupKey1 = new DefaultGroupKey("abc".getBytes()); |
| 75 | GroupKey groupKey2 = new DefaultGroupKey("def".getBytes()); | 76 | GroupKey groupKey2 = new DefaultGroupKey("def".getBytes()); |
| 77 | + GroupKey groupKey3 = new DefaultGroupKey("ghi".getBytes()); | ||
| 76 | 78 | ||
| 77 | TrafficTreatment treatment = | 79 | TrafficTreatment treatment = |
| 78 | DefaultTrafficTreatment.emptyTreatment(); | 80 | DefaultTrafficTreatment.emptyTreatment(); |
| ... | @@ -97,6 +99,13 @@ public class DistributedGroupStoreTest { | ... | @@ -97,6 +99,13 @@ public class DistributedGroupStoreTest { |
| 97 | groupKey2, | 99 | groupKey2, |
| 98 | groupId2.id(), | 100 | groupId2.id(), |
| 99 | APP_ID); | 101 | APP_ID); |
| 102 | + GroupDescription groupDescription3 = new DefaultGroupDescription( | ||
| 103 | + deviceId2, | ||
| 104 | + GroupDescription.Type.INDIRECT, | ||
| 105 | + buckets, | ||
| 106 | + groupKey3, | ||
| 107 | + groupId3.id(), | ||
| 108 | + APP_ID); | ||
| 100 | 109 | ||
| 101 | DistributedGroupStore groupStoreImpl; | 110 | DistributedGroupStore groupStoreImpl; |
| 102 | GroupStore groupStore; | 111 | GroupStore groupStore; |
| ... | @@ -202,6 +211,30 @@ public class DistributedGroupStoreTest { | ... | @@ -202,6 +211,30 @@ public class DistributedGroupStoreTest { |
| 202 | } | 211 | } |
| 203 | 212 | ||
| 204 | /** | 213 | /** |
| 214 | + * Tests removing all groups on the given device. | ||
| 215 | + */ | ||
| 216 | + @Test | ||
| 217 | + public void testRemoveGroupOnDevice() throws Exception { | ||
| 218 | + groupStore.deviceInitialAuditCompleted(deviceId1, true); | ||
| 219 | + assertThat(groupStore.deviceInitialAuditStatus(deviceId1), is(true)); | ||
| 220 | + groupStore.deviceInitialAuditCompleted(deviceId2, true); | ||
| 221 | + assertThat(groupStore.deviceInitialAuditStatus(deviceId2), is(true)); | ||
| 222 | + | ||
| 223 | + // Make sure the pending list starts out empty | ||
| 224 | + assertThat(auditPendingReqQueue.size(), is(0)); | ||
| 225 | + | ||
| 226 | + groupStore.storeGroupDescription(groupDescription1); | ||
| 227 | + groupStore.storeGroupDescription(groupDescription2); | ||
| 228 | + groupStore.storeGroupDescription(groupDescription3); | ||
| 229 | + assertThat(groupStore.getGroupCount(deviceId1), is(1)); | ||
| 230 | + assertThat(groupStore.getGroupCount(deviceId2), is(2)); | ||
| 231 | + | ||
| 232 | + groupStore.purgeGroupEntry(deviceId2); | ||
| 233 | + assertThat(groupStore.getGroupCount(deviceId1), is(1)); | ||
| 234 | + assertThat(groupStore.getGroupCount(deviceId2), is(0)); | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + /** | ||
| 205 | * Tests adding and removing a group. | 238 | * Tests adding and removing a group. |
| 206 | */ | 239 | */ |
| 207 | @Test | 240 | @Test | ... | ... |
-
Please register or login to post a comment