[ONOS-5070] Adds mirroring functionality.

Changes:
- Adds mirroring behaviour;
- Adds mirroring description;
- Adds mirroring name;
- Implements for Ovsdb the mirroring;
- Adds OvsdbMirror entity;
- Adds constants related to Mirror table;
- Fix one issue related to Mirror table
- Extends OvsdbClientService introducing mirroring;
- Implements mirroring functionality in DefaulOvsdbClient;
- Support for different types of device id

Change-Id: Ie291f49b3c61b7998010f555ae11deb8c021063d
diff --git a/drivers/ovsdb/src/main/java/org/onosproject/drivers/ovsdb/OvsdbControllerConfig.java b/drivers/ovsdb/src/main/java/org/onosproject/drivers/ovsdb/OvsdbControllerConfig.java
index 9597169..25210a2 100644
--- a/drivers/ovsdb/src/main/java/org/onosproject/drivers/ovsdb/OvsdbControllerConfig.java
+++ b/drivers/ovsdb/src/main/java/org/onosproject/drivers/ovsdb/OvsdbControllerConfig.java
@@ -28,6 +28,7 @@
 import org.onosproject.net.driver.DriverHandler;
 import org.onosproject.ovsdb.controller.OvsdbBridge;
 import org.onosproject.ovsdb.controller.OvsdbClientService;
+import org.onosproject.ovsdb.controller.OvsdbConstant;
 import org.onosproject.ovsdb.controller.OvsdbController;
 import org.onosproject.ovsdb.controller.OvsdbNodeId;
 
@@ -83,7 +84,7 @@
         if (nodeIds.size() == 0) {
             //TODO decide what port?
             ovsController.connect(IpAddress.valueOf(targetIp),
-                                  targetPort == null ? TpPort.tpPort(6640) : targetPort);
+                                  targetPort == null ? TpPort.tpPort(OvsdbConstant.OVSDBPORT) : targetPort);
             delay(1000); //FIXME... connect is async
         }
         List<OvsdbClientService> clientServices = ovsController.getNodeIds().stream()
diff --git a/drivers/ovsdb/src/main/java/org/onosproject/drivers/ovsdb/OvsdbMirroringConfig.java b/drivers/ovsdb/src/main/java/org/onosproject/drivers/ovsdb/OvsdbMirroringConfig.java
new file mode 100644
index 0000000..9a1896e
--- /dev/null
+++ b/drivers/ovsdb/src/main/java/org/onosproject/drivers/ovsdb/OvsdbMirroringConfig.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2016-present 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.drivers.ovsdb;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.BridgeName;
+import org.onosproject.net.behaviour.MirroringConfig;
+import org.onosproject.net.behaviour.MirroringDescription;
+import org.onosproject.net.behaviour.MirroringStatistics;
+import org.onosproject.net.behaviour.MirroringName;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.ovsdb.controller.OvsdbBridge;
+import org.onosproject.ovsdb.controller.OvsdbClientService;
+import org.onosproject.ovsdb.controller.OvsdbConstant;
+import org.onosproject.ovsdb.controller.OvsdbController;
+import org.onosproject.ovsdb.controller.OvsdbMirror;
+import org.onosproject.ovsdb.controller.OvsdbNodeId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static org.onlab.util.Tools.delay;
+
+/**
+ * Implementation of mirror config which allows to add, delete and get mirrorings statistics.
+ */
+public class OvsdbMirroringConfig extends AbstractHandlerBehaviour implements MirroringConfig {
+
+    private static Logger log = LoggerFactory.getLogger(OvsdbMirroringConfig.class);
+
+    /**
+     * Adds a mirroring with a given description.
+     *
+     * @param bridge the bridge name
+     * @param mirroringDescription mirroring description
+     * @return true if succeeds, or false
+     */
+    @Override
+    public boolean addMirroring(BridgeName bridge, MirroringDescription mirroringDescription) {
+        DriverHandler handler = handler();
+        OvsdbClientService ovsdbClient = getOvsdbClientService(handler);
+        OvsdbMirror mirror = OvsdbMirror.builder(mirroringDescription).build();
+        return ovsdbClient.createMirror(bridge.name(), mirror);
+    }
+
+    /**
+     * Removes a mirroring.
+     *
+     * @param mirroringName mirroring name
+     */
+    @Override
+    public void deleteMirroring(MirroringName mirroringName) {
+        DriverHandler handler = handler();
+        OvsdbClientService ovsdbClient = getOvsdbClientService(handler);
+        ovsdbClient.dropMirror(mirroringName);
+    }
+
+    /**
+     * Returns a collection of MirroringStatistics.
+     *
+     * @return statistics collection
+     */
+    @Override
+    public Collection<MirroringStatistics> getMirroringStatistics() {
+        DriverHandler handler = handler();
+        OvsdbClientService ovsdbClient = getOvsdbClientService(handler);
+        return ovsdbClient.getMirroringStatistics(handler.data().deviceId());
+    }
+
+    /**
+     * Helper method which is used for getting OvsdbClientService.
+     */
+    private OvsdbClientService getOvsdbClientService(DriverHandler handler) {
+
+        OvsdbController ovsController = handler.get(OvsdbController.class);
+        DeviceService deviceService = handler.get(DeviceService.class);
+        DeviceId deviceId = handler.data().deviceId();
+
+        String[] splits = deviceId.toString().split(":");
+        if (splits == null || splits.length < 1) {
+            log.warn("Wrong deviceId format");
+            return null;
+        }
+
+        /**
+         * Each type of device has to be managed in a different way.
+         */
+        switch (splits[0]) {
+            case "ovsdb":
+                OvsdbNodeId nodeId = changeDeviceIdToNodeId(deviceId);
+                return ovsController.getOvsdbClient(nodeId);
+            case "of":
+                String[] mgmtAddress = deviceService.getDevice(deviceId)
+                        .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
+                String targetIp = mgmtAddress[0];
+                TpPort targetPort = null;
+                if (mgmtAddress.length > 1) {
+                    targetPort = TpPort.tpPort(Integer.parseInt(mgmtAddress[1]));
+                }
+                List<OvsdbNodeId> nodeIds = ovsController.getNodeIds().stream()
+                        .filter(nodeID -> nodeID.getIpAddress().equals(targetIp))
+                        .collect(Collectors.toList());
+                if (nodeIds.size() == 0) {
+                    //TODO decide what port?
+                    ovsController.connect(IpAddress.valueOf(targetIp),
+                                          targetPort == null ? TpPort.tpPort(OvsdbConstant.OVSDBPORT) : targetPort);
+                    delay(1000); //FIXME... connect is async
+                }
+                List<OvsdbClientService> clientServices = ovsController.getNodeIds().stream()
+                        .filter(nodeID -> nodeID.getIpAddress().equals(targetIp))
+                        .map(ovsController::getOvsdbClient)
+                        .filter(cs -> cs.getBridges().stream().anyMatch(b -> dpidMatches(b, deviceId)))
+                        .collect(Collectors.toList());
+                checkState(clientServices.size() > 0, "No clientServices found");
+                //FIXME add connection to management address if null --> done ?
+                return clientServices.size() > 0 ? clientServices.get(0) : null;
+            default:
+                log.warn("Unmanaged device type");
+        }
+        return null;
+
+    }
+
+    private static boolean dpidMatches(OvsdbBridge bridge, DeviceId deviceId) {
+        checkArgument(bridge.datapathId().isPresent());
+
+        String bridgeDpid = "of:" + bridge.datapathId().get();
+        String ofDpid = deviceId.toString();
+        return bridgeDpid.equals(ofDpid);
+    }
+
+    /**
+     * OvsdbNodeId(IP) is used in the adaptor while DeviceId(ovsdb:IP)
+     * is used in the core. So DeviceId need be changed to OvsdbNodeId.
+     *
+     * @param deviceId the device id in ovsdb:ip format
+     * @return the ovsdb node id
+     */
+    private OvsdbNodeId changeDeviceIdToNodeId(DeviceId deviceId) {
+        String[] splits = deviceId.toString().split(":");
+        if (splits == null || splits.length < 1) {
+            return null;
+        }
+        IpAddress ipAddress = IpAddress.valueOf(splits[1]);
+        return new OvsdbNodeId(ipAddress, 0);
+    }
+
+}
diff --git a/drivers/ovsdb/src/main/resources/ovsdb-drivers.xml b/drivers/ovsdb/src/main/resources/ovsdb-drivers.xml
index 9e88ba9..15d7d5f 100644
--- a/drivers/ovsdb/src/main/resources/ovsdb-drivers.xml
+++ b/drivers/ovsdb/src/main/resources/ovsdb-drivers.xml
@@ -28,6 +28,8 @@
             manufacturer="Nicira, Inc\." hwVersion="Open vSwitch" swVersion="2\..*">
         <behaviour api="org.onosproject.net.behaviour.ControllerConfig"
                    impl="org.onosproject.drivers.ovsdb.OvsdbControllerConfig"/>
+        <behaviour api="org.onosproject.net.behaviour.MirroringConfig"
+                   impl="org.onosproject.drivers.ovsdb.OvsdbMirroringConfig"/>
     </driver>
 </drivers>