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
diff --git a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
index 570383a..9162ff6 100644
--- a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
@@ -15,7 +15,6 @@
*/
package org.onosproject.net.flow.impl;
-import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -31,8 +30,9 @@
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
-import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.provider.AbstractListenerProviderRegistry;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
@@ -75,6 +75,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_ADD_REQUESTED;
import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVE_REQUESTED;
@@ -101,9 +102,14 @@
label = "Allow flow rules in switch not installed by ONOS")
private boolean allowExtraneousRules = ALLOW_EXTRANEOUS_RULES;
+ @Property(name = "purgeOnDisconnection", boolValue = false,
+ label = "Purge entries associated with a device when the device goes offline")
+ private boolean purgeOnDisconnection = false;
+
private final Logger log = getLogger(getClass());
private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate();
+ private final DeviceListener deviceListener = new InternalDeviceListener();
protected ExecutorService deviceInstallers =
Executors.newFixedThreadPool(32, groupedThreads("onos/flowservice", "device-installer-%d"));
@@ -130,13 +136,12 @@
@Activate
public void activate(ComponentContext context) {
- cfgService.registerProperties(getClass());
- idGenerator = coreService.getIdGenerator(FLOW_OP_TOPIC);
-
- modified(context);
-
store.setDelegate(delegate);
eventDispatcher.addSink(FlowRuleEvent.class, listenerRegistry);
+ deviceService.addListener(deviceListener);
+ cfgService.registerProperties(getClass());
+ idGenerator = coreService.getIdGenerator(FLOW_OP_TOPIC);
+ modified(context);
log.info("Started");
}
@@ -152,18 +157,59 @@
@Modified
public void modified(ComponentContext context) {
- if (context == null) {
- return;
+ if (context != null) {
+ readComponentConfiguration(context);
}
+ }
+ /**
+ * Extracts properties from the component configuration context.
+ *
+ * @param context the component context
+ */
+ private void readComponentConfiguration(ComponentContext context) {
Dictionary<?, ?> properties = context.getProperties();
+ Boolean flag;
- String s = Tools.get(properties, "allowExtraneousRules");
- allowExtraneousRules = Strings.isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_RULES : Boolean.valueOf(s);
-
- if (allowExtraneousRules) {
- log.info("Allowing flow rules not installed by ONOS");
+ flag = isPropertyEnabled(properties, "allowExtraneousRules");
+ if (flag == null) {
+ log.info("AllowExtraneousRules is not configured, " +
+ "using current value of {}", allowExtraneousRules);
+ } else {
+ allowExtraneousRules = flag;
+ log.info("Configured. AllowExtraneousRules is {}",
+ allowExtraneousRules ? "enabled" : "disabled");
}
+
+ flag = isPropertyEnabled(properties, "purgeOnDisconnection");
+ if (flag == null) {
+ log.info("PurgeOnDisconnection is not configured, " +
+ "using current value of {}", purgeOnDisconnection);
+ } else {
+ purgeOnDisconnection = flag;
+ log.info("Configured. PurgeOnDisconnection is {}",
+ purgeOnDisconnection ? "enabled" : "disabled");
+ }
+ }
+
+ /**
+ * Check property name is defined and set to true.
+ *
+ * @param properties properties to be looked up
+ * @param propertyName the name of the property to look up
+ * @return value when the propertyName is defined or return null
+ */
+ private static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
+ String propertyName) {
+ Boolean value = null;
+ try {
+ String s = (String) properties.get(propertyName);
+ value = isNullOrEmpty(s) ? null : s.trim().equals("true");
+ } catch (ClassCastException e) {
+ // No propertyName defined.
+ value = null;
+ }
+ return value;
}
@Override
@@ -613,4 +659,23 @@
checkPermission(FLOWRULE_READ);
return store.getTableStatistics(deviceId);
}
+
+ private class InternalDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ switch (event.type()) {
+ case DEVICE_REMOVED:
+ case DEVICE_AVAILABILITY_CHANGED:
+ DeviceId deviceId = event.subject().id();
+ if (!deviceService.isAvailable(deviceId)) {
+ if (purgeOnDisconnection) {
+ store.purgeFlowRule(deviceId);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
}
diff --git a/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java b/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java
index 96e9b19..d6158b5 100644
--- a/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java
+++ b/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java
@@ -18,9 +18,12 @@
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
+import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.net.provider.AbstractListenerProviderRegistry;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
@@ -43,11 +46,14 @@
import org.onosproject.net.group.GroupStore.UpdateType;
import org.onosproject.net.group.GroupStoreDelegate;
import org.onosproject.net.provider.AbstractProviderService;
+import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import java.util.Collection;
import java.util.Collections;
+import java.util.Dictionary;
+import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onosproject.security.AppGuard.checkPermission;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onosproject.security.AppPermission.Type.*;
@@ -75,21 +81,78 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ @Property(name = "purgeOnDisconnection", boolValue = false,
+ label = "Purge entries associated with a device when the device goes offline")
+ private boolean purgeOnDisconnection = false;
+
@Activate
- public void activate() {
+ public void activate(ComponentContext context) {
store.setDelegate(delegate);
eventDispatcher.addSink(GroupEvent.class, listenerRegistry);
deviceService.addListener(deviceListener);
+ cfgService.registerProperties(getClass());
+ modified(context);
log.info("Started");
}
@Deactivate
public void deactivate() {
+ cfgService.unregisterProperties(getClass(), false);
store.unsetDelegate(delegate);
eventDispatcher.removeSink(GroupEvent.class);
log.info("Stopped");
}
+ @Modified
+ public void modified(ComponentContext context) {
+ if (context != null) {
+ readComponentConfiguration(context);
+ }
+ }
+
+ /**
+ * Extracts properties from the component configuration context.
+ *
+ * @param context the component context
+ */
+ private void readComponentConfiguration(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ Boolean flag;
+
+ flag = isPropertyEnabled(properties, "purgeOnDisconnection");
+ if (flag == null) {
+ log.info("PurgeOnDisconnection is not configured, " +
+ "using current value of {}", purgeOnDisconnection);
+ } else {
+ purgeOnDisconnection = flag;
+ log.info("Configured. PurgeOnDisconnection is {}",
+ purgeOnDisconnection ? "enabled" : "disabled");
+ }
+ }
+
+ /**
+ * Check property name is defined and set to true.
+ *
+ * @param properties properties to be looked up
+ * @param propertyName the name of the property to look up
+ * @return value when the propertyName is defined or return null
+ */
+ private static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
+ String propertyName) {
+ Boolean value = null;
+ try {
+ String s = (String) properties.get(propertyName);
+ value = isNullOrEmpty(s) ? null : s.trim().equals("true");
+ } catch (ClassCastException e) {
+ // No propertyName defined.
+ value = null;
+ }
+ return value;
+ }
+
/**
* Create a group in the specified device with the provided parameters.
*
@@ -303,13 +366,17 @@
switch (event.type()) {
case DEVICE_REMOVED:
case DEVICE_AVAILABILITY_CHANGED:
- if (!deviceService.isAvailable(event.subject().id())) {
+ DeviceId deviceId = event.subject().id();
+ if (!deviceService.isAvailable(deviceId)) {
log.debug("Device {} became un available; clearing initial audit status",
event.type(), event.subject().id());
store.deviceInitialAuditCompleted(event.subject().id(), false);
+
+ if (purgeOnDisconnection) {
+ store.purgeGroupEntry(deviceId);
+ }
}
break;
-
default:
break;
}
diff --git a/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java b/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java
index 73ce393..27aaaf9 100644
--- a/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/group/impl/GroupManagerTest.java
@@ -32,6 +32,7 @@
import org.junit.Test;
import org.onlab.packet.MacAddress;
import org.onlab.packet.MplsLabel;
+import org.onosproject.cfg.ComponentConfigAdapter;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.core.DefaultGroupId;
@@ -89,11 +90,12 @@
mgr = new GroupManager();
groupService = mgr;
mgr.deviceService = new DeviceManager();
+ mgr.cfgService = new ComponentConfigAdapter();
mgr.store = new SimpleGroupStore();
injectEventDispatcher(mgr, new TestEventDispatcher());
providerRegistry = mgr;
- mgr.activate();
+ mgr.activate(null);
mgr.addListener(listener);
internalProvider = new TestGroupProvider(PID);