From 32a9c0b2d92a76e743e9e64eb2f1c6db2da77d12 Mon Sep 17 00:00:00 2001 From: Andrea Campanella Date: Fri, 27 Mar 2020 12:53:46 +0100 Subject: [PATCH] [ONOS-8087] Per device purgeOnDisconnection Flag Change-Id: I7cb1db12a4d910d70123f116107a898bf9e6d278 --- .../segmentrouting/SegmentRoutingManager.java | 4 --- .../config/DeviceConfiguration.java | 7 +++++ .../segmentrouting/IcmpHandlerTest.java | 1 + .../MockNetworkConfigRegistry.java | 23 ++++++++++++++ .../config/DeviceConfigurationTest.java | 22 ++++++++++++- .../net/config/basics/BasicDeviceConfig.java | 31 ++++++++++++++++++- .../net/flow/impl/FlowRuleManager.java | 14 ++++++++- .../net/group/impl/GroupManager.java | 19 +++++++++--- .../net/meter/impl/MeterManager.java | 18 +++++++++-- 9 files changed, 125 insertions(+), 14 deletions(-) diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java index a948bfde72..12bd9e4970 100644 --- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java +++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java @@ -486,10 +486,6 @@ public class SegmentRoutingManager implements SegmentRoutingService { l2TunnelHandler = new DefaultL2TunnelHandler(this); topologyHandler = new TopologyHandler(this); - compCfgService.preSetProperty("org.onosproject.net.group.impl.GroupManager", - "purgeOnDisconnection", "true", false); - compCfgService.preSetProperty("org.onosproject.net.flow.impl.FlowRuleManager", - "purgeOnDisconnection", "true", false); compCfgService.preSetProperty("org.onosproject.provider.host.impl.HostLocationProvider", "requestInterceptsEnabled", "false", false); compCfgService.preSetProperty("org.onosproject.net.neighbour.impl.NeighbourResolutionManager", diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java index 123da370b1..5013e83cfe 100644 --- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java +++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java @@ -35,6 +35,7 @@ import org.onosproject.net.DeviceId; import org.onosproject.net.HostId; import org.onosproject.net.PortNumber; import org.onosproject.net.config.ConfigException; +import org.onosproject.net.config.basics.BasicDeviceConfig; import org.onosproject.net.config.basics.InterfaceConfig; import org.onosproject.net.host.InterfaceIpAddress; import org.onosproject.net.intf.Interface; @@ -109,6 +110,12 @@ public class DeviceConfiguration implements DeviceProperties { Set deviceSubjects = srManager.cfgService.getSubjects(DeviceId.class, SegmentRoutingDeviceConfig.class); deviceSubjects.forEach(subject -> { + //Setting purge on disconnection flag for the device SR has control over. + // addConfig returns a config if it exists or creates a new one. + log.info("PurgeOnDisconnection set to true for device {}", subject); + BasicDeviceConfig basicDeviceConfig = srManager.cfgService.addConfig(subject, BasicDeviceConfig.class); + basicDeviceConfig.purgeOnDisconnection(true); + srManager.cfgService.applyConfig(subject, BasicDeviceConfig.class, basicDeviceConfig.node()); SegmentRoutingDeviceConfig config = srManager.cfgService.getConfig(subject, SegmentRoutingDeviceConfig.class); SegmentRouterInfo info = new SegmentRouterInfo(); diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/IcmpHandlerTest.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/IcmpHandlerTest.java index cc7c6a8267..68472de3b3 100644 --- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/IcmpHandlerTest.java +++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/IcmpHandlerTest.java @@ -139,6 +139,7 @@ public class IcmpHandlerTest { // Apply config MockNetworkConfigRegistry mockNetworkConfigRegistry = new MockNetworkConfigRegistry(); + mockNetworkConfigRegistry.applyConfig(remoteLeafConfig); mockNetworkConfigRegistry.applyConfig(remoteLeafPorts1Config); mockNetworkConfigRegistry.applyConfig(remoteLeafPorts2Config); diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockNetworkConfigRegistry.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockNetworkConfigRegistry.java index e18483e5be..12d57ea709 100644 --- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockNetworkConfigRegistry.java +++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/MockNetworkConfigRegistry.java @@ -16,10 +16,14 @@ package org.onosproject.segmentrouting; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +import org.onosproject.net.DeviceId; import org.onosproject.net.config.Config; import org.onosproject.net.config.NetworkConfigRegistryAdapter; +import org.onosproject.net.config.basics.BasicDeviceConfig; import java.util.Objects; import java.util.Set; @@ -43,6 +47,25 @@ class MockNetworkConfigRegistry extends NetworkConfigRegistryAdapter { return (C) c; } + @Override + public > C addConfig(S subject, Class configClass) { + Config c = configs.stream() + .filter(config -> subject.equals(config.subject())) + .filter(config -> configClass.equals(config.getClass())) + .findFirst().orElseGet(() -> { + if (configClass.equals(BasicDeviceConfig.class)) { + BasicDeviceConfig deviceConfig = new BasicDeviceConfig(); + ObjectMapper mapper = new ObjectMapper(); + deviceConfig.init((DeviceId) subject, ((DeviceId) subject).toString(), + JsonNodeFactory.instance.objectNode(), mapper, config -> { + }); + return deviceConfig; + } + return null; + }); + return (C) c; + } + @Override public > Set getSubjects(Class subject, Class configClass) { ImmutableSet.Builder builder = ImmutableSet.builder(); diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/config/DeviceConfigurationTest.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/config/DeviceConfigurationTest.java index 469def0bfe..1674f6c6f6 100644 --- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/config/DeviceConfigurationTest.java +++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/config/DeviceConfigurationTest.java @@ -18,6 +18,7 @@ package org.onosproject.segmentrouting.config; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.junit.Before; @@ -29,6 +30,7 @@ import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.PortNumber; import org.onosproject.net.config.NetworkConfigRegistry; +import org.onosproject.net.config.basics.BasicDeviceConfig; import org.onosproject.net.config.basics.InterfaceConfig; import org.onosproject.net.host.InterfaceIpAddress; import org.onosproject.net.intf.Interface; @@ -73,10 +75,12 @@ public class DeviceConfigurationTest { private DeviceConfiguration devConfig; + private NetworkConfigRegistry networkConfigService; + @Before public void setUp() throws Exception { InterfaceService interfaceService; - NetworkConfigRegistry networkConfigService; + networkConfigService = null; NeighbourResolutionService neighbourResolutionService; SegmentRoutingManager srManager; @@ -86,6 +90,10 @@ public class DeviceConfigurationTest { JsonNode jsonNode = mapper.readTree(jsonStream); SegmentRoutingDeviceConfig srDevConfig = new SegmentRoutingDeviceConfig(); srDevConfig.init(DEV1, CONFIG_KEY, jsonNode, mapper, config -> { }); + BasicDeviceConfig basicDeviceConfig = new BasicDeviceConfig(); + basicDeviceConfig.init(DEV1, DEV1.toString(), JsonNodeFactory.instance.objectNode(), mapper, config -> { }); + BasicDeviceConfig purgeOnDisconnectConfig = basicDeviceConfig.purgeOnDisconnection(true); + // Mock interface netcfg jsonStream = InterfaceConfig.class.getResourceAsStream("/interface1.json"); @@ -102,6 +110,12 @@ public class DeviceConfigurationTest { .andReturn(Sets.newHashSet(DEV1)).anyTimes(); expect(networkConfigService.getConfig(DEV1, SegmentRoutingDeviceConfig.class)) .andReturn(srDevConfig).anyTimes(); + expect(networkConfigService.addConfig(DEV1, BasicDeviceConfig.class)) + .andReturn(basicDeviceConfig).anyTimes(); + expect(networkConfigService.getConfig(DEV1, BasicDeviceConfig.class)) + .andReturn(basicDeviceConfig).anyTimes(); + expect(networkConfigService.applyConfig(DEV1, BasicDeviceConfig.class, purgeOnDisconnectConfig.node())) + .andReturn(purgeOnDisconnectConfig).anyTimes(); expect(networkConfigService.getSubjects(ConnectPoint.class, InterfaceConfig.class)) .andReturn(Sets.newHashSet(CP1, CP2)).anyTimes(); expect(networkConfigService.getConfig(CP1, InterfaceConfig.class)).andReturn(interfaceConfig1).anyTimes(); @@ -157,4 +171,10 @@ public class DeviceConfigurationTest { assertTrue(devConfig.inSameSubnet(DEV1, PREFIX2.address())); assertFalse(devConfig.inSameSubnet(DEV1, ROUTE1.address())); } + + @Test + public void getPurgeOnDisconnect() { + assertNotNull(networkConfigService.getConfig(DEV1, BasicDeviceConfig.class)); + assertTrue(networkConfigService.getConfig(DEV1, BasicDeviceConfig.class).purgeOnDisconnection()); + } } \ No newline at end of file diff --git a/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java b/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java index 1a7ffd611b..47c5375ccf 100644 --- a/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java +++ b/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java @@ -34,6 +34,7 @@ public final class BasicDeviceConfig extends BasicElementConfig { private static final String HW_VERSION = "hwVersion"; private static final String SW_VERSION = "swVersion"; private static final String SERIAL = "serial"; + private static final String PURGE_ON_DISCONNECT = "purgeOnDisconnection"; private static final String DEVICE_KEY_ID = "deviceKeyId"; private static final int DRIVER_MAX_LENGTH = 256; @@ -54,7 +55,7 @@ public final class BasicDeviceConfig extends BasicElementConfig { && hasOnlyFields(ALLOWED, NAME, LOC_TYPE, LATITUDE, LONGITUDE, GRID_Y, GRID_X, UI_TYPE, RACK_ADDRESS, OWNER, TYPE, DRIVER, ROLES, MANUFACTURER, HW_VERSION, SW_VERSION, SERIAL, - MANAGEMENT_ADDRESS, PIPECONF, DEVICE_KEY_ID) + MANAGEMENT_ADDRESS, PIPECONF, DEVICE_KEY_ID, PURGE_ON_DISCONNECT) && isValidLength(DRIVER, DRIVER_MAX_LENGTH) && isValidLength(MANUFACTURER, MANUFACTURER_MAX_LENGTH) && isValidLength(HW_VERSION, MANUFACTURER_MAX_LENGTH) @@ -252,6 +253,34 @@ public final class BasicDeviceConfig extends BasicElementConfig { deviceKeyId != null ? deviceKeyId.id() : null); } + /** + * Returns the device purgeOnDisconnection flag for this device. + * + * @return device purgeOnDisconnection, false if not set. + */ + public boolean purgeOnDisconnection() { + return get(PURGE_ON_DISCONNECT, false); + } + + /** + * Sets the purgeOnDisconnection flag for the device. + * + * @param purgeOnDisconnection purges flows, groups, meters on disconnection. + * @return self + */ + public BasicDeviceConfig purgeOnDisconnection(boolean purgeOnDisconnection) { + return (BasicDeviceConfig) setOrClear(PURGE_ON_DISCONNECT, purgeOnDisconnection); + } + + /** + * Returns if the device purgeOnDisconnection flag for this device has been explicitly configured. + * + * @return device purgeOnDisconnection explicitly configured, false if not. + */ + public boolean isPurgeOnDisconnectionConfigured() { + return hasField(PURGE_ON_DISCONNECT); + } + // TODO: device port meta-data to be configured via BasicPortsConfig // TODO: device credentials/keys; in a separate config 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 d601d8de44..dd7f8029e9 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 @@ -31,6 +31,8 @@ import org.onosproject.core.IdGenerator; import org.onosproject.mastership.MastershipService; import org.onosproject.net.Device; import org.onosproject.net.DeviceId; +import org.onosproject.net.config.NetworkConfigRegistry; +import org.onosproject.net.config.basics.BasicDeviceConfig; import org.onosproject.net.device.DeviceEvent; import org.onosproject.net.device.DeviceListener; import org.onosproject.net.device.DeviceService; @@ -174,6 +176,9 @@ public class FlowRuleManager @Reference(cardinality = ReferenceCardinality.MANDATORY) protected ClusterService clusterService; + @Reference(cardinality = ReferenceCardinality.MANDATORY) + protected NetworkConfigRegistry netCfgService; + @Activate public void activate(ComponentContext context) { store.setDelegate(delegate); @@ -828,7 +833,14 @@ public class FlowRuleManager case DEVICE_AVAILABILITY_CHANGED: DeviceId deviceId = event.subject().id(); if (!deviceService.isAvailable(deviceId)) { - if (purgeOnDisconnection) { + BasicDeviceConfig cfg = netCfgService.getConfig(deviceId, BasicDeviceConfig.class); + //if purgeOnDisconnection is set for the device or it's a global configuration + // lets remove the flows. Priority is given to the per device flag + boolean purge = cfg != null && cfg.isPurgeOnDisconnectionConfigured() ? + cfg.purgeOnDisconnection() : purgeOnDisconnection; + if (purge) { + log.info("PurgeOnDisconnection is requested for device {}, " + + "removing flows", deviceId); store.purgeFlowRule(deviceId); } } 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 47c8f855f2..e633a960c6 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 @@ -21,6 +21,8 @@ import org.onosproject.cfg.ComponentConfigService; import org.onosproject.core.ApplicationId; import org.onosproject.mastership.MastershipService; import org.onosproject.net.DeviceId; +import org.onosproject.net.config.NetworkConfigRegistry; +import org.onosproject.net.config.basics.BasicDeviceConfig; import org.onosproject.net.device.DeviceEvent; import org.onosproject.net.device.DeviceListener; import org.onosproject.net.device.DeviceService; @@ -114,6 +116,9 @@ public class GroupManager @Reference(cardinality = ReferenceCardinality.MANDATORY) protected MastershipService mastershipService; + @Reference(cardinality = ReferenceCardinality.MANDATORY) + protected NetworkConfigRegistry netCfgService; + /** Frequency (in seconds) for polling groups via fallback provider. */ private int fallbackGroupPollFrequency = GM_POLL_FREQUENCY_DEFAULT; @@ -169,11 +174,11 @@ public class GroupManager flag = Tools.isPropertyEnabled(properties, GM_PURGE_ON_DISCONNECTION); if (flag == null) { log.info("PurgeOnDisconnection is not configured, " + - "using current value of {}", purgeOnDisconnection); + "using current value of {}", purgeOnDisconnection); } else { purgeOnDisconnection = flag; log.info("Configured. PurgeOnDisconnection is {}", - purgeOnDisconnection ? "enabled" : "disabled"); + purgeOnDisconnection ? "enabled" : "disabled"); } String s = get(properties, GM_POLL_FREQUENCY); try { @@ -451,8 +456,14 @@ public class GroupManager log.debug("Device {} became unavailable for {}; clearing initial audit status", deviceId, event.type()); store.deviceInitialAuditCompleted(deviceId, false); - - if (purgeOnDisconnection) { + BasicDeviceConfig cfg = netCfgService.getConfig(deviceId, BasicDeviceConfig.class); + //if purgeOnDisconnection is set for the device or it's a global configuration + // lets remove the groups. + boolean purge = cfg != null && cfg.isPurgeOnDisconnectionConfigured() ? + cfg.purgeOnDisconnection() : purgeOnDisconnection; + if (purge) { + log.info("PurgeOnDisconnection is requested for device {}, " + + "removing groups", deviceId); store.purgeGroupEntry(deviceId); } } diff --git a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java index cdc3244dcb..03f488994e 100644 --- a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java +++ b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java @@ -20,6 +20,8 @@ import org.onlab.util.TriConsumer; import org.onosproject.cfg.ComponentConfigService; import org.onosproject.mastership.MastershipService; import org.onosproject.net.DeviceId; +import org.onosproject.net.config.NetworkConfigRegistry; +import org.onosproject.net.config.basics.BasicDeviceConfig; import org.onosproject.net.device.DeviceEvent; import org.onosproject.net.device.DeviceListener; import org.onosproject.net.device.DeviceService; @@ -114,6 +116,9 @@ public class MeterManager @Reference(cardinality = ReferenceCardinality.MANDATORY) protected MastershipService mastershipService; + @Reference(cardinality = ReferenceCardinality.MANDATORY) + protected NetworkConfigRegistry netCfgService; + /** Number of worker threads. */ private int numThreads = MM_NUM_THREADS_DEFAULT; @@ -189,11 +194,11 @@ public class MeterManager flag = Tools.isPropertyEnabled(properties, MM_PURGE_ON_DISCONNECTION); if (flag == null) { log.info("PurgeOnDisconnection is not configured," + - "using current value of {}", purgeOnDisconnection); + "using current value of {}", purgeOnDisconnection); } else { purgeOnDisconnection = flag; log.info("Configured. PurgeOnDisconnection is {}", - purgeOnDisconnection ? "enabled" : "disabled"); + purgeOnDisconnection ? "enabled" : "disabled"); } String s = get(properties, MM_FALLBACK_METER_POLL_FREQUENCY); @@ -442,7 +447,14 @@ public class MeterManager case DEVICE_AVAILABILITY_CHANGED: DeviceId deviceId = event.subject().id(); if (!deviceService.isAvailable(deviceId)) { - if (purgeOnDisconnection) { + BasicDeviceConfig cfg = netCfgService.getConfig(deviceId, BasicDeviceConfig.class); + //if purgeOnDisconnection is set for the device or it's a global configuration + // lets remove the meters. + boolean purge = cfg != null && cfg.isPurgeOnDisconnectionConfigured() ? + cfg.purgeOnDisconnection() : purgeOnDisconnection; + if (purge) { + log.info("PurgeOnDisconnection is requested for device {}, " + + "removing meters", deviceId); store.purgeMeter(deviceId); } }