Update spectrum pipeline config

Change-Id: I6d663e1a2851d812ab64cf6cb268c20914520570
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java
index e3664e1..c7142bf 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipeconf.java
@@ -100,6 +100,11 @@
         BMV2_JSON,
 
         /**
+         * Mellanox Spectrum configuration binary.
+         */
+        SPECTRUM_BIN,
+
+        /**
          * Barefoot's Tofino configuration binary.
          */
         TOFINO_BIN,
diff --git a/drivers/mellanox/src/main/java/org/onosproject/drivers/mellanox/SpectrumPipelineProgrammable.java b/drivers/mellanox/src/main/java/org/onosproject/drivers/mellanox/SpectrumPipelineProgrammable.java
index c13ab77..d421be4 100644
--- a/drivers/mellanox/src/main/java/org/onosproject/drivers/mellanox/SpectrumPipelineProgrammable.java
+++ b/drivers/mellanox/src/main/java/org/onosproject/drivers/mellanox/SpectrumPipelineProgrammable.java
@@ -16,17 +16,21 @@
 
 package org.onosproject.drivers.mellanox;
 
+import org.apache.commons.io.IOUtils;
 import org.onosproject.drivers.p4runtime.AbstractP4RuntimePipelineProgrammable;
 import org.onosproject.net.behaviour.PiPipelineProgrammable;
 import org.onosproject.net.pi.model.PiPipeconf;
 import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.model.PiPipeconf.ExtensionType;
 import org.onosproject.net.pi.service.PiPipeconfService;
 
+import java.io.IOException;
+
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.Optional;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.lang.String.format;
+import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.SPECTRUM_BIN;
 
 /**
  * Implementation of the PiPipelineProgrammable behaviour for a Spectrum-based
@@ -36,23 +40,48 @@
         extends AbstractP4RuntimePipelineProgrammable
         implements PiPipelineProgrammable {
 
-    private static final PiPipeconfId FABRIC_PIPECONF_ID =
-            new PiPipeconfId("org.onosproject.pipelines.fabric");
+    private static final PiPipeconfId MLNX_FABRIC_PIPECONF_ID =
+            new PiPipeconfId("org.onosproject.pipelines.fabric-mlnx");
 
     @Override
     public ByteBuffer createDeviceDataBuffer(PiPipeconf pipeconf) {
-        checkArgument(pipeconf.id().equals(FABRIC_PIPECONF_ID),
-                      format("Cannot program Spectrum device with a pipeconf " +
-                                     "other than '%s' (found '%s')",
-                             FABRIC_PIPECONF_ID, pipeconf.id()));
-        // Dummy value.
-        // We assume switch to be already configured with fabric.p4 profile.
-        return ByteBuffer.allocate(1).put((byte) 1);
+        log.debug("Creating device data buffer for {} in pipeconf {}", SPECTRUM_BIN, pipeconf.id());
+        ByteBuffer deviceData;
+        try {
+            deviceData = extensionBuffer(pipeconf, SPECTRUM_BIN);
+        } catch (ExtensionException e) {
+            log.error("Failed to create device data buffer for {} in pipeconf {}", SPECTRUM_BIN, pipeconf.id());
+            return null;
+        }
+        // flip buffer data so they can be read
+        deviceData.flip();
+        return deviceData.asReadOnlyBuffer();
     }
 
     @Override
     public Optional<PiPipeconf> getDefaultPipeconf() {
         return handler().get(PiPipeconfService.class)
-                .getPipeconf(FABRIC_PIPECONF_ID);
+                .getPipeconf(MLNX_FABRIC_PIPECONF_ID);
+    }
+
+    private ByteBuffer extensionBuffer(PiPipeconf pipeconf, ExtensionType extType) {
+        if (!pipeconf.extension(extType).isPresent()) {
+            log.warn("Missing extension {} in pipeconf {}", extType, pipeconf.id());
+            throw new ExtensionException();
+        }
+        try {
+            byte[] bytes = IOUtils.toByteArray(pipeconf.extension(extType).get());
+            // Length of the extension + bytes.
+            return ByteBuffer.allocate(bytes.length)
+                    .order(ByteOrder.LITTLE_ENDIAN)
+                    .put(bytes);
+        } catch (IOException ex) {
+            log.warn("Unable to read extension {} from pipeconf {}: {}",
+                     extType, pipeconf.id(), ex.getMessage());
+            throw new ExtensionException();
+        }
+    }
+
+    private static class ExtensionException extends IllegalArgumentException {
     }
 }
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java
index 1609794..cac245c 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/PipeconfLoader.java
@@ -71,12 +71,14 @@
     private static final String P4C_RES_BASE_PATH = P4C_OUT_PATH + "/%s/%s/%s/";
 
     private static final String SEP = File.separator;
+    private static final String SPECTRUM = "spectrum";
     private static final String TOFINO = "tofino";
     private static final String BMV2 = "bmv2";
     private static final String DEFAULT_PLATFORM = "default";
     private static final String BMV2_JSON = "bmv2.json";
     private static final String P4INFO_TXT = "p4info.txt";
     private static final String CPU_PORT_TXT = "cpu_port.txt";
+    private static final String SPECTRUM_BIN = "spectrum.bin";
     private static final String TOFINO_BIN = "tofino.bin";
     private static final String TOFINO_CTX_JSON = "context.json";
     private static final String INT_PROFILE_SUFFIX = "-int";
@@ -133,6 +135,9 @@
                 case BMV2:
                     pipeconfBuilder = bmv2Pipeconf(profile, platform);
                     break;
+                case SPECTRUM:
+                    pipeconfBuilder = spectrumPipeconf(profile, platform);
+                    break;
                 case TOFINO:
                     pipeconfBuilder = tofinoPipeconf(profile, platform);
                     break;
@@ -177,6 +182,28 @@
                 .addExtension(ExtensionType.BMV2_JSON, bmv2JsonUrl);
     }
 
+    private static DefaultPiPipeconf.Builder  spectrumPipeconf(String profile, String platform)
+            throws FileNotFoundException {
+        final URL spectrumBinUrl = PipeconfLoader.class.getResource(format(
+                P4C_RES_BASE_PATH + SPECTRUM_BIN, profile, SPECTRUM, platform));
+        final URL p4InfoUrl = PipeconfLoader.class.getResource(format(
+                P4C_RES_BASE_PATH + P4INFO_TXT, profile, SPECTRUM, platform));
+        final URL cpuPortUrl = PipeconfLoader.class.getResource(format(
+                P4C_RES_BASE_PATH + CPU_PORT_TXT, profile, SPECTRUM, platform));
+        if (spectrumBinUrl == null) {
+            throw new FileNotFoundException(SPECTRUM_BIN);
+        }
+        if (p4InfoUrl == null) {
+            throw new FileNotFoundException(P4INFO_TXT);
+        }
+        if (cpuPortUrl == null) {
+            throw new FileNotFoundException(CPU_PORT_TXT);
+        }
+        return basePipeconfBuilder(
+                profile, platform, p4InfoUrl, cpuPortUrl)
+                .addExtension(ExtensionType.SPECTRUM_BIN, spectrumBinUrl);
+    }
+
     private static DefaultPiPipeconf.Builder tofinoPipeconf(
             String profile, String platform)
             throws FileNotFoundException {
diff --git a/pipelines/fabric/src/main/resources/p4c-out/fabric-mlnx/spectrum/default/README.md b/pipelines/fabric/src/main/resources/p4c-out/fabric-mlnx/spectrum/default/README.md
new file mode 100644
index 0000000..b3611d3
--- /dev/null
+++ b/pipelines/fabric/src/main/resources/p4c-out/fabric-mlnx/spectrum/default/README.md
@@ -0,0 +1,18 @@
+# Running ONOS with Mellanox Spectrum/Spectrum2 Switches
+
+## Spectrum and Fabric.p4
+The Spectrum architecture supports the fabric.p4 pipeline, but using the spectrum_model.p4 instead of the v1model.p4.
+The folder location p4c-out/fabric-mlnx/spectrum/default is where the P4 compiler artifacts should be placed for 
+ONOS to properly load and configure the pipeline for the Spectrum switch. 
+These files include:
+
+* cpu_port.txt:   defines the SDN port number to be used when sending a packet to the controller
+* p4info.txt:     the P4Runtime output file, in protobuf text format, when compiling fabric.p4
+* spectrum.bin:   The "binary blob" P4 compiler output, which contains all the data necessary to reconfigure the 
+  switch pipeline (P4 device config, as described in the P4Runtime specification)
+
+Since at this time the Mellanox P4 compiler backend is under active development and is not currently open sourced,
+please contact your Mellanox representative for access to the compiler and/or the compiler artifacts described above.
+
+For the latest details, please take a look at the wiki page instructions:
+https://wiki.onosproject.org/display/ONOS/Controlling+P4Runtime-enabled+Mellanox+Spectrum+switch+with+ONOS