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); |
157 | } | 162 | } |
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; | ||
173 | + | ||
174 | + flag = isPropertyEnabled(properties, "allowExtraneousRules"); | ||
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 | + } | ||
160 | 183 | ||
161 | - String s = Tools.get(properties, "allowExtraneousRules"); | 184 | + flag = isPropertyEnabled(properties, "purgeOnDisconnection"); |
162 | - allowExtraneousRules = Strings.isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_RULES : Boolean.valueOf(s); | 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