From: selias Date: Fri, 25 Nov 2016 09:50:29 +0000 (+0100) Subject: CSIT-481: HC Test: Port mirroring (SPAN) X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=commitdiff_plain;h=4f70ebf481d496f91d2b1ec27361c9898b19c451 CSIT-481: HC Test: Port mirroring (SPAN) - add port mirroring test suite - add relevant keywords Change-Id: I4ff398dd2ab4323e2a53af2812bf07cbb7043252 Signed-off-by: selias --- diff --git a/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py index 3271413d1c..01a2eba0ad 100644 --- a/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py +++ b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py @@ -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") diff --git a/resources/libraries/python/telemetry/SPAN.py b/resources/libraries/python/telemetry/SPAN.py index 7933898c02..aff465c0ea 100644 --- a/resources/libraries/python/telemetry/SPAN.py +++ b/resources/libraries/python/telemetry/SPAN.py @@ -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 diff --git a/resources/libraries/python/topology.py b/resources/libraries/python/topology.py index 3c903e5c94..0214cc9c02 100644 --- a/resources/libraries/python/topology.py +++ b/resources/libraries/python/topology.py @@ -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 index 0000000000..854da311e9 --- /dev/null +++ b/resources/libraries/robot/honeycomb/port_mirroring.robot @@ -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 diff --git a/resources/templates/vat/span_create.vat b/resources/templates/vat/span_create.vat index 62c423248b..3671d0b19e 100644 --- a/resources/templates/vat/span_create.vat +++ b/resources/templates/vat/span_create.vat @@ -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 index 0000000000..0f83af73c3 --- /dev/null +++ b/resources/templates/vat/span_dump.vat @@ -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 index 0000000000..521d59a244 --- /dev/null +++ b/tests/func/honeycomb/120_port_mirroring.robot @@ -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}