diff options
10 files changed, 433 insertions, 31 deletions
diff --git a/resources/libraries/python/VPPUtil.py b/resources/libraries/python/VPPUtil.py index da59d1958d..5881d18966 100644 --- a/resources/libraries/python/VPPUtil.py +++ b/resources/libraries/python/VPPUtil.py @@ -46,3 +46,35 @@ class VPPUtil(object): ssh.connect(node) for _, value in def_setting_tb_displayed.items(): ssh.exec_command_sudo('vppctl sh {}'.format(value)) + + @staticmethod + def stop_vpp_service(node): + """Stop VPP service on the specified node. + + :param node: VPP node. + :type node: dict + :raises RuntimeError: If VPP fails to stop. + """ + + ssh = SSH() + ssh.connect(node) + cmd = "service vpp stop" + ret_code, _, _ = ssh.exec_command_sudo(cmd, timeout=80) + if int(ret_code) != 0: + raise RuntimeError("VPP service did not shut down gracefully.") + + @staticmethod + def start_vpp_service(node): + """start VPP service on the specified node. + + :param node: VPP node. + :type node: dict + :raises RuntimeError: If VPP fails to start. + """ + + ssh = SSH() + ssh.connect(node) + cmd = "service vpp start" + ret_code, _, _ = ssh.exec_command_sudo(cmd) + if int(ret_code) != 0: + raise RuntimeError("VPP service did not start.") diff --git a/resources/libraries/python/honeycomb/HoneycombSetup.py b/resources/libraries/python/honeycomb/HoneycombSetup.py index 975a36c753..d10a5ccb4e 100644 --- a/resources/libraries/python/honeycomb/HoneycombSetup.py +++ b/resources/libraries/python/honeycomb/HoneycombSetup.py @@ -637,21 +637,6 @@ class HoneycombSetup(object): logger.info("ODL client service stopped.") - @staticmethod - def stop_vpp_service(node): - """Stop VPP service on the specified node. - - :param node: VPP node. - :type node: dict - :raises RuntimeError: If VPP fails to stop. - """ - - ssh = SSH() - ssh.connect(node) - cmd = "service vpp stop" - ret_code, _, _ = ssh.exec_command_sudo(cmd, timeout=80) - if int(ret_code) != 0: - logger.debug("VPP service refused to shut down.") class HoneycombStartupConfig(object): diff --git a/resources/libraries/python/honeycomb/HoneycombUtil.py b/resources/libraries/python/honeycomb/HoneycombUtil.py index 86feb78d94..b7338d3ed8 100644 --- a/resources/libraries/python/honeycomb/HoneycombUtil.py +++ b/resources/libraries/python/honeycomb/HoneycombUtil.py @@ -448,7 +448,7 @@ class HoneycombUtil(object): if not perf: cmd = "cp /tmp/honeycomb.log /scratch/" - ssh.exec_command_sudo(cmd) + ssh.exec_command_sudo(cmd, timeout=60) else: ssh.scp( ".", diff --git a/resources/libraries/python/honeycomb/IPv6Management.py b/resources/libraries/python/honeycomb/IPv6Management.py new file mode 100644 index 0000000000..deec041103 --- /dev/null +++ b/resources/libraries/python/honeycomb/IPv6Management.py @@ -0,0 +1,147 @@ +# Copyright (c) 2017 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. + +"""Keywords used for setup and testing of Honeycomb's control-plane interface +using IPv6. +""" + +from resources.libraries.python.ssh import SSH + + +class IPv6Management(object): + """Utilities for managing IPv6 contol-plane interfaces.""" + + def __init__(self): + pass + + @staticmethod + def get_interface_name_by_mac(node, mac): + """Get the name of an interface using its MAC address. + + :param node: Node in topology. + :param mac: MAC address. + :type node: dict + :type mac: str + :returns: Name of the interface. + :rtype: str + :raises RuntimeError: If no interface is found. + """ + + cmd = " | ".join([ + "fgrep -ls '{0}' /sys/class/net/*/address".format(mac), + "awk -F '/' '{print $5}'" + ]) + + ssh = SSH() + ssh.connect(node) + ret_code, stdout, _ = ssh.exec_command(cmd) + + if ret_code == 0: + return stdout.strip() + else: + raise RuntimeError("No interface found using the specified MAC " + "address.") + + @staticmethod + def clear_interface_configuration(node, interface): + """Remove all configured IP addresses from the specified interface + and set it into down state. + + :param node: Node in topology. + :param interface: Name of an interface on the node. + :type node: dict + :type interface: str + :raises RuntimeError: If the configuration could not be cleared. + """ + + cmd = " && ".join([ + "sudo ip addr flush dev {interface}".format(interface=interface), + "sudo ip link set dev {interface} down".format(interface=interface) + ]) + + ssh = SSH() + ssh.connect(node) + ret_code, _, _ = ssh.exec_command(cmd) + if ret_code != 0: + raise RuntimeError("Could not clear interface configuration.") + + @staticmethod + def set_management_interface_address(node, interface, address, prefix): + """Configure an IP address on the specified interface. + + :param node: Node in topology. + :param interface: Name of an interface on the node. + :param address: IP address to configure. + :param prefix: IP network prefix. + :type node: dict + :type interface: str + :type address: str + :type prefix: int + :raises RuntimeError: If the configuration fails. + """ + + ssh = SSH() + ssh.connect(node) + + # Enable IPv6 for only the specified interface + cmd = "sudo sysctl net.ipv6.conf.{0}.disable_ipv6=0".format(interface) + + ret_code, _, _ = ssh.exec_command(cmd) + if ret_code != 0: + raise RuntimeError("Could not enable IPv6 on interface.") + + # Configure IPv6 address on the interface + cmd = "sudo ip address add {address}/{prefix} dev {interface}".format( + interface=interface, + address=address, + prefix=prefix) + + ret_code, _, _ = ssh.exec_command(cmd) + if ret_code != 0: + raise RuntimeError("Could not configure IP address on interface.") + + # Set the interface up + cmd = "sudo ip link set {interface} up".format(interface=interface) + + ret_code, _, _ = ssh.exec_command(cmd) + if ret_code != 0: + raise RuntimeError("Could not change the interface to 'up' state.") + + @staticmethod + def configure_control_interface_tunnel(node, src_port, dst_ip, dst_port): + """Configure a tunnel on the specified node, tunelling any IPv4 traffic + from one port to the specified address. + + :param node: Node in topology. + :param src_port: Port to tunnel traffic from. + :param dst_ip: IP address to tunnel traffic to. + :param dst_port: Port to tunnel traffic to. + :type node: dict + :type src_port: int + :type dst_ip: str + :type dst_port: int + :raises RuntimeError: If tunnel creation is not successful. + """ + + cmd = "nohup socat TCP4-LISTEN:{src_port},fork,su=nobody " \ + "TCP6:[{dst_ip}]:{dst_port} $@ > " \ + "/tmp/socat.log 2>&1 &".format( + src_port=src_port, + dst_ip=dst_ip, + dst_port=dst_port) + + ssh = SSH() + ssh.connect(node) + ret_code, _, _ = ssh.exec_command_sudo(cmd) + if ret_code != 0: + raise RuntimeError("Could not configure tunnel.") diff --git a/resources/libraries/robot/honeycomb/honeycomb.robot b/resources/libraries/robot/honeycomb/honeycomb.robot index fa5cea7978..cb2e58e2fc 100644 --- a/resources/libraries/robot/honeycomb/honeycomb.robot +++ b/resources/libraries/robot/honeycomb/honeycomb.robot @@ -284,19 +284,6 @@ | | [arguments] | ${node} | ${feature} | | Manage Honeycomb Features | ${node} | ${feature} | disable=${True} -| Stop VPP Service on DUT -| | [Documentation] | Stop the VPP service on the specified node. -| | ... -| | ... | *Arguments:* -| | ... | - node - information about a DUT node. Type: dictionary -| | ... -| | ... | *Example:* -| | ... -| | ... | \| Stop VPP Service on DUT \| ${nodes['DUT1']} \| -| | ... -| | [Arguments] | ${node} -| | Stop VPP Service | ${node} - | Honeycomb Performance Suite Setup Generic | | [Documentation] | Generic test suite setup for Honeycomb performance tests. | | ... | Performs multiple attempts to start Honeycomb+VPP stack. diff --git a/resources/libraries/robot/honeycomb/ipv6_control.robot b/resources/libraries/robot/honeycomb/ipv6_control.robot new file mode 100644 index 0000000000..8e50a31927 --- /dev/null +++ b/resources/libraries/robot/honeycomb/ipv6_control.robot @@ -0,0 +1,44 @@ +# Copyright (c) 2017 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.DHCP.DHCPRelayKeywords +| Library | resources.libraries.python.Dhcp.DhcpProxy +| Library | resources.libraries.python.DUTSetup +| Documentation | Keywords used to test Honeycomb DHCP features. + +*** Keywords *** +| Convert data-plane interface to control-plane +| | [Documentation] | Unbinds an interface from VPP and binds it to kernel\ +| | ... | driver specified in topology. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - bd_name - Name of the interface in topology. Type: string +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Convert data-plane interface to control-plane \| ${nodes['DUT1']} \ +| | ... | \| port3 \| +| | ... +| | [Arguments] | ${node} | ${interface} +| | ${new_driver}= | Get Variable Value +| | ... | ${node['interfaces']['${interface}']['driver']} +| | PCI Driver Unbind | ${node} +| | ... | ${node['interfaces']['${interface}']['pci_address']} +| | Run Keyword If | '${new_driver}' == 'None' +| | ... | PCI Driver Bind | ${node} +| | ... | ${node['interfaces']['${interface}']['pci_address']} | virtio-pci +| | ... | ELSE +| | ... | PCI Driver Bind | ${node} +| | ... | ${node['interfaces']['${interface}']['pci_address']} | ${new_driver} diff --git a/resources/libraries/robot/shared/default.robot b/resources/libraries/robot/shared/default.robot index ddf20405fe..250380dcc0 100644 --- a/resources/libraries/robot/shared/default.robot +++ b/resources/libraries/robot/shared/default.robot @@ -25,6 +25,7 @@ | Library | resources.libraries.python.Tap | Library | resources.libraries.python.VppConfigGenerator | Library | resources.libraries.python.VppCounters +| Library | resources.libraries.python.VPPUtil | Library | Collections *** Keywords *** @@ -375,3 +376,29 @@ | | Tear down functional test | | Linux Del Bridge | ${nodes['DUT1']} | ${bid_TAP} | | Clean Up Namespaces | ${nodes['DUT1']} + +| Stop VPP Service on DUT +| | [Documentation] | Stop the VPP service on the specified node. +| | ... +| | ... | *Arguments:* +| | ... | - node - information about a DUT node. Type: dictionary +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Stop VPP Service on DUT \| ${nodes['DUT1']} \| +| | ... +| | [Arguments] | ${node} +| | Stop VPP Service | ${node} + +| Start VPP Service on DUT +| | [Documentation] | Start the VPP service on the specified node. +| | ... +| | ... | *Arguments:* +| | ... | - node - information about a DUT node. Type: dictionary +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Start VPP Service on DUT \| ${nodes['DUT1']} \| +| | ... +| | [Arguments] | ${node} +| | Start VPP Service | ${node}
\ No newline at end of file diff --git a/tests/vpp/func/honeycomb/mgmt-cfg-apihcv6-func.robot b/tests/vpp/func/honeycomb/mgmt-cfg-apihcv6-func.robot new file mode 100644 index 0000000000..0be827eaaf --- /dev/null +++ b/tests/vpp/func/honeycomb/mgmt-cfg-apihcv6-func.robot @@ -0,0 +1,181 @@ +# Copyright (c) 2017 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. + +*** Variables *** +# IP addresses for IPv6 link +| ${tg_to_dut_if2_ip}= | fd00:1234::1 +| ${dut_to_tg_if2_ip}= | fd00:1234::2 +| ${ipv6_prefix}= | ${64} +# Configuration which will be set and verified during tests. +| ${bd1_name}= | bd-01 +| ${bd2_name}= | bd-02 +| &{bd_settings}= | flood=${True} | forward=${True} | learn=${True} +| ... | unknown-unicast-flood=${True} | arp-termination=${True} +| &{if_settings}= | split_horizon_group=${1} | bvi=${False} +| &{if_settings2}= | split_horizon_group=${2} | bvi=${True} +| ${vhost_interface}= | test_vhost +| &{vhost_user_server}= | socket=/tmp/soc1 | role=server +| &{vhost_user_server_edit_1}= | socket=/tmp/soc12 | role=server +| &{vhost_user_server_edit_2}= | socket=/tmp/soc12 | role=client + +*** Settings *** +| Library | resources.libraries.python.honeycomb.IPv6Management +| Library | resources.libraries.python.VPPUtil +| Resource | resources/libraries/robot/shared/default.robot +| Resource | resources/libraries/robot/honeycomb/honeycomb.robot +| Resource | resources/libraries/robot/honeycomb/interfaces.robot +| Resource | resources/libraries/robot/honeycomb/bridge_domain.robot +| Resource | resources/libraries/robot/honeycomb/ipv6_control.robot +| Resource | resources/libraries/robot/shared/testing_path.robot +| Resource | resources/libraries/robot/honeycomb/netconf.robot +| Resource | resources/libraries/robot/honeycomb/vhost_user.robot +| Variables | resources/test_data/honeycomb/netconf/triggers.py +| ... +| Suite Setup | Set Up Honeycomb Functional Test Suite | ${node} +| ... +| Suite Teardown | Run Keywords +| ... | Unconfigure IPv6 Management Interface | AND +| ... | Tear Down Honeycomb Functional Test Suite | ${node} +| ... +# IPv6 is disabled in VIRL images +| Force Tags | HC_FUNC | EXPECTED_FAILING +| ... +| Documentation | *Honeycomb IPv6 control interface test suite.* + +*** Test Cases *** +| TC01: Honeycomb sets up l2 bridge domain +| | [Documentation] | Check if Honeycomb can create bridge domains on VPP node. +| | ... +| | [Setup] | Configure IPv6 Management Interface +| | When Honeycomb creates first l2 bridge domain +| | ... | ${tunneled_node} | ${bd1_name} | ${bd_settings} +| | Then Bridge domain Operational Data From Honeycomb Should Be +| | ... | ${tunneled_node} | ${bd1_name} | ${bd_settings} + +| TC02: Honeycomb removes bridge domains +| | [Documentation] | Check if Honeycomb can remove bridge domains from a VPP\ +| | ... | node. +| | ... +| | Given Bridge domain Operational Data From Honeycomb Should Be +| | ... | ${tunneled_node} | ${bd1_name} | ${bd_settings} +| | When Honeycomb removes all bridge domains | ${tunneled_node} +| | Then Honeycomb should show no bridge domains | ${tunneled_node} + +| TC03: Honeycomb creates vhost-user interface - server +| | [Documentation] | Check if Honeycomb creates a vhost-user interface, role:\ +| | ... | server. +| | ... +| | Given vhost-user Operational Data From Honeycomb Should Be empty +| | ... | ${tunneled_node} | ${vhost_interface} +| | When Honeycomb creates vhost-user interface +| | ... | ${tunneled_node} | ${vhost_interface} | ${vhost_user_server} +| | Then vhost-user Operational Data From Honeycomb Should Be +| | ... | ${tunneled_node} | ${vhost_interface} | ${vhost_user_server} + +| TC04: Honeycomb modifies vhost-user interface - server +| | [Documentation] | Check if Honeycomb can modify properties of existing\ +| | ... | vhost-user interface, role: server. +| | ... +| | Given vhost-user Operational Data From Honeycomb Should Be +| | ... | ${tunneled_node} | ${vhost_interface} | ${vhost_user_server} +| | When Honeycomb configures vhost-user interface +| | ... | ${tunneled_node} | ${vhost_interface} | ${vhost_user_server_edit_1} +| | Then vhost-user Operational Data From Honeycomb Should Be +| | ... | ${tunneled_node} | ${vhost_interface} | ${vhost_user_server_edit_1} +| | When Honeycomb configures vhost-user interface +| | ... | ${tunneled_node} | ${vhost_interface} | ${vhost_user_server_edit_2} +| | Then vhost-user Operational Data From Honeycomb Should Be +| | ... | ${tunneled_node} | ${vhost_interface} | ${vhost_user_server_edit_2} +| | When Honeycomb configures vhost-user interface +| | ... | ${tunneled_node} | ${vhost_interface} | ${vhost_user_server} +| | Then vhost-user Operational Data From Honeycomb Should Be +| | ... | ${tunneled_node} | ${vhost_interface} | ${vhost_user_server} + +| TC05: Honeycomb deletes vhost-user interface - server +| | [Documentation] | Check if Honeycomb can delete an existing vhost-user\ +| | ... | interface, role: server. +| | ... +| | Given vhost-user Operational Data From Honeycomb Should Be +| | ... | ${tunneled_node} | ${vhost_interface} | ${vhost_user_server} +| | When Honeycomb removes vhost-user interface +| | ... | ${tunneled_node} | ${vhost_interface} +| | Then vhost-user Operational Data From Honeycomb Should Be empty +| | ... | ${tunneled_node} | ${vhost_interface} + +| TC06: Honeycomb can create and delete interfaces +| | [Documentation] | Repeatedly create and delete an interface through Netconf\ +| | ... | and check the reply for any errors. +| | ... +| | Given Netconf session should be established | ${tunneled_node} +| | And Honeycomb creates first L2 bridge domain +| | ... | ${tunneled_node} | bd_netconf | ${bd_settings} +| | :FOR | ${index} | IN RANGE | 20 +| | | When Error trigger is sent | ${trigger_105} +| | | Then Replies should not contain RPC errors + +| TC07: Honeycomb can create vlan subinterface +| | [Documentation] | Configure a Vlan sub-interface under a physical interface. +| | ... +| | Given Netconf session should be established | ${tunneled_node} +| | When Error Trigger Is Sent +| | ... | ${trigger_vlan} | interface=${interface} +| | Then Replies should not contain RPC errors + +*** Keywords *** +| Configure IPv6 Management Interface +| | [Documentation] | Change one of VPP's data-plane interfaces on DUT into\ +| | ... | a control-plane interface that Honeycomb can listen on. Setup IPv6\ +| | ... | addresses on the link. Create an IPv4 to IPv6 tunnel on TG and create\ +| | ... | suite variables. +| | ... +| | Configure path in 2-node circular topology +| | ... | ${nodes['TG']} | ${nodes['DUT1']} | ${nodes['TG']} +| | Stop VPP service on DUT | ${dut_node} +| | Stop Honeycomb Service on DUTs | ${dut_node} +| | Convert data-plane interface to control-plane +| | ... | ${dut_node} | ${dut_to_tg_if2} +| | Sleep | 5sec | Wait until Linux reclaims the interface. +| | ${tg_to_dut_if2_name}= | Get Interface Name by MAC +| | ... | ${tg_node} | ${tg_to_dut_if2_mac} +| | ${dut_to_tg_if2_name}= | Get Interface Name by MAC +| | ... | ${dut_node} | ${dut_to_tg_if2_mac} +| | ${tunneled_node}= | Copy Dictionary | ${dut_node} +| | Set To Dictionary | ${tunneled_node} | host | ${tg_node['host']} +| | ${interface}= | Get Interface Name | ${dut_node} | ${dut_to_tg_if1} +| | Set Suite Variable | ${interface} +| | Set Suite Variable | ${tunneled_node} +| | Set Suite Variable | ${tg_node} +| | Set Suite Variable | ${dut_node} +| | Set Suite Variable | ${dut_to_tg_if2} +| | Set Suite Variable | ${dut_to_tg_if2_name} +| | Set Suite Variable | ${tg_to_dut_if2_name} +| | Set management interface address +| | ... | ${tg_node} | ${tg_to_dut_if2_name} +| | ... | ${tg_to_dut_if2_ip} | ${ipv6_prefix} +| | Set management interface address +| | ... | ${dut_node} | ${dut_to_tg_if2_name} +| | ... | ${dut_to_tg_if2_ip} | ${ipv6_prefix} +| | Configure Control Interface Tunnel +| | ... | ${tg_node} | ${dut_node['honeycomb']['port']} +| | ... | ${dut_to_tg_if2_ip} | ${dut_node['honeycomb']['port']} +| | Configure Control Interface Tunnel +| | ... | ${tg_node} | ${dut_node['honeycomb']['netconf_port']} +| | ... | ${dut_to_tg_if2_ip} | ${dut_node['honeycomb']['netconf_port']} +| | Start VPP service on DUT | ${dut_node} +| | Configure Honeycomb service on DUTs | ${dut_node} + +| Unconfigure IPv6 Management Interface +| | [Documentation] | Remove all IP addresses from interfaces in the IPv6 link. +| | ... +| | Clear Interface Configuration | ${tg_node} | ${tg_to_dut_if2_name} +| | Clear Interface Configuration | ${dut_node} | ${dut_to_tg_if2_name} diff --git a/tests/vpp/func/honeycomb/mgmt-cfg-l2fib-apihc-apivat-func.robot b/tests/vpp/func/honeycomb/mgmt-cfg-l2fib-apihc-apivat-func.robot index 011824f39a..b2e4aec725 100644 --- a/tests/vpp/func/honeycomb/mgmt-cfg-l2fib-apihc-apivat-func.robot +++ b/tests/vpp/func/honeycomb/mgmt-cfg-l2fib-apihc-apivat-func.robot @@ -26,7 +26,7 @@ | ... | Suite Teardown | Tear Down Honeycomb Functional Test Suite | ${node} | ... -| Force tags | HC_FUNC +| Force Tags | HC_FUNC *** Variables *** # Interface to run tests on. diff --git a/tests/vpp/func/honeycomb/mgmt-cfg-lispgpe-apihc-apivat-func.robot b/tests/vpp/func/honeycomb/mgmt-cfg-lispgpe-apihc-apivat-func.robot index 61dbb95c2a..b9cf898567 100644 --- a/tests/vpp/func/honeycomb/mgmt-cfg-lispgpe-apihc-apivat-func.robot +++ b/tests/vpp/func/honeycomb/mgmt-cfg-lispgpe-apihc-apivat-func.robot @@ -90,7 +90,6 @@ | | [Documentation] | Check if Honeycomb can configure a LISP mapping\ | | ... | with VRF. | | ... -| | [Tags] | HC_FUNC | | [Teardown] | Honeycomb removes LISP GPE mapping | | ... | ${node} | ${negative_mapping_ip6['id']} | | Given LISP GPE mappings from Honeycomb should not exist |