Devices,hosts, and links can be blocked and kicked off with the network configuration api

Change-Id: I68d427f4886a7b63475df8d35383e2e347946946
diff --git a/core/api/src/main/java/org/onosproject/net/device/DefaultDeviceDescription.java b/core/api/src/main/java/org/onosproject/net/device/DefaultDeviceDescription.java
index d58e5b3..3a8c8c1 100644
--- a/core/api/src/main/java/org/onosproject/net/device/DefaultDeviceDescription.java
+++ b/core/api/src/main/java/org/onosproject/net/device/DefaultDeviceDescription.java
@@ -76,6 +76,18 @@
              base.chassisId(), annotations);
     }
 
+    /**
+     * Creates a device description using the supplied information.
+     * @param base DeviceDescription to basic information (except for type)
+     * @param type device type
+     * @param annotations Annotations to use.
+     */
+    public DefaultDeviceDescription(DeviceDescription base, Type type, SparseAnnotations... annotations) {
+        this(base.deviceURI(), type, base.manufacturer(),
+                base.hwVersion(), base.swVersion(), base.serialNumber(),
+                base.chassisId(), annotations);
+    }
+
     @Override
     public URI deviceURI() {
         return uri;
diff --git a/core/api/src/main/java/org/onosproject/net/link/LinkAdminService.java b/core/api/src/main/java/org/onosproject/net/link/LinkAdminService.java
index 2e308b4..a0b5e1e 100644
--- a/core/api/src/main/java/org/onosproject/net/link/LinkAdminService.java
+++ b/core/api/src/main/java/org/onosproject/net/link/LinkAdminService.java
@@ -39,4 +39,12 @@
      */
     void removeLinks(DeviceId deviceId);
 
+    /**
+     * Removes all links between between the specified src and
+     * dst connection points.
+     *
+     * @param src link source
+     * @param dst link destination
+     */
+    void removeLink(ConnectPoint src, ConnectPoint dst);
 }
diff --git a/core/net/pom.xml b/core/net/pom.xml
index c54be45..e262d4a 100644
--- a/core/net/pom.xml
+++ b/core/net/pom.xml
@@ -59,12 +59,25 @@
         </dependency>
 
         <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-incubator-api</artifactId>
+            <scope>test</scope>
+            <classifier>tests</classifier>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
             <groupId>org.easymock</groupId>
             <artifactId>easymock</artifactId>
             <scope>test</scope>
         </dependency>
 
         <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-incubator-api</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.scr.annotations</artifactId>
         </dependency>
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());
+                }
+            }
+        }
+    }
 }
diff --git a/core/net/src/test/java/org/onosproject/net/device/impl/DeviceManagerTest.java b/core/net/src/test/java/org/onosproject/net/device/impl/DeviceManagerTest.java
index 4d8d2fc..7597e7c 100644
--- a/core/net/src/test/java/org/onosproject/net/device/impl/DeviceManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/device/impl/DeviceManagerTest.java
@@ -32,6 +32,7 @@
 import org.onosproject.cluster.NodeId;
 import org.onosproject.event.Event;
 import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.incubator.net.config.NetworkConfigServiceAdapter;
 import org.onosproject.mastership.MastershipServiceAdapter;
 import org.onosproject.mastership.MastershipTerm;
 import org.onosproject.mastership.MastershipTermService;
@@ -115,8 +116,10 @@
         mgr.termService = mastershipManager;
         mgr.clusterService = new TestClusterService();
         mgr.deviceClockProviderService = new TestClockProviderService();
+        mgr.networkConfigService = new TestNetworkConfigService();
         mgr.activate();
 
+
         service.addListener(listener);
 
         provider = new TestProvider();
@@ -349,4 +352,7 @@
             return registerdBefore.contains(deviceId);
         }
     }
+
+    private class TestNetworkConfigService extends NetworkConfigServiceAdapter {
+    }
 }
diff --git a/core/net/src/test/java/org/onosproject/net/host/impl/HostManagerTest.java b/core/net/src/test/java/org/onosproject/net/host/impl/HostManagerTest.java
index 77b199b..9e113f3 100644
--- a/core/net/src/test/java/org/onosproject/net/host/impl/HostManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/host/impl/HostManagerTest.java
@@ -37,6 +37,7 @@
 import org.onlab.packet.VlanId;
 import org.onosproject.event.Event;
 import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.incubator.net.config.NetworkConfigServiceAdapter;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
@@ -123,6 +124,7 @@
         mgr.store = new SimpleHostStore();
         mgr.eventDispatcher = new TestEventDispatcher();
         registry = mgr;
+        mgr.networkConfigService = new TestNetworkConfigService();
         mgr.activate();
 
         mgr.addListener(listener);
@@ -520,4 +522,7 @@
         assertTrue(storedAddresses.size() == 2);
         assertTrue(storedAddresses.equals(Sets.newHashSet(add1, add2)));
     }
+
+    private class TestNetworkConfigService extends NetworkConfigServiceAdapter {
+    }
 }
diff --git a/core/net/src/test/java/org/onosproject/net/link/impl/LinkManagerTest.java b/core/net/src/test/java/org/onosproject/net/link/impl/LinkManagerTest.java
index f164fa8..391b2fd 100644
--- a/core/net/src/test/java/org/onosproject/net/link/impl/LinkManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/link/impl/LinkManagerTest.java
@@ -20,6 +20,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.onosproject.event.Event;
+import org.onosproject.incubator.net.config.NetworkConfigServiceAdapter;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultDevice;
 import org.onosproject.net.Device;
@@ -86,6 +87,7 @@
     protected DeviceManager devmgr = new TestDeviceManager();
 
 
+
     @Before
     public void setUp() {
         mgr = new LinkManager();
@@ -95,6 +97,7 @@
         mgr.store = new SimpleLinkStore();
         mgr.eventDispatcher = new TestEventDispatcher();
         mgr.deviceService = devmgr;
+        mgr.networkConfigService = new TestNetworkConfigService();
         mgr.activate();
 
         DEVICEIDMAP.put(DID1, DEV1);
@@ -302,5 +305,6 @@
         }
 
     }
-
+    private class TestNetworkConfigService extends NetworkConfigServiceAdapter {
+    }
 }
diff --git a/incubator/api/src/test/java/org/onosproject/incubator/net/config/NetworkConfigServiceAdapter.java b/incubator/api/src/test/java/org/onosproject/incubator/net/config/NetworkConfigServiceAdapter.java
new file mode 100644
index 0000000..0e108c9
--- /dev/null
+++ b/incubator/api/src/test/java/org/onosproject/incubator/net/config/NetworkConfigServiceAdapter.java
@@ -0,0 +1,75 @@
+package org.onosproject.incubator.net.config;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.util.Set;
+
+/**
+ * Test adapter for network configuration service.
+ */
+public class NetworkConfigServiceAdapter implements NetworkConfigService {
+    @Override
+    public Set<Class> getSubjectClasses() {
+        return null;
+    }
+
+    @Override
+    public SubjectFactory getSubjectFactory(String subjectKey) {
+        return null;
+    }
+
+    @Override
+    public SubjectFactory getSubjectFactory(Class subjectClass) {
+        return null;
+    }
+
+    @Override
+    public Class<? extends Config> getConfigClass(String subjectKey, String configKey) {
+        return null;
+    }
+
+    @Override
+    public <S> Set<S> getSubjects(Class<S> subjectClass) {
+        return null;
+    }
+
+    @Override
+    public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass) {
+        return null;
+    }
+
+    @Override
+    public <S> Set<? extends Config<S>> getConfigs(S subject) {
+        return null;
+    }
+
+    @Override
+    public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
+        return null;
+    }
+
+    @Override
+    public <S, C extends Config<S>> C addConfig(S subject, Class<C> configClass) {
+        return null;
+    }
+
+    @Override
+    public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, ObjectNode json) {
+        return null;
+    }
+
+    @Override
+    public <S, C extends Config<S>> void removeConfig(S subject, Class<C> configClass) {
+
+    }
+
+    @Override
+    public void addListener(NetworkConfigListener listener) {
+
+    }
+
+    @Override
+    public void removeListener(NetworkConfigListener listener) {
+
+    }
+}
diff --git a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
index a8c8383..10a845f 100644
--- a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
+++ b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
@@ -270,7 +270,11 @@
         private void updateLocation(HostId hid, MacAddress mac,
                                     VlanId vlan, HostLocation hloc) {
             HostDescription desc = new DefaultHostDescription(mac, vlan, hloc);
-            providerService.hostDetected(hid, desc);
+            try {
+                providerService.hostDetected(hid, desc);
+            } catch (IllegalStateException e) {
+                log.debug("Host {} suppressed", hid);
+            }
         }
 
         /**
@@ -286,7 +290,11 @@
                                       VlanId vlan, HostLocation hloc,
                                       IpAddress ip) {
             HostDescription desc = new DefaultHostDescription(mac, vlan, hloc, ip);
-            providerService.hostDetected(hid, desc);
+            try {
+                providerService.hostDetected(hid, desc);
+            } catch (IllegalStateException e) {
+                log.debug("Host {} suppressed", hid);
+            }
         }
 
         @Override
diff --git a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java
index 6f3ffdc..edf9a7b 100644
--- a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java
+++ b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java
@@ -229,7 +229,12 @@
             } else {
                 ld = new DefaultLinkDescription(src, dst, Type.DIRECT);
             }
-            linkProvider.linkDetected(ld);
+
+            try {
+                linkProvider.linkDetected(ld);
+            } catch (IllegalStateException e) {
+                return true;
+            }
             return true;
         }
         return false;
diff --git a/tools/test/configs/override-basic.json b/tools/test/configs/override-basic.json
new file mode 100644
index 0000000..c1f23ce
--- /dev/null
+++ b/tools/test/configs/override-basic.json
@@ -0,0 +1,38 @@
+{
+  "devices": {
+    "of:0000000000000009": {
+      "basic": {
+        "allowed": true,
+        "owner": "Luigi"
+      }
+    },
+    "of:0000000000000008": {
+      "basic": {
+        "name": "NameChangeAgain",
+        "allowed": true,
+        "owner": "Mario"
+      }
+    },
+    "of:0000000000000007": {
+      "basic": {
+        "allowed": true,
+        "owner": "Peach",
+        "latitude": "25"
+      }
+    }
+  },
+  "links": {
+    "of:0000000000000006/2-of:0000000000000007/2": {
+      "basic": {
+        "allowed": true
+      }
+    }
+  },
+  "hosts": {
+    "00:00:00:00:00:03/-1": {
+      "basic": {
+        "allowed": true
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tools/test/configs/sample-basic.json b/tools/test/configs/sample-basic.json
index c3929db..73cbd30 100644
--- a/tools/test/configs/sample-basic.json
+++ b/tools/test/configs/sample-basic.json
@@ -1,13 +1,54 @@
 {
   "devices": {
-    "of:001122334455667788" : {
-      "basic" : {
-        "allowed": false,
-        "name": "Bad Device",
+    "of:0000000000000009": {
+      "basic": {
+        "allowed": true,
         "owner": "Luigi"
       }
+    },
+    "of:0000000000000008": {
+      "basic": {
+        "name": "NameChange",
+        "allowed": true,
+        "owner": "Mario"
+      }
+    },
+    "of:0000000000000007": {
+      "basic": {
+        "allowed": true,
+        "owner": "Peach",
+        "latitude": "25"
+      }
+    },
+    "of:0000000000000003": {
+      "basic": {
+        "allowed": true,
+        "owner": "Wario"
+      }
     }
   },
-  "hosts": {},
-  "links": {}
+  "links": {
+    "of:0000000000000006/2-of:0000000000000007/2": {
+      "basic": {
+        "allowed": true
+      }
+    }
+  },
+  "hosts": {
+    "00:00:00:00:00:03/-1": {
+      "basic": {
+        "allowed": false
+      }
+    },
+    "00:00:00:00:00:02/-1": {
+      "basic": {
+        "allowed": false
+      }
+    },
+    "00:00:00:00:00:01/-1": {
+      "basic": {
+        "allowed": false
+      }
+    }
+  }
 }
\ No newline at end of file
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
index 15c472d..7837fca 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java
@@ -87,7 +87,7 @@
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = mapper().createObjectNode();
         produceSubjectJson(service, root,
-                           service.getSubjectFactory(subjectKey).createSubject(subject));
+                service.getSubjectFactory(subjectKey).createSubject(subject));
         return ok(root).build();
     }
 
@@ -140,7 +140,7 @@
         ObjectNode root = (ObjectNode) mapper().readTree(request);
         root.fieldNames()
                 .forEachRemaining(sk -> consumeJson(service, (ObjectNode) root.path(sk),
-                                                    service.getSubjectFactory(sk)));
+                        service.getSubjectFactory(sk)));
         return Response.ok().build();
     }
 
@@ -183,8 +183,8 @@
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = (ObjectNode) mapper().readTree(request);
         consumeSubjectJson(service, root,
-                           service.getSubjectFactory(subjectKey).createSubject(subject),
-                           subjectKey);
+                service.getSubjectFactory(subjectKey).createSubject(subject),
+                subjectKey);
         return Response.ok().build();
     }
 
@@ -210,16 +210,16 @@
         NetworkConfigService service = get(NetworkConfigService.class);
         ObjectNode root = (ObjectNode) mapper().readTree(request);
         service.applyConfig(service.getSubjectFactory(subjectKey).createSubject(subject),
-                            service.getConfigClass(subjectKey, configKey), root);
+                service.getConfigClass(subjectKey, configKey), root);
         return Response.ok().build();
     }
 
     private void consumeJson(NetworkConfigService service, ObjectNode classNode,
                              SubjectFactory subjectFactory) {
         classNode.fieldNames().forEachRemaining(s ->
-            consumeSubjectJson(service, (ObjectNode) classNode.path(s),
-                               subjectFactory.createSubject(s),
-                               subjectFactory.subjectKey()));
+                consumeSubjectJson(service, (ObjectNode) classNode.path(s),
+                        subjectFactory.createSubject(s),
+                        subjectFactory.subjectKey()));
     }
 
     private void consumeSubjectJson(NetworkConfigService service,
@@ -227,7 +227,7 @@
                                     String subjectKey) {
         subjectNode.fieldNames().forEachRemaining(c ->
             service.applyConfig(subject, service.getConfigClass(subjectKey, c),
-                                (ObjectNode) subjectNode.path(c)));
+                    (ObjectNode) subjectNode.path(c)));
     }
 
 
@@ -272,4 +272,46 @@
         return Response.ok().build();
     }
 
+
+    /**
+     * Clears all network configurations.
+     *
+     * @return empty response
+     */
+    @DELETE
+    @Consumes(MediaType.APPLICATION_JSON)
+    @SuppressWarnings("unchecked")
+    public Response upload() {
+        NetworkConfigService service = get(NetworkConfigService.class);
+        service.getSubjectClasses().forEach(subjectClass -> {
+            service.getSubjects(subjectClass).forEach(subject -> {
+                service.getConfigs(subject).forEach(config -> {
+                    service.removeConfig(subject, config.getClass());
+                });
+            });
+        });
+        return Response.ok().build();
+    }
+
+
+    // TODO: this one below doesn't work correctly
+    /**
+     * Clears network configuration for the specified subject class.
+     *
+     * @param subjectKey subject class key
+     * @return empty response
+     */
+    @DELETE
+    @Path("{subjectKey}/")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @SuppressWarnings("unchecked")
+    public Response upload(@PathParam("subjectKey") String subjectKey) {
+        NetworkConfigService service = get(NetworkConfigService.class);
+        service.getSubjects(service.getSubjectFactory(subjectKey).getClass()).forEach(subject -> {
+            service.getConfigs(subject).forEach(config -> {
+                service.removeConfig(subject, config.getClass());
+            });
+        });
+        return Response.ok().build();
+    }
 }