Support creation of vendor-specific versions of the fabric pipeconf

We provide a new service to facilitate registration of vendor-specific
versions of the Fabric pipeconf (e.g., for Tofino) from third-party
apps. This service is designed such that third-party apps do not need to
depend on internal classes at compile time, such as the behaviour
implementations.

To make this possible, the package structure has been refactored to
separate APIs from implementation.

Change-Id: I487cb806541eb9e6877ebf398a94f057613df8cc
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/FabricPipeconfManager.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/FabricPipeconfManager.java
new file mode 100644
index 0000000..76122d9
--- /dev/null
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/FabricPipeconfManager.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.pipelines.fabric.impl;
+
+import org.onosproject.inbandtelemetry.api.IntProgrammable;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.pi.model.DefaultPiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipelineInterpreter;
+import org.onosproject.net.pi.model.PiPipelineModel;
+import org.onosproject.p4runtime.model.P4InfoParser;
+import org.onosproject.p4runtime.model.P4InfoParserException;
+import org.onosproject.pipelines.fabric.FabricPipeconfService;
+import org.onosproject.pipelines.fabric.impl.behaviour.FabricIntProgrammable;
+import org.onosproject.pipelines.fabric.impl.behaviour.FabricInterpreter;
+import org.onosproject.pipelines.fabric.impl.behaviour.pipeliner.FabricPipeliner;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.slf4j.Logger;
+
+import java.net.URL;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the FabricPipeconfService.
+ */
+@Component(immediate = true, service = FabricPipeconfService.class)
+public final class FabricPipeconfManager implements FabricPipeconfService {
+
+    private static final String INT_PROFILE_SUFFIX = "-int";
+    private static final String FULL_PROFILE_SUFFIX = "-full";
+
+    private static Logger log = getLogger(FabricPipeconfLoader.class);
+
+    @Activate
+    public void activate() {
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("Stopped");
+    }
+
+    @Override
+    public PiPipeconf buildFabricPipeconf(
+            DefaultPiPipeconf.Builder builder, String profile, URL p4InfoUrl, URL cpuPortUrl) {
+        return build(builder, profile, p4InfoUrl, cpuPortUrl);
+    }
+
+    static PiPipeconf build(
+            DefaultPiPipeconf.Builder pipeconfBuilder,
+            String profileName, URL p4InfoUrl, URL cpuPortUrl) {
+        checkNotNull(pipeconfBuilder,
+                     "pipeconfBuilder cannot be null");
+        checkArgument(profileName != null && !profileName.isEmpty(),
+                      "profileName cannot be null or empty");
+        checkNotNull(p4InfoUrl,
+                     "p4InfoUrl cannot be null (check if file exists)");
+        checkNotNull(cpuPortUrl,
+                     "cpuPortUrl cannot be null (check if file exists)");
+
+        pipeconfBuilder
+                .withPipelineModel(parseP4Info(p4InfoUrl))
+                .addBehaviour(PiPipelineInterpreter.class, FabricInterpreter.class)
+                .addBehaviour(Pipeliner.class, FabricPipeliner.class)
+                .addExtension(PiPipeconf.ExtensionType.P4_INFO_TEXT, p4InfoUrl)
+                .addExtension(PiPipeconf.ExtensionType.CPU_PORT_TXT, cpuPortUrl);
+
+        // Add IntProgrammable behaviour for INT-enabled profiles.
+        if (profileName.endsWith(INT_PROFILE_SUFFIX) ||
+                profileName.endsWith(FULL_PROFILE_SUFFIX)) {
+            pipeconfBuilder.addBehaviour(IntProgrammable.class, FabricIntProgrammable.class);
+        }
+
+        return pipeconfBuilder.build();
+    }
+
+    private static PiPipelineModel parseP4Info(URL p4InfoUrl) {
+        try {
+            return P4InfoParser.parse(p4InfoUrl);
+        } catch (P4InfoParserException e) {
+            // FIXME: propagate exception that can be handled by whoever is
+            //  trying to build pipeconfs.
+            throw new IllegalStateException(e);
+        }
+    }
+}