aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/providers/lldp
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/providers/lldp')
-rw-r--r--framework/src/onos/providers/lldp/pom.xml4
-rw-r--r--framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromDevice.java31
-rw-r--r--framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromPort.java31
-rw-r--r--framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java760
-rw-r--r--framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionConfig.java144
-rw-r--r--framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRules.java43
-rw-r--r--framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRulesStore.java174
-rw-r--r--framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LldpLinkProviderTest.java944
-rw-r--r--framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionConfigTest.java71
-rw-r--r--framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesStoreTest.java80
-rw-r--r--framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesTest.java23
-rw-r--r--framework/src/onos/providers/lldp/src/test/resources/lldp_suppression.json6
12 files changed, 2016 insertions, 295 deletions
diff --git a/framework/src/onos/providers/lldp/pom.xml b/framework/src/onos/providers/lldp/pom.xml
index 7bf92ed2..f9052760 100644
--- a/framework/src/onos/providers/lldp/pom.xml
+++ b/framework/src/onos/providers/lldp/pom.xml
@@ -55,5 +55,9 @@
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
</dependencies>
</project>
diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromDevice.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromDevice.java
new file mode 100644
index 00000000..32b12d47
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromDevice.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 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 org.onosproject.net.DeviceId;
+import org.onosproject.net.config.basics.BasicFeatureConfig;
+
+/**
+ * Configuration to see LinkDiscovery should be enabled on Device.
+ */
+public class LinkDiscoveryFromDevice extends BasicFeatureConfig<DeviceId> {
+
+ public LinkDiscoveryFromDevice() {
+ // default: enabled
+ super(true);
+ }
+
+}
diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromPort.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromPort.java
new file mode 100644
index 00000000..b9a502f8
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromPort.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 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 org.onosproject.net.ConnectPoint;
+import org.onosproject.net.config.basics.BasicFeatureConfig;
+
+/**
+ * Configuration to see LinkDiscovery should be enabled on a Port.
+ */
+public class LinkDiscoveryFromPort extends BasicFeatureConfig<ConnectPoint> {
+
+ public LinkDiscoveryFromPort() {
+ // default: enabled
+ super(true);
+ }
+
+}
diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java
new file mode 100644
index 00000000..94abebaa
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java
@@ -0,0 +1,760 @@
+/*
+ * 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.Strings.isNullOrEmpty;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.onlab.packet.Ethernet.TYPE_BSN;
+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.onosproject.net.config.basics.SubjectFactories.CONNECT_POINT_SUBJECT_FACTORY;
+import static org.onosproject.net.config.basics.SubjectFactories.DEVICE_SUBJECT_FACTORY;
+import static org.slf4j.LoggerFactory.getLogger;
+
+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;
+
+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.onlab.packet.Ethernet;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipListener;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+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;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.link.DefaultLinkDescription;
+import org.onosproject.net.link.LinkProvider;
+import org.onosproject.net.link.LinkProviderRegistry;
+import org.onosproject.net.link.LinkProviderService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+/**
+ * Provider which uses LLDP and BDDP packets to detect network infrastructure links.
+ */
+@Component(immediate = true)
+public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
+
+ private static final String PROVIDER_NAME = "org.onosproject.provider.lldp";
+
+ private static final String FORMAT =
+ "Settings: enabled={}, useBDDP={}, probeRate={}, " +
+ "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)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService masterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry cfgRegistry;
+
+ private LinkProviderService providerService;
+
+ private ScheduledExecutorService executor;
+
+ // TODO: Add sanity checking for the configurable params based on the delays
+ private static final long DEVICE_SYNC_DELAY = 5;
+ private static final long LINK_PRUNER_DELAY = 3;
+
+ private static final String PROP_ENABLED = "enabled";
+ @Property(name = PROP_ENABLED, boolValue = true,
+ label = "If false, link discovery is disabled")
+ private boolean enabled = false;
+
+ private static final String PROP_USE_BDDP = "useBDDP";
+ @Property(name = PROP_USE_BDDP, boolValue = true,
+ label = "Use BDDP for link discovery")
+ private boolean useBddp = true;
+
+ private static final String PROP_PROBE_RATE = "probeRate";
+ private static final int DEFAULT_PROBE_RATE = 3_000;
+ @Property(name = PROP_PROBE_RATE, intValue = DEFAULT_PROBE_RATE,
+ label = "LLDP and BDDP probe rate specified in millis")
+ private int probeRate = DEFAULT_PROBE_RATE;
+
+ private static final String PROP_STALE_LINK_AGE = "staleLinkAge";
+ private static final int DEFAULT_STALE_LINK_AGE = 10_000;
+ @Property(name = PROP_STALE_LINK_AGE, intValue = DEFAULT_STALE_LINK_AGE,
+ label = "Number of millis beyond which links will be considered stale")
+ private int staleLinkAge = DEFAULT_STALE_LINK_AGE;
+
+ private final DiscoveryContext context = new InternalDiscoveryContext();
+ private final InternalRoleListener roleListener = new InternalRoleListener();
+ private final InternalDeviceListener deviceListener = new InternalDeviceListener();
+ private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
+
+ // Device link discovery helpers.
+ protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
+
+ // Most recent time a tracked link was seen; links are tracked if their
+ // destination connection point is mastered by this controller instance.
+ private final Map<LinkKey, Long> linkTimes = Maps.newConcurrentMap();
+
+ private ApplicationId appId;
+
+ static final SuppressionRules DEFAULT_RULES
+ = new SuppressionRules(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";
+ public static final String FEATURE_NAME = "linkDiscovery";
+
+ 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();
+ }
+ },
+ new ConfigFactory<DeviceId, LinkDiscoveryFromDevice>(DEVICE_SUBJECT_FACTORY,
+ LinkDiscoveryFromDevice.class, FEATURE_NAME) {
+ @Override
+ public LinkDiscoveryFromDevice createConfig() {
+ return new LinkDiscoveryFromDevice();
+ }
+ },
+ new ConfigFactory<ConnectPoint, LinkDiscoveryFromPort>(CONNECT_POINT_SUBJECT_FACTORY,
+ LinkDiscoveryFromPort.class, FEATURE_NAME) {
+ @Override
+ public LinkDiscoveryFromPort createConfig() {
+ return new LinkDiscoveryFromPort();
+ }
+ }
+ );
+
+ private final InternalConfigListener cfgListener = new InternalConfigListener();
+
+
+ /**
+ * Creates an OpenFlow link provider.
+ */
+ public LldpLinkProvider() {
+ super(new ProviderId("lldp", PROVIDER_NAME));
+ }
+
+ @Activate
+ 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.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");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
+
+ boolean newEnabled, newUseBddp;
+ int newProbeRate, newStaleLinkAge;
+ try {
+ String s = get(properties, PROP_ENABLED);
+ newEnabled = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim());
+
+ s = get(properties, PROP_USE_BDDP);
+ newUseBddp = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim());
+
+ s = get(properties, PROP_PROBE_RATE);
+ newProbeRate = isNullOrEmpty(s) ? probeRate : Integer.parseInt(s.trim());
+
+ s = get(properties, PROP_STALE_LINK_AGE);
+ newStaleLinkAge = isNullOrEmpty(s) ? staleLinkAge : Integer.parseInt(s.trim());
+
+ } catch (NumberFormatException e) {
+ log.warn("Component configuration had invalid values", e);
+ newEnabled = enabled;
+ newUseBddp = useBddp;
+ newProbeRate = probeRate;
+ newStaleLinkAge = staleLinkAge;
+ }
+
+ boolean wasEnabled = enabled;
+
+ enabled = newEnabled;
+ useBddp = newUseBddp;
+ probeRate = newProbeRate;
+ staleLinkAge = newStaleLinkAge;
+
+ if (!wasEnabled && enabled) {
+ enable();
+ } else if (wasEnabled && !enabled) {
+ disable();
+ } else {
+ if (enabled) {
+ // update all discovery helper state
+ loadDevices();
+ }
+ }
+
+ log.info(FORMAT, enabled, useBddp, probeRate, staleLinkAge);
+ }
+
+ /**
+ * Enables link discovery processing.
+ */
+ private void enable() {
+ providerService = providerRegistry.register(this);
+ masterService.addListener(roleListener);
+ deviceService.addListener(deviceListener);
+ packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0));
+
+ loadDevices();
+
+ executor = newSingleThreadScheduledExecutor(groupedThreads("onos/link", "discovery-%d"));
+ executor.scheduleAtFixedRate(new SyncDeviceInfoTask(),
+ DEVICE_SYNC_DELAY, DEVICE_SYNC_DELAY, SECONDS);
+ executor.scheduleAtFixedRate(new LinkPrunerTask(),
+ LINK_PRUNER_DELAY, LINK_PRUNER_DELAY, SECONDS);
+
+ requestIntercepts();
+ }
+
+ /**
+ * Disables link discovery processing.
+ */
+ private void disable() {
+ withdrawIntercepts();
+
+ providerRegistry.unregister(this);
+ masterService.removeListener(roleListener);
+ deviceService.removeListener(deviceListener);
+ packetService.removeProcessor(packetProcessor);
+
+
+ if (executor != null) {
+ executor.shutdownNow();
+ }
+ discoverers.values().forEach(LinkDiscovery::stop);
+ discoverers.clear();
+
+ providerService = null;
+ }
+
+ /**
+ * 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())));
+ }
+
+ private boolean isBlacklisted(DeviceId did) {
+ LinkDiscoveryFromDevice cfg = cfgRegistry.getConfig(did, LinkDiscoveryFromDevice.class);
+ if (cfg == null) {
+ return false;
+ }
+ return !cfg.enabled();
+ }
+
+ private boolean isBlacklisted(ConnectPoint cp) {
+ // if parent device is blacklisted, so is the port
+ if (isBlacklisted(cp.deviceId())) {
+ return true;
+ }
+ LinkDiscoveryFromPort cfg = cfgRegistry.getConfig(cp, LinkDiscoveryFromPort.class);
+ if (cfg == null) {
+ return false;
+ }
+ return !cfg.enabled();
+ }
+
+ private boolean isBlacklisted(Port port) {
+ return isBlacklisted(new ConnectPoint(port.element().id(), port.number()));
+ }
+
+ /**
+ * Updates discovery helper for specified device.
+ *
+ * Adds and starts a discovery helper for specified device if enabled,
+ * calls {@link #removeDevice(DeviceId)} otherwise.
+ *
+ * @param device device to add
+ * @return discovery helper if discovery is enabled for the device
+ */
+ private Optional<LinkDiscovery> updateDevice(Device device) {
+ if (device == null) {
+ return Optional.empty();
+ }
+ if (rules.isSuppressed(device) || isBlacklisted(device.id())) {
+ log.trace("LinkDiscovery from {} disabled by configuration", device.id());
+ removeDevice(device.id());
+ return Optional.empty();
+ }
+ LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
+ did -> new LinkDiscovery(device, context));
+ if (ld.isStopped()) {
+ ld.start();
+ }
+ return Optional.of(ld);
+ }
+
+ /**
+ * Removes after stopping discovery helper for specified device.
+ * @param deviceId device to remove
+ */
+ private void removeDevice(final DeviceId deviceId) {
+ discoverers.computeIfPresent(deviceId, (did, ld) -> {
+ ld.stop();
+ return null;
+ });
+
+ }
+
+ /**
+ * Updates ports of the specified device to the specified discovery helper.
+ */
+ private void updatePorts(LinkDiscovery discoverer, DeviceId deviceId) {
+ deviceService.getPorts(deviceId).forEach(p -> updatePort(discoverer, p));
+ }
+
+ /**
+ * Updates discovery helper state of the specified port.
+ *
+ * Adds a port to the discovery helper if up and discovery is enabled,
+ * or calls {@link #removePort(Port)} otherwise.
+ */
+ private void updatePort(LinkDiscovery discoverer, Port port) {
+ if (port == null) {
+ return;
+ }
+ if (port.number().isLogical()) {
+ // silently ignore logical ports
+ return;
+ }
+
+ if (rules.isSuppressed(port) || isBlacklisted(port)) {
+ log.trace("LinkDiscovery from {} disabled by configuration", port);
+ removePort(port);
+ return;
+ }
+
+ // check if enabled and turn off discovery?
+ if (!port.isEnabled()) {
+ removePort(port);
+ return;
+ }
+
+ discoverer.addPort(port);
+ }
+
+ /**
+ * Removes a port from the specified discovery helper.
+ * @param port the port
+ */
+ private void removePort(Port port) {
+ if (port.element() instanceof Device) {
+ Device d = (Device) port.element();
+ LinkDiscovery ld = discoverers.get(d.id());
+ if (ld != null) {
+ ld.removePort(port.number());
+ }
+ } else {
+ log.warn("Attempted to remove non-Device port", port);
+ }
+ }
+
+ /**
+ * Requests packet intercepts.
+ */
+ private void requestIntercepts() {
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(TYPE_LLDP);
+ packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+
+ selector.matchEthType(TYPE_BSN);
+ if (useBddp) {
+ packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+ } else {
+ packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+ }
+ }
+
+ /**
+ * Withdraws packet intercepts.
+ */
+ private void withdrawIntercepts() {
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(TYPE_LLDP);
+ packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+ selector.matchEthType(TYPE_BSN);
+ 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.
+ */
+ private class InternalRoleListener implements MastershipListener {
+ @Override
+ public void event(MastershipEvent event) {
+ if (MastershipEvent.Type.BACKUPS_CHANGED.equals(event.type())) {
+ // only need new master events
+ return;
+ }
+
+ DeviceId deviceId = event.subject();
+ Device device = deviceService.getDevice(deviceId);
+ if (device == null) {
+ log.debug("Device {} doesn't exist, or isn't there yet", deviceId);
+ return;
+ }
+ if (clusterService.getLocalNode().id().equals(event.roleInfo().master())) {
+ updateDevice(device).ifPresent(ld -> updatePorts(ld, device.id()));
+ }
+ }
+ }
+
+ /**
+ * Processes device events.
+ */
+ private class InternalDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ Device device = event.subject();
+ Port port = event.port();
+ if (device == null) {
+ log.error("Device is null.");
+ return;
+ }
+ log.trace("{} {} {}", event.type(), event.subject(), event);
+ final DeviceId deviceId = device.id();
+ switch (event.type()) {
+ case DEVICE_ADDED:
+ case DEVICE_UPDATED:
+ updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId));
+ break;
+ case PORT_ADDED:
+ case PORT_UPDATED:
+ if (port.isEnabled()) {
+ updateDevice(device).ifPresent(ld -> updatePort(ld, port));
+ } 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)) {
+ log.debug("Device up {}", deviceId);
+ updateDevice(device);
+ } else {
+ log.debug("Device down {}", deviceId);
+ removeDevice(deviceId);
+ providerService.linksVanished(deviceId);
+ }
+ break;
+ case PORT_STATS_UPDATED:
+ break;
+ default:
+ log.debug("Unknown event {}", event);
+ }
+ }
+ }
+
+ /**
+ * Processes incoming packets.
+ */
+ private class InternalPacketProcessor implements PacketProcessor {
+ @Override
+ public void process(PacketContext context) {
+ if (context == null || context.isHandled()) {
+ return;
+ }
+
+ Ethernet eth = context.inPacket().parsed();
+ if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) {
+ return;
+ }
+
+ LinkDiscovery ld = discoverers.get(context.inPacket().receivedFrom().deviceId());
+ if (ld == null) {
+ return;
+ }
+
+ if (ld.handleLldp(context)) {
+ context.block();
+ }
+ }
+ }
+
+ /**
+ * Auxiliary task to keep device ports up to date.
+ */
+ private final class SyncDeviceInfoTask implements Runnable {
+ @Override
+ public void run() {
+ if (Thread.currentThread().isInterrupted()) {
+ log.info("Interrupted, quitting");
+ return;
+ }
+ // check what deviceService sees, to see if we are missing anything
+ try {
+ loadDevices();
+ } catch (Exception e) {
+ // Catch all exceptions to avoid task being suppressed
+ log.error("Exception thrown during synchronization process", e);
+ }
+ }
+ }
+
+ /**
+ * Auxiliary task for pruning stale links.
+ */
+ private class LinkPrunerTask implements Runnable {
+ @Override
+ public void run() {
+ if (Thread.currentThread().isInterrupted()) {
+ log.info("Interrupted, quitting");
+ return;
+ }
+
+ try {
+ // TODO: There is still a slight possibility of mastership
+ // change occurring right with link going stale. This will
+ // result in the stale link not being pruned.
+ Maps.filterEntries(linkTimes, e -> {
+ if (!masterService.isLocalMaster(e.getKey().dst().deviceId())) {
+ return true;
+ }
+ if (isStale(e.getValue())) {
+ providerService.linkVanished(new DefaultLinkDescription(e.getKey().src(),
+ e.getKey().dst(),
+ DIRECT));
+ return true;
+ }
+ return false;
+ }).clear();
+
+ } catch (Exception e) {
+ // Catch all exceptions to avoid task being suppressed
+ log.error("Exception thrown during link pruning process", e);
+ }
+ }
+
+ private boolean isStale(long lastSeen) {
+ return lastSeen < System.currentTimeMillis() - staleLinkAge;
+ }
+ }
+
+ /**
+ * Provides processing context for the device link discovery helpers.
+ */
+ private class InternalDiscoveryContext implements DiscoveryContext {
+ @Override
+ public MastershipService mastershipService() {
+ return masterService;
+ }
+
+ @Override
+ public LinkProviderService providerService() {
+ return providerService;
+ }
+
+ @Override
+ public PacketService packetService() {
+ return packetService;
+ }
+
+ @Override
+ public long probeRate() {
+ return probeRate;
+ }
+
+ @Override
+ public boolean useBddp() {
+ return useBddp;
+ }
+
+ @Override
+ public void touchLink(LinkKey key) {
+ linkTimes.put(key, System.currentTimeMillis());
+ }
+ }
+
+ static final EnumSet<NetworkConfigEvent.Type> CONFIG_CHANGED
+ = EnumSet.of(NetworkConfigEvent.Type.CONFIG_ADDED,
+ NetworkConfigEvent.Type.CONFIG_UPDATED,
+ NetworkConfigEvent.Type.CONFIG_REMOVED);
+
+ 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.deviceTypes(),
+ cfg.annotation());
+
+ updateRules(newRules);
+ }
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ if (event.configClass() == LinkDiscoveryFromDevice.class &&
+ CONFIG_CHANGED.contains(event.type())) {
+
+ if (event.subject() instanceof DeviceId) {
+ final DeviceId did = (DeviceId) event.subject();
+ Device device = deviceService.getDevice(did);
+ updateDevice(device).ifPresent(ld -> updatePorts(ld, did));
+ }
+
+ } else if (event.configClass() == LinkDiscoveryFromPort.class &&
+ CONFIG_CHANGED.contains(event.type())) {
+
+ if (event.subject() instanceof ConnectPoint) {
+ ConnectPoint cp = (ConnectPoint) event.subject();
+ if (cp.elementId() instanceof DeviceId) {
+ final DeviceId did = (DeviceId) cp.elementId();
+ Device device = deviceService.getDevice(did);
+ Port port = deviceService.getPort(did, cp.port());
+ updateDevice(device).ifPresent(ld -> updatePort(ld, port));
+ }
+ }
+
+ } else 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");
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionConfig.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionConfig.java
new file mode 100644
index 00000000..5b10f6d2
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionConfig.java
@@ -0,0 +1,144 @@
+/*
+ * 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.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;
+
+/**
+ * LinkDiscovery suppression config class.
+ */
+public class SuppressionConfig extends Config<ApplicationId> {
+
+ private static final String DEVICE_TYPES = "deviceTypes";
+ private static final String ANNOTATION = "annotation";
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private static final List<Device.Type> DEFAULT_DEVICE_TYPES
+ = ImmutableList.copyOf(DEFAULT_RULES.getSuppressedDeviceType());
+
+ private final Logger log = getLogger(getClass());
+
+ /**
+ * Returns types of devices on which LinkDiscovery 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 LinkDiscovery 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 LinkDiscovery 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 LinkDiscovery 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);
+ }
+}
diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRules.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRules.java
index 27c75ebd..14bc2200 100644
--- a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRules.java
+++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRules.java
@@ -18,38 +18,33 @@ 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;
import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
import org.onosproject.net.Element;
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 {
public static final String ANY_VALUE = "(any)";
- private final Set<DeviceId> suppressedDevice;
private final Set<Device.Type> suppressedDeviceType;
private final Map<String, String> suppressedAnnotation;
- public SuppressionRules(Set<DeviceId> suppressedDevice,
- Set<Device.Type> suppressedType,
- Map<String, String> suppressedAnnotation) {
+ public SuppressionRules(Set<Device.Type> suppressedType,
+ Map<String, String> suppressedAnnotation) {
- this.suppressedDevice = ImmutableSet.copyOf(suppressedDevice);
this.suppressedDeviceType = ImmutableSet.copyOf(suppressedType);
this.suppressedAnnotation = ImmutableMap.copyOf(suppressedAnnotation);
}
public boolean isSuppressed(Device device) {
- if (suppressedDevice.contains(device.id())) {
- return true;
- }
if (suppressedDeviceType.contains(device.type())) {
return true;
}
@@ -92,10 +87,6 @@ public class SuppressionRules {
return false;
}
- Set<DeviceId> getSuppressedDevice() {
- return suppressedDevice;
- }
-
Set<Device.Type> getSuppressedDeviceType() {
return suppressedDeviceType;
}
@@ -103,4 +94,30 @@ public class SuppressionRules {
Map<String, String> getSuppressedAnnotation() {
return suppressedAnnotation;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(suppressedDeviceType,
+ suppressedAnnotation);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object != null && getClass() == object.getClass()) {
+ SuppressionRules that = (SuppressionRules) object;
+ return Objects.equals(this.suppressedDeviceType,
+ that.suppressedDeviceType)
+ && Objects.equals(this.suppressedAnnotation,
+ that.suppressedAnnotation);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("suppressedDeviceType", suppressedDeviceType)
+ .add("suppressedAnnotation", suppressedAnnotation)
+ .toString();
+ }
}
diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRulesStore.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRulesStore.java
deleted file mode 100644
index 360bebd2..00000000
--- a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRulesStore.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * 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);
- }
-}
diff --git a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LldpLinkProviderTest.java b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LldpLinkProviderTest.java
new file mode 100644
index 00000000..758c34e6
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LldpLinkProviderTest.java
@@ -0,0 +1,944 @@
+/*
+ * 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.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;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ONOSLLDP;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.cluster.RoleInfo;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.mastership.MastershipListener;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+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.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigEvent.Type;
+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;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.link.LinkDescription;
+import org.onosproject.net.link.LinkProvider;
+import org.onosproject.net.link.LinkProviderRegistry;
+import org.onosproject.net.link.LinkProviderService;
+import org.onosproject.net.link.LinkServiceAdapter;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketServiceAdapter;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.onosproject.net.provider.ProviderId;
+
+import java.nio.ByteBuffer;
+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.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 {
+
+ private static final DeviceId DID1 = DeviceId.deviceId("of:0000000000000001");
+ private static final DeviceId DID2 = DeviceId.deviceId("of:0000000000000002");
+ private static final DeviceId DID3 = DeviceId.deviceId("of:0000000000000003");
+
+ private static Port pd1;
+ private static Port pd2;
+ private static Port pd3;
+ private static Port pd4;
+
+ private final LldpLinkProvider provider = new LldpLinkProvider();
+ private final TestLinkRegistry linkRegistry = new TestLinkRegistry();
+ private final TestLinkService linkService = new TestLinkService();
+ 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;
+
+ private Set<DeviceId> deviceBlacklist;
+
+ private Set<ConnectPoint> portBlacklist;
+
+ @Before
+ public void setUp() {
+ deviceBlacklist = new HashSet<>();
+ portBlacklist = new HashSet<>();
+ cfg = new TestSuppressionConfig();
+ coreService = createMock(CoreService.class);
+ expect(coreService.registerApplication(appId.name()))
+ .andReturn(appId).anyTimes();
+ replay(coreService);
+
+ provider.cfgService = new ComponentConfigAdapter();
+ provider.coreService = coreService;
+ provider.cfgRegistry = configRegistry;
+
+ provider.deviceService = deviceService;
+ provider.linkService = linkService;
+ provider.packetService = packetService;
+ provider.providerRegistry = linkRegistry;
+ provider.masterService = masterService;
+
+ provider.activate(null);
+ }
+
+ @Test
+ public void basics() {
+ assertNotNull("registration expected", providerService);
+ assertEquals("incorrect provider", provider, providerService.provider());
+ }
+
+ @Test
+ public void switchAdd() {
+ DeviceEvent de = deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1);
+ deviceListener.event(de);
+
+ assertFalse("Device not added", provider.discoverers.isEmpty());
+ }
+
+ @Test
+ public void switchRemove() {
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_REMOVED, DID1));
+
+ final LinkDiscovery linkDiscovery = provider.discoverers.get(DID1);
+ if (linkDiscovery != null) {
+ // If LinkDiscovery helper is there after DEVICE_REMOVED,
+ // it should be stopped
+ assertTrue("Discoverer is not stopped", linkDiscovery.isStopped());
+ }
+ assertTrue("Device is not gone.", vanishedDpid(DID1));
+ }
+
+ /**
+ * Checks that links on a reconfigured switch are properly removed.
+ */
+ @Test
+ public void switchSuppressedByAnnotation() {
+
+ // add device to stub DeviceService
+ deviceService.putDevice(device(DID3));
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3));
+
+ assertFalse("Device not added", provider.discoverers.isEmpty());
+
+ // update device in stub DeviceService with suppression config
+ deviceService.putDevice(device(DID3, DefaultAnnotations.builder()
+ .set(LldpLinkProvider.NO_LLDP, "true")
+ .build()));
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_UPDATED, 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
+ public void switchSuppressByBlacklist() {
+ // add device in stub DeviceService
+ deviceService.putDevice(device(DID3));
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3));
+
+ // add deviveId to device blacklist
+ deviceBlacklist.add(DID3);
+ configListener.event(new NetworkConfigEvent(Type.CONFIG_ADDED,
+ DID3,
+ LinkDiscoveryFromDevice.class));
+
+ // discovery helper for 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
+ public void portUp() {
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
+ deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID1, port(DID1, 3, true)));
+
+ assertTrue("Port not added to discoverer",
+ provider.discoverers.get(DID1).containsPort(3L));
+ }
+
+ @Test
+ public void portDown() {
+
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
+ deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID1, port(DID1, 1, false)));
+
+ assertFalse("Port added to discoverer",
+ provider.discoverers.get(DID1).containsPort(1L));
+ assertTrue("Port is not gone.", vanishedPort(1L));
+ }
+
+ @Test
+ public void portRemoved() {
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
+ deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID1, port(DID1, 3, true)));
+ deviceListener.event(portEvent(DeviceEvent.Type.PORT_REMOVED, DID1, port(DID1, 3, true)));
+
+ assertTrue("Port is not gone.", vanishedPort(3L));
+ assertFalse("Port was not removed from discoverer",
+ provider.discoverers.get(DID1).containsPort(3L));
+ }
+
+ /**
+ * Checks that discovery on reconfigured switch are properly restarted.
+ */
+ @Test
+ public void portSuppressedByDeviceAnnotationConfig() {
+
+ /// When Device is configured with suppression:ON, Port also is same
+
+ // add device in stub DeviceService with suppression configured
+ deviceService.putDevice(device(DID3, DefaultAnnotations.builder()
+ .set(LldpLinkProvider.NO_LLDP, "true")
+ .build()));
+ 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 on device is expected to be stopped
+ LinkDiscovery linkDiscovery = provider.discoverers.get(DID3);
+ if (linkDiscovery != null) {
+ assertTrue("Discovery expected to be stopped", linkDiscovery.isStopped());
+ }
+
+ /// When Device is reconfigured without suppression:OFF,
+ /// Port should be included for discovery
+
+ // update device in stub DeviceService without 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 should come back on
+ assertFalse("Discoverer is expected to start", provider.discoverers.get(DID3).isStopped());
+ assertTrue("Discoverer should contain the port there", provider.discoverers.get(DID3).containsPort(portno3));
+ }
+
+ /**
+ * Checks that discovery on reconfigured switch are properly restarted.
+ */
+ @Test
+ public void portSuppressedByParentDeviceIdBlacklist() {
+
+ /// 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""
+ deviceBlacklist.add(DID3);
+ configListener.event(new NetworkConfigEvent(Type.CONFIG_ADDED,
+ DID3,
+ LinkDiscoveryFromDevice.class));
+
+
+ /// 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 helper for 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());
+ }
+ }
+
+ /**
+ * 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));
+
+ // suppressed port added to non-suppressed device
+ final long portno3 = 3L;
+ final Port port3 = port(DID3, portno3, true,
+ DefaultAnnotations.builder()
+ .set(LldpLinkProvider.NO_LLDP, "true")
+ .build());
+ deviceService.putPorts(DID3, port3);
+ deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID3, port3));
+
+ // discovery helper should be there turned on
+ assertFalse("Discoverer is expected to start", provider.discoverers.get(DID3).isStopped());
+ assertFalse("Discoverer should not contain the port there",
+ provider.discoverers.get(DID3).containsPort(portno3));
+ }
+
+ @Test
+ public void portSuppressedByPortBlacklist() {
+
+ // add device in stub DeviceService without suppression configured
+ deviceService.putDevice(device(DID3));
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3));
+
+ final long portno3 = 3L;
+ final Port port3 = port(DID3, portno3, true);
+
+ final ConnectPoint cpDid3no3 = new ConnectPoint(DID3, PortNumber.portNumber(portno3));
+ portBlacklist.add(cpDid3no3);
+
+ // suppressed port added to non-suppressed device
+ deviceService.putPorts(DID3, port3);
+ deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID3, port3));
+
+ configListener.event(new NetworkConfigEvent(Type.CONFIG_ADDED,
+ cpDid3no3,
+ LinkDiscoveryFromPort.class));
+
+ // discovery helper should be there turned on
+ assertFalse("Discoverer is expected to start", provider.discoverers.get(DID3).isStopped());
+ // but port is not a discovery target
+ assertFalse("Discoverer should not contain the port there",
+ provider.discoverers.get(DID3).containsPort(portno3));
+ }
+
+ @Test
+ public void portUnknown() {
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
+ // Note: DID3 hasn't been added to TestDeviceService, but only port is added
+ deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID3, port(DID3, 1, false)));
+
+
+ assertNull("DeviceId exists",
+ provider.discoverers.get(DID3));
+ }
+
+ @Test
+ public void unknownPktCtx() {
+
+ // Note: DID3 hasn't been added to TestDeviceService
+ PacketContext pktCtx = new TestPacketContext(device(DID3));
+
+ testProcessor.process(pktCtx);
+ assertFalse("Context should still be free", pktCtx.isHandled());
+ }
+
+ @Test
+ public void knownPktCtx() {
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID2));
+ PacketContext pktCtx = new TestPacketContext(deviceService.getDevice(DID2));
+
+
+ testProcessor.process(pktCtx);
+
+ assertTrue("Link not detected", detectedLink(DID1, DID2));
+
+ }
+
+
+ @After
+ public void tearDown() {
+ provider.deactivate();
+ provider.coreService = null;
+ provider.providerRegistry = null;
+ provider.deviceService = null;
+ provider.packetService = null;
+ }
+
+ private DeviceEvent deviceEvent(DeviceEvent.Type type, DeviceId did) {
+ return new DeviceEvent(type, deviceService.getDevice(did));
+
+ }
+
+ private DefaultDevice device(DeviceId did) {
+ return new DefaultDevice(ProviderId.NONE, did, Device.Type.SWITCH,
+ "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);
+ }
+
+ @SuppressWarnings(value = { "unused" })
+ private DeviceEvent portEvent(DeviceEvent.Type type, DeviceId did, PortNumber port) {
+ return new DeviceEvent(type, deviceService.getDevice(did),
+ deviceService.getPort(did, port));
+ }
+
+ private DeviceEvent portEvent(DeviceEvent.Type type, DeviceId did, Port port) {
+ return new DeviceEvent(type, deviceService.getDevice(did), port);
+ }
+
+ private Port port(DeviceId did, long port, boolean enabled) {
+ return new DefaultPort(deviceService.getDevice(did),
+ PortNumber.portNumber(port), enabled);
+ }
+
+ private Port port(DeviceId did, long port, boolean enabled, Annotations annotations) {
+ return new DefaultPort(deviceService.getDevice(did),
+ PortNumber.portNumber(port), enabled, annotations);
+ }
+
+ private boolean vanishedDpid(DeviceId... dids) {
+ for (int i = 0; i < dids.length; i++) {
+ if (!providerService.vanishedDpid.contains(dids[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean vanishedPort(Long... ports) {
+ for (int i = 0; i < ports.length; i++) {
+ if (!providerService.vanishedPort.contains(ports[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean detectedLink(DeviceId src, DeviceId dst) {
+ for (DeviceId key : providerService.discoveredLinks.keySet()) {
+ if (key.equals(src)) {
+ return providerService.discoveredLinks.get(src).equals(dst);
+ }
+ }
+ return false;
+ }
+
+ @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
+ public LinkProviderService register(LinkProvider provider) {
+ providerService = new TestLinkProviderService(provider);
+ return providerService;
+ }
+
+ @Override
+ public void unregister(LinkProvider provider) {
+ }
+
+ @Override
+ public Set<ProviderId> getProviders() {
+ return null;
+ }
+
+ }
+
+ private class TestLinkProviderService
+ extends AbstractProviderService<LinkProvider>
+ implements LinkProviderService {
+
+ List<DeviceId> vanishedDpid = Lists.newLinkedList();
+ List<Long> vanishedPort = Lists.newLinkedList();
+ Map<DeviceId, DeviceId> discoveredLinks = Maps.newHashMap();
+
+ protected TestLinkProviderService(LinkProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void linkDetected(LinkDescription linkDescription) {
+ DeviceId sDid = linkDescription.src().deviceId();
+ DeviceId dDid = linkDescription.dst().deviceId();
+ discoveredLinks.put(sDid, dDid);
+ }
+
+ @Override
+ public void linkVanished(LinkDescription linkDescription) {
+ }
+
+ @Override
+ public void linksVanished(ConnectPoint connectPoint) {
+ vanishedPort.add(connectPoint.port().toLong());
+
+ }
+
+ @Override
+ public void linksVanished(DeviceId deviceId) {
+ vanishedDpid.add(deviceId);
+ }
+
+
+ }
+
+
+
+ private class TestPacketContext implements PacketContext {
+
+ protected Device device;
+ protected boolean blocked = false;
+
+ public TestPacketContext(Device dev) {
+ device = dev;
+ }
+
+ @Override
+ public long time() {
+ return 0;
+ }
+
+ @Override
+ public InboundPacket inPacket() {
+ ONOSLLDP lldp = new ONOSLLDP();
+ lldp.setChassisId(device.chassisId());
+ lldp.setPortId((int) pd1.number().toLong());
+ lldp.setDevice(deviceService.getDevice(DID1).id().toString());
+
+
+ Ethernet ethPacket = new Ethernet();
+ ethPacket.setEtherType(Ethernet.TYPE_LLDP);
+ ethPacket.setDestinationMACAddress(ONOSLLDP.LLDP_NICIRA);
+ ethPacket.setPayload(lldp);
+ ethPacket.setPad(true);
+
+
+
+ ethPacket.setSourceMACAddress("DE:AD:BE:EF:BA:11");
+
+ ConnectPoint cp = new ConnectPoint(device.id(), pd3.number());
+
+ return new DefaultInboundPacket(cp, ethPacket,
+ ByteBuffer.wrap(ethPacket.serialize()));
+
+ }
+
+ @Override
+ public OutboundPacket outPacket() {
+ return null;
+ }
+
+ @Override
+ public TrafficTreatment.Builder treatmentBuilder() {
+ return null;
+ }
+
+ @Override
+ public void send() {
+
+ }
+
+ @Override
+ public boolean block() {
+ blocked = true;
+ return blocked;
+ }
+
+ @Override
+ public boolean isHandled() {
+ return blocked;
+ }
+
+ }
+
+ private class TestPacketService extends PacketServiceAdapter {
+ @Override
+ public void addProcessor(PacketProcessor processor, int priority) {
+ testProcessor = processor;
+ }
+ }
+
+ private class TestDeviceService extends DeviceServiceAdapter {
+
+ private final Map<DeviceId, Device> devices = new HashMap<>();
+ private final ArrayListMultimap<DeviceId, Port> ports =
+ ArrayListMultimap.create();
+ public TestDeviceService() {
+ Device d1 = new DefaultDevice(ProviderId.NONE, DID1, Device.Type.SWITCH,
+ "TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId());
+ Device d2 = new DefaultDevice(ProviderId.NONE, DID2, Device.Type.SWITCH,
+ "TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId());
+ devices.put(DID1, d1);
+ devices.put(DID2, d2);
+ pd1 = new DefaultPort(d1, PortNumber.portNumber(1), true);
+ pd2 = new DefaultPort(d1, PortNumber.portNumber(2), true);
+ pd3 = new DefaultPort(d2, PortNumber.portNumber(1), true);
+ pd4 = new DefaultPort(d2, PortNumber.portNumber(2), true);
+
+ ports.putAll(DID1, Lists.newArrayList(pd1, pd2));
+ ports.putAll(DID2, Lists.newArrayList(pd3, pd4));
+ }
+
+ private void putDevice(Device device) {
+ DeviceId deviceId = device.id();
+ devices.put(deviceId, device);
+ }
+
+ private void putPorts(DeviceId did, Port...ports) {
+ this.ports.putAll(did, Lists.newArrayList(ports));
+ }
+
+ @Override
+ public int getDeviceCount() {
+ return devices.values().size();
+ }
+
+ @Override
+ public Iterable<Device> getDevices() {
+ return ImmutableList.copyOf(devices.values());
+ }
+
+ @Override
+ public Device getDevice(DeviceId deviceId) {
+ return devices.get(deviceId);
+ }
+
+ @Override
+ public MastershipRole getRole(DeviceId deviceId) {
+ return MastershipRole.MASTER;
+ }
+
+ @Override
+ public List<Port> getPorts(DeviceId deviceId) {
+ return ports.get(deviceId);
+ }
+
+ @Override
+ public Port getPort(DeviceId deviceId, PortNumber portNumber) {
+ for (Port p : ports.get(deviceId)) {
+ if (p.number().equals(portNumber)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isAvailable(DeviceId deviceId) {
+ return true;
+ }
+
+ @Override
+ public void addListener(DeviceListener listener) {
+ deviceListener = listener;
+
+ }
+
+ @Override
+ public void removeListener(DeviceListener listener) {
+
+ }
+ }
+
+ private final class TestMasterShipService implements MastershipService {
+
+ @Override
+ public MastershipRole getLocalRole(DeviceId deviceId) {
+ return MastershipRole.MASTER;
+ }
+
+ @Override
+ public CompletableFuture<MastershipRole> requestRoleFor(DeviceId deviceId) {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ @Override
+ public CompletableFuture<Void> relinquishMastership(DeviceId deviceId) {
+ return null;
+ }
+
+ @Override
+ public NodeId getMasterFor(DeviceId deviceId) {
+ return null;
+ }
+
+ @Override
+ public Set<DeviceId> getDevicesOf(NodeId nodeId) {
+ return null;
+ }
+
+ @Override
+ public void addListener(MastershipListener listener) {
+
+ }
+
+ @Override
+ public void removeListener(MastershipListener listener) {
+
+ }
+
+ @Override
+ public RoleInfo getNodesFor(DeviceId deviceId) {
+ return new RoleInfo(new NodeId("foo"), Collections.<NodeId>emptyList());
+ }
+ }
+
+
+ private class TestLinkService extends LinkServiceAdapter {
+ }
+
+ private final class TestNetworkConfigRegistry
+ extends NetworkConfigRegistryAdapter {
+ @SuppressWarnings("unchecked")
+ @Override
+ public <S, C extends Config<S>> C getConfig(S subj, Class<C> configClass) {
+ if (configClass == SuppressionConfig.class) {
+ return (C) cfg;
+ } else if (configClass == LinkDiscoveryFromDevice.class) {
+ return (C) new LinkDiscoveryFromDevice() {
+ @Override
+ public boolean enabled() {
+ return !deviceBlacklist.contains(subj);
+ }
+ };
+ } else if (configClass == LinkDiscoveryFromPort.class) {
+ return (C) new LinkDiscoveryFromPort() {
+ @Override
+ public boolean enabled() {
+ return !portBlacklist.contains(subj);
+ }
+ };
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void addListener(NetworkConfigListener listener) {
+ configListener = listener;
+ }
+ }
+
+ private final class TestSuppressionConfig extends SuppressionConfig {
+ private Set<Device.Type> deviceTypes = new HashSet<>(DEFAULT_RULES.getSuppressedDeviceType());
+ private Map<String, String> annotation = new HashMap<>(DEFAULT_RULES.getSuppressedAnnotation());
+
+ @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;
+ }
+ }
+}
diff --git a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionConfigTest.java b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionConfigTest.java
new file mode 100644
index 00000000..85061ca0
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionConfigTest.java
@@ -0,0 +1,71 @@
+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 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);
+ }
+
+}
diff --git a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesStoreTest.java b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesStoreTest.java
deleted file mode 100644
index 0ac31123..00000000
--- a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesStoreTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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);
- }
-}
diff --git a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesTest.java b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesTest.java
index 03d431af..c18c248e 100644
--- a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesTest.java
+++ b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesTest.java
@@ -51,22 +51,12 @@ public class SuppressionRulesTest {
@Before
public void setUp() throws Exception {
- rules = new SuppressionRules(ImmutableSet.of(SUPPRESSED_DID),
- ImmutableSet.of(Device.Type.ROADM),
+ rules = new SuppressionRules(ImmutableSet.of(Device.Type.ROADM),
ImmutableMap.of("no-lldp", SuppressionRules.ANY_VALUE,
"sendLLDP", "false"));
}
@Test
- public void testSuppressedDeviceId() {
- Device device = new DefaultDevice(PID,
- SUPPRESSED_DID,
- Device.Type.SWITCH,
- MFR, HW, SW1, SN, CID);
- assertTrue(rules.isSuppressed(device));
- }
-
- @Test
public void testSuppressedDeviceType() {
Device device = new DefaultDevice(PID,
NON_SUPPRESSED_DID,
@@ -111,17 +101,6 @@ public class SuppressionRulesTest {
}
@Test
- public void testSuppressedPortOnSuppressedDevice() {
- Device device = new DefaultDevice(PID,
- SUPPRESSED_DID,
- Device.Type.SWITCH,
- MFR, HW, SW1, SN, CID);
- Port port = new DefaultPort(device, P1, true);
-
- assertTrue(rules.isSuppressed(port));
- }
-
- @Test
public void testSuppressedPortAnnotation() {
Annotations annotation = DefaultAnnotations.builder()
.set("no-lldp", "random")
diff --git a/framework/src/onos/providers/lldp/src/test/resources/lldp_suppression.json b/framework/src/onos/providers/lldp/src/test/resources/lldp_suppression.json
deleted file mode 100644
index 062e73aa..00000000
--- a/framework/src/onos/providers/lldp/src/test/resources/lldp_suppression.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "deviceId" : [ "of:2222000000000000" ],
- "deviceType" : [ "ROADM" ],
- "annotation" : { "no-lldp" : null, "sendLLDP" : "false" }
-}
-