CSIT-481: HC Test: Port mirroring (SPAN) 83/3983/5
authorselias <samelias@cisco.com>
Fri, 25 Nov 2016 09:50:29 +0000 (10:50 +0100)
committerPeter Mikus <pmikus@cisco.com>
Thu, 1 Dec 2016 07:26:51 +0000 (07:26 +0000)
 - add port mirroring test suite
 - add relevant keywords

Change-Id: I4ff398dd2ab4323e2a53af2812bf07cbb7043252
Signed-off-by: selias <samelias@cisco.com>
resources/libraries/python/honeycomb/HcAPIKwInterfaces.py
resources/libraries/python/telemetry/SPAN.py
resources/libraries/python/topology.py
resources/libraries/robot/honeycomb/port_mirroring.robot [new file with mode: 0644]
resources/templates/vat/span_create.vat
resources/templates/vat/span_dump.vat [new file with mode: 0644]
tests/func/honeycomb/120_port_mirroring.robot [new file with mode: 0644]

index 3271413..01a2eba 100644 (file)
@@ -1576,3 +1576,55 @@ class InterfaceKeywords(object):
                 return True
         raise HoneycombError("Interface index {0} not present in list"
                              " of disabled interfaces.".format(interface))
+
+    @staticmethod
+    def configure_interface_span(node, dst_interface, *src_interfaces):
+        """Configure SPAN port mirroring on the specified interfaces. If no
+         source interface is provided, SPAN will be disabled.
+
+        :param node: Honeycomb node.
+        :param dst_interface: Interface to mirror packets to.
+        :param src_interfaces: List of interfaces to mirror packets from.
+        :type node: dict
+        :type dst_interface: str
+        :type src_interfaces: list of str
+        :returns: Content of response.
+        :rtype: bytearray
+        :raises HoneycombError: If SPAN could not be configured.
+        """
+
+        interface = dst_interface.replace("/", "%2F")
+        path = "/interface/" + interface + "/span"
+
+        if not src_interfaces:
+            status_code, _ = HcUtil.delete_honeycomb_data(
+                node, "config_vpp_interfaces", path)
+
+        data = {
+            "span": {
+                "mirrored-interfaces": {
+                    "mirrored-interface": src_interfaces
+                }
+            }
+        }
+
+        status_code, _ = HcUtil.put_honeycomb_data(
+            node, "config_vpp_interfaces", data, path)
+
+        if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
+            raise HoneycombError(
+                "Configuring SPAN failed. Status code:{0}".format(status_code))
+
+    @staticmethod
+    def add_interface_local0_to_topology(node):
+        """Use Topology methods to add interface "local0" to working topology,
+        if not already present.
+
+        :param node: DUT node.
+        :type node: dict
+        """
+
+        if Topology.get_interface_by_sw_index(node, 0) is None:
+            local0_key = Topology.add_new_port(node, "localzero")
+            Topology.update_interface_sw_if_index(node, local0_key, 0)
+            Topology.update_interface_name(node, local0_key, "local0")
index 7933898..aff465c 100644 (file)
@@ -37,12 +37,60 @@ class SPAN(object):
         :type dst_if: str
         """
 
-        src_if = Topology.get_interface_name(node, src_if)
-        dst_if = Topology.get_interface_name(node, dst_if)
+        src_if = Topology.get_interface_sw_index(node, src_if)
+        dst_if = Topology.get_interface_sw_index(node, dst_if)
 
         with VatTerminal(node, json_param=False) as vat:
             vat.vat_terminal_exec_cmd_from_template('span_create.vat',
-                                                    src_if=src_if,
-                                                    dst_if=dst_if,
+                                                    src_sw_if_index=src_if,
+                                                    dst_sw_if_index=dst_if,
                                                     )
-    # TODO: Update "span_create.vat" to use VAT command, once available
+
+    @staticmethod
+    def vpp_get_span_configuration(node):
+        """Get full SPAN configuration from VPP node.
+
+        :param node: DUT node.
+        :type node: dict
+        :returns: Full SPAN configuration as list. One list entry for every
+        source/destination interface pair.
+        :rtype: list of dict
+        """
+
+        with VatTerminal(node, json_param=True) as vat:
+            data = vat.vat_terminal_exec_cmd_from_template('span_dump.vat')
+            return data[0]
+
+    @staticmethod
+    def vpp_get_span_configuration_by_interface(node, dst_interface,
+                                                ret_format="sw_if_index"):
+        """Get a list of all interfaces currently being mirrored
+        to the specified interface.
+
+        :param node: DUT node.
+        :param dst_interface: Name, sw_if_index or key of interface.
+        :param ret_format: Optional. Desired format of returned interfaces.
+        :type node: dict
+        :type dst_interface: str or int
+        :type ret_format: string
+        :returns: List of SPAN source interfaces for the provided destination
+        interface.
+        :rtype: list
+        """
+
+        data = SPAN.vpp_get_span_configuration(node)
+
+        dst_interface = Topology.convert_interface_reference(
+            node, dst_interface, "sw_if_index")
+        src_interfaces = []
+        for item in data:
+            if item["dst-if-index"] == dst_interface:
+                src_interfaces.append(item["src-if-index"])
+
+        if ret_format != "sw_if_index":
+            src_interfaces = [
+                Topology.convert_interface_reference(
+                    node, interface, ret_format
+                ) for interface in src_interfaces]
+
+        return src_interfaces
index 3c903e5..0214cc9 100644 (file)
@@ -125,6 +125,19 @@ class Topology(object):
         """
         node['interfaces'][iface_key]['vpp_sw_index'] = int(sw_if_index)
 
+    @staticmethod
+    def update_interface_name(node, iface_key, name):
+        """Update name on the interface from the node.
+
+        :param node: Node to update name on.
+        :param iface_key: Topology key of the interface.
+        :param name: Interface name to store.
+        :type node: dict
+        :type iface_key: str
+        :type name: str
+        """
+        node['interfaces'][iface_key]['name'] = str(name)
+
     @staticmethod
     def update_interface_mac_address(node, iface_key, mac_address):
         """Update mac_address on the interface from the node.
diff --git a/resources/libraries/robot/honeycomb/port_mirroring.robot b/resources/libraries/robot/honeycomb/port_mirroring.robot
new file mode 100644 (file)
index 0000000..854da31
--- /dev/null
@@ -0,0 +1,128 @@
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# 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.
+
+*** Settings ***
+| Library | resources.libraries.python.honeycomb.HcAPIKwInterfaces.InterfaceKeywords
+| ...     | WITH NAME | InterfaceAPI
+| Library | resources.libraries.python.telemetry.SPAN
+
+*** Keywords ***
+| Honeycomb Configures SPAN on interface
+| | [Documentation] | Uses Honeycomb API to configure SPAN on the specified\
+| | ... | interface, mirroring one or more interfaces.
+| | ...
+| | ... | *Arguments:*
+| | ... | - node - information about a DUT node. Type: dictionary
+| | ... | - dst_interface - Mirroring destination interface. Type: string
+| | ... | - src_interfaces - Mirroring source interfaces. Type: Argument list -\
+| | ... | any number of strings
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Honeycomb Configures SPAN on interface \| ${nodes['DUT1']} \
+| | ... | \| GigabitEthernet0/8/0 \| GigabitEthernet0/9/0 \|
+| | ...
+| | [Arguments] | ${node} | ${dst_interface} | @{src_interfaces}
+| | InterfaceAPI.Configure interface SPAN
+| | ... | ${node} | ${dst_interface} | @{src_interfaces}
+
+| Interface SPAN configuration from Honeycomb should be
+| | [Documentation] | Retrieves interface operational data and verifies that\
+| | ... | SPAN mirroring is configured with the provided interfaces.
+| | ...
+| | ... | *Arguments:*
+| | ... | - node - information about a DUT node. Type: dictionary
+| | ... | - dst_interface - Mirroring destination interface. Type: string
+| | ... | - src_interfaces - Mirroring source interfaces. Type: Argument list -\
+| | ... | any number of strings
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Interface SPAN configuration from Honeycomb should be \
+| | ... | \| ${nodes['DUT1']} \| GigabitEthernet0/8/0 \| GigabitEthernet0/9/0 \|
+| | ...
+| | [Arguments] | ${node} | ${dst_interface} | @{src_interfaces}
+| | ${data}= | InterfaceAPI.Get interface oper data | ${node} | ${dst_interface}
+| | ${data}= | Set Variable
+| | ... | ${data['span']['mirrored-interfaces']['mirrored-interface']}
+| | Sort list | ${data}
+| | Sort list | ${src_interfaces}
+| | Lists should be equal | ${data} | ${src_interfaces}
+
+| Interface SPAN configuration from VAT should be
+| | [Documentation] | Retrieves SPAN configuration from VAT dump and verifies\
+| | ... | that SPAN mirroring is configured with the provided interfaces.
+| | ...
+| | ... | *Arguments:*
+| | ... | - node - information about a DUT node. Type: dictionary
+| | ... | - dst_interface - Mirroring destination interface. Type: string
+| | ... | - src_interfaces - Mirroring source interfaces. Type: Argument list -\
+| | ... | any number of strings
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Interface SPAN configuration from VAT should be \
+| | ... | \| ${nodes['DUT1']} \| GigabitEthernet0/8/0 \| GigabitEthernet0/9/0 \|
+| | ...
+| | [Arguments] | ${node} | ${dst_interface} | @{src_interfaces}
+| | ${data}= | VPP get SPAN configuration by interface
+| | ... | ${node} | ${dst_interface} | name
+| | Sort list | ${data}
+| | Sort list | ${src_interfaces}
+| | Lists should be equal | ${data} | ${src_interfaces}
+
+| Honeycomb removes interface SPAN configuration
+| | [Documentation] | Uses Honeycomb API to remove SPAN confirugation\
+| | ... | from the specified interface.
+| | ...
+| | ... | *Arguments:*
+| | ... | - node - information about a DUT node. Type: dictionary
+| | ... | - dst_interface - Mirroring destination interface. Type: string
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Honeycomb removes interface SPAN configuration \
+| | ... | \| ${nodes['DUT1']} \| GigabitEthernet0/8/0 \|
+| | ...
+| | [Arguments] | ${node} | ${dst_interface}
+| | InterfaceAPI.Configure interface SPAN | ${node} | ${dst_interface}
+
+| Interface SPAN configuration from Honeycomb should not exist
+| | [Documentation] | Retrieves interface operational data and verifies that\
+| | ... | SPAN mirroring is not configured.
+| | ...
+| | ... | *Arguments:*
+| | ... | - node - information about a DUT node. Type: dictionary
+| | ... | - dst_interface - Mirroring destination interface. Type: string
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \|
+| | [Arguments] | ${node} | ${dst_interface}
+| | ${data}= | InterfaceAPI.Get interface oper data | ${node} | ${dst_interface}
+| | Run keyword and expect error | *KeyError* | Set Variable
+| | ... | ${data['span']['mirrored-interfaces']['mirrored-interface']}
+
+| SPAN configuration from VAT should not exist
+| | [Documentation] | Attmepts to retrieve SPAN configuration from VAT dump,\
+| | ... | and expects to fail with no data retrieved.
+| | ...
+| | ... | *Arguments:*
+| | ... | - node - information about a DUT node. Type: dictionary
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| SPAN configuration from VAT should not exist \| ${nodes['DUT1']} \|
+| | [Arguments] | ${node}
+| | Run keyword and expect error | ValueError: No JSON object could be decoded
+| | ... | VPP get SPAN configuration by interface | ${node} | local0
index 62c4232..3671d0b 100644 (file)
@@ -1 +1 @@
-exec set span src {src_if} dst {dst_if}
+sw_interface_span_enable_disable src_sw_if_index {src_sw_if_index} dst_sw_if_index {dst_sw_if_index}
diff --git a/resources/templates/vat/span_dump.vat b/resources/templates/vat/span_dump.vat
new file mode 100644 (file)
index 0000000..0f83af7
--- /dev/null
@@ -0,0 +1 @@
+sw_interface_span_dump
\ No newline at end of file
diff --git a/tests/func/honeycomb/120_port_mirroring.robot b/tests/func/honeycomb/120_port_mirroring.robot
new file mode 100644 (file)
index 0000000..521d59a
--- /dev/null
@@ -0,0 +1,64 @@
+# Copyright (c) 2016 Cisco and/or its affiliates.
+# 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.
+
+*** Settings ***
+| Resource | resources/libraries/robot/default.robot
+| Resource | resources/libraries/robot/honeycomb/port_mirroring.robot
+| Resource | resources/libraries/robot/honeycomb/interfaces.robot
+| Resource | resources/libraries/robot/honeycomb/honeycomb.robot
+| Force Tags | honeycomb_sanity
+| Suite Setup | Add Interface local0 To Topology | ${node}
+| Suite Teardown | Run Keyword If Any Tests Failed
+| | ... | Restart Honeycomb And VPP And Clear Persisted Configuration | ${node}
+| Documentation | *Honeycomb port mirroring test suite.*
+| ...
+| ... | Test suite uses the first interface of the first DUT node.
+
+*** Variables ***
+| ${interface1}= | ${node['interfaces']['port1']['name']}
+| ${interface2}= | ${node['interfaces']['port3']['name']}
+| ${interface3}= | local0
+
+*** Test Cases ***
+# TODO: Add verification once operational data is available (HONEYCOMB-306)
+| Honeycomb can configure SPAN on an interface
+| | [Documentation] | Honeycomb configures SPAN on interface and verifies/
+| | ... | against VPP SPAN dump.
+| | Given SPAN configuration from VAT should not exist
+| | ... | ${node}
+| | When Honeycomb Configures SPAN on interface
+| | ... | ${node} | ${interface1} | ${interface2}
+| | Then Interface SPAN configuration from VAT should be
+| | ... | ${node} | ${interface1} | ${interface2}
+
+| Honeycomb can disable SPAN on interface
+| | [Documentation] | Honeycomb removes existing SPAN configuration\
+| | ... | on interface and verifies against VPP SPAN dump.
+| | Given Interface SPAN configuration from VAT should be
+| | ... | ${node} | ${interface1} | ${interface2}
+| | When Honeycomb removes interface SPAN configuration
+| | ... | ${node} | ${interface1}
+| | Then SPAN configuration from VAT should not exist
+| | ... | ${node}
+
+| Honeycomb can configure SPAN on one interface to mirror two interfaces
+| | [Documentation] | Honeycomb configures SPAN on interface, mirroring\
+| | ... | two interfaces at the same time. Then verifies against VPP SPAN dump.
+| | [Teardown] | Honeycomb removes interface SPAN configuration
+| | ... | ${node} | ${interface1}
+| | Given SPAN configuration from VAT should not exist
+| | ... | ${node}
+| | When Honeycomb Configures SPAN on interface
+| | ... | ${node} | ${interface1} | ${interface2} | ${interface3}
+| | Then Interface SPAN configuration from VAT should be
+| | ... | ${node} | ${interface1} | ${interface2} | ${interface3}