Modified PI-ECMP demo app to work with 2x2 clos topology

Also minor fix to general device provider.

Change-Id: Ia2c76a55d5e28d2be8edb75ee3c34ada557ada12
diff --git a/apps/pi-demo/common/src/main/java/org/onosproject/pi/demo/app/common/AbstractUpgradableFabricApp.java b/apps/pi-demo/common/src/main/java/org/onosproject/pi/demo/app/common/AbstractUpgradableFabricApp.java
index 4221eea..f77763e 100644
--- a/apps/pi-demo/common/src/main/java/org/onosproject/pi/demo/app/common/AbstractUpgradableFabricApp.java
+++ b/apps/pi-demo/common/src/main/java/org/onosproject/pi/demo/app/common/AbstractUpgradableFabricApp.java
@@ -58,7 +58,6 @@
 import org.slf4j.Logger;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -72,6 +71,7 @@
 import java.util.stream.Stream;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
 import static java.util.stream.Collectors.toSet;
 import static java.util.stream.Stream.concat;
 import static org.onlab.util.Tools.groupedThreads;
@@ -87,8 +87,8 @@
 
     private static final Map<String, AbstractUpgradableFabricApp> APP_HANDLES = Maps.newConcurrentMap();
 
-    private static final int NUM_LEAFS = 3;
-    private static final int NUM_SPINES = 3;
+    private static final int NUM_LEAFS = 2;
+    private static final int NUM_SPINES = 2;
     private static final int FLOW_PRIORITY = 100;
 
     private static final int CLEANUP_SLEEP = 2000;
@@ -103,7 +103,6 @@
             .newFixedThreadPool(8, groupedThreads("onos/pi-demo-app", "pi-app-task", log));
 
     private final String appName;
-    private final String configurationName;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected TopologyService topologyService;
@@ -126,6 +125,8 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private PiPipeconfService piPipeconfService;
 
+    private boolean withImbalancedStriping = false;
+
     private boolean appActive = false;
     private boolean appFreezed = false;
 
@@ -135,7 +136,7 @@
     private boolean flowRuleGenerated = false;
     private ApplicationId appId;
 
-    private PiPipeconf pipeconf;
+    private PiPipeconf appPipeconf;
 
     private Set<DeviceId> leafSwitches;
     private Set<DeviceId> spineSwitches;
@@ -150,13 +151,11 @@
      * Creates a new PI fabric app.
      *
      * @param appName           app name
-     * @param configurationName a common name for the P4 program / PI configuration used by this app
-     * @param pipeconf a P4Runtime device context to be used on devices
+     * @param appPipeconf       a P4Runtime device context to be used on devices
      */
-    protected AbstractUpgradableFabricApp(String appName, String configurationName, PiPipeconf pipeconf) {
+    protected AbstractUpgradableFabricApp(String appName, PiPipeconf appPipeconf) {
         this.appName = checkNotNull(appName);
-        this.configurationName = checkNotNull(configurationName);
-        this.pipeconf = checkNotNull(pipeconf);
+        this.appPipeconf = checkNotNull(appPipeconf);
     }
 
     @Activate
@@ -183,7 +182,7 @@
         topologyService.addListener(topologyListener);
         deviceService.addListener(deviceListener);
         hostService.addListener(hostListener);
-        piPipeconfService.register(pipeconf);
+        piPipeconfService.register(appPipeconf);
 
         init();
 
@@ -204,7 +203,7 @@
         topologyService.removeListener(topologyListener);
         hostService.removeListener(hostListener);
         flowRuleService.removeFlowRulesById(appId);
-        piPipeconfService.remove(pipeconf.id());
+        piPipeconfService.remove(appPipeconf.id());
 
         appActive = false;
         APP_HANDLES.remove(appName);
@@ -306,10 +305,13 @@
         try {
             // Set pipeconfflag if not already done.
             if (!pipeconfFlags.getOrDefault(deviceId, false)) {
-                if (pipeconf.id().equals(piPipeconfService.ofDevice(deviceId))) {
+                if (piPipeconfService.ofDevice(deviceId).isPresent() &&
+                        appPipeconf.id().equals(piPipeconfService.ofDevice(deviceId).get())) {
                     pipeconfFlags.put(device.id(), true);
                 } else {
-                    log.warn("No pipeconf can be associated to the device {}.", deviceId);
+                    log.warn("Wrong pipeconf for {}, expecting {}, but found {}, aborting deploy",
+                             deviceId, appPipeconf.id(), piPipeconfService.ofDevice(deviceId).get());
+                    return;
                 }
             }
 
@@ -319,13 +321,11 @@
             }
 
             // Install rules.
-            if (!ruleFlags.getOrDefault(deviceId, false)) {
-                List<FlowRule> rules = deviceFlowRules.getOrDefault(deviceId, Collections.emptyList());
-                if (rules.size() > 0) {
-                    log.info("Installing rules for {}...", deviceId);
-                    installFlowRules(rules);
-                    ruleFlags.put(deviceId, true);
-                }
+            if (!ruleFlags.getOrDefault(deviceId, false) &&
+                    deviceFlowRules.containsKey(deviceId)) {
+                log.info("Installing rules for {}...", deviceId);
+                installFlowRules(deviceFlowRules.get(deviceId));
+                ruleFlags.put(deviceId, true);
             }
         } finally {
             lock.unlock();
@@ -350,7 +350,7 @@
     }
 
     /**
-     * Generates the flow rules to provide host-to-host connectivity for the given topology and hosts.
+     * Generates flow rules to provide host-to-host connectivity for the given topology and hosts.
      *
      * @param topo  a topology
      * @param hosts a collection of hosts
@@ -379,8 +379,9 @@
 
         for (DeviceId did : spines) {
             int portCount = deviceService.getPorts(did).size();
-            // Expected port count: num leafs + 1 redundant leaf link
-            if (portCount != (NUM_LEAFS + 1)) {
+            // Expected port count: num leafs + 1 redundant leaf link (if imbalanced)
+            int expectedSpinePortCount = NUM_LEAFS + (withImbalancedStriping ? 1 : 0);
+            if (portCount != expectedSpinePortCount) {
                 log.info("Invalid port count for spine, aborting... > deviceId={}, portCount={}", did, portCount);
                 return;
             }
@@ -388,7 +389,8 @@
         for (DeviceId did : leafs) {
             int portCount = deviceService.getPorts(did).size();
             // Expected port count: num spines + host port + 1 redundant spine link
-            if (portCount != (NUM_SPINES + 2)) {
+            int expectedLeafPortCount = NUM_LEAFS + (withImbalancedStriping ? 2 : 1);
+            if (portCount != expectedLeafPortCount) {
                 log.info("Invalid port count for leaf, aborting... > deviceId={}, portCount={}", did, portCount);
                 return;
             }
@@ -414,7 +416,7 @@
                 newFlowRules.addAll(generateSpineRules(deviceId, hosts, topo));
             }
         } catch (FlowRuleGeneratorException e) {
-            log.warn("Exception while executing flow rule generator: ", e.toString());
+            log.warn("Exception while executing flow rule generator: {}", e.toString());
             return;
         }
 
@@ -455,20 +457,16 @@
      */
     protected FlowRule.Builder flowRuleBuilder(DeviceId did, String tableName) throws FlowRuleGeneratorException {
 
-        final PiPipelineInterpreter interpreter;
-        try {
-            interpreter = (PiPipelineInterpreter) pipeconf.implementation(PiPipelineInterpreter.class)
-                    .orElse(null)
-                    .newInstance();
-        } catch (InstantiationException | IllegalAccessException e) {
-            throw new FlowRuleGeneratorException("Unable to instantiate interpreter of pipeconf " + pipeconf.id());
+        final Device device = deviceService.getDevice(did);
+        if (!device.is(PiPipelineInterpreter.class)) {
+            throw new FlowRuleGeneratorException(format("Device %s has no PiPipelineInterpreter", did));
         }
-
-        int flowRuleTableId;
+        final PiPipelineInterpreter interpreter = device.as(PiPipelineInterpreter.class);
+        final int flowRuleTableId;
         if (interpreter.mapPiTableId(PiTableId.of(tableName)).isPresent()) {
-            flowRuleTableId = interpreter.mapPiTableId(PiTableId.of(tableName)).get().intValue();
+            flowRuleTableId = interpreter.mapPiTableId(PiTableId.of(tableName)).get();
         } else {
-            throw new FlowRuleGeneratorException("Unknown table " + tableName);
+            throw new FlowRuleGeneratorException(format("Unknown table %s in interpreter", tableName));
         }
 
         return DefaultFlowRule.builder()
@@ -512,11 +510,9 @@
         public boolean isRelevant(TopologyEvent event) {
             return !appFreezed &&
                     // If at least one reason is of type DEVICE_ADDED.
-                    event.reasons().stream().
-                            filter(r -> r instanceof DeviceEvent)
-                            .filter(r -> ((DeviceEvent) r).type() == DEVICE_ADDED)
-                            .findAny()
-                            .isPresent();
+                    event.reasons().stream()
+                            .filter(r -> r instanceof DeviceEvent)
+                            .anyMatch(r -> ((DeviceEvent) r).type() == DEVICE_ADDED);
         }
     }
 
diff --git a/apps/pi-demo/ecmp/src/main/java/org/onosproject/pi/demo/app/ecmp/EcmpFabricApp.java b/apps/pi-demo/ecmp/src/main/java/org/onosproject/pi/demo/app/ecmp/EcmpFabricApp.java
index 1817997..3aabca8 100644
--- a/apps/pi-demo/ecmp/src/main/java/org/onosproject/pi/demo/app/ecmp/EcmpFabricApp.java
+++ b/apps/pi-demo/ecmp/src/main/java/org/onosproject/pi/demo/app/ecmp/EcmpFabricApp.java
@@ -73,8 +73,7 @@
 public class EcmpFabricApp extends AbstractUpgradableFabricApp {
 
     private static final String APP_NAME = "org.onosproject.pi-ecmp-fabric";
-    private static final String MODEL_NAME = "ECMP";
-    private static final String PIPECONF_ID = "org.project.pipeconf.ecmp";
+    private static final String PIPECONF_ID = "pi-demo-ecmp";
     private static final URL P4INFO_URL = EcmpFabricApp.class.getResource("/ecmp.p4info");
     private static final URL JSON_URL = EcmpFabricApp.class.getResource("/ecmp.json");
 
@@ -90,7 +89,7 @@
     private static final Map<DeviceId, Map<Set<PortNumber>, Short>> DEVICE_GROUP_ID_MAP = Maps.newHashMap();
 
     public EcmpFabricApp() {
-        super(APP_NAME, MODEL_NAME, ECMP_PIPECONF);
+        super(APP_NAME, ECMP_PIPECONF);
     }
 
     @Override
diff --git a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
index 260e41c..40c5370 100644
--- a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
+++ b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
@@ -441,14 +441,16 @@
 
         if (pipeconfId == null) {
             log.warn("Device {} is pipeline programmable but no pipeconf can be associated to it.", deviceId);
-            return true;
+            return false;
+        }
+
+        if (!piPipeconfService.getPipeconf(pipeconfId).isPresent()) {
+            log.warn("Pipeconf {} is not registered", pipeconfId);
+            return false;
         }
 
 
-        PiPipeconf pipeconf = piPipeconfService.getPipeconf(pipeconfId).orElseThrow(
-                () -> new IllegalStateException("Pipeconf is not registered: " + pipeconfId)
-        );
-
+        PiPipeconf pipeconf = piPipeconfService.getPipeconf(pipeconfId).get();
 
         try {
             if (!pipelineProg.deployPipeconf(pipeconf).get()) {