From 19c91adadd57bfc4e7514993b2a711a826d52e04 Mon Sep 17 00:00:00 2001 From: selias Date: Tue, 13 Sep 2016 16:51:37 +0200 Subject: CSIT-405: Honeycomb test update and cleanup - update suite setup and constants to allow test runs again - cleanup basic interface keywords - cleanup L2-fib test data - add "continue on failure" keyword to some partially failing tests - add teardown to all suites, restarts honeycomb if suite had test failures - fix minor PEP-8 violations in Topology.py Change-Id: Ic5b434af71f77855f81461b280299b8318932c5a Signed-off-by: selias --- resources/libraries/python/constants.py | 5 +- .../libraries/python/honeycomb/HcPersistence.py | 7 +- .../libraries/python/honeycomb/HoneycombSetup.py | 41 ++- .../libraries/python/honeycomb/HoneycombUtil.py | 1 - resources/libraries/python/topology.py | 34 ++- .../libraries/robot/honeycomb/honeycomb.robot | 24 +- .../libraries/robot/honeycomb/interfaces.robot | 3 +- .../libraries/robot/honeycomb/persistence.robot | 2 +- .../libraries/robot/honeycomb/vxlan_gpe.robot | 18 +- resources/test_data/honeycomb/l2_fib.py | 284 ++++++++++++--------- 10 files changed, 270 insertions(+), 149 deletions(-) (limited to 'resources') diff --git a/resources/libraries/python/constants.py b/resources/libraries/python/constants.py index 6e71eb0110..2978f46759 100644 --- a/resources/libraries/python/constants.py +++ b/resources/libraries/python/constants.py @@ -26,7 +26,10 @@ class Constants(object): VAT_BIN_NAME = 'vpp_api_test' # Honeycomb directory location at topology nodes: - REMOTE_HC_DIR = '/opt/honeycomb/vpp-integration-karaf-1.0.0-SNAPSHOT' + REMOTE_HC_DIR = '/opt/honeycomb' + + # Honeycomb persistence files location + REMOTE_HC_PERSIST = '/var/lib/honeycomb/persist' # Honeycomb templates location RESOURCES_TPL_HC = 'resources/templates/honeycomb' diff --git a/resources/libraries/python/honeycomb/HcPersistence.py b/resources/libraries/python/honeycomb/HcPersistence.py index 4d192525d8..3bbc52fa91 100644 --- a/resources/libraries/python/honeycomb/HcPersistence.py +++ b/resources/libraries/python/honeycomb/HcPersistence.py @@ -40,7 +40,7 @@ class HcPersistence(object): :type nodes: list :raises HoneycombError: If persisted configuration could not be removed. """ - cmd = "rm {0}/data/persistence/honeycomb/*".format(Const.REMOTE_HC_DIR) + cmd = "rm -rf {}/*".format(Const.REMOTE_HC_PERSIST) for node in nodes: if node['type'] == NodeType.DUT: ssh = SSH() @@ -60,7 +60,7 @@ class HcPersistence(object): @staticmethod def modify_persistence_files(node, find, replace): - """Searches contents of persistence file config.json for the provided + """Searches contents of persistence file data.json for the provided string, and replaces all occurrences with another string. :param node: Honeycomb node. @@ -74,8 +74,7 @@ class HcPersistence(object): """ argument = "\"s/{0}/{1}/g\"".format(find, replace) - path = "{0}/etc/opendaylight/honeycomb/config.json".format( - Const.REMOTE_HC_DIR) + path = "{0}/config/data.json".format(Const.REMOTE_HC_PERSIST) command = "sed -i {0} {1}".format(argument, path) ssh = SSH() diff --git a/resources/libraries/python/honeycomb/HoneycombSetup.py b/resources/libraries/python/honeycomb/HoneycombSetup.py index 04af9a5a8b..b8c47fac03 100644 --- a/resources/libraries/python/honeycomb/HoneycombSetup.py +++ b/resources/libraries/python/honeycomb/HoneycombSetup.py @@ -58,7 +58,7 @@ class HoneycombSetup(object): logger.console("\nStarting Honeycomb service ...") - cmd = "{0}/bin/start".format(Const.REMOTE_HC_DIR) + cmd = "sudo service honeycomb start" for node in nodes: if node['type'] == NodeType.DUT: @@ -86,7 +86,7 @@ class HoneycombSetup(object): """ logger.console("\nShutting down Honeycomb service ...") - cmd = "{0}/bin/stop".format(Const.REMOTE_HC_DIR) + cmd = "sudo service honeycomb stop" errors = [] for node in nodes: @@ -143,6 +143,13 @@ class HoneycombSetup(object): else: raise HoneycombError('Unexpected return code: {0}.'. format(status_code)) + + status_code, _ = HcUtil.get_honeycomb_data( + node, "config_vpp_interfaces") + if status_code != HTTPCodes.OK: + raise HoneycombError('Honeycomb on node {0} running but ' + 'not yet ready.'.format(node['host']), + enable_logging=False) return True @staticmethod @@ -157,7 +164,7 @@ class HoneycombSetup(object): :return: True if all GETs fail to connect. :rtype bool """ - cmd = "ps -ef | grep -v grep | grep karaf" + cmd = "ps -ef | grep -v grep | grep honeycomb" for node in nodes: if node['type'] == NodeType.DUT: try: @@ -190,6 +197,33 @@ class HoneycombSetup(object): format(node['host'])) return True + @staticmethod + def configure_unsecured_access(*nodes): + """Configure Honeycomb to allow restconf requests through insecure HTTP + used by tests. By default this is only allowed for localhost. + + :param nodes: All nodes in test topology. + :type nodes: dict + :raises HoneycombError: If the configuration could not be changed. + """ + # TODO: Modify tests to use HTTPS instead. + + find = "restconf-binding-address" + replace = '\\"restconf-binding-address\\": \\"0.0.0.0\\",' + + argument = '"/{0}/c\\ {1}"'.format(find, replace) + path = "{0}/config/honeycomb.json".format(Const.REMOTE_HC_DIR) + command = "sed -i {0} {1}".format(argument, path) + + ssh = SSH() + for node in nodes: + if node['type'] == NodeType.DUT: + ssh.connect(node) + (ret_code, _, stderr) = ssh.exec_command_sudo(command) + if ret_code != 0: + raise HoneycombError("Failed to modify configuration on " + "node {0}, {1}".format(node, stderr)) + @staticmethod def print_environment(nodes): """Print information about the nodes to log. The information is defined @@ -239,3 +273,4 @@ class HoneycombSetup(object): ssh = SSH() ssh.connect(node) ssh.exec_command_sudo(cmd) + diff --git a/resources/libraries/python/honeycomb/HoneycombUtil.py b/resources/libraries/python/honeycomb/HoneycombUtil.py index 8f1392c972..2b8e28a5a6 100644 --- a/resources/libraries/python/honeycomb/HoneycombUtil.py +++ b/resources/libraries/python/honeycomb/HoneycombUtil.py @@ -86,7 +86,6 @@ class HoneycombError(Exception): self._msg = "{0}: {1}".format(self.__class__.__name__, msg) self._details = details if enable_logging: - logger.error(self._msg) logger.debug(self._details) def __repr__(self): diff --git a/resources/libraries/python/topology.py b/resources/libraries/python/topology.py index a5c67d313c..c02991fbde 100644 --- a/resources/libraries/python/topology.py +++ b/resources/libraries/python/topology.py @@ -278,11 +278,12 @@ class Topology(object): :return: Interface name of the interface connected to the given link. :rtype: str """ - return Topology._get_interface_by_key_value(node, "vpp_sw_index", sw_index) + return Topology._get_interface_by_key_value(node, "vpp_sw_index", + sw_index) @staticmethod def get_interface_sw_index(node, iface_key): - """Get VPP sw_if_index for the interface. + """Get VPP sw_if_index for the interface using interface key. :param node: Node to get interface sw_if_index on. :param iface_key: Interface key from topology file, or sw_index. @@ -299,6 +300,26 @@ class Topology(object): except (KeyError, ValueError): return None + @staticmethod + def get_interface_sw_index_by_name(node, iface_name): + """Get VPP sw_if_index for the interface using interface name. + + :param node: Node to get interface sw_if_index on. + :param iface_name: Interface name. + :type node: dict + :type iface_name: str + :return: Return sw_if_index or None if not found. + :raises TypeError: If provided interface name is not a string. + """ + try: + if isinstance(iface_name, basestring): + iface_key = Topology.get_interface_by_name(node, iface_name) + return node['interfaces'][iface_key].get('vpp_sw_index') + else: + raise TypeError("Interface name must be a string.") + except (KeyError, ValueError): + return None + @staticmethod def get_interface_mtu(node, iface_key): """Get interface MTU. @@ -514,7 +535,7 @@ class Topology(object): if filt == interface['model']: link_names.append(interface['link']) elif (filter_list is not None) and ('model' not in interface): - logger.trace("Cannot apply filter on interface: {}" \ + logger.trace("Cannot apply filter on interface: {}" .format(str(interface))) else: link_names.append(interface['link']) @@ -534,8 +555,8 @@ class Topology(object): :param filter_list_node2: Link filter criteria for node2. :type node1: dict :type node2: dict - :type filter_list1: list of strings - :type filter_list2: list of strings + :type filter_list_node1: list of strings + :type filter_list_node2: list of strings :return: List of strings that represent connecting link names. :rtype: list """ @@ -578,7 +599,8 @@ class Topology(object): else: return connecting_links[0] - @keyword('Get egress interfaces name on "${node1}" for link with "${node2}"') + @keyword('Get egress interfaces name on "${node1}" for link with ' + '"${node2}"') def get_egress_interfaces_name_for_nodes(self, node1, node2): """Get egress interfaces on node1 for link with node2. diff --git a/resources/libraries/robot/honeycomb/honeycomb.robot b/resources/libraries/robot/honeycomb/honeycomb.robot index c04bd23a57..698b20f83f 100644 --- a/resources/libraries/robot/honeycomb/honeycomb.robot +++ b/resources/libraries/robot/honeycomb/honeycomb.robot @@ -14,6 +14,7 @@ *** Settings *** | Library | resources/libraries/python/honeycomb/HoneycombSetup.py | Library | resources/libraries/python/honeycomb/HoneycombUtil.py +| Library | resources/libraries/python/honeycomb/HcPersistence.py *** Keywords *** | Setup Honeycomb service on DUTs @@ -36,7 +37,7 @@ | | ... | | [Arguments] | @{duts} | | Start honeycomb on DUTs | @{duts} -| | Wait until keyword succeeds | 4min | 20sec +| | Wait until keyword succeeds | 1min | 10sec | | ... | Check honeycomb startup state | @{duts} | Stop honeycomb service on DUTs @@ -58,7 +59,7 @@ | | ... | | [Arguments] | @{duts} | | Stop honeycomb on DUTs | @{duts} -| | Wait until keyword succeeds | 2m | 10s +| | Wait until keyword succeeds | 30sec | 5sec | | ... | Check honeycomb shutdown state | @{duts} | Clear persisted Honeycomb configuration @@ -71,4 +72,21 @@ | | ... | | ... | \| Clear persisted Honeycomb configuration \| ${nodes['DUT1']} \| | | [Arguments] | @{duts} -| | Clear persisted Honeycomb config | @{duts} \ No newline at end of file +| | Clear persisted Honeycomb config | @{duts} + +| Restart Honeycomb and VPP and clear persisted configuration +| | [Documentation] | Restarts Honeycomb and VPP with default configuration. +| | ... +| | ... | *Arguments:* +| | ... | - node - information about a DUT node. Type: dictionary +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Restart Honeycomb and VPP and clear persisted configuration \ +| | ... | \| ${nodes['DUT1']} \| +| | [Arguments] | ${node} +| | Log | Performing clean restart of Honeycomb and VPP. | console=True +| | Stop Honeycomb service on DUTs | ${node} +| | Clear persisted Honeycomb configuration | ${node} +| | Setup DUT | ${node} +| | Setup Honeycomb service on DUTs | ${node} \ No newline at end of file diff --git a/resources/libraries/robot/honeycomb/interfaces.robot b/resources/libraries/robot/honeycomb/interfaces.robot index 81b5d82048..f94d03d7d4 100644 --- a/resources/libraries/robot/honeycomb/interfaces.robot +++ b/resources/libraries/robot/honeycomb/interfaces.robot @@ -190,7 +190,7 @@ | | ... | ${api_data['ietf-ip:ipv4']['neighbor'][0]['link-layer-address']} | | :FOR | ${key} | IN | @{settings.keys()} | | | Should be equal -| | | ... | ${settings['{key']} | ${api_data['ietf-ip:ipv4']['{$key}']} +| | | ... | ${settings['${key}']} | ${api_data['ietf-ip:ipv4']['${key}']} | IPv4 config from VAT should be | | [Documentation] | Retrieves interface ipv4 configuration through VAT and\ @@ -209,7 +209,6 @@ | | [Arguments] | ${node} | ${interface} | ${address} | ${netmask} | | ${vpp_data}= | interfaceCLI.VPP get interface ip addresses | | ... | ${node} | ${interface} | ipv4 -#TODO: update based on resolution of bug https://jira.fd.io/browse/VPP-132 | | Should be equal | ${vpp_data[0]['ip']} | ${address} | | Should be equal | ${vpp_data[0]['netmask']} | ${netmask} diff --git a/resources/libraries/robot/honeycomb/persistence.robot b/resources/libraries/robot/honeycomb/persistence.robot index aacf560f1d..6d2cc1f2e3 100644 --- a/resources/libraries/robot/honeycomb/persistence.robot +++ b/resources/libraries/robot/honeycomb/persistence.robot @@ -14,7 +14,7 @@ *** Settings *** | Library | resources.libraries.python.honeycomb.HcAPIKwInterfaces.InterfaceKeywords | ... | WITH NAME | InterfaceAPI -| Library | resources.libraries.python.honeycomb.HcPersistence +| Library | resources/libraries/python/honeycomb/HcPersistence.py | Resource | resources/libraries/robot/honeycomb/honeycomb.robot | Resource | resources/libraries/robot/honeycomb/interfaces.robot | Resource | resources/libraries/robot/honeycomb/vxlan.robot diff --git a/resources/libraries/robot/honeycomb/vxlan_gpe.robot b/resources/libraries/robot/honeycomb/vxlan_gpe.robot index 364a23228c..ef20ed946c 100644 --- a/resources/libraries/robot/honeycomb/vxlan_gpe.robot +++ b/resources/libraries/robot/honeycomb/vxlan_gpe.robot @@ -153,9 +153,25 @@ | | Should be equal as strings | | ... | ${api_data['if-index']} | ${sw_if_index} +| VxLAN GPE configuration from Honeycomb should be empty +| | [Documentation] | Uses Honeycomb API to get operational data about\ +| | ... | the given interface and expects to fail. +| | ... +| | ... | *Arguments:* +| | ... | - node - information about a DUT node. Type: dictionary +| | ... +| | ... | *Example:* +| | ... | \| VxLAN GPE configuration from Honeycomb should be empty\ +| | ... | \| ${nodes['DUT1']} \| vxlan_gpe_tunnel0 \| +| | ... +| | [Arguments] | ${node} | ${interface} +| | ... +| | ${api_data}= | interfaceAPI.Get interface oper data | ${node} | ${interface} +| | Should be empty | ${api_data} + | VxLAN GPE configuration from VAT should be empty | | [Documentation] | Uses VAT to get operational data about the given\ -| | ... | interface and expects empty dictionary. +| | ... | interface and expects an empty dictionary. | | ... | | ... | *Arguments:* | | ... | - node - information about a DUT node. Type: dictionary diff --git a/resources/test_data/honeycomb/l2_fib.py b/resources/test_data/honeycomb/l2_fib.py index b06193ad1d..d1600fc46d 100644 --- a/resources/test_data/honeycomb/l2_fib.py +++ b/resources/test_data/honeycomb/l2_fib.py @@ -13,130 +13,160 @@ """Test variables for Honeycomb L2 FIB test suite.""" -# Bridge domain name. -bd_name = 'test-l2-bd' -bd_index = 1 - -# Bridge domain settings used while creating a test bridge domain. -bd_settings = { - 'flood': True, - 'forward': True, - 'learn': True, - 'unknown-unicast-flood': True, - 'arp-termination': True -} - -# Bridge domain configuration used while adding the bridge domain to an -# interface. -if_bd_settings = { - 'bridge-domain': bd_name, - 'split-horizon-group': 1, - 'bridged-virtual-interface': False -} - -# Add L2 FIB entry (forward). -# Configuration data: -l2_fib_forward_cfg = { - "phys-address": "aa:bb:cc:dd:ee:ff", - "outgoing-interface": "GigabitEthernet0/8/0", - "action": "l2-fib-forward" -} - -# Expected operational data: -l2_fib_forward_oper = { - "phys-address": "aa:bb:cc:dd:ee:ff", - "outgoing-interface": "GigabitEthernet0/8/0", - "bridged-virtual-interface": False, - "action": "v3po:l2-fib-forward", - "static-config": False -} - -# Expected VAT data: -l2_fib_forward_vat = { - "mac": int("".join(l2_fib_forward_oper["phys-address"].split(':')), 16), - "static_mac": 0, - "filter_mac": 0, - "bvi_mac": 0 - } - -# Add L2 FIB entry (static, forward). -# Configuration data: -l2_fib_static_forward_cfg = { - "phys-address": "22:22:33:44:55:66", - "outgoing-interface": "GigabitEthernet0/8/0", - "static-config": True, - "action": "l2-fib-forward" -} - -# Expected operational data: -l2_fib_static_forward_oper = { - "phys-address": "22:22:33:44:55:66", - "outgoing-interface": "GigabitEthernet0/8/0", - "bridged-virtual-interface": False, - "action": "v3po:l2-fib-forward", - "static-config": True -} - -# Expected VAT data: -l2_fib_static_forward_vat = { - "mac": int("".join(l2_fib_static_forward_oper["phys-address"]. - split(':')), 16), - "sw_if_index": 5, - "static_mac": 1, - "filter_mac": 0, - "bvi_mac": 0 -} - -# Add L2 FIB entry (filter). -# Configuration data: -l2_fib_filter_cfg = { - "phys-address": "00:01:02:03:04:05", - "outgoing-interface": "GigabitEthernet0/8/0", - "static-config": True, - "action": "l2-fib-filter" -} - -# Expected operational data: -l2_fib_filter_oper = { - "phys-address": "00:01:02:03:04:05", - "outgoing-interface": "GigabitEthernet0/8/0", - "bridged-virtual-interface": False, - "action": "v3po:l2-fib-filter", - "static-config": True -} - -# Expected VAT data: -l2_fib_filter_vat = { - "mac": int("".join(l2_fib_filter_oper["phys-address"].split(':')), 16), - "sw_if_index": 5, - "static_mac": 1, - "filter_mac": 1, - "bvi_mac": 0 -} - -# WRONG configuration data - Add L2 FIB entry. -l2_fib_forward_cfg_wrong_mac = { - "phys-address": "WRONG-MAC", - "outgoing-interface": "GigabitEthernet0/8/0", - "action": "l2-fib-forward" -} - -l2_fib_forward_cfg_wrong_if = { - "phys-address": "aa:bb:cc:dd:ee:ff", - "outgoing-interface": "WRONG-INTERFACE", - "action": "l2-fib-forward" -} - -l2_fib_forward_cfg_wrong_action = { - "phys-address": "aa:bb:cc:dd:ee:ff", - "outgoing-interface": "GigabitEthernet0/8/0", - "action": "WRONG-ACTION" -} - -# Modify L2 FIB entry (forward). -# Configuration data: -l2_fib_forward_modified_cfg = { - "phys-address": "aa:bb:cc:dd:ee:ff", - "outgoing-interface": "GigabitEthernet0/9/0", - "action": "l2-fib-forward" -} +from resources.libraries.python.topology import Topology + + +def get_variables(node, interface, interface2): + """Creates and returns dictionary of test variables. + + :param node: A Honeycomb node. + :param interface: Name of an interface on the specified node. + :param interface: Name of another interface on the specified node. + :type node: dict + :type interface: str + :type interface2: str + :return: Dictionary of test variables. + :rtype: dict + """ + # Interface sw_if_index + sw_if_index = Topology.get_interface_sw_index_by_name(node, interface) + sw_if_index2 = Topology.get_interface_sw_index_by_name(node, interface2) + + # Bridge domain name. + bd_name = 'test-l2-bd' + + # L2 FIB MACs used in configuration. + notstatic = "aa:bb:cc:dd:ee:ff" + static = "22:22:33:44:55:66" + filtered = "00:01:02:03:04:05" + + variables = { + 'bd_name': bd_name, + # Bridge domain settings used while creating a test bridge domain. + 'bd_settings': { + 'flood': True, + 'forward': True, + 'learn': True, + 'unknown-unicast-flood': True, + 'arp-termination': True + }, + + # Index of created bridge domain. + 'bd_index': 1, + + # Bridge domain configuration used while adding the bridge domain to an + # interface. + 'if_bd_settings': { + 'bridge-domain': bd_name, + 'split-horizon-group': 1, + 'bridged-virtual-interface': False + }, + + # Add L2 FIB entry (forward). + # Configuration data: + 'l2_fib_forward_cfg': { + "phys-address": notstatic, + "outgoing-interface": interface, + "action": "l2-fib-forward" + }, + + # Expected operational data: + 'l2_fib_forward_oper': { + "phys-address": notstatic, + "outgoing-interface": interface, + "bridged-virtual-interface": False, + "action": "v3po:l2-fib-forward", + "static-config": False + }, + + # Expected VAT data: + 'l2_fib_forward_vat': { + "mac": int("".join(notstatic.split(':')), 16), + "sw_if_index": sw_if_index, + "static_mac": 0, + "filter_mac": 0, + "bvi_mac": 0 + }, + + # Add L2 FIB entry (static, forward). + # Configuration data: + 'l2_fib_static_forward_cfg': { + "phys-address": static, + "outgoing-interface": interface, + "static-config": True, + "action": "l2-fib-forward" + }, + + # Expected operational data: + 'l2_fib_static_forward_oper': { + "phys-address": static, + "outgoing-interface": interface, + "bridged-virtual-interface": False, + "action": "v3po:l2-fib-forward", + "static-config": True + }, + + # Expected VAT data: + 'l2_fib_static_forward_vat': { + "mac": int("".join(static.split(':')), 16), + "sw_if_index": sw_if_index, + "static_mac": 1, + "filter_mac": 0, + "bvi_mac": 0 + }, + + # Add L2 FIB entry (filter). + # Configuration data: + 'l2_fib_filter_cfg': { + "phys-address": filtered, + "outgoing-interface": interface, + "static-config": True, + "action": "l2-fib-filter" + }, + + # Expected operational data: + 'l2_fib_filter_oper': { + "phys-address": filtered, + "outgoing-interface": interface, + "bridged-virtual-interface": False, + "action": "v3po:l2-fib-filter", + "static-config": True + }, + + # Expected VAT data: + 'l2_fib_filter_vat': { + "mac": int("".join(filtered.split(':')), 16), + "sw_if_index": sw_if_index, + "static_mac": 1, + "filter_mac": 1, + "bvi_mac": 0 + }, + + # WRONG configuration data - Add L2 FIB entry. + 'l2_fib_forward_cfg_wrong_mac': { + "phys-address": "WRONG-MAC", + "outgoing-interface": interface, + "action": "l2-fib-forward" + }, + + 'l2_fib_forward_cfg_wrong_if': { + "phys-address": notstatic, + "outgoing-interface": "WRONG-INTERFACE", + "action": "l2-fib-forward" + }, + + 'l2_fib_forward_cfg_wrong_action': { + "phys-address": notstatic, + "outgoing-interface": interface, + "action": "WRONG-ACTION" + }, + + # Modify L2 FIB entry (forward). + # Configuration data: + 'l2_fib_forward_modified_cfg': { + "phys-address": notstatic, + "outgoing-interface": sw_if_index2, + "action": "l2-fib-forward" + } + } + return variables -- cgit 1.2.3-korg