Make scheme and remote URI configurable on big switch app (ONOS-3614)

Change-Id: I8b77a2e5e23e0191367c3a5b7130bba865c2dc09
diff --git a/ecord/co/pom.xml b/ecord/co/pom.xml
index 48a7698..9337cc2 100644
--- a/ecord/co/pom.xml
+++ b/ecord/co/pom.xml
@@ -98,6 +98,13 @@
             <artifactId>jersey-client</artifactId>
             <version>2.22.1</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <version>4.3.1</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/ecord/co/src/main/java/org/onosproject/ecord/co/BigSwitchDeviceProvider.java b/ecord/co/src/main/java/org/onosproject/ecord/co/BigSwitchDeviceProvider.java
index a098193..e37f588 100644
--- a/ecord/co/src/main/java/org/onosproject/ecord/co/BigSwitchDeviceProvider.java
+++ b/ecord/co/src/main/java/org/onosproject/ecord/co/BigSwitchDeviceProvider.java
@@ -16,13 +16,18 @@
 package org.onosproject.ecord.co;
 
 import org.apache.commons.lang3.tuple.Pair;
+import com.google.common.base.Strings;
 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.glassfish.jersey.client.ClientProperties;
 import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.incubator.rpc.RemoteServiceContext;
 import org.onosproject.incubator.rpc.RemoteServiceDirectory;
@@ -41,9 +46,9 @@
 import org.onosproject.net.device.DeviceProviderRegistry;
 import org.onosproject.net.device.DeviceProviderService;
 import org.onosproject.net.device.PortDescription;
-import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
 import org.slf4j.Logger;
+import org.osgi.service.component.ComponentContext;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -58,20 +63,41 @@
 import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import java.util.Dictionary;
 
 import static org.onosproject.ecord.co.BigSwitchManager.REALIZED_BY;
 import static org.onosproject.net.config.basics.SubjectFactories.CONNECT_POINT_SUBJECT_FACTORY;
 import static org.slf4j.LoggerFactory.getLogger;
 
+/* To configure the BigSwitchDevice parameters at startup, add configuration to tools/package/config/component-cfg.json
+   before using onos-package command.
+   Configuration example:
+   {
+     "org.onosproject.ecord.co.BigSwitchDeviceProvider": {
+       "providerScheme": "bigswitch",
+       "providerId": "org.onosproject.bigswitch",
+       "remoteUri": "local://localhost",
+       "metroIp": "localhost"
+     }
+   }
+ */
+
 /**
  * Device provider which exposes a big switch abstraction of the underlying data path.
  */
 @Component(immediate = true)
-public class BigSwitchDeviceProvider extends AbstractProvider implements DeviceProvider {
+public class BigSwitchDeviceProvider implements DeviceProvider {
 
     private static final Logger LOG = getLogger(BigSwitchDeviceProvider.class);
 
-    private static final String SCHEME = "bigswitch";
+    private static final String PROP_SCHEME = "providerScheme";
+    private static final String DEFAULT_SCHEME = "bigswitch";
+    private static final String PROP_ID = "providerId";
+    private static final String DEFAULT_ID = "org.onosproject.bigswitch";
+    private static final String PROP_REMOTE_URI = "remoteUri";
+    private static final String DEFAULT_REMOTE_URI = "local://localhost";
+    private static final String PROP_METRO_IP = "metroIp";
+    private static final String DEFAULT_METRO_IP = "localhost";
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected BigSwitchService bigSwitchService;
@@ -85,13 +111,8 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetworkConfigRegistry cfgRegistry;
 
-    private String dpidScheme = SCHEME;
-
-    private String rpcScheme = "grpc";
-
-    private int rpcPort = 11984;
-    // Metro ONOS IP
-    private String metroIp = "172.16.218.128";
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService cfgService;
 
     private BigSwitch bigSwitch;
     private DeviceDescription bigSwitchDescription;
@@ -109,49 +130,111 @@
             }
         };
 
+    @Property(name = PROP_SCHEME, value = DEFAULT_SCHEME,
+            label = "Provider scheme used to register a big switch device")
+    private String schemeProp = DEFAULT_SCHEME;
+
+    @Property(name = PROP_ID, value = DEFAULT_ID,
+            label = "Provider ID used to register a big switch device")
+    private String idProp = DEFAULT_ID;
+
+    @Property(name = PROP_REMOTE_URI, value = DEFAULT_REMOTE_URI,
+            label = "URI of remote host to connect via RPC service")
+    private String remoteUri = DEFAULT_REMOTE_URI;
+
+    @Property(name = PROP_METRO_IP, value = DEFAULT_METRO_IP,
+            label = "IP address or hostname of metro ONOS instance to make REST calls")
+    private String metroIp = DEFAULT_METRO_IP;
+
+    private ProviderId providerId;
+
     @Activate
     public void activate() {
-        // Create big switch device and description
-        DeviceId deviceId = DeviceId.deviceId(dpidScheme + ':' + clusterService.getLocalNode().ip());
-        bigSwitch = new BigSwitch(deviceId, this.id());
-        buildDeviceDescription();
-        // Register this device provider remotely
-        // TODO: make remote configurable
-        RemoteServiceContext remoteServiceContext
-            = rpcService.get(URI.create(rpcScheme + "://" + metroIp + ":" + rpcPort));
-        providerRegistry = remoteServiceContext.get(DeviceProviderRegistry.class);
-        providerService = providerRegistry.register(this);
-
+        cfgService.registerProperties(getClass());
+        registerToDeviceProvider();
         NetworkConfigListener cfglistener = new InternalConfigListener();
         cfgRegistry.addListener(cfglistener);
         cfgRegistry.registerConfigFactory(xcConfigFactory);
 
-        // Start big switch service and register device
-        bigSwitchService.addListener(listener);
-        registerDevice();
-
         LOG.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
-        unregisterDevice();
         cfgRegistry.unregisterConfigFactory(xcConfigFactory);
-        providerRegistry.unregister(this);
+        cfgService.unregisterProperties(getClass(), false);
+
+        unregisterFromDeviceProvider();
         // Won't hurt but necessary?
         providerService = null;
+        providerId = null;
         LOG.info("Stopped");
     }
 
-    private void registerDevice() {
-        providerService.deviceConnected(bigSwitch.id(), bigSwitchDescription);
-        providerService.updatePorts(bigSwitch.id(), bigSwitchService.getPorts());
-        bigSwitchService.getPorts().stream()
-            .forEach(this::advertiseCrossConnectLinks);
+    @Modified
+    public void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+        boolean needsRegistration = false;
+        String s = Tools.get(properties, PROP_SCHEME);
+        if (!schemeProp.equals(s)) {
+            schemeProp = Strings.isNullOrEmpty(s) ? DEFAULT_SCHEME : s;
+            needsRegistration = true;
+        }
+        s = Tools.get(properties, PROP_ID);
+        if (!idProp.equals(s)) {
+            idProp = Strings.isNullOrEmpty(s) ? DEFAULT_ID : s;
+            needsRegistration = true;
+        }
+        s = Tools.get(properties, PROP_REMOTE_URI);
+        if (!remoteUri.equals(s)) {
+            remoteUri = Strings.isNullOrEmpty(s) ? DEFAULT_REMOTE_URI : s;
+            needsRegistration = true;
+        }
+
+        if (needsRegistration) {
+            // unregister from DeviceProvider with old parameters
+            unregisterFromDeviceProvider();
+            // register to DeviceProvider with new parameters
+            registerToDeviceProvider();
+        }
+
+        s = Tools.get(properties, PROP_METRO_IP);
+        if (!s.equals(metroIp)) {
+            metroIp = Strings.isNullOrEmpty(s) ? DEFAULT_METRO_IP : s;
+            advertiseCrossConnectLinksOnAllPorts();
+        }
+
+        LOG.info("Restarted");
     }
 
-    private void unregisterDevice() {
+    private void registerToDeviceProvider() {
+        RemoteServiceContext remoteServiceContext;
+        try {
+            remoteServiceContext = rpcService.get(URI.create(remoteUri));
+        } catch (UnsupportedOperationException e) {
+            LOG.error("Unsupported URI: {}", remoteUri);
+            return;
+        }
+
+        providerId = new ProviderId(schemeProp, idProp);
+        // Create big switch device and description
+        DeviceId deviceId = DeviceId.deviceId(schemeProp + ':' + clusterService.getLocalNode().ip());
+        bigSwitch = new BigSwitch(deviceId, this.id());
+        bigSwitchDescription = new DefaultDeviceDescription(bigSwitch.id().uri(),
+                bigSwitch.type(), bigSwitch.manufacturer(),
+                bigSwitch.hwVersion(), bigSwitch.swVersion(), bigSwitch.serialNumber(), bigSwitch.chassisId());
+        providerRegistry = remoteServiceContext.get(DeviceProviderRegistry.class);
+        providerService = providerRegistry.register(this);
+        // Start big switch service and register device
+        providerService.deviceConnected(bigSwitch.id(), bigSwitchDescription);
+        providerService.updatePorts(bigSwitch.id(), bigSwitchService.getPorts());
+        advertiseCrossConnectLinksOnAllPorts();
+        bigSwitchService.addListener(listener);
+    }
+
+    private void unregisterFromDeviceProvider() {
         providerService.deviceDisconnected(bigSwitch.id());
+        providerRegistry.unregister(this);
     }
 
     private ConnectPoint toConnectPoint(String strCp) {
@@ -227,7 +310,7 @@
                         .post(Entity.entity(cfg.toString(), MediaType.APPLICATION_JSON));
 
 
-        if (response.getStatusInfo() != Response.Status.OK) {
+        if (response.getStatus() != Response.Status.OK.getStatusCode()) {
             LOG.error("POST failed {}\n{}", response, cfg.toString());
             return false;
         }
@@ -244,6 +327,11 @@
         });
     }
 
+    private void advertiseCrossConnectLinksOnAllPorts() {
+        bigSwitchService.getPorts().stream()
+            .forEach(BigSwitchDeviceProvider.this::advertiseCrossConnectLinks);
+    }
+
     private class InternalListener implements BigSwitchListener {
         @Override
         public void event(BigSwitchEvent event) {
@@ -268,17 +356,15 @@
         }
     }
 
-    private void buildDeviceDescription() {
-        bigSwitchDescription = new DefaultDeviceDescription(bigSwitch.id().uri(),
-                bigSwitch.type(), bigSwitch.manufacturer(),
-                bigSwitch.hwVersion(), bigSwitch.swVersion(), bigSwitch.serialNumber(), bigSwitch.chassisId());
-    }
-
     /**
      * A big switch device provider implementation.
      */
     public BigSwitchDeviceProvider() {
-        super(new ProviderId(SCHEME, "org.onosproject.bigswitch"));
+    }
+
+    @Override
+    public ProviderId id() {
+        return providerId;
     }
 
     @Override
@@ -299,8 +385,7 @@
         @Override
         public void event(NetworkConfigEvent event) {
             if (event.configClass() == CrossConnectConfig.class) {
-                bigSwitchService.getPorts().stream()
-                    .forEach(BigSwitchDeviceProvider.this::advertiseCrossConnectLinks);
+                advertiseCrossConnectLinksOnAllPorts();
             }
         }
     }