[ONOS-5749] Initial implementation LISP device provider

Change-Id: Ie1ac2ff82c62717fc8e2b045f964d026fe30effa
diff --git a/protocols/lisp/api/src/main/java/org/onosproject/lisp/ctl/LispController.java b/protocols/lisp/api/src/main/java/org/onosproject/lisp/ctl/LispController.java
index 1a3641d..bbfd5c2 100644
--- a/protocols/lisp/api/src/main/java/org/onosproject/lisp/ctl/LispController.java
+++ b/protocols/lisp/api/src/main/java/org/onosproject/lisp/ctl/LispController.java
@@ -36,6 +36,24 @@
     Iterable<LispRouter> getSubscribedRouters();
 
     /**
+     * Connects to a specific LISP router.
+     * If the connection is established, it creates and adds the device to
+     * ONOS core as a LISP router.
+     *
+     * @param routerId router identifier
+     * @return LispRouter LISP router
+     */
+    LispRouter connectRouter(LispRouterId routerId);
+
+    /**
+     * Disconnects a LISP router and notify router removal event.
+     *
+     * @param routerId router identifier
+     * @param remove   true only if want to notify router removal event
+     */
+    void disconnectRouter(LispRouterId routerId, boolean remove);
+
+    /**
      * Obtains the actual router for the given LispRouterId.
      *
      * @param routerId the router to fetch
diff --git a/protocols/lisp/api/src/test/java/org/onosproject/lisp/ctl/LispControllerAdapter.java b/protocols/lisp/api/src/test/java/org/onosproject/lisp/ctl/LispControllerAdapter.java
index aeebeeb..f628c5d 100644
--- a/protocols/lisp/api/src/test/java/org/onosproject/lisp/ctl/LispControllerAdapter.java
+++ b/protocols/lisp/api/src/test/java/org/onosproject/lisp/ctl/LispControllerAdapter.java
@@ -30,6 +30,16 @@
     }
 
     @Override
+    public LispRouter connectRouter(LispRouterId routerId) {
+        return null;
+    }
+
+    @Override
+    public void disconnectRouter(LispRouterId routerId, boolean remove) {
+
+    }
+
+    @Override
     public LispRouter getRouter(LispRouterId routerId) {
         return null;
     }
diff --git a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispControllerImpl.java b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispControllerImpl.java
index 7fa1e98..a409d94 100644
--- a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispControllerImpl.java
+++ b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispControllerImpl.java
@@ -186,6 +186,30 @@
     }
 
     @Override
+    public LispRouter connectRouter(LispRouterId routerId) {
+        if (connectedRouters.containsKey(routerId)) {
+            log.debug("LISP router {} is already existing", routerId);
+            return connectedRouters.get(routerId);
+        } else {
+            // TODO: currently we do not consider to add LISP router from netcfg
+            log.warn("Adding router from netcfg is not supported currently");
+            return null;
+        }
+    }
+
+    @Override
+    public void disconnectRouter(LispRouterId routerId, boolean remove) {
+        if (!connectedRouters.containsKey(routerId)) {
+            log.warn("LISP router {} is not existing", routerId);
+        } else {
+            connectedRouters.get(routerId).disconnectRouter();
+            if (remove) {
+                agent.removeConnectedRouter(routerId);
+            }
+        }
+    }
+
+    @Override
     public LispRouter getRouter(LispRouterId routerId) {
         return connectedRouters.get(routerId);
     }
diff --git a/providers/lisp/device/src/main/java/org/onosproject/provider/lisp/device/impl/LispDeviceProvider.java b/providers/lisp/device/src/main/java/org/onosproject/provider/lisp/device/impl/LispDeviceProvider.java
index 0183959..fa032cc 100644
--- a/providers/lisp/device/src/main/java/org/onosproject/provider/lisp/device/impl/LispDeviceProvider.java
+++ b/providers/lisp/device/src/main/java/org/onosproject/provider/lisp/device/impl/LispDeviceProvider.java
@@ -15,33 +15,55 @@
  */
 package org.onosproject.provider.lisp.device.impl;
 
-import org.apache.felix.scr.annotations.Activate;
+import com.google.common.base.Preconditions;
 import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+
+import org.onlab.packet.ChassisId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.lisp.ctl.LispController;
+import org.onosproject.lisp.ctl.LispRouterId;
+import org.onosproject.lisp.ctl.LispRouterListener;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.MastershipRole;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.SparseAnnotations;
 import org.onosproject.net.device.DeviceProvider;
+import org.onosproject.net.device.DeviceProviderRegistry;
+import org.onosproject.net.device.DeviceProviderService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DefaultDeviceDescription;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.net.URI;
+import java.net.URISyntaxException;
+
 /**
  * Provider which uses an LISP controller to detect device.
  */
 @Component(immediate = true)
-public class LispDeviceProvider extends AbstractProvider
-        implements DeviceProvider {
+public class LispDeviceProvider extends AbstractProvider implements DeviceProvider {
 
     private static final Logger log = LoggerFactory.getLogger(LispDeviceProvider.class);
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceProviderRegistry providerRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -51,6 +73,14 @@
     private static final String SCHEME_NAME = "lisp";
     private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.lisp.provider.device";
 
+    private static final String UNKNOWN = "unknown";
+    private static final String IS_NULL_MSG = "LISP device info is null";
+    private static final String IPADDRESS = "ipaddress";
+    private static final String LISP = "lisp";
+
+    protected DeviceProviderService providerService;
+    private InternalLispRouterListener routerListener = new InternalLispRouterListener();
+
     private ApplicationId appId;
 
     /**
@@ -62,18 +92,25 @@
 
     @Activate
     public void activate() {
+        providerService = providerRegistry.register(this);
         appId = coreService.registerApplication(APP_NAME);
+        controller.addRouterListener(routerListener);
         log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
+        controller.getRouters().forEach(router -> controller.disconnectRouter(
+                            new LispRouterId(router.routerId()), true));
+        controller.removeRouterListener(routerListener);
+        providerRegistry.unregister(this);
+        providerService = null;
         log.info("Stopped");
     }
 
     @Override
     public void triggerProbe(DeviceId deviceId) {
-
+        log.info("Triggering probe on device {}", deviceId);
     }
 
     @Override
@@ -83,11 +120,105 @@
 
     @Override
     public boolean isReachable(DeviceId deviceId) {
-        return false;
+        // TODO: need to provide a way to send probe message to LISP router,
+        // to check the device reachability.
+        return true;
     }
 
     @Override
     public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
+        log.info("This operation is irrelevant for LISP router");
+    }
 
+    /**
+     * Adds a LISP router into device store.
+     */
+    private void connectDevice(LispRouterId routerId) {
+        DeviceId deviceId = getDeviceId(routerId.id().toString());
+        Preconditions.checkNotNull(deviceId, IS_NULL_MSG);
+
+        // formulate LISP router object
+        ChassisId cid = new ChassisId();
+        String ipAddress = routerId.id().toString();
+        SparseAnnotations annotations = DefaultAnnotations.builder()
+                .set(IPADDRESS, ipAddress)
+                .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
+                .build();
+        DeviceDescription deviceDescription = new DefaultDeviceDescription(
+                deviceId.uri(),
+                Device.Type.ROUTER,
+                UNKNOWN, UNKNOWN,
+                UNKNOWN, UNKNOWN,
+                cid, false,
+                annotations);
+        if (deviceService.getDevice(deviceId) == null) {
+            providerService.deviceConnected(deviceId, deviceDescription);
+        }
+        checkAndUpdateDevice(deviceId, deviceDescription);
+    }
+
+    /**
+     * Checks whether a specified device is available.
+     *
+     * @param deviceId          device identifier
+     * @param deviceDescription device description
+     */
+    private void checkAndUpdateDevice(DeviceId deviceId, DeviceDescription deviceDescription) {
+        if (deviceService.getDevice(deviceId) == null) {
+            log.warn("LISP router {} has not been added to store", deviceId);
+        } else {
+            boolean isReachable = isReachable(deviceId);
+            if (isReachable && !deviceService.isAvailable(deviceId)) {
+                // TODO: handle the mastership logic
+            } else if (!isReachable && deviceService.isAvailable(deviceId)) {
+                providerService.deviceDisconnected(deviceId);
+            }
+        }
+    }
+
+    /**
+     * Listener for LISP router events.
+     */
+    private class InternalLispRouterListener implements LispRouterListener {
+
+        @Override
+        public void routerAdded(LispRouterId routerId) {
+            connectDevice(routerId);
+            log.debug("LISP router {} added to core.", routerId);
+        }
+
+        @Override
+        public void routerRemoved(LispRouterId routerId) {
+            Preconditions.checkNotNull(routerId, IS_NULL_MSG);
+
+            DeviceId deviceId = getDeviceId(routerId.id().toString());
+            if (deviceService.getDevice(deviceId) != null) {
+                providerService.deviceDisconnected(deviceId);
+                log.debug("LISP router {} removed from LISP controller", deviceId);
+            } else {
+                log.warn("LISP router {} does not exist in the store, " +
+                         "or it may already have been removed", deviceId);
+            }
+        }
+
+        @Override
+        public void routerChanged(LispRouterId routerId) {
+
+        }
+    }
+
+    /**
+     * Obtains the DeviceId contains IP address of LISP router.
+     *
+     * @param ip IP address
+     * @return DeviceId device identifier
+     */
+    private DeviceId getDeviceId(String ip) {
+        try {
+            return DeviceId.deviceId(new URI(LISP, ip, null));
+        } catch (URISyntaxException e) {
+            throw new IllegalArgumentException("Unable to build deviceID for device "
+                    + ip, e);
+        }
     }
 }