Added ability to remove host by CLI and by the provider on device/port down events.

Change-Id: I28de4b6b5bbfb5a00f35e1808bcd916369d7d1a4
diff --git a/providers/host/src/main/java/org/onlab/onos/provider/host/impl/HostLocationProvider.java b/providers/host/src/main/java/org/onlab/onos/provider/host/impl/HostLocationProvider.java
index 48762bb..8821d76 100644
--- a/providers/host/src/main/java/org/onlab/onos/provider/host/impl/HostLocationProvider.java
+++ b/providers/host/src/main/java/org/onlab/onos/provider/host/impl/HostLocationProvider.java
@@ -15,22 +15,27 @@
  */
 package org.onlab.onos.provider.host.impl;
 
-import static org.slf4j.LoggerFactory.getLogger;
-
 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.onos.net.ConnectPoint;
+import org.onlab.onos.net.DeviceId;
 import org.onlab.onos.net.Host;
 import org.onlab.onos.net.HostId;
 import org.onlab.onos.net.HostLocation;
+import org.onlab.onos.net.device.DeviceEvent;
+import org.onlab.onos.net.device.DeviceListener;
+import org.onlab.onos.net.device.DeviceService;
 import org.onlab.onos.net.host.DefaultHostDescription;
 import org.onlab.onos.net.host.HostDescription;
 import org.onlab.onos.net.host.HostProvider;
 import org.onlab.onos.net.host.HostProviderRegistry;
 import org.onlab.onos.net.host.HostProviderService;
+import org.onlab.onos.net.host.HostService;
 import org.onlab.onos.net.packet.PacketContext;
 import org.onlab.onos.net.packet.PacketProcessor;
 import org.onlab.onos.net.packet.PacketService;
@@ -42,8 +47,14 @@
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.VlanId;
+import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
+import java.util.Dictionary;
+import java.util.Set;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * Provider which uses an OpenFlow controller to detect network
  * end-station hosts.
@@ -62,9 +73,20 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected TopologyService topologyService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
     private HostProviderService providerService;
 
     private final InternalHostProvider processor = new InternalHostProvider();
+    private final DeviceListener deviceListener = new InternalDeviceListener();
+
+    @Property(name = "hostRemovalEnabled", boolValue = true,
+            label = "Enable host removal on port/device down events")
+    private boolean hostRemovalEnabled = true;
 
 
     /**
@@ -75,9 +97,11 @@
     }
 
     @Activate
-    public void activate() {
+    public void activate(ComponentContext context) {
+        modified(context);
         providerService = providerRegistry.register(this);
         pktService.addProcessor(processor, 1);
+        deviceService.addListener(deviceListener);
         log.info("Started");
     }
 
@@ -89,6 +113,20 @@
         log.info("Stopped");
     }
 
+    @Modified
+    public void modified(ComponentContext context) {
+        Dictionary properties = context.getProperties();
+        try {
+            String flag = (String) properties.get("hostRemovalEnabled");
+            if (flag != null) {
+                hostRemovalEnabled = flag.equals("true");
+            }
+        } catch (Exception e) {
+            hostRemovalEnabled = true;
+        }
+        log.info("Host removal is {}", hostRemovalEnabled ? "enabled" : "disabled");
+    }
+
     @Override
     public void triggerProbe(Host host) {
         log.info("Triggering probe on device {}", host);
@@ -120,8 +158,8 @@
             if (eth.getEtherType() == Ethernet.TYPE_ARP) {
                 ARP arp = (ARP) eth.getPayload();
                 IpAddress ip =
-                    IpAddress.valueOf(IpAddress.Version.INET,
-                                      arp.getSenderProtocolAddress());
+                        IpAddress.valueOf(IpAddress.Version.INET,
+                                          arp.getSenderProtocolAddress());
                 HostDescription hdescr =
                         new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
                 providerService.hostDetected(hid, hdescr);
@@ -135,4 +173,37 @@
             }
         }
     }
+
+    // Auxiliary listener to device events.
+    private class InternalDeviceListener implements DeviceListener {
+        @Override
+        public void event(DeviceEvent event) {
+            if (!hostRemovalEnabled) {
+                return;
+            }
+
+            DeviceEvent.Type type = event.type();
+            DeviceId deviceId = event.subject().id();
+            if (type == DeviceEvent.Type.PORT_UPDATED) {
+                ConnectPoint point = new ConnectPoint(deviceId, event.port().number());
+                removeHosts(hostService.getConnectedHosts(point));
+
+            } else if (type == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED) {
+                if (!deviceService.isAvailable(deviceId)) {
+                    removeHosts(hostService.getConnectedHosts(deviceId));
+                }
+
+            } else if (type == DeviceEvent.Type.DEVICE_REMOVED) {
+                removeHosts(hostService.getConnectedHosts(deviceId));
+            }
+        }
+    }
+
+    // Signals host vanish for all specified hosts.
+    private void removeHosts(Set<Host> hosts) {
+        for (Host host : hosts) {
+            providerService.hostVanished(host.id());
+        }
+    }
+
 }