Device driver framework enhancements and CLI.

Change-Id: I5dea67620259797eff89a985718934034a86d63e
diff --git a/cli/src/main/java/org/onosproject/cli/net/DriverNameCompleter.java b/cli/src/main/java/org/onosproject/cli/net/DriverNameCompleter.java
new file mode 100644
index 0000000..2ba8a20
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/DriverNameCompleter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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.cli.net;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.driver.DriverAdminService;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Device driver name completer.
+ */
+public class DriverNameCompleter implements Completer {
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+        SortedSet<String> strings = delegate.getStrings();
+
+        // Fetch our service and feed it's offerings to the string completer
+        DriverAdminService service = AbstractShellCommand.get(DriverAdminService.class);
+        service.getDrivers().forEach(d -> strings.add(d.name()));
+
+        // Now let the completer do the work for figuring out what to offer.
+        return delegate.complete(buffer, cursor, candidates);
+    }
+
+}
diff --git a/cli/src/main/java/org/onosproject/cli/net/DriversListCommand.java b/cli/src/main/java/org/onosproject/cli/net/DriversListCommand.java
new file mode 100644
index 0000000..fd4c96c
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/DriversListCommand.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverAdminService;
+
+/**
+ * Lists device drivers.
+ */
+@Command(scope = "onos", name = "drivers",
+        description = "Lists device drivers")
+public class DriversListCommand extends AbstractShellCommand {
+
+    private static final String FMT = "driver=%s, mfr=%s, hw=%s, sw=%s";
+    private static final String FMT_B = "   %s via %s";
+    private static final String FMT_P = "   %s=%s";
+
+    @Argument(index = 0, name = "driverName", description = "Driver name",
+            required = false, multiValued = false)
+    String driverName = null;
+
+    @Override
+    protected void execute() {
+        DriverAdminService service = get(DriverAdminService.class);
+
+        if (driverName != null) {
+            printDriver(service.getDriver(driverName));
+        } else {
+            service.getDrivers().forEach(this::printDriver);
+        }
+    }
+
+    private void printDriver(Driver driver) {
+        print(FMT, driver.name(), driver.manufacturer(),
+              driver.hwVersion(), driver.swVersion());
+        driver.behaviours().forEach(b -> print(FMT_B, b.getCanonicalName(),
+                                               driver.implementation(b).getCanonicalName()));
+        driver.properties().forEach((k, v) -> print(FMT_P, k, v));
+    }
+
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 5fefe45..8234462 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -71,6 +71,14 @@
         </command>
 
         <command>
+            <action class="org.onosproject.cli.net.DriversListCommand"/>
+            <completers>
+                <ref component-id="driverNameCompleter"/>
+                <null/>
+            </completers>
+        </command>
+
+        <command>
             <action class="org.onosproject.cli.net.DevicesListCommand"/>
         </command>
         <command>
@@ -316,5 +324,6 @@
     <bean id="nullCompleter" class="org.apache.karaf.shell.console.completer.NullCompleter"/>
     <bean id="ethTypeCompleter" class="org.onosproject.cli.net.EthTypeCompleter"/>
     <bean id="ipProtocolCompleter" class="org.onosproject.cli.net.IpProtocolCompleter"/>
+    <bean id="driverNameCompleter" class="org.onosproject.cli.net.DriverNameCompleter"/>
 
 </blueprint>
diff --git a/core/api/src/main/java/org/onosproject/net/driver/DefaultDriver.java b/core/api/src/main/java/org/onosproject/net/driver/DefaultDriver.java
index e95ddca..e7cd89e 100644
--- a/core/api/src/main/java/org/onosproject/net/driver/DefaultDriver.java
+++ b/core/api/src/main/java/org/onosproject/net/driver/DefaultDriver.java
@@ -69,15 +69,17 @@
      * @param other other driver
      * @return new driver
      */
-    DefaultDriver merge(DefaultDriver other) {
+    @Override
+    public Driver merge(Driver other) {
         // Merge the behaviours.
         ImmutableMap.Builder<Class<? extends Behaviour>, Class<? extends Behaviour>>
                 behaviours = ImmutableMap.builder();
-        behaviours.putAll(other.behaviours).putAll(this.behaviours);
+        behaviours.putAll(this.behaviours);
+        other.behaviours().forEach(b -> behaviours.put(b, other.implementation(b)));
 
         // Merge the properties.
         ImmutableMap.Builder<String, String> properties = ImmutableMap.builder();
-        properties.putAll(other.properties).putAll(this.properties);
+        properties.putAll(this.properties).putAll(other.properties());
 
         return new DefaultDriver(name, manufacturer, hwVersion, swVersion,
                                  behaviours.build(), properties.build());
@@ -109,6 +111,11 @@
     }
 
     @Override
+    public Class<? extends Behaviour> implementation(Class<? extends Behaviour> behaviour) {
+        return behaviours.get(behaviour);
+    }
+
+    @Override
     public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
         return behaviours.containsKey(behaviourClass);
     }
diff --git a/core/api/src/main/java/org/onosproject/net/driver/DefaultDriverProvider.java b/core/api/src/main/java/org/onosproject/net/driver/DefaultDriverProvider.java
index 3110dc8..b2b5281 100644
--- a/core/api/src/main/java/org/onosproject/net/driver/DefaultDriverProvider.java
+++ b/core/api/src/main/java/org/onosproject/net/driver/DefaultDriverProvider.java
@@ -16,8 +16,8 @@
 package org.onosproject.net.driver;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
@@ -28,7 +28,7 @@
  */
 public class DefaultDriverProvider implements DriverProvider {
 
-    private final Map<String, DefaultDriver> drivers = new HashMap<>();
+    protected final Map<String, Driver> drivers = Maps.newConcurrentMap();
 
     @Override
     public Set<Driver> getDrivers() {
@@ -36,30 +36,47 @@
     }
 
     /**
-     * Adds the specified driver to be provided.
+     * Adds the specified drivers to the provider.
      *
-     * @param driverClasses driver to be provided
+     * @param drivers drivers to be added
      */
-    public void addDrivers(Set<DefaultDriver> driverClasses) {
-        for (DefaultDriver driverClass : driverClasses) {
-            addDriver(driverClass);
+    public void addDrivers(Set<Driver> drivers) {
+        drivers.forEach(this::addDriver);
+    }
+
+    /**
+     * Adds the specified driver to the provider.
+     *
+     * @param driver driver to be provided
+     */
+    public void addDriver(Driver driver) {
+        Driver ddc = drivers.get(driver.name());
+        if (ddc == null) {
+            // If we don't have the driver yet, just use the new one.
+            drivers.put(driver.name(), driver);
+        } else {
+            // Otherwise merge the existing driver with the new one and rebind.
+            drivers.put(driver.name(), ddc.merge(driver));
         }
     }
 
     /**
-     * Adds the specified driver to be provided.
+     * Removes the specified drivers from the provider.
      *
-     * @param driverClass driver to be provided
+     * @param drivers drivers to be removed
      */
-    public void addDriver(DefaultDriver driverClass) {
-        DefaultDriver ddc = drivers.get(driverClass.name());
-        if (ddc == null) {
-            // If we don't have the driver yet, just use the new one.
-            drivers.put(driverClass.name(), driverClass);
-        } else {
-            // Otherwise merge the existing driver with the new one and rebind.
-            drivers.put(driverClass.name(), ddc.merge(driverClass));
-        }
+    public void removeDrivers(Set<Driver> drivers) {
+        drivers.forEach(this::removeDriver);
+    }
+
+    /**
+     * Removes the specified driver from the provider.
+     *
+     * @param driver driver to be removed
+     */
+    public void removeDriver(Driver driver) {
+        // TODO: make selective if possible
+        drivers.remove(driver.name());
     }
 
     @Override
diff --git a/core/api/src/main/java/org/onosproject/net/driver/Driver.java b/core/api/src/main/java/org/onosproject/net/driver/Driver.java
index a6883d5..c8d6134 100644
--- a/core/api/src/main/java/org/onosproject/net/driver/Driver.java
+++ b/core/api/src/main/java/org/onosproject/net/driver/Driver.java
@@ -63,6 +63,14 @@
     Set<Class<? extends Behaviour>> behaviours();
 
     /**
+     * Returns the implementation class for the specified behaviour.
+     *
+     * @param behaviour behaviour interface
+     * @return implementation class
+     */
+    Class<? extends Behaviour> implementation(Class<? extends Behaviour> behaviour);
+
+    /**
      * Indicates whether or not the driver supports the specified class
      * of behaviour.
      *
@@ -90,4 +98,13 @@
      */
     Map<String, String> properties();
 
+    /**
+     * Merges the specified driver behaviours and properties into this one,
+     * giving preference to the other driver when dealing with conflicts.
+     *
+     * @param other other driver
+     * @return merged driver
+     */
+    Driver merge(Driver other);
+
 }
diff --git a/core/api/src/main/java/org/onosproject/net/driver/DriverService.java b/core/api/src/main/java/org/onosproject/net/driver/DriverService.java
index ffb7c93..fa37f1b 100644
--- a/core/api/src/main/java/org/onosproject/net/driver/DriverService.java
+++ b/core/api/src/main/java/org/onosproject/net/driver/DriverService.java
@@ -25,13 +25,19 @@
 public interface DriverService {
 
     /**
-     * Returns the overall set of drivers being provided, optionally
-     * filtered to only those that support all specified behaviours.
+     * Returns the overall set of drivers being provided.
      *
-     * @param withBehaviours optional behaviour classes to query by
      * @return provided drivers
      */
-    Set<Driver> getDrivers(Class<? extends Behaviour>... withBehaviours);
+    Set<Driver> getDrivers();
+
+    /**
+     * Returns the set of drivers which support the specified behaviour.
+     *
+     * @param withBehaviour behaviour class to query by
+     * @return provided drivers
+     */
+    Set<Driver> getDrivers(Class<? extends Behaviour> withBehaviour);
 
     /**
      * Returns the specified driver.
diff --git a/core/api/src/test/java/org/onosproject/net/driver/DefaultDriverTest.java b/core/api/src/test/java/org/onosproject/net/driver/DefaultDriverTest.java
index f10602a..204eb3b 100644
--- a/core/api/src/test/java/org/onosproject/net/driver/DefaultDriverTest.java
+++ b/core/api/src/test/java/org/onosproject/net/driver/DefaultDriverTest.java
@@ -50,7 +50,7 @@
                                               ImmutableMap.of(TestBehaviour.class,
                                                               TestBehaviourImpl.class),
                                               ImmutableMap.of("foo", "bar"));
-        DefaultDriver ddc =
+        Driver ddc =
                 one.merge(new DefaultDriver("foo.bar", "", "", "",
                                             ImmutableMap.of(TestBehaviourTwo.class,
                                                             TestBehaviourTwoImpl.class),
diff --git a/core/net/src/main/java/org/onosproject/net/driver/impl/DriverManager.java b/core/net/src/main/java/org/onosproject/net/driver/impl/DriverManager.java
index 9c7fddb..5f7e2e2 100644
--- a/core/net/src/main/java/org/onosproject/net/driver/impl/DriverManager.java
+++ b/core/net/src/main/java/org/onosproject/net/driver/impl/DriverManager.java
@@ -30,6 +30,7 @@
 import org.onosproject.net.driver.Behaviour;
 import org.onosproject.net.driver.DefaultDriverData;
 import org.onosproject.net.driver.DefaultDriverHandler;
+import org.onosproject.net.driver.DefaultDriverProvider;
 import org.onosproject.net.driver.Driver;
 import org.onosproject.net.driver.DriverAdminService;
 import org.onosproject.net.driver.DriverHandler;
@@ -40,6 +41,7 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static org.onlab.util.Tools.nullIsNotFound;
 import static org.onosproject.net.AnnotationKeys.DRIVER;
@@ -49,7 +51,7 @@
  */
 @Component(immediate = true)
 @Service
-public class DriverManager implements DriverAdminService {
+public class DriverManager extends DefaultDriverProvider implements DriverAdminService {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
@@ -61,7 +63,6 @@
     protected DeviceService deviceService;
 
     private Set<DriverProvider> providers = Sets.newConcurrentHashSet();
-    private Map<String, Driver> driverByName = Maps.newConcurrentMap();
     private Map<String, Driver> driverByKey = Maps.newConcurrentMap();
 
     @Activate
@@ -83,7 +84,7 @@
     @Override
     public void registerProvider(DriverProvider provider) {
         provider.getDrivers().forEach(driver -> {
-            driverByName.put(driver.name(), driver);
+            addDrivers(provider.getDrivers());
             driverByKey.put(key(driver.manufacturer(),
                                 driver.hwVersion(),
                                 driver.swVersion()), driver);
@@ -94,7 +95,7 @@
     @Override
     public void unregisterProvider(DriverProvider provider) {
         provider.getDrivers().forEach(driver -> {
-            driverByName.remove(driver.name());
+            removeDrivers(provider.getDrivers());
             driverByKey.remove(key(driver.manufacturer(),
                                    driver.hwVersion(),
                                    driver.swVersion()));
@@ -103,21 +104,22 @@
     }
 
     @Override
-    public Set<Driver> getDrivers(Class<? extends Behaviour>... withBehaviours) {
+    public Set<Driver> getDrivers() {
         ImmutableSet.Builder<Driver> builder = ImmutableSet.builder();
-        for (Class<? extends Behaviour> behaviour : withBehaviours) {
-            driverByName.forEach((name, driver) -> {
-                if (driver.hasBehaviour(behaviour)) {
-                    builder.add(driver);
-                }
-            });
-        }
+        drivers.values().forEach(builder::add);
         return builder.build();
     }
 
     @Override
+    public Set<Driver> getDrivers(Class<? extends Behaviour> withBehaviour) {
+        return drivers.values().stream()
+                .filter(d -> d.hasBehaviour(withBehaviour))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
     public Driver getDriver(String driverName) {
-        return nullIsNotFound(driverByName.get(driverName), NO_DRIVER);
+        return nullIsNotFound(drivers.get(driverName), NO_DRIVER);
     }
 
     @Override
@@ -134,7 +136,7 @@
                 .filter(d -> matches(d, mfr, hw, sw)).findFirst();
 
         // If no matching driver is found, return default.
-        return optional.isPresent() ? optional.get() : driverByName.get(DEFAULT);
+        return optional.isPresent() ? optional.get() : drivers.get(DEFAULT);
     }
 
     // Matches the given driver using ERE matching against the given criteria.
@@ -163,6 +165,7 @@
         return new DefaultDriverHandler(new DefaultDriverData(driver));
     }
 
+    // Produces a composite driver key using the specified components.
     private String key(String mfr, String hw, String sw) {
         return String.format("%s-%s-%s", mfr, hw, sw);
     }
diff --git a/drivers/src/main/java/org/onosproject/driver/pipeline/DefaultSingleTablePipeline.java b/drivers/src/main/java/org/onosproject/driver/pipeline/DefaultSingleTablePipeline.java
index 6e99b35..866f57e 100644
--- a/drivers/src/main/java/org/onosproject/driver/pipeline/DefaultSingleTablePipeline.java
+++ b/drivers/src/main/java/org/onosproject/driver/pipeline/DefaultSingleTablePipeline.java
@@ -21,7 +21,7 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.behaviour.Pipeliner;
 import org.onosproject.net.behaviour.PipelinerContext;
-import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.driver.AbstractBehaviour;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleOperations;
@@ -41,7 +41,7 @@
 /**
  * Simple single table pipeline abstraction.
  */
-public class DefaultSingleTablePipeline implements Pipeliner {
+public class DefaultSingleTablePipeline extends AbstractBehaviour implements Pipeliner {
 
     private final Logger log = getLogger(getClass());
 
@@ -89,7 +89,7 @@
                     flowBuilder.remove(rule);
                     break;
                 default:
-                log.warn("Unknown operation {}", fwd.op());
+                    log.warn("Unknown operation {}", fwd.op());
             }
 
         });
@@ -115,8 +115,4 @@
         throw new UnsupportedOperationException("Single table does not next hop.");
     }
 
-    @Override
-    public void setData(DriverData data) {
-
-    }
 }
diff --git a/drivers/src/main/java/org/onosproject/driver/pipeline/OVSCorsaPipeline.java b/drivers/src/main/java/org/onosproject/driver/pipeline/OVSCorsaPipeline.java
index 2ac846c..3da786a 100644
--- a/drivers/src/main/java/org/onosproject/driver/pipeline/OVSCorsaPipeline.java
+++ b/drivers/src/main/java/org/onosproject/driver/pipeline/OVSCorsaPipeline.java
@@ -24,7 +24,7 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.behaviour.Pipeliner;
 import org.onosproject.net.behaviour.PipelinerContext;
-import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.driver.AbstractBehaviour;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -45,9 +45,9 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Created by ash on 07/04/15.
+ * Corsa pipeline handler.
  */
-public class OVSCorsaPipeline implements Pipeliner {
+public class OVSCorsaPipeline extends AbstractBehaviour implements Pipeliner {
 
     private static final int CONTROLLER_PRIORITY = 255;
     private static final int DROP_PRIORITY = 0;
@@ -92,12 +92,6 @@
         return null;
     }
 
-    @Override
-    public void setData(DriverData data) {
-
-    }
-
-
     private void pushDefaultRules() {
         boolean install = true;
         processTableZero(install);
@@ -130,7 +124,6 @@
         ops = install ? ops.add(rule) : ops.remove(rule);
 
 
-
         //Drop rule
         selector = DefaultTrafficSelector.builder();
         treatment = DefaultTrafficTreatment.builder();
@@ -195,7 +188,6 @@
         FlowRule rule;
 
 
-
         //Drop rule
         selector = DefaultTrafficSelector.builder();
         treatment = DefaultTrafficTreatment.builder();