Devices,hosts, and links can be blocked and kicked off with the network configuration api
Change-Id: I68d427f4886a7b63475df8d35383e2e347946946
diff --git a/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java b/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
index d71b796..17ff85c 100644
--- a/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
+++ b/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
@@ -16,6 +16,7 @@
package org.onosproject.net.device.impl;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.MastershipRole.MASTER;
@@ -45,17 +46,23 @@
import org.onosproject.core.Permission;
import org.onosproject.event.EventDeliveryService;
import org.onosproject.event.ListenerRegistry;
+import org.onosproject.incubator.net.config.NetworkConfigEvent;
+import org.onosproject.incubator.net.config.NetworkConfigListener;
+import org.onosproject.incubator.net.config.NetworkConfigService;
+import org.onosproject.incubator.net.config.basics.BasicDeviceConfig;
import org.onosproject.mastership.MastershipEvent;
import org.onosproject.mastership.MastershipListener;
import org.onosproject.mastership.MastershipService;
import org.onosproject.mastership.MastershipTerm;
import org.onosproject.mastership.MastershipTermService;
+import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.Device.Type;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceAdminService;
@@ -104,6 +111,8 @@
private ScheduledExecutorService backgroundService;
+ private final NetworkConfigListener networkConfigListener = new InternalNetworkConfigListener();
+
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceStore store;
@@ -122,6 +131,11 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceClockProviderService deviceClockProviderService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService networkConfigService;
+
+
+
@Activate
public void activate() {
backgroundService = newSingleThreadScheduledExecutor(groupedThreads("onos/device", "manager-background"));
@@ -130,6 +144,7 @@
store.setDelegate(delegate);
eventDispatcher.addSink(DeviceEvent.class, listenerRegistry);
mastershipService.addListener(mastershipListener);
+ networkConfigService.addListener(networkConfigListener);
backgroundService.scheduleWithFixedDelay(new Runnable() {
@@ -148,7 +163,7 @@
@Deactivate
public void deactivate() {
backgroundService.shutdown();
-
+ networkConfigService.removeListener(networkConfigListener);
store.unsetDelegate(delegate);
mastershipService.removeListener(mastershipListener);
eventDispatcher.removeSink(DeviceEvent.class);
@@ -286,7 +301,8 @@
continue;
}
- log.info("{} is reachable but did not have a valid role, reasserting", deviceId);
+ log.info("{} is reachable but did not have a valid role, reasserting",
+ deviceId);
// isReachable but was not MASTER or STANDBY, get a role and apply
// Note: NONE triggers request to MastershipService
@@ -319,7 +335,8 @@
DeviceProvider provider = provider();
if (provider == null) {
- log.warn("Provider for {} was not found. Cannot apply role {}", deviceId, newRole);
+ log.warn("Provider for {} was not found. Cannot apply role {}",
+ deviceId, newRole);
return false;
}
provider.roleChanged(deviceId, newRole);
@@ -335,8 +352,8 @@
checkNotNull(deviceId, DEVICE_ID_NULL);
checkNotNull(deviceDescription, DEVICE_DESCRIPTION_NULL);
checkValidity();
+ deviceDescription = validateDevice(deviceDescription, deviceId);
- log.info("Device {} connected", deviceId);
// check my Role
CompletableFuture<MastershipRole> role = mastershipService.requestRoleFor(deviceId);
try {
@@ -362,16 +379,33 @@
deviceClockProviderService.setMastershipTerm(deviceId, term);
applyRole(deviceId, MastershipRole.MASTER);
}
-
- DeviceEvent event = store.createOrUpdateDevice(provider().id(),
- deviceId, deviceDescription);
-
+ DeviceEvent event = store.createOrUpdateDevice(provider().id(), deviceId,
+ deviceDescription);
if (event != null) {
log.trace("event: {} {}", event.type(), event);
post(event);
}
}
+ // returns a DeviceDescription made from the union of the BasicDeviceConfig
+ // annotations if it exists
+ private DeviceDescription validateDevice(DeviceDescription deviceDescription, DeviceId deviceId) {
+ BasicDeviceConfig cfg = networkConfigService.getConfig(deviceId, BasicDeviceConfig.class);
+ checkState(cfg == null || cfg.isAllowed(), "Device " + deviceId + " is not allowed");
+ log.info("Device {} connected", deviceId);
+ if (cfg != null) {
+ SparseAnnotations finalSparse = processAnnotations(cfg, deviceDescription, deviceId);
+ if (cfg.type() != Type.SWITCH) {
+ deviceDescription = new DefaultDeviceDescription(deviceDescription,
+ cfg.type(), finalSparse);
+ } else {
+ deviceDescription = new DefaultDeviceDescription(deviceDescription,
+ deviceDescription.type(), finalSparse);
+ }
+ }
+ return deviceDescription;
+ }
+
@Override
public void deviceDisconnected(DeviceId deviceId) {
checkNotNull(deviceId, DEVICE_ID_NULL);
@@ -433,7 +467,7 @@
List<PortDescription> portDescriptions) {
checkNotNull(deviceId, DEVICE_ID_NULL);
checkNotNull(portDescriptions,
- "Port descriptions list cannot be null");
+ "Port descriptions list cannot be null");
checkValidity();
if (!deviceClockProviderService.isTimestampAvailable(deviceId)) {
// Never been a master for this device
@@ -459,7 +493,8 @@
if (!deviceClockProviderService.isTimestampAvailable(deviceId)) {
// Never been a master for this device
// any update will be ignored.
- log.trace("Ignoring {} port update on standby node. {}", deviceId, portDescription);
+ log.trace("Ignoring {} port update on standby node. {}", deviceId,
+ portDescription);
return;
}
@@ -486,7 +521,7 @@
// FIXME: implement response to this notification
log.debug("got reply to a role request for {}: asked for {}, and got {}",
- deviceId, requested, response);
+ deviceId, requested, response);
if (requested == null && response == null) {
// something was off with DeviceProvider, maybe check channel too?
@@ -525,6 +560,37 @@
deviceId, portStatistics);
post(event);
}
+
+ // supplements or replaces deviceDescription annotations with
+ // BasicDeviceConfig annotations
+ private SparseAnnotations processAnnotations(BasicDeviceConfig cfg, DeviceDescription deviceDescription,
+ DeviceId deviceId) {
+ SparseAnnotations originalAnnotations = deviceDescription.annotations();
+ DefaultAnnotations.Builder newBuilder = DefaultAnnotations.builder();
+ if (cfg.driver() != deviceId.toString()) {
+ newBuilder.set(cfg.DRIVER, cfg.driver());
+ }
+ if (cfg.type() != Type.SWITCH) {
+ newBuilder.set(cfg.TYPE, cfg.type().toString());
+ }
+ if (cfg.name() != null) {
+ newBuilder.set(cfg.NAME, cfg.name());
+ }
+ if (cfg.latitude() != -1) {
+ newBuilder.set(cfg.LATITUDE, Double.toString(cfg.latitude()));
+ }
+ if (cfg.longitude() != -1) {
+ newBuilder.set(cfg.LONGITUDE, Double.toString(cfg.longitude()));
+ }
+ if (cfg.rackAddress() != null) {
+ newBuilder.set(cfg.RACK_ADDRESS, cfg.rackAddress());
+ }
+ if (cfg.owner() != null) {
+ newBuilder.set(cfg.OWNER, cfg.owner());
+ }
+ DefaultAnnotations newAnnotations = newBuilder.build();
+ return DefaultAnnotations.union(originalAnnotations, newAnnotations);
+ }
}
// Posts the specified event to the local event dispatcher.
@@ -727,4 +793,30 @@
}
return results;
}
+
+ private class InternalNetworkConfigListener implements NetworkConfigListener {
+ @Override
+ public void event(NetworkConfigEvent event) {
+ if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
+ event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
+ event.configClass().equals(BasicDeviceConfig.class)) {
+ log.info("Detected Device network config event {}", event.type());
+ kickOutBadDevice(((DeviceId) event.subject()));
+ }
+ }
+ }
+
+ // checks if the specified device is allowed by the BasicDeviceConfig
+ // and if not, removes it
+ private void kickOutBadDevice(DeviceId deviceId) {
+ BasicDeviceConfig cfg = networkConfigService.getConfig(deviceId, BasicDeviceConfig.class);
+ if (!cfg.isAllowed()) {
+ Device badDevice = getDevice(deviceId);
+ if (badDevice != null) {
+ removeDevice(deviceId);
+ } else {
+ log.info("Failed removal: Device {} does not exist", deviceId);
+ }
+ }
+ }
}
diff --git a/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java b/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
index 3335f5a..d36db84 100644
--- a/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
+++ b/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
@@ -27,11 +27,18 @@
import org.onosproject.core.Permission;
import org.onosproject.event.EventDeliveryService;
import org.onosproject.event.ListenerRegistry;
+import org.onosproject.incubator.net.config.NetworkConfigEvent;
+import org.onosproject.incubator.net.config.NetworkConfigListener;
+import org.onosproject.incubator.net.config.NetworkConfigService;
+import org.onosproject.incubator.net.config.basics.BasicHostConfig;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
+import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.host.DefaultHostDescription;
import org.onosproject.net.host.HostAdminService;
import org.onosproject.net.host.HostDescription;
import org.onosproject.net.host.HostEvent;
@@ -51,6 +58,7 @@
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onosproject.security.AppGuard.checkPermission;
@@ -70,6 +78,8 @@
private final ListenerRegistry<HostEvent, HostListener>
listenerRegistry = new ListenerRegistry<>();
+ private final NetworkConfigListener networkConfigListener = new InternalNetworkConfigListener();
+
private HostStoreDelegate delegate = new InternalStoreDelegate();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -84,6 +94,9 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService networkConfigService;
+
private HostMonitor monitor;
@Activate
@@ -91,7 +104,7 @@
log.info("Started");
store.setDelegate(delegate);
eventDispatcher.addSink(HostEvent.class, listenerRegistry);
-
+ networkConfigService.addListener(networkConfigListener);
monitor = new HostMonitor(deviceService, packetService, this);
monitor.start();
}
@@ -100,6 +113,7 @@
public void deactivate() {
store.unsetDelegate(delegate);
eventDispatcher.removeSink(HostEvent.class);
+ networkConfigService.removeListener(networkConfigListener);
log.info("Stopped");
}
@@ -246,7 +260,6 @@
private class InternalHostProviderService
extends AbstractProviderService<HostProvider>
implements HostProviderService {
-
InternalHostProviderService(HostProvider provider) {
super(provider);
}
@@ -255,6 +268,7 @@
public void hostDetected(HostId hostId, HostDescription hostDescription) {
checkNotNull(hostId, HOST_ID_NULL);
checkValidity();
+ hostDescription = validateHost(hostDescription, hostId);
HostEvent event = store.createOrUpdateHost(provider().id(), hostId,
hostDescription);
if (event != null) {
@@ -262,6 +276,21 @@
}
}
+ // returns a HostDescription made from the union of the BasicHostConfig
+ // annotations if it exists
+ private HostDescription validateHost(HostDescription hostDescription, HostId hostId) {
+ BasicHostConfig cfg = networkConfigService.getConfig(hostId, BasicHostConfig.class);
+ checkState(cfg == null || cfg.isAllowed(), "Host {} is not allowed", hostId);
+ if (cfg != null) {
+ SparseAnnotations finalSparse = processAnnotations(cfg, hostDescription);
+ hostDescription = new DefaultHostDescription(hostId.mac(),
+ hostDescription.vlan(),
+ hostDescription.location(),
+ finalSparse);
+ }
+ return hostDescription;
+ }
+
@Override
public void hostVanished(HostId hostId) {
checkNotNull(hostId, HOST_ID_NULL);
@@ -273,6 +302,30 @@
}
}
+ // Supplements or replaces hostDescriptions's annotations with BasicHostConfig's
+ // annotations
+ private SparseAnnotations processAnnotations(BasicHostConfig cfg, HostDescription hostDescription) {
+ SparseAnnotations originalAnnotations = hostDescription.annotations();
+ DefaultAnnotations.Builder newBuilder = DefaultAnnotations.builder();
+ if (cfg.name() != null) {
+ newBuilder.set(cfg.NAME, cfg.name());
+ }
+ if (cfg.latitude() != -1) {
+ newBuilder.set(cfg.LATITUDE, Double.toString(cfg.latitude()));
+ }
+ if (cfg.longitude() != -1) {
+ newBuilder.set(cfg.LONGITUDE, Double.toString(cfg.longitude()));
+ }
+ if (cfg.rackAddress() != null) {
+ newBuilder.set(cfg.RACK_ADDRESS, cfg.rackAddress());
+ }
+ if (cfg.owner() != null) {
+ newBuilder.set(cfg.OWNER, cfg.owner());
+ }
+ DefaultAnnotations newAnnotations = newBuilder.build();
+ return DefaultAnnotations.union(originalAnnotations, newAnnotations);
+ }
+
// Posts the specified event to the local event dispatcher.
private void post(HostEvent event) {
if (event != null) {
@@ -287,4 +340,32 @@
post(event);
}
}
+
+ // listens for NetworkConfigEvents of type BasicHostConfig and removes
+ // links that the config does not allow
+ private class InternalNetworkConfigListener implements NetworkConfigListener {
+ @Override
+ public void event(NetworkConfigEvent event) {
+ if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
+ event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
+ event.configClass().equals(BasicHostConfig.class)) {
+ log.info("Detected Host network config event {}", event.type());
+ kickOutBadHost(((HostId) event.subject()));
+ }
+ }
+ }
+
+ // checks if the specified host is allowed by the BasicHostConfig
+ // and if not, removes it
+ private void kickOutBadHost(HostId hostId) {
+ BasicHostConfig cfg = networkConfigService.getConfig(hostId, BasicHostConfig.class);
+ if (cfg != null && !cfg.isAllowed()) {
+ Host badHost = getHost(hostId);
+ if (badHost != null) {
+ removeHost(hostId);
+ } else {
+ log.info("Failed removal: Host {} does not exist", hostId);
+ }
+ }
+ }
}
diff --git a/core/net/src/main/java/org/onosproject/net/link/impl/LinkManager.java b/core/net/src/main/java/org/onosproject/net/link/impl/LinkManager.java
index 75134f3..e6fa300 100644
--- a/core/net/src/main/java/org/onosproject/net/link/impl/LinkManager.java
+++ b/core/net/src/main/java/org/onosproject/net/link/impl/LinkManager.java
@@ -27,14 +27,22 @@
import org.onosproject.core.Permission;
import org.onosproject.event.EventDeliveryService;
import org.onosproject.event.ListenerRegistry;
+import org.onosproject.incubator.net.config.NetworkConfigEvent;
+import org.onosproject.incubator.net.config.NetworkConfigListener;
+import org.onosproject.incubator.net.config.NetworkConfigService;
+import org.onosproject.incubator.net.config.basics.BasicLinkConfig;
import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.Link.State;
+import org.onosproject.net.LinkKey;
import org.onosproject.net.MastershipRole;
+import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.link.DefaultLinkDescription;
import org.onosproject.net.link.LinkAdminService;
import org.onosproject.net.link.LinkDescription;
import org.onosproject.net.link.LinkEvent;
@@ -49,9 +57,12 @@
import org.onosproject.net.provider.AbstractProviderService;
import org.slf4j.Logger;
+import java.time.Duration;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.onosproject.net.LinkKey.linkKey;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onosproject.security.AppGuard.checkPermission;
@@ -78,6 +89,8 @@
private final DeviceListener deviceListener = new InternalDeviceListener();
+ private final NetworkConfigListener networkConfigListener = new InternalNetworkConfigListener();
+
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LinkStore store;
@@ -87,11 +100,15 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected EventDeliveryService eventDispatcher;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService networkConfigService;
+
@Activate
public void activate() {
store.setDelegate(delegate);
eventDispatcher.addSink(LinkEvent.class, listenerRegistry);
deviceService.addListener(deviceListener);
+ networkConfigService.addListener(networkConfigListener);
log.info("Started");
}
@@ -100,6 +117,7 @@
store.unsetDelegate(delegate);
eventDispatcher.removeSink(LinkEvent.class);
deviceService.removeListener(deviceListener);
+ networkConfigService.removeListener(networkConfigListener);
log.info("Stopped");
}
@@ -206,17 +224,19 @@
removeLinks(getDeviceLinks(deviceId), false);
}
+ public void removeLink(ConnectPoint src, ConnectPoint dst) {
+ post(store.removeLink(src, dst));
+ }
+
@Override
public void addListener(LinkListener listener) {
checkPermission(Permission.LINK_EVENT);
-
listenerRegistry.addListener(listener);
}
@Override
public void removeListener(LinkListener listener) {
checkPermission(Permission.LINK_EVENT);
-
listenerRegistry.removeListener(listener);
}
@@ -229,7 +249,7 @@
removeLinks(event.subject().id());
} else if (event.type() == DeviceEvent.Type.PORT_REMOVED) {
removeLinks(new ConnectPoint(event.subject().id(),
- event.port().number()));
+ event.port().number()));
}
}
}
@@ -252,15 +272,62 @@
public void linkDetected(LinkDescription linkDescription) {
checkNotNull(linkDescription, LINK_DESC_NULL);
checkValidity();
-
+ linkDescription = validateLink(linkDescription);
LinkEvent event = store.createOrUpdateLink(provider().id(),
- linkDescription);
+ linkDescription);
if (event != null) {
log.info("Link {} detected", linkDescription);
post(event);
}
}
+ // returns a LinkDescription made from the union of the BasicLinkConfig
+ // annotations if it exists
+ private LinkDescription validateLink(LinkDescription linkDescription) {
+ // TODO Investigate whether this can be made more efficient
+ BasicLinkConfig cfg = networkConfigService.getConfig(linkKey(linkDescription.src(),
+ linkDescription.dst()),
+ BasicLinkConfig.class);
+ BasicLinkConfig cfgTwo = networkConfigService.getConfig(linkKey(linkDescription.dst(),
+ linkDescription.src()),
+ BasicLinkConfig.class);
+
+ checkState(cfg == null || cfg.isAllowed(), "Link " + linkDescription.toString() + " is not allowed");
+ checkState(cfgTwo == null || cfgTwo.isAllowed(), "Link " + linkDescription.toString() + " is not allowed");
+ if (cfg != null) {
+ SparseAnnotations finalSparse = processAnnotations(cfg, linkDescription);
+ // check whether config has a specified type
+ if (cfg.type() != Link.Type.DIRECT) {
+ linkDescription = new DefaultLinkDescription(linkDescription.src(),
+ linkDescription.dst(),
+ cfg.type(), finalSparse);
+ } else {
+ linkDescription = new DefaultLinkDescription(linkDescription.src(),
+ linkDescription.dst(),
+ linkDescription.type(), finalSparse);
+ }
+ }
+ return linkDescription;
+ }
+
+ // supplements or replaces linkDescriptions's annotations with BasicLinkConfig's
+ // annotations
+ private SparseAnnotations processAnnotations(BasicLinkConfig cfg, LinkDescription linkDescription) {
+ SparseAnnotations originalAnnotations = linkDescription.annotations();
+ DefaultAnnotations.Builder newBuilder = DefaultAnnotations.builder();
+ if (cfg.type() != Link.Type.DIRECT) {
+ newBuilder.set(cfg.TYPE, cfg.type().toString());
+ }
+ if (cfg.latency() != Duration.ofNanos(-1)) {
+ newBuilder.set(cfg.LATENCY, cfg.latency().toString());
+ }
+ if (cfg.bandwidth() != -1) {
+ newBuilder.set(cfg.BANDWIDTH, String.valueOf(cfg.bandwidth()));
+ }
+ DefaultAnnotations newAnnotations = newBuilder.build();
+ return DefaultAnnotations.union(originalAnnotations, newAnnotations);
+ }
+
@Override
public void linkVanished(LinkDescription linkDescription) {
checkNotNull(linkDescription, LINK_DESC_NULL);
@@ -297,7 +364,7 @@
}
// Removes all links in the specified set and emits appropriate events.
- private void removeLinks(Set<Link> links, boolean isSoftRemove) {
+ private void removeLinks(Set<Link> links, boolean isSoftRemove) {
for (Link link : links) {
LinkEvent event = isSoftRemove ?
store.removeOrDownLink(link.src(), link.dst()) :
@@ -323,4 +390,24 @@
post(event);
}
}
+
+ // listens for NetworkConfigEvents of type BasicLinkConfig and removes
+ // links that the config does not allow
+ private class InternalNetworkConfigListener implements NetworkConfigListener {
+ @Override
+ public void event(NetworkConfigEvent event) {
+ if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
+ event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
+ event.configClass().equals(BasicLinkConfig.class)) {
+ log.info("Detected Link network config event {}", event.type());
+ LinkKey lk = (LinkKey) event.subject();
+ BasicLinkConfig cfg = networkConfigService.getConfig(lk, BasicLinkConfig.class);
+ if (cfg != null && !cfg.isAllowed()) {
+ log.info("Kicking out links between {} and {}", lk.src(), lk.dst());
+ removeLink(lk.src(), lk.dst());
+ removeLink(lk.dst(), lk.src());
+ }
+ }
+ }
+ }
}