Naoki Shiota
Committed by Gerrit Code Review

[ONOS-3205] Migrate LLDP Link Discovery configuration to Network Configuration System

- deviceIds under suppression will be moved out to different location. (See ONOS-3461)

Change-Id: I6ebe0ce7f5f2d26e7ee7175974e19305f7c17fad
......@@ -333,6 +333,26 @@ public abstract class Config<S> {
}
/**
* Gets the specified array property as a list of items.
*
* @param name property name
* @param function mapper from string to item
* @param defaultValue default value if property not set
* @param <T> type of item
* @return list of items
*/
protected <T> List<T> getList(String name, Function<String, T> function, List<T> defaultValue) {
List<T> list = Lists.newArrayList();
JsonNode jsonNode = object.path(name);
if (jsonNode.isMissingNode()) {
return defaultValue;
}
ArrayNode arrayNode = (ArrayNode) jsonNode;
arrayNode.forEach(i -> list.add(function.apply(i.asText())));
return list;
}
/**
* Sets the specified property as an array of items in a given collection or
* clears it if null is given.
*
......
......@@ -55,5 +55,9 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>
......
......@@ -38,6 +38,9 @@ import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.LinkKey;
import org.onosproject.net.Port;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
......@@ -58,12 +61,12 @@ import org.onosproject.net.provider.ProviderId;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import java.io.IOException;
import java.util.Dictionary;
import java.util.EnumSet;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
......@@ -75,6 +78,7 @@ import static org.onlab.packet.Ethernet.TYPE_LLDP;
import static org.onlab.util.Tools.get;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.Link.Type.DIRECT;
import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
import static org.slf4j.LoggerFactory.getLogger;
/**
......@@ -87,12 +91,11 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
private static final String FORMAT =
"Settings: enabled={}, useBDDP={}, probeRate={}, " +
"staleLinkAge={}, lldpSuppression={}";
"staleLinkAge={}";
// When a Device/Port has this annotation, do not send out LLDP/BDDP
public static final String NO_LLDP = "no-lldp";
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
......@@ -152,13 +155,6 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
label = "Number of millis beyond which links will be considered stale")
private int staleLinkAge = DEFAULT_STALE_LINK_AGE;
// FIXME: convert to use network config subsystem instead
private static final String PROP_LLDP_SUPPRESSION = "lldpSuppression";
private static final String DEFAULT_LLDP_SUPPRESSION_CONFIG = "../config/lldp_suppression.json";
@Property(name = PROP_LLDP_SUPPRESSION, value = DEFAULT_LLDP_SUPPRESSION_CONFIG,
label = "Path to LLDP suppression configuration file")
private String lldpSuppression = DEFAULT_LLDP_SUPPRESSION_CONFIG;
private final DiscoveryContext context = new InternalDiscoveryContext();
private final InternalRoleListener roleListener = new InternalRoleListener();
private final InternalDeviceListener deviceListener = new InternalDeviceListener();
......@@ -171,9 +167,31 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
// destination connection point is mastered by this controller instance.
private final Map<LinkKey, Long> linkTimes = Maps.newConcurrentMap();
private SuppressionRules rules;
private ApplicationId appId;
static final SuppressionRules DEFAULT_RULES
= new SuppressionRules(ImmutableSet.of(),
EnumSet.of(Device.Type.ROADM),
ImmutableMap.of(NO_LLDP, SuppressionRules.ANY_VALUE));
private SuppressionRules rules = LldpLinkProvider.DEFAULT_RULES;
public static final String CONFIG_KEY = "suppression";
private final Set<ConfigFactory> factories = ImmutableSet.of(
new ConfigFactory<ApplicationId, SuppressionConfig>(APP_SUBJECT_FACTORY,
SuppressionConfig.class,
CONFIG_KEY) {
@Override
public SuppressionConfig createConfig() {
return new SuppressionConfig();
}
}
);
private final InternalConfigListener cfgListener = new InternalConfigListener();
/**
* Creates an OpenFlow link provider.
*/
......@@ -185,12 +203,30 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
public void activate(ComponentContext context) {
cfgService.registerProperties(getClass());
appId = coreService.registerApplication(PROVIDER_NAME);
cfgRegistry.addListener(cfgListener);
factories.forEach(cfgRegistry::registerConfigFactory);
SuppressionConfig cfg = cfgRegistry.getConfig(appId, SuppressionConfig.class);
if (cfg == null) {
// If no configuration is found, register default.
cfg = cfgRegistry.addConfig(appId, SuppressionConfig.class);
cfg.deviceIds(DEFAULT_RULES.getSuppressedDevice())
.deviceTypes(DEFAULT_RULES.getSuppressedDeviceType())
.annotation(DEFAULT_RULES.getSuppressedAnnotation())
.apply();
}
cfgListener.reconfigureSuppressionRules(cfg);
modified(context);
log.info("Started");
}
@Deactivate
public void deactivate() {
cfgRegistry.removeListener(cfgListener);
factories.forEach(cfgRegistry::unregisterConfigFactory);
cfgService.unregisterProperties(getClass(), false);
disable();
log.info("Stopped");
......@@ -202,7 +238,6 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
boolean newEnabled, newUseBddp;
int newProbeRate, newStaleLinkAge;
String newLldpSuppression;
try {
String s = get(properties, PROP_ENABLED);
newEnabled = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim());
......@@ -216,16 +251,12 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
s = get(properties, PROP_STALE_LINK_AGE);
newStaleLinkAge = isNullOrEmpty(s) ? staleLinkAge : Integer.parseInt(s.trim());
s = get(properties, PROP_LLDP_SUPPRESSION);
newLldpSuppression = isNullOrEmpty(s) ? DEFAULT_LLDP_SUPPRESSION_CONFIG : s;
} catch (NumberFormatException e) {
log.warn("Component configuration had invalid values", e);
newEnabled = enabled;
newUseBddp = useBddp;
newProbeRate = probeRate;
newStaleLinkAge = staleLinkAge;
newLldpSuppression = lldpSuppression;
}
boolean wasEnabled = enabled;
......@@ -234,23 +265,19 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
useBddp = newUseBddp;
probeRate = newProbeRate;
staleLinkAge = newStaleLinkAge;
lldpSuppression = newLldpSuppression;
if (!wasEnabled && enabled) {
enable();
} else if (wasEnabled && !enabled) {
disable();
} else {
// reflect changes in suppression rules to discovery helpers
// FIXME: After migrating to Network Configuration Subsystem,
// it should be possible to update only changed subset
if (enabled) {
// update all discovery helper state
loadDevices();
}
}
log.info(FORMAT, enabled, useBddp, probeRate, staleLinkAge, lldpSuppression);
log.info(FORMAT, enabled, useBddp, probeRate, staleLinkAge);
}
/**
......@@ -262,7 +289,6 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
deviceService.addListener(deviceListener);
packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0));
loadSuppressionRules();
loadDevices();
executor = newSingleThreadScheduledExecutor(groupedThreads("onos/link", "discovery-%d"));
......@@ -285,6 +311,7 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
deviceService.removeListener(deviceListener);
packetService.removeProcessor(packetProcessor);
if (executor != null) {
executor.shutdownNow();
}
......@@ -298,6 +325,9 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
* Loads available devices and registers their ports to be probed.
*/
private void loadDevices() {
if (!enabled) {
return;
}
deviceService.getAvailableDevices()
.forEach(d -> updateDevice(d)
.ifPresent(ld -> updatePorts(ld, d.id())));
......@@ -333,7 +363,6 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
private void removeDevice(final DeviceId deviceId) {
discoverers.computeIfPresent(deviceId, (did, ld) -> {
ld.stop();
providerService.linksVanished(deviceId);
return null;
});
......@@ -357,6 +386,7 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
// silently ignore logical ports
return;
}
if (rules.isSuppressed(port)) {
log.trace("LinkDiscovery from {} disabled by configuration", port);
removePort(port);
......@@ -383,35 +413,12 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
if (ld != null) {
ld.removePort(port.number());
}
ConnectPoint point = new ConnectPoint(d.id(), port.number());
providerService.linksVanished(point);
} else {
log.warn("Attempted to remove non-Device port", port);
}
}
/**
* Loads LLDP suppression rules.
*/
private void loadSuppressionRules() {
// FIXME: convert to use network configuration
SuppressionRulesStore store = new SuppressionRulesStore(lldpSuppression);
try {
log.info("Reading suppression rules from {}", lldpSuppression);
rules = store.read();
} catch (IOException e) {
log.info("Failed to load {}, using built-in rules", lldpSuppression);
// default rule to suppress ROADM to maintain compatibility
rules = new SuppressionRules(ImmutableSet.of(),
EnumSet.of(Device.Type.ROADM),
ImmutableMap.of(NO_LLDP, SuppressionRules.ANY_VALUE));
}
// should refresh discoverers when we need dynamic reconfiguration
}
/**
* Requests packet intercepts.
*/
private void requestIntercepts() {
......@@ -438,6 +445,17 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
}
protected SuppressionRules rules() {
return rules;
}
protected void updateRules(SuppressionRules newRules) {
if (!rules.equals(newRules)) {
rules = newRules;
loadDevices();
}
}
/**
* Processes device mastership role changes.
*/
......@@ -459,7 +477,6 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
updateDevice(device).ifPresent(ld -> updatePorts(ld, device.id()));
}
}
}
/**
......@@ -488,16 +505,21 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
} else {
log.debug("Port down {}", port);
removePort(port);
providerService.linksVanished(new ConnectPoint(port.element().id(),
port.number()));
}
break;
case PORT_REMOVED:
log.debug("Port removed {}", port);
removePort(port);
providerService.linksVanished(new ConnectPoint(port.element().id(),
port.number()));
break;
case DEVICE_REMOVED:
case DEVICE_SUSPENDED:
log.debug("Device removed {}", deviceId);
removeDevice(deviceId);
providerService.linksVanished(deviceId);
break;
case DEVICE_AVAILABILITY_CHANGED:
if (deviceService.isAvailable(deviceId)) {
......@@ -506,6 +528,7 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
} else {
log.debug("Device down {}", deviceId);
removeDevice(deviceId);
providerService.linksVanished(deviceId);
}
break;
case PORT_STATS_UPDATED:
......@@ -636,4 +659,30 @@ public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
}
}
private class InternalConfigListener implements NetworkConfigListener {
private synchronized void reconfigureSuppressionRules(SuppressionConfig cfg) {
if (cfg == null) {
log.error("Suppression Config is null.");
return;
}
SuppressionRules newRules = new SuppressionRules(cfg.deviceIds(),
cfg.deviceTypes(),
cfg.annotation());
updateRules(newRules);
}
@Override
public void event(NetworkConfigEvent event) {
if (event.configClass().equals(SuppressionConfig.class) &&
(event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) {
SuppressionConfig cfg = cfgRegistry.getConfig(appId, SuppressionConfig.class);
reconfigureSuppressionRules(cfg);
log.trace("Network config reconfigured");
}
}
}
}
......
/*
* Copyright 2014-2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.lldp.impl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.config.Config;
import org.slf4j.Logger;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.onosproject.provider.lldp.impl.LldpLinkProvider.DEFAULT_RULES;
import static org.slf4j.LoggerFactory.getLogger;
/**
* LLDP suppression config class.
*/
public class SuppressionConfig extends Config<ApplicationId> {
private static final String DEVICE_IDS = "deviceIds";
private static final String DEVICE_TYPES = "deviceTypes";
private static final String ANNOTATION = "annotation";
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final List<DeviceId> DEFAULT_DEVICE_IDS
= ImmutableList.copyOf(DEFAULT_RULES.getSuppressedDevice());
private static final List<Device.Type> DEFAULT_DEVICE_TYPES
= ImmutableList.copyOf(DEFAULT_RULES.getSuppressedDeviceType());
private final Logger log = getLogger(getClass());
/**
* Returns device IDs on which LLDP is suppressed.
*
* @return Set of DeviceId objects
*/
@Deprecated
public Set<DeviceId> deviceIds() {
return ImmutableSet.copyOf(getList(DEVICE_IDS, DeviceId::deviceId, DEFAULT_DEVICE_IDS));
}
/**
* Sets device IDs on which LLDP is suppressed.
*
* @param deviceIds new set of device IDs; null to clear
* @return self
*/
@Deprecated
public SuppressionConfig deviceIds(Set<DeviceId> deviceIds) {
return (SuppressionConfig) setOrClear(DEVICE_IDS, deviceIds);
}
/**
* Returns types of devices on which LLDP is suppressed.
*
* @return set of device types
*/
public Set<Device.Type> deviceTypes() {
return ImmutableSet.copyOf(getList(DEVICE_TYPES, Device.Type::valueOf, DEFAULT_DEVICE_TYPES));
}
/**
* Sets types of devices on which LLDP is suppressed.
*
* @param deviceTypes new set of device types; null to clear
* @return self
*/
public SuppressionConfig deviceTypes(Set<Device.Type> deviceTypes) {
return (SuppressionConfig) setOrClear(DEVICE_TYPES, deviceTypes);
}
/**
* Returns annotation of Ports on which LLDP is suppressed.
*
* @return key-value pairs of annotation
*/
public Map<String, String> annotation() {
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
String jsonAnnotation = get(ANNOTATION, null);
if (jsonAnnotation == null || jsonAnnotation.isEmpty()) {
return ImmutableMap.of();
}
JsonNode annotationNode;
try {
annotationNode = MAPPER.readTree(jsonAnnotation);
} catch (IOException e) {
log.error("Failed to read JSON tree from: {}", jsonAnnotation);
return ImmutableMap.of();
}
if (annotationNode.isObject()) {
ObjectNode obj = (ObjectNode) annotationNode;
Iterator<Map.Entry<String, JsonNode>> it = obj.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> entry = it.next();
final String key = entry.getKey();
final JsonNode value = entry.getValue();
if (value.isValueNode()) {
if (value.isNull()) {
builder.put(key, SuppressionRules.ANY_VALUE);
} else {
builder.put(key, value.asText());
}
} else {
log.warn("Encountered unexpected JSON field {} for annotation", entry);
}
}
} else {
log.error("Encountered unexpected JSONNode {} for annotation", annotationNode);
return ImmutableMap.of();
}
return builder.build();
}
/**
* Sets annotation of Ports on which LLDP is suppressed.
*
* @param annotation new key-value pair of annotation; null to clear
* @return self
*/
public SuppressionConfig annotation(Map<String, String> annotation) {
// ANY_VALUE should be null in JSON
Map<String, String> config = Maps.transformValues(annotation,
v -> (v == SuppressionRules.ANY_VALUE) ? null : v);
String jsonAnnotation = null;
try {
// TODO Store annotation as a Map instead of a String (which needs NetworkConfigRegistry modification)
jsonAnnotation = MAPPER.writeValueAsString(config);
} catch (JsonProcessingException e) {
log.error("Failed to write JSON from: {}", annotation);
}
return (SuppressionConfig) setOrClear(ANNOTATION, jsonAnnotation);
}
}
......@@ -18,6 +18,7 @@ package org.onosproject.provider.lldp.impl;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import org.onosproject.net.Annotations;
......@@ -28,6 +29,7 @@ import org.onosproject.net.Port;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.base.MoreObjects;
public class SuppressionRules {
......@@ -103,4 +105,34 @@ public class SuppressionRules {
Map<String, String> getSuppressedAnnotation() {
return suppressedAnnotation;
}
@Override
public int hashCode() {
return Objects.hash(suppressedDevice,
suppressedDeviceType,
suppressedAnnotation);
}
@Override
public boolean equals(Object object) {
if (object != null && getClass() == object.getClass()) {
SuppressionRules that = (SuppressionRules) object;
return Objects.equals(this.suppressedDevice,
that.suppressedDevice)
&& Objects.equals(this.suppressedDeviceType,
that.suppressedDeviceType)
&& Objects.equals(this.suppressedAnnotation,
that.suppressedAnnotation);
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("suppressedDevice", suppressedDevice)
.add("suppressedDeviceType", suppressedDeviceType)
.add("suppressedAnnotation", suppressedAnnotation)
.toString();
}
}
......
/*
* Copyright 2014-2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.lldp.impl;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.slf4j.Logger;
import java.io.File;
import java.io.IOException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/*
* JSON file example
*
{
"deviceId" : [ "of:2222000000000000" ],
"deviceType" : [ "ROADM" ],
"annotation" : { "no-lldp" : null, "sendLLDP" : "false" }
}
*/
/**
* Allows for reading and writing LLDP suppression definition as a JSON file.
*/
public class SuppressionRulesStore {
private static final String DEVICE_ID = "deviceId";
private static final String DEVICE_TYPE = "deviceType";
private static final String ANNOTATION = "annotation";
private final Logger log = getLogger(getClass());
private final File file;
/**
* Creates a reader/writer of the LLDP suppression definition file.
*
* @param filePath location of the definition file
*/
public SuppressionRulesStore(String filePath) {
file = new File(filePath);
}
/**
* Creates a reader/writer of the LLDP suppression definition file.
*
* @param file definition file
*/
public SuppressionRulesStore(File file) {
this.file = checkNotNull(file);
}
/**
* Returns SuppressionRules.
*
* @return SuppressionRules
* @throws IOException if error occurred while reading the data
*/
public SuppressionRules read() throws IOException {
final Set<DeviceId> suppressedDevice = new HashSet<>();
final EnumSet<Device.Type> suppressedDeviceType = EnumSet.noneOf(Device.Type.class);
final Map<String, String> suppressedAnnotation = new HashMap<>();
ObjectMapper mapper = new ObjectMapper();
ObjectNode root = (ObjectNode) mapper.readTree(file);
for (JsonNode deviceId : root.get(DEVICE_ID)) {
if (deviceId.isTextual()) {
suppressedDevice.add(DeviceId.deviceId(deviceId.asText()));
} else {
log.warn("Encountered unexpected JSONNode {} for deviceId", deviceId);
}
}
for (JsonNode deviceType : root.get(DEVICE_TYPE)) {
if (deviceType.isTextual()) {
suppressedDeviceType.add(Device.Type.valueOf(deviceType.asText()));
} else {
log.warn("Encountered unexpected JSONNode {} for deviceType", deviceType);
}
}
JsonNode annotation = root.get(ANNOTATION);
if (annotation.isObject()) {
ObjectNode obj = (ObjectNode) annotation;
Iterator<Entry<String, JsonNode>> it = obj.fields();
while (it.hasNext()) {
Entry<String, JsonNode> entry = it.next();
final String key = entry.getKey();
final JsonNode value = entry.getValue();
if (value.isValueNode()) {
if (value.isNull()) {
suppressedAnnotation.put(key, SuppressionRules.ANY_VALUE);
} else {
suppressedAnnotation.put(key, value.asText());
}
} else {
log.warn("Encountered unexpected JSON field {} for annotation", entry);
}
}
} else {
log.warn("Encountered unexpected JSONNode {} for annotation", annotation);
}
return new SuppressionRules(suppressedDevice,
suppressedDeviceType,
suppressedAnnotation);
}
/**
* Writes the given SuppressionRules.
*
* @param rules SuppressionRules
* @throws IOException if error occurred while writing the data
*/
public void write(SuppressionRules rules) throws IOException {
ObjectMapper mapper = new ObjectMapper();
ObjectNode root = mapper.createObjectNode();
ArrayNode deviceIds = mapper.createArrayNode();
ArrayNode deviceTypes = mapper.createArrayNode();
ObjectNode annotations = mapper.createObjectNode();
root.set(DEVICE_ID, deviceIds);
root.set(DEVICE_TYPE, deviceTypes);
root.set(ANNOTATION, annotations);
rules.getSuppressedDevice()
.forEach(deviceId -> deviceIds.add(deviceId.toString()));
rules.getSuppressedDeviceType()
.forEach(type -> deviceTypes.add(type.toString()));
rules.getSuppressedAnnotation().forEach((key, value) -> {
if (value == SuppressionRules.ANY_VALUE) {
annotations.putNull(key);
} else {
annotations.put(key, value);
}
});
mapper.writeTree(new JsonFactory().createGenerator(file, JsonEncoding.UTF8),
root);
}
}
......@@ -15,8 +15,11 @@
*/
package org.onosproject.provider.lldp.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.junit.After;
......@@ -43,6 +46,11 @@ import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.config.Config;
import org.onosproject.net.config.ConfigApplyDelegate;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistryAdapter;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceServiceAdapter;
......@@ -62,15 +70,25 @@ import org.onosproject.net.provider.AbstractProviderService;
import org.onosproject.net.provider.ProviderId;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.onosproject.provider.lldp.impl.LldpLinkProvider.DEFAULT_RULES;
import static org.junit.Assert.assertFalse;
public class LldpLinkProviderTest {
......@@ -89,18 +107,23 @@ public class LldpLinkProviderTest {
private final TestPacketService packetService = new TestPacketService();
private final TestDeviceService deviceService = new TestDeviceService();
private final TestMasterShipService masterService = new TestMasterShipService();
private final TestNetworkConfigRegistry configRegistry = new TestNetworkConfigRegistry();
private CoreService coreService;
private TestLinkProviderService providerService;
private PacketProcessor testProcessor;
private DeviceListener deviceListener;
private NetworkConfigListener configListener;
private ApplicationId appId =
new DefaultApplicationId(100, "org.onosproject.provider.lldp");
private TestSuppressionConfig cfg;
@Before
public void setUp() {
cfg = new TestSuppressionConfig();
coreService = createMock(CoreService.class);
expect(coreService.registerApplication(appId.name()))
.andReturn(appId).anyTimes();
......@@ -108,6 +131,7 @@ public class LldpLinkProviderTest {
provider.cfgService = new ComponentConfigAdapter();
provider.coreService = coreService;
provider.cfgRegistry = configRegistry;
provider.deviceService = deviceService;
provider.linkService = linkService;
......@@ -151,6 +175,7 @@ public class LldpLinkProviderTest {
*/
@Test
public void switchSuppressed() {
// add device to stub DeviceService
deviceService.putDevice(device(DID3));
deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3));
......@@ -163,7 +188,11 @@ public class LldpLinkProviderTest {
.build()));
deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_UPDATED, DID3));
assertTrue("Links on suppressed Device was expected to vanish.", vanishedDpid(DID3));
// discovery on device is expected to be gone or stopped
LinkDiscovery linkDiscovery = provider.discoverers.get(DID3);
if (linkDiscovery != null) {
assertTrue("Discovery expected to be stopped", linkDiscovery.isStopped());
}
}
@Test
......@@ -201,7 +230,7 @@ public class LldpLinkProviderTest {
* Checks that discovery on reconfigured switch are properly restarted.
*/
@Test
public void portSuppressedByDeviceConfig() {
public void portSuppressedByDeviceAnnotationConfig() {
/// When Device is configured with suppression:ON, Port also is same
......@@ -237,10 +266,91 @@ public class LldpLinkProviderTest {
}
/**
* Checks that discovery on reconfigured switch are properly restarted.
*/
@Test
public void portSuppressedByDeviceIdConfig() {
/// When Device is configured without suppression:OFF,
/// Port should be included for discovery
// add device in stub DeviceService without suppression configured
deviceService.putDevice(device(DID3));
deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3));
// non-suppressed port added to suppressed device
final long portno3 = 3L;
deviceService.putPorts(DID3, port(DID3, portno3, true));
deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID3, port(DID3, portno3, true)));
// discovery should succeed
assertFalse("Discoverer is expected to start", provider.discoverers.get(DID3).isStopped());
assertTrue("Discoverer should contain the port there", provider.discoverers.get(DID3).containsPort(portno3));
// add suppression rule for "deviceId: "of:0000000000000003""
provider.updateRules(new SuppressionRules(ImmutableSet.of(DID3),
ImmutableSet.of(),
ImmutableMap.of()));
/// When Device is reconfigured with suppression:ON, Port also is same
// update device in stub DeviceService with suppression configured
deviceService.putDevice(device(DID3));
// update the Port in stub DeviceService. (Port has reference to Device)
deviceService.putPorts(DID3, port(DID3, portno3, true));
deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_UPDATED, DID3));
// discovery on device is expected to be stopped
LinkDiscovery linkDiscovery = provider.discoverers.get(DID3);
if (linkDiscovery != null) {
assertTrue("Discovery expected to be stopped", linkDiscovery.isStopped());
}
}
/**
* Checks that discovery on reconfigured switch are properly restarted.
*/
@Test
public void portSuppressedByDeviceTypeConfig() {
/// When Device is configured without suppression:OFF,
/// Port should be included for discovery
// add device in stub DeviceService without suppression configured
deviceService.putDevice(device(DID1, Device.Type.SWITCH));
deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
// non-suppressed port added to suppressed device
final long portno3 = 3L;
deviceService.putPorts(DID1, port(DID1, portno3, true));
deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID1, port(DID1, portno3, true)));
// add device in stub DeviceService with suppression configured
deviceService.putDevice(device(DID2, Device.Type.ROADM));
deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID2));
// non-suppressed port added to suppressed device
final long portno4 = 4L;
deviceService.putPorts(DID2, port(DID2, portno4, true));
deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID2, port(DID2, portno4, true)));
// discovery should succeed for this device
assertFalse("Discoverer is expected to start", provider.discoverers.get(DID1).isStopped());
assertTrue("Discoverer should contain the port there", provider.discoverers.get(DID1).containsPort(portno3));
// discovery on device is expected to be stopped for this device
LinkDiscovery linkDiscovery = provider.discoverers.get(DID2);
if (linkDiscovery != null) {
assertTrue("Discovery expected to be stopped", linkDiscovery.isStopped());
}
}
/**
* Checks that discovery on reconfigured port are properly restarted.
*/
@Test
public void portSuppressedByPortConfig() {
// add device in stub DeviceService without suppression configured
deviceService.putDevice(device(DID3));
deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3));
......@@ -314,6 +424,11 @@ public class LldpLinkProviderTest {
"TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId());
}
private DefaultDevice device(DeviceId did, Device.Type type) {
return new DefaultDevice(ProviderId.NONE, did, type,
"TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId());
}
private DefaultDevice device(DeviceId did, Annotations annotations) {
return new DefaultDevice(ProviderId.NONE, did, Device.Type.SWITCH,
"TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId(), annotations);
......@@ -367,6 +482,127 @@ public class LldpLinkProviderTest {
}
@Test
public void addDeviceIdRule() {
DeviceId deviceId1 = DeviceId.deviceId("of:0000000000000001");
DeviceId deviceId2 = DeviceId.deviceId("of:0000000000000002");
Set<DeviceId> deviceIds = new HashSet<>();
deviceIds.add(deviceId1);
cfg.deviceIds(deviceIds);
configEvent(NetworkConfigEvent.Type.CONFIG_ADDED);
assertTrue(provider.rules().getSuppressedDevice().contains(deviceId1));
assertFalse(provider.rules().getSuppressedDevice().contains(deviceId2));
}
@Test
public void updateDeviceIdRule() {
DeviceId deviceId1 = DeviceId.deviceId("of:0000000000000001");
DeviceId deviceId2 = DeviceId.deviceId("of:0000000000000002");
Set<DeviceId> deviceIds = new HashSet<>();
deviceIds.add(deviceId1);
cfg.deviceIds(deviceIds);
configEvent(NetworkConfigEvent.Type.CONFIG_ADDED);
deviceIds.add(deviceId2);
cfg.deviceIds(deviceIds);
configEvent(NetworkConfigEvent.Type.CONFIG_UPDATED);
assertTrue(provider.rules().getSuppressedDevice().contains(deviceId1));
assertTrue(provider.rules().getSuppressedDevice().contains(deviceId2));
}
@Test
public void addDeviceTypeRule() {
Device.Type deviceType1 = Device.Type.ROADM;
Device.Type deviceType2 = Device.Type.SWITCH;
Set<Device.Type> deviceTypes = new HashSet<>();
deviceTypes.add(deviceType1);
cfg.deviceTypes(deviceTypes);
configEvent(NetworkConfigEvent.Type.CONFIG_ADDED);
assertTrue(provider.rules().getSuppressedDeviceType().contains(deviceType1));
assertFalse(provider.rules().getSuppressedDeviceType().contains(deviceType2));
}
@Test
public void updateDeviceTypeRule() {
Device.Type deviceType1 = Device.Type.ROADM;
Device.Type deviceType2 = Device.Type.SWITCH;
Set<Device.Type> deviceTypes = new HashSet<>();
deviceTypes.add(deviceType1);
cfg.deviceTypes(deviceTypes);
configEvent(NetworkConfigEvent.Type.CONFIG_ADDED);
deviceTypes.add(deviceType2);
cfg.deviceTypes(deviceTypes);
configEvent(NetworkConfigEvent.Type.CONFIG_UPDATED);
assertTrue(provider.rules().getSuppressedDeviceType().contains(deviceType1));
assertTrue(provider.rules().getSuppressedDeviceType().contains(deviceType2));
}
@Test
public void addAnnotationRule() {
final String key1 = "key1", key2 = "key2";
final String value1 = "value1";
Map<String, String> annotation = new HashMap<>();
annotation.put(key1, value1);
cfg.annotation(annotation);
configEvent(NetworkConfigEvent.Type.CONFIG_ADDED);
assertTrue(provider.rules().getSuppressedAnnotation().containsKey(key1));
assertEquals(value1, provider.rules().getSuppressedAnnotation().get(key1));
assertFalse(provider.rules().getSuppressedAnnotation().containsKey(key2));
}
@Test
public void updateAnnotationRule() {
final String key1 = "key1", key2 = "key2";
final String value1 = "value1", value2 = "value2";
Map<String, String> annotation = new HashMap<>();
annotation.put(key1, value1);
cfg.annotation(annotation);
configEvent(NetworkConfigEvent.Type.CONFIG_ADDED);
assertTrue(provider.rules().getSuppressedAnnotation().containsKey(key1));
assertEquals(value1, provider.rules().getSuppressedAnnotation().get(key1));
assertFalse(provider.rules().getSuppressedAnnotation().containsKey(key2));
annotation.put(key2, value2);
cfg.annotation(annotation);
configEvent(NetworkConfigEvent.Type.CONFIG_UPDATED);
assertTrue(provider.rules().getSuppressedAnnotation().containsKey(key1));
assertEquals(value1, provider.rules().getSuppressedAnnotation().get(key1));
assertTrue(provider.rules().getSuppressedAnnotation().containsKey(key2));
assertEquals(value2, provider.rules().getSuppressedAnnotation().get(key2));
}
private void configEvent(NetworkConfigEvent.Type evType) {
configListener.event(new NetworkConfigEvent(evType,
appId,
SuppressionConfig.class));
}
private class TestLinkRegistry implements LinkProviderRegistry {
@Override
......@@ -627,4 +863,58 @@ public class LldpLinkProviderTest {
private class TestLinkService extends LinkServiceAdapter {
}
private final class TestNetworkConfigRegistry
extends NetworkConfigRegistryAdapter {
@Override
public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
ConfigApplyDelegate delegate = config -> { };
ObjectMapper mapper = new ObjectMapper();
return (C) cfg;
}
@Override
public void addListener(NetworkConfigListener listener) {
configListener = listener;
}
}
private final class TestSuppressionConfig extends SuppressionConfig {
private Set<DeviceId> deviceIds = new HashSet<>(DEFAULT_RULES.getSuppressedDevice());
private Set<Device.Type> deviceTypes = new HashSet<>(DEFAULT_RULES.getSuppressedDeviceType());
private Map<String, String> annotation = new HashMap<>(DEFAULT_RULES.getSuppressedAnnotation());
@Override
public Set<DeviceId> deviceIds() {
return ImmutableSet.copyOf(deviceIds);
}
@Override
public SuppressionConfig deviceIds(Set<DeviceId> deviceIds) {
this.deviceIds = ImmutableSet.copyOf(deviceIds);
return this;
}
@Override
public Set<Device.Type> deviceTypes() {
return ImmutableSet.copyOf(deviceTypes);
}
@Override
public SuppressionConfig deviceTypes(Set<Device.Type> deviceTypes) {
this.deviceTypes = ImmutableSet.copyOf(deviceTypes);
return this;
}
@Override
public Map<String, String> annotation() {
return ImmutableMap.copyOf(annotation);
}
@Override
public SuppressionConfig annotation(Map<String, String> annotation) {
this.annotation = ImmutableMap.copyOf(annotation);
return this;
}
}
}
......
package org.onosproject.provider.lldp.impl;
import static org.junit.Assert.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.TestApplicationId;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.config.ConfigApplyDelegate;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class SuppressionConfigTest {
private static final String APP_NAME = "SuppressionConfigTest";
private static final TestApplicationId APP_ID = new TestApplicationId(APP_NAME);
private static final DeviceId DEVICE_ID_1 = DeviceId.deviceId("of:1111000000000000");
private static final DeviceId DEVICE_ID_2 = DeviceId.deviceId("of:2222000000000000");
private static final Device.Type DEVICE_TYPE_1 = Device.Type.ROADM;
private static final Device.Type DEVICE_TYPE_2 = Device.Type.FIBER_SWITCH;
private static final String ANNOTATION_KEY_1 = "no_lldp";
private static final String ANNOTATION_VALUE_1 = "true";
private static final String ANNOTATION_KEY_2 = "sendLLDP";
private static final String ANNOTATION_VALUE_2 = "false";
private SuppressionConfig cfg;
@Before
public void setUp() throws Exception {
ConfigApplyDelegate delegate = config -> { };
ObjectMapper mapper = new ObjectMapper();
cfg = new SuppressionConfig();
cfg.init(APP_ID, LldpLinkProvider.CONFIG_KEY, JsonNodeFactory.instance.objectNode(), mapper, delegate);
}
@Test
public void testDeviceIds() {
Set<DeviceId> inputIds = new HashSet<DeviceId>() { {
add(DEVICE_ID_1);
add(DEVICE_ID_2);
} };
assertNotNull(cfg.deviceIds(inputIds));
Set<DeviceId> outputIds = cfg.deviceIds();
assertTrue(outputIds.contains(DEVICE_ID_1));
assertTrue(outputIds.contains(DEVICE_ID_2));
assertEquals(outputIds.size(), 2);
}
@Test
public void testDeviceTypes() {
Set<Device.Type> inputTypes = new HashSet<Device.Type>() { {
add(DEVICE_TYPE_1);
add(DEVICE_TYPE_2);
} };
assertNotNull(cfg.deviceTypes(inputTypes));
Set<Device.Type> outputTypes = cfg.deviceTypes();
assertTrue(outputTypes.contains(DEVICE_TYPE_1));
assertTrue(outputTypes.contains(DEVICE_TYPE_2));
assertEquals(outputTypes.size(), 2);
}
@Test
public void testDeviceAnnotation() {
Map<String, String> inputMap = new HashMap<String, String>() { {
put(ANNOTATION_KEY_1, ANNOTATION_VALUE_1);
put(ANNOTATION_KEY_2, ANNOTATION_VALUE_2);
} };
assertNotNull(cfg.annotation(inputMap));
Map<String, String> outputMap = cfg.annotation();
assertEquals(outputMap.get(ANNOTATION_KEY_1), ANNOTATION_VALUE_1);
assertEquals(outputMap.get(ANNOTATION_KEY_2), ANNOTATION_VALUE_2);
assertEquals(outputMap.size(), 2);
}
}
/*
* Copyright 2014-2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.lldp.impl;
import static org.junit.Assert.*;
import static org.onosproject.net.DeviceId.deviceId;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.onosproject.net.Device;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Resources;
public class SuppressionRulesStoreTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
// "lldp_suppression.json"
SuppressionRules testData
= new SuppressionRules(ImmutableSet.of(deviceId("of:2222000000000000")),
ImmutableSet.of(Device.Type.ROADM),
ImmutableMap.of("no-lldp", SuppressionRules.ANY_VALUE,
"sendLLDP", "false"));
private static void assertRulesEqual(SuppressionRules expected, SuppressionRules actual) {
assertEquals(expected.getSuppressedDevice(),
actual.getSuppressedDevice());
assertEquals(expected.getSuppressedDeviceType(),
actual.getSuppressedDeviceType());
assertEquals(expected.getSuppressedAnnotation(),
actual.getSuppressedAnnotation());
}
@Test
public void testRead() throws URISyntaxException, IOException {
Path path = Paths.get(Resources.getResource("lldp_suppression.json").toURI());
SuppressionRulesStore store = new SuppressionRulesStore(path.toString());
SuppressionRules rules = store.read();
assertRulesEqual(testData, rules);
}
@Test
public void testWrite() throws IOException {
File newFile = tempFolder.newFile();
SuppressionRulesStore store = new SuppressionRulesStore(newFile);
store.write(testData);
SuppressionRulesStore reload = new SuppressionRulesStore(newFile);
SuppressionRules rules = reload.read();
assertRulesEqual(testData, rules);
}
}
{
"deviceId" : [ "of:2222000000000000" ],
"deviceType" : [ "ROADM" ],
"annotation" : { "no-lldp" : null, "sendLLDP" : "false" }
}
......@@ -62,5 +62,12 @@
]
}
}
"org.onosproject.provider.lldp": {
"suppression": {
"deviceIds": [ "of:2222000000000000" ],
"deviceTypes": [ "ROADM" ],
"annotation": { "no-lldp": null, "sendLLDP" : "false" }
}
}
}
}
......