X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2Fhoneycomb%2FHcAPIKwInterfaces.py;h=e9ef13352bf08311a56adecc1afd0a179985a94f;hp=83267b5e55166234881e1c02d0b302c33d34eb0a;hb=67f247b4015b76641c6fab7c49e7d9862043c856;hpb=233683de57527f477bf7e8d042a5f3d1f08c7744 diff --git a/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py index 83267b5e55..e9ef13352b 100644 --- a/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py +++ b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2018 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: @@ -16,17 +16,17 @@ The keywords make possible to put and get configuration data and to get operational data. """ -from resources.libraries.python.topology import Topology +from robot.api import logger + from resources.libraries.python.HTTPRequest import HTTPCodes from resources.libraries.python.honeycomb.HoneycombSetup import HoneycombError from resources.libraries.python.honeycomb.HoneycombUtil \ import DataRepresentation from resources.libraries.python.honeycomb.HoneycombUtil \ import HoneycombUtil as HcUtil +from resources.libraries.python.topology import Topology -# pylint: disable=too-many-public-methods -# pylint: disable=too-many-lines class InterfaceKeywords(object): """Keywords for Interface manipulation. @@ -44,11 +44,14 @@ class InterfaceKeywords(object): "temporary-valid-lifetime", "temporary-preferred-lifetime") ETH_PARAMS = ("mtu", ) - ROUTING_PARAMS = ("vrf-id", ) + ROUTING_PARAMS = ("ipv4-vrf-id", "ipv6-vrf-id") VXLAN_PARAMS = ("src", "dst", "vni", "encap-vrf-id") L2_PARAMS = ("bridge-domain", "split-horizon-group", "bridged-virtual-interface") - TAP_PARAMS = ("tap-name", "mac", "device-instance") + TAP_PARAMS = ("id", "tx-ring-size", "rx-ring-size", "host-mac", + "host-interface-name", "host-namespace", "host-bridge", + "host-ipv4-address", "host-ipv6-address", "tag", + "host-ipv4-gateway", "host-ipv6-gateway", "mac") VHOST_USER_PARAMS = ("socket", "role") SUB_IF_PARAMS = ("identifier", "vlan-type", @@ -83,16 +86,16 @@ class InterfaceKeywords(object): :type interface: str :type data: dict :type data_representation: DataRepresentation - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the status code in response on PUT is not - 200 = OK. + 200 = OK. """ status_code, resp = HcUtil.\ put_honeycomb_data(node, "config_vpp_interfaces", data, data_representation=data_representation) - if status_code != HTTPCodes.OK: + if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED): raise HoneycombError( "The configuration of interface '{0}' was not successful. " "Status code: {1}.".format(interface, status_code)) @@ -104,7 +107,7 @@ class InterfaceKeywords(object): :param node: Honeycomb node. :type node: dict - :return: Configuration data about all interfaces from Honeycomb. + :returns: Configuration data about all interfaces from Honeycomb. :rtype: list :raises HoneycombError: If it is not possible to get configuration data. """ @@ -129,7 +132,7 @@ class InterfaceKeywords(object): :param interface: The name of interface. :type node: dict :type interface: str - :return: Configuration data about the given interface from Honeycomb. + :returns: Configuration data about the given interface from Honeycomb. :rtype: dict """ @@ -145,7 +148,7 @@ class InterfaceKeywords(object): :param node: Honeycomb node. :type node: dict - :return: Operational data about all interfaces from Honeycomb. + :returns: Operational data about all interfaces from Honeycomb. :rtype: list :raises HoneycombError: If it is not possible to get operational data. """ @@ -162,6 +165,33 @@ class InterfaceKeywords(object): except (KeyError, TypeError): return [] + @staticmethod + def get_disabled_interfaces_oper_data(node): + """Get operational data about all disabled interfaces from Honeycomb. + + :param node: Honeycomb node. + :type node: dict + :returns: Operational data about disabled interfaces. + :rtype: list + :raises HoneycombError: If it is not possible to get operational data. + """ + + status_code, resp = HcUtil. \ + get_honeycomb_data(node, "oper_disabled_interfaces") + if status_code == HTTPCodes.NOT_FOUND: + raise HoneycombError( + "No disabled interfaces present on node." + ) + if status_code != HTTPCodes.OK: + raise HoneycombError( + "Not possible to get operational information about the " + "interfaces. Status code: {0}.".format(status_code)) + try: + return resp["disabled-interfaces"]["disabled-interface-index"] + + except (KeyError, TypeError): + return [] + @staticmethod def get_interface_oper_data(node, interface): """Get operational data about the given interface from Honeycomb. @@ -170,10 +200,20 @@ class InterfaceKeywords(object): :param interface: The name of interface. :type node: dict :type interface: str - :return: Operational data about the given interface from Honeycomb. + :returns: Operational data about the given interface from Honeycomb. :rtype: dict """ + try: + interface = Topology.convert_interface_reference( + node, interface, "name") + except RuntimeError: + if isinstance(interface, basestring): + # Probably name of a custom interface (TAP, VxLAN, Vhost, ...) + pass + else: + raise + intfs = InterfaceKeywords.get_all_interfaces_oper_data(node) for intf in intfs: if intf["name"] == interface: @@ -191,12 +231,12 @@ class InterfaceKeywords(object): :param interface: The name of interface. :param path: Path to data we want to change / create / remove. :param new_value: The new value to be set. If None, the item will be - removed. + removed. :type node: dict :type interface: str :type path: tuple :type new_value: str, dict or list - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If it is not possible to get or set the data. """ @@ -215,7 +255,7 @@ class InterfaceKeywords(object): return InterfaceKeywords._configure_interface(node, interface, new_data) @staticmethod - def set_interface_state(node, interface, state="up"): + def honeycomb_set_interface_state(node, interface, state="up"): """Set VPP interface state. The keyword changes the administration state of interface to up or down @@ -224,11 +264,11 @@ class InterfaceKeywords(object): :param node: Honeycomb node. :param interface: Interface name, key, link name or sw_if_index. :param state: The requested state, only "up" and "down" are valid - values. + values. :type node: dict :type interface: str :type state: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises KeyError: If the argument "state" is nor "up" or "down". :raises HoneycombError: If the interface is not present on the node. @@ -255,7 +295,7 @@ class InterfaceKeywords(object): status_code, resp = HcUtil. \ put_honeycomb_data(node, "config_vpp_interfaces", resp, path, data_representation=DataRepresentation.JSON) - if status_code != HTTPCodes.OK: + if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED): raise HoneycombError( "The configuration of interface '{0}' was not successful. " "Status code: {1}.".format(interface, status_code)) @@ -269,11 +309,12 @@ class InterfaceKeywords(object): :param interface: The name of interface. :type node: dict :type interface: str - :return: Content of response + :returns: Content of response :rtype: bytearray """ - return InterfaceKeywords.set_interface_state(node, interface, "up") + return InterfaceKeywords.honeycomb_set_interface_state( + node, interface, "up") @staticmethod def set_interface_down(node, interface): @@ -283,11 +324,12 @@ class InterfaceKeywords(object): :param interface: The name of interface. :type node: dict :type interface: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ - return InterfaceKeywords.set_interface_state(node, interface, "down") + return InterfaceKeywords.honeycomb_set_interface_state( + node, interface, "down") @staticmethod def add_bridge_domain_to_interface(node, interface, bd_name, @@ -304,7 +346,7 @@ class InterfaceKeywords(object): :type bd_name: str :type split_horizon_group: str :type bvi: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the interface is not present on the node. """ @@ -331,7 +373,6 @@ class InterfaceKeywords(object): :param interface: Interface name, key, link name or sw_if_index. :type node: dict :type interface: str or int - :raises HoneycombError: If the operation fails. """ @@ -342,13 +383,16 @@ class InterfaceKeywords(object): path = "/interface/{0}/v3po:l2".format(intf) - status_code, _ = HcUtil.delete_honeycomb_data( + status_code, response = HcUtil.delete_honeycomb_data( node, "config_vpp_interfaces", path) if status_code != HTTPCodes.OK: - raise HoneycombError( - "Could not remove bridge domain assignment from interface " - "'{0}'. Status code: {1}.".format(interface, status_code)) + if '"error-tag":"data-missing"' in response: + logger.debug("Data does not exist in path.") + else: + raise HoneycombError( + "Could not remove bridge domain assignment from interface " + "'{0}'. Status code: {1}.".format(interface, status_code)) @staticmethod def get_bd_oper_data_from_interface(node, interface): @@ -359,8 +403,8 @@ class InterfaceKeywords(object): :param interface: The name of interface. :type node: dict :type interface: str - :return: Operational data about bridge domain settings in the - interface. + :returns: Operational data about bridge domain settings in the + interface. :rtype: dict """ @@ -381,12 +425,12 @@ class InterfaceKeywords(object): :param interface: The name of interface. :param param: Parameter to configure (set, change, remove) :param value: The value of parameter. If None, the parameter will be - removed. + removed. :type node: dict :type interface: str :type param: str :type value: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ @@ -406,12 +450,12 @@ class InterfaceKeywords(object): :param interface: The name of interface. :param param: Parameter to configure (set, change, remove) :param value: The value of parameter. If None, the parameter will be - removed. + removed. :type node: dict :type interface: str :type param: str :type value: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ @@ -441,24 +485,34 @@ class InterfaceKeywords(object): :type interface: str :type ip_addr: str :type network: str or int - :return: Content of response. + :returns: Content of response. :rtype: bytearray - :raises HoneycombError: If the provided netmask or prefix is not valid. + :raises ValueError: If the provided netmask or prefix is not valid. + :raises HoneycombError: If the operation fails. """ - interface = Topology.convert_interface_reference( - node, interface, "name") + interface = InterfaceKeywords.handle_interface_reference( + node, interface) - path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4") + path = "/interface/{0}/ietf-ip:ipv4".format(interface) if isinstance(network, basestring): - address = {"address": [{"ip": ip_addr, "netmask": network}, ]} + data = { + "ietf-ip:ipv4": { + "address": [{"ip": ip_addr, "netmask": network}, ]}} elif isinstance(network, int) and (0 < network < 33): - address = {"address": [{"ip": ip_addr, "prefix-length": network}, ]} + data = { + "ietf-ip:ipv4": { + "address": [{"ip": ip_addr, "prefix-length": network}, ]}} else: - raise HoneycombError("Value {0} is not a valid netmask or network " - "prefix length.".format(network)) - return InterfaceKeywords._set_interface_properties( - node, interface, path, address) + raise ValueError("Value {0} is not a valid netmask or network " + "prefix length.".format(network)) + status_code, _ = HcUtil.put_honeycomb_data( + node, "config_vpp_interfaces", data, path) + + if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED): + raise HoneycombError( + "Configuring IPv4 address failed. " + "Status code:{0}".format(status_code)) @staticmethod def add_ipv4_address(node, interface, ip_addr, network): @@ -472,7 +526,7 @@ class InterfaceKeywords(object): :type interface: str :type ip_addr: str :type network: str or int - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the provided netmask or prefix is not valid. """ @@ -483,9 +537,9 @@ class InterfaceKeywords(object): path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4", "address") if isinstance(network, basestring): - address = {"address": [{"ip": ip_addr, "netmask": network}, ]} + address = [{"ip": ip_addr, "netmask": network}] elif isinstance(network, int) and (0 < network < 33): - address = {"address": [{"ip": ip_addr, "prefix-length": network}, ]} + address = [{"ip": ip_addr, "prefix-length": network}] else: raise HoneycombError("Value {0} is not a valid netmask or network " "prefix length.".format(network)) @@ -500,10 +554,13 @@ class InterfaceKeywords(object): :param interface: The name of interface. :type node: dict :type interface: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ + interface = Topology.convert_interface_reference( + node, interface, "name") + path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4", "address") return InterfaceKeywords._set_interface_properties( @@ -521,10 +578,13 @@ class InterfaceKeywords(object): :type interface: str :type ip_addr: str :type link_layer_address: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ + interface = Topology.convert_interface_reference( + node, interface, "name") + path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4", "neighbor") neighbor = [{"ip": ip_addr, "link-layer-address": link_layer_address}, ] @@ -539,10 +599,13 @@ class InterfaceKeywords(object): :param interface: The name of interface. :type node: dict :type interface: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ + interface = Topology.convert_interface_reference( + node, interface, "name") + path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4", "neighbor") return InterfaceKeywords._set_interface_properties( @@ -556,12 +619,12 @@ class InterfaceKeywords(object): :param interface: The name of interface. :param param: Parameter to configure (set, change, remove) :param value: The value of parameter. If None, the parameter will be - removed. + removed. :type node: dict :type interface: str :type param: str :type value: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ @@ -592,10 +655,13 @@ class InterfaceKeywords(object): :type interface: str :type ip_addr: str :type prefix_len: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ + interface = Topology.convert_interface_reference( + node, interface, "name") + path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6") address = {"address": [{"ip": ip_addr, "prefix-length": prefix_len}, ]} return InterfaceKeywords._set_interface_properties( @@ -613,10 +679,13 @@ class InterfaceKeywords(object): :type interface: str :type ip_addr: str :type prefix_len: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ + interface = Topology.convert_interface_reference( + node, interface, "name") + path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6", "address") address = [{"ip": ip_addr, "prefix-length": prefix_len}, ] @@ -631,10 +700,13 @@ class InterfaceKeywords(object): :param interface: The name of interface. :type node: dict :type interface: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ + interface = Topology.convert_interface_reference( + node, interface, "name") + path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6", "address") return InterfaceKeywords._set_interface_properties( @@ -652,10 +724,13 @@ class InterfaceKeywords(object): :type interface: str :type ip_addr: str :type link_layer_address: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ + interface = Topology.convert_interface_reference( + node, interface, "name") + path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6", "neighbor") neighbor = [{"ip": ip_addr, "link-layer-address": link_layer_address}, ] @@ -670,10 +745,13 @@ class InterfaceKeywords(object): :param interface: The name of interface. :type node: dict :type interface: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ + interface = Topology.convert_interface_reference( + node, interface, "name") + path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv6", "neighbor") return InterfaceKeywords._set_interface_properties( @@ -687,12 +765,12 @@ class InterfaceKeywords(object): :param interface: The name of interface. :param param: Parameter to configure (set, change, remove) :param value: The value of parameter. If None, the parameter will be - removed. + removed. :type node: dict :type interface: str :type param: str :type value: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ @@ -712,16 +790,19 @@ class InterfaceKeywords(object): :param interface: The name of interface. :param param: Parameter to configure (set, change, remove) :param value: The value of parameter. If None, the parameter will be - removed. + removed. :type node: dict :type interface: str :type param: str :type value: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ + interface = Topology.convert_interface_reference( + node, interface, "name") + if param not in InterfaceKeywords.ROUTING_PARAMS: raise HoneycombError("The parameter {0} is invalid.".format(param)) @@ -731,17 +812,17 @@ class InterfaceKeywords(object): node, interface, path, value) @staticmethod - def create_vxlan_interface(node, interface, **kwargs): + def honeycomb_create_vxlan_interface(node, interface, **kwargs): """Create a new VxLAN interface. :param node: Honeycomb node. :param interface: The name of interface. :param kwargs: Parameters and their values. The accepted parameters are - defined in InterfaceKeywords.VXLAN_PARAMS. + defined in InterfaceKeywords.VXLAN_PARAMS. :type node: dict :type interface: str :type kwargs: dict - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ @@ -770,10 +851,10 @@ class InterfaceKeywords(object): :param interface: The name of interface. :type node: dict :type interface: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If it is not possible to get information about - interfaces or it is not possible to delete the interface. + interfaces or it is not possible to delete the interface. """ path = ("interfaces", ("interface", "name", interface)) @@ -795,7 +876,7 @@ class InterfaceKeywords(object): return resp @staticmethod - def configure_interface_vxlan(node, interface, **kwargs): + def honeycomb_configure_interface_vxlan(node, interface, **kwargs): """Configure VxLAN on the interface. The keyword configures VxLAN parameters on the given interface. The type @@ -808,11 +889,11 @@ class InterfaceKeywords(object): :param node: Honeycomb node. :param interface: The name of interface. :param kwargs: Parameters and their values. The accepted parameters are - defined in InterfaceKeywords.VXLAN_PARAMS. + defined in InterfaceKeywords.VXLAN_PARAMS. :type node: dict :type interface: str :type kwargs: dict - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ @@ -836,12 +917,12 @@ class InterfaceKeywords(object): :param interface: The name of interface. :param param: Parameter to configure (set, change, remove) :param value: The value of parameter. If None, the parameter will be - removed. + removed. :type node: dict :type interface: str :type param: str :type value: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ @@ -860,25 +941,25 @@ class InterfaceKeywords(object): :param node: Honeycomb node. :param interface: The name of interface. :param kwargs: Parameters and their values. The accepted parameters are - defined in InterfaceKeywords.TAP_PARAMS. + defined in InterfaceKeywords.TAP_PARAMS. :type node: dict :type interface: str :type kwargs: dict - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ new_tap = { "name": interface, - "type": "v3po:tap", - "v3po:tap": {} + "type": "v3po:tap-v2", + "v3po:tap-v2": {} } for param, value in kwargs.items(): if param not in InterfaceKeywords.TAP_PARAMS: - raise HoneycombError("The parameter {0} is invalid.". - format(param)) - new_tap["v3po:tap"][param] = value + raise HoneycombError( + "The parameter {0} is invalid.".format(param)) + new_tap["v3po:tap-v2"][param] = value path = ("interfaces", "interface") new_tap_structure = [new_tap, ] @@ -890,7 +971,7 @@ class InterfaceKeywords(object): """Configure TAP on the interface. The keyword configures TAP parameters on the given interface. The type - of interface must be set to "v3po:tap". + of interface must be set to "v3po:tap-v2". The new TAP parameters overwrite the current configuration. If a parameter in new configuration is missing, it is removed from TAP configuration. @@ -899,11 +980,11 @@ class InterfaceKeywords(object): :param node: Honeycomb node. :param interface: The name of interface. :param kwargs: Parameters and their values. The accepted parameters are - defined in InterfaceKeywords.TAP_PARAMS. + defined in InterfaceKeywords.TAP_PARAMS. :type node: dict :type interface: str :type kwargs: dict - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ @@ -915,7 +996,7 @@ class InterfaceKeywords(object): format(param)) tap_structure[param] = value - path = ("interfaces", ("interface", "name", interface), "v3po:tap") + path = ("interfaces", ("interface", "name", interface), "v3po:tap-v2") return InterfaceKeywords._set_interface_properties( node, interface, path, tap_structure) @@ -933,11 +1014,11 @@ class InterfaceKeywords(object): :param node: Honeycomb node. :param interface: The name of interface. :param kwargs: Parameters and their values. The accepted parameters are - defined in InterfaceKeywords.VHOST_USER_PARAMS. + defined in InterfaceKeywords.VHOST_USER_PARAMS. :type node: dict :type interface: str :type kwargs: dict - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ @@ -961,11 +1042,11 @@ class InterfaceKeywords(object): :param node: Honeycomb node. :param interface: The name of interface. :param kwargs: Parameters and their values. The accepted parameters are - defined in InterfaceKeywords.VHOST_USER_PARAMS. + defined in InterfaceKeywords.VHOST_USER_PARAMS. :type node: dict :type interface: str :type kwargs: dict - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. """ @@ -987,27 +1068,31 @@ class InterfaceKeywords(object): node, interface, path, new_vhost_structure) @staticmethod - def create_sub_interface(node, super_interface, match, tags=None, **kwargs): + def honeycomb_create_sub_interface(node, super_interface, match, tags=None, + **kwargs): """Create a new sub-interface. :param node: Honeycomb node. :param super_interface: Super interface. :param match: Match type. The valid values are defined in - InterfaceKeywords.SUB_IF_MATCH. + InterfaceKeywords.SUB_IF_MATCH. :param tags: List of tags. :param kwargs: Parameters and their values. The accepted parameters are - defined in InterfaceKeywords.SUB_IF_PARAMS. + defined in InterfaceKeywords.SUB_IF_PARAMS. :type node: dict :type super_interface: str :type match: str :type tags: list :type kwargs: dict - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the parameter is not valid. :raises KeyError: If the parameter 'match' is invalid. """ + super_interface = Topology.convert_interface_reference( + node, super_interface, "name") + match_type = { "default": {"default": {}}, @@ -1058,7 +1143,7 @@ class InterfaceKeywords(object): :type node: dict :type super_interface: str :type identifier: int - :return: Sub-interface operational data. + :returns: Sub-interface operational data. :rtype: dict :raises HoneycombError: If there is no sub-interface with the given ID. """ @@ -1080,7 +1165,7 @@ class InterfaceKeywords(object): :param super_interface: Super interface. :type node: dict :type super_interface: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ @@ -1103,17 +1188,20 @@ class InterfaceKeywords(object): :type super_interface: str :type identifier: int :type state: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ + super_interface = Topology.convert_interface_reference( + node, super_interface, "name") + intf_state = {"up": "true", "down": "false"} path = ("interfaces", ("interface", "name", super_interface), "vpp-vlan:sub-interfaces", - ("sub-interface", "identifier", identifier), + ("sub-interface", "identifier", int(identifier)), "enabled") return InterfaceKeywords._set_interface_properties( @@ -1132,7 +1220,7 @@ class InterfaceKeywords(object): :type super_interface: str :type identifier: int :type config: dict - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ @@ -1155,7 +1243,7 @@ class InterfaceKeywords(object): :type node: dict :type super_interface: str :type identifier: int - :return: Operational data about the bridge domain. + :returns: Operational data about the bridge domain. :rtype: dict :raises HoneycombError: If there is no sub-interface with the given ID. """ @@ -1180,7 +1268,7 @@ class InterfaceKeywords(object): :type super_interface: str :type identifier: int :type config: dict - :return: Content of response. + :returns: Content of response. :rtype: bytearray """ @@ -1204,7 +1292,7 @@ class InterfaceKeywords(object): :type node: dict :type super_interface: str :type identifier: int - :return: Operational data about tag rewrite. + :returns: Operational data about tag rewrite. :rtype: dict :raises HoneycombError: If there is no sub-interface with the given ID. """ @@ -1218,8 +1306,8 @@ class InterfaceKeywords(object): "information about the tag-rewrite.") @staticmethod - def add_ipv4_address_to_sub_interface(node, super_interface, identifier, - ip_addr, network): + def add_ip_address_to_sub_interface(node, super_interface, identifier, + ip_addr, network, ip_version): """Add an ipv4 address to the specified sub-interface, with the provided netmask or network prefix length. Any existing ipv4 addresses on the sub-interface will be replaced. @@ -1229,12 +1317,14 @@ class InterfaceKeywords(object): :param identifier: The ID of sub-interface. :param ip_addr: IPv4 address to be set. :param network: Network mask or network prefix length. + :param ip_version: ipv4 or ipv6 :type node: dict :type super_interface: str :type identifier: int :type ip_addr: str :type network: str or int - :return: Content of response. + :type ip_version: string + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the provided netmask or prefix is not valid. """ @@ -1243,9 +1333,9 @@ class InterfaceKeywords(object): ("interface", "name", super_interface), "vpp-vlan:sub-interfaces", ("sub-interface", "identifier", int(identifier)), - "ipv4") + ip_version.lower()) - if isinstance(network, basestring): + if isinstance(network, basestring) and ip_version.lower() == "ipv4": address = {"address": [{"ip": ip_addr, "netmask": network}, ]} elif isinstance(network, int) and 0 < network < 33: @@ -1259,17 +1349,19 @@ class InterfaceKeywords(object): node, super_interface, path, address) @staticmethod - def remove_all_ipv4_addresses_from_sub_interface(node, super_interface, # pylint: disable=invalid-name - identifier): + def remove_all_ip_addresses_from_sub_interface(node, super_interface, + identifier, ip_version): """Remove all ipv4 addresses from the specified sub-interface. :param node: Honeycomb node. :param super_interface: Super interface. :param identifier: The ID of sub-interface. + :param ip_version: ipv4 or ipv6 :type node: dict :type super_interface: str :type identifier: int - :return: Content of response. + :type ip_version: string + :returns: Content of response. :rtype: bytearray """ @@ -1277,42 +1369,79 @@ class InterfaceKeywords(object): ("interface", "name", super_interface), "vpp-vlan:sub-interfaces", ("sub-interface", "identifier", int(identifier)), - "ipv4", "address") + str(ip_version), "address") return InterfaceKeywords._set_interface_properties( node, super_interface, path, None) @staticmethod - def compare_data_structures(data, ref, ignore=()): - """Checks if data obtained from UUT is as expected. + def compare_data_structures(data, ref, _path=''): + """Checks if data obtained from UUT is as expected. If it is not, + proceeds down the list/dictionary tree and finds the point of mismatch. :param data: Data to be checked. :param ref: Referential data used for comparison. - :param ignore: Dictionary keys to be ignored. + :param _path: Used in recursive calls, stores the path taken down + the JSON tree. :type data: dict :type ref: dict - :type ignore: iterable - :raises HoneycombError: If a parameter from referential data is not - present in operational data or if it has different value. + :type _path: str + + :raises HoneycombError: If the data structures do not match in some way, + or if they are not in deserialized JSON format. """ - errors = "" + if data == ref: + return True + + elif isinstance(data, dict) and isinstance(ref, dict): + for key in ref: + if key not in data: + raise HoneycombError( + "Key {key} is not present in path {path}. Keys in path:" + "{data_keys}".format( + key=key, + path=_path, + data_keys=data.keys())) + + if data[key] != ref[key]: + if isinstance(data[key], list) \ + or isinstance(data[key], dict): + InterfaceKeywords.compare_data_structures( + data[key], ref[key], + _path + '[{0}]'.format(key)) + else: + raise HoneycombError( + "Data mismatch, key {key} in path {path} has value" + " {data}, but should be {ref}".format( + key=key, + path=_path, + data=data[key], + ref=ref[key])) + + elif isinstance(data, list) and isinstance(ref, list): + for item in ref: + if item not in data: + if isinstance(item, dict): + InterfaceKeywords.compare_data_structures( + data[0], item, + _path + '[{0}]'.format(ref.index(item))) + else: + raise HoneycombError( + "Data mismatch, list item {index} in path {path}" + " has value {data}, but should be {ref}".format( + index=ref.index(item), + path=_path, + data=data[0], + ref=item)) - for key, item in ref.items(): - if key in ignore: - continue - try: - if data[key] != item: - errors += ("\nThe value of parameter '{0}' is " - "incorrect. It should be " - "'{1}' but it is '{2}'". - format(key, item, data[key])) - except KeyError: - errors += ("\nThe parameter '{0}' is not present in " - "operational data".format(key)) - - if errors: - raise HoneycombError(errors) + else: + raise HoneycombError( + "Unexpected data type {data_type} in path {path}, reference" + " type is {ref_type}. Must be list or dictionary.".format( + data_type=type(data), + ref_type=type(ref), + path=_path)) @staticmethod def compare_interface_lists(list1, list2): @@ -1347,11 +1476,11 @@ class InterfaceKeywords(object): :param node: Honeycomb node. :param interface: The name of interface to be created. :param kwargs: Parameters and their values. The accepted parameters are - defined in InterfaceKeywords.VXLAN_GPE_PARAMS. + defined in InterfaceKeywords.VXLAN_GPE_PARAMS. :type node: dict :type interface: str :type kwargs: dict - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If a parameter in kwargs is not valid. """ @@ -1384,29 +1513,92 @@ class InterfaceKeywords(object): :type node: dict :type interface: str :type table_name: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the configuration of interface is not - successful. + successful. """ interface = interface.replace("/", "%2F") data = { - "v3po:acl": { - "l2-acl": { - "classify-table": table_name - }, - "ip4-acl": { - "classify-table": table_name + "vpp-interface-acl:acl": { + "ingress": { + "ip4-acl": { + "classify-table": table_name + }, + "l2-acl": { + "classify-table": table_name + } } } } - path = "/interface/" + interface + "/v3po:acl" + path = "/interface/" + interface + "/vpp-interface-acl:acl" + status_code, resp = HcUtil.\ + put_honeycomb_data(node, "config_vpp_interfaces", data, path, + data_representation=DataRepresentation.JSON) + if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED): + raise HoneycombError( + "The configuration of interface '{0}' was not successful. " + "Status code: {1}.".format(interface, status_code)) + return resp + + @staticmethod + def enable_policer_on_interface(node, interface, table_name): + """Enable Policer on the given interface. + + :param node: Honeycomb node. + :param interface: The interface where policer will be enabled. + :param table_name: Name of the classify table. + :type node: dict + :type interface: str + :type table_name: str + :returns: Content of response. + :rtype: bytearray + :raises HoneycombError: If the configuration of interface is not + successful. + """ + interface = Topology.convert_interface_reference( + node, interface, "name") + interface = interface.replace("/", "%2F") + + data = { + "interface-policer:policer": { + "ip4-table": table_name + } + } + + path = "/interface/" + interface + "/interface-policer:policer" status_code, resp = HcUtil.\ put_honeycomb_data(node, "config_vpp_interfaces", data, path, data_representation=DataRepresentation.JSON) + if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED): + raise HoneycombError( + "The configuration of interface '{0}' was not successful. " + "Status code: {1}.".format(interface, status_code)) + return resp + + @staticmethod + def disable_policer_on_interface(node, interface): + """Disable Policer on the given interface. + + :param node: Honeycomb node. + :param interface: The interface where policer will be disabled. + :type node: dict + :type interface: str + :returns: Content of response. + :rtype: bytearray + :raises HoneycombError: If the configuration of interface is not + successful. + """ + interface = Topology.convert_interface_reference( + node, interface, "name") + interface = interface.replace("/", "%2F") + + path = "/interface/" + interface + "/interface-policer:policer" + status_code, resp = HcUtil.\ + delete_honeycomb_data(node, "config_vpp_interfaces", path) if status_code != HTTPCodes.OK: raise HoneycombError( "The configuration of interface '{0}' was not successful. " @@ -1421,15 +1613,15 @@ class InterfaceKeywords(object): :param interface: The interface where the ACL will be disabled. :type node: dict :type interface: str - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the configuration of interface is not - successful. + successful. """ interface = interface.replace("/", "%2F") - path = "/interface/" + interface + "/v3po:acl" + path = "/interface/" + interface + "/vpp-interface-acl:acl" status_code, resp = HcUtil.\ delete_honeycomb_data(node, "config_vpp_interfaces", path) @@ -1441,56 +1633,50 @@ class InterfaceKeywords(object): return resp @staticmethod - def create_pbb_sub_interface(node, intf, sub_if_id, params): + def create_pbb_sub_interface(node, intf, params): """Creates a PBB sub-interface on the given interface and sets its parameters. :param node: Honeycomb node. :param intf: The interface where PBB sub-interface will be configured. - :param sub_if_id: Sub-interface ID. :param params: Configuration parameters of the sub-interface to be - created. + created. :type node: dict :type intf: str - :type sub_if_id: str or int :type params: dict - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the configuration of sub-interface is not - successful. + successful. """ interface = intf.replace("/", "%2F") - path = "/interface/{0}/sub-interfaces:sub-interfaces/sub-interface/" \ - "{1}".format(interface, sub_if_id) + path = "/interface/{0}/pbb-rewrite".format(interface) status_code, resp = HcUtil. \ put_honeycomb_data(node, "config_vpp_interfaces", params, path, data_representation=DataRepresentation.JSON) - if status_code != HTTPCodes.OK: + if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED): raise HoneycombError( "The configuration of PBB sub-interface '{0}' was not " "successful. Status code: {1}.".format(intf, status_code)) return resp @staticmethod - def delete_pbb_sub_interface(node, intf, sub_if_id): + def delete_pbb_sub_interface(node, intf): """Deletes the given PBB sub-interface. :param node: Honeycomb node. :param intf: The interface where PBB sub-interface will be deleted. - :param sub_if_id: ID of the PBB sub-interface to be deleted. :type node: dict :type intf: str - :type sub_if_id: str or int - :return: Content of response. + :returns: Content of response. :rtype: bytearray :raises HoneycombError: If the removal of sub-interface is not - successful. + successful. """ interface = intf.replace("/", "%2F") - path = "/interface/{0}/sub-interfaces:sub-interfaces/sub-interface/" \ - "{1}".format(interface, sub_if_id) + path = "/interface/{0}/pbb-rewrite".format(interface) status_code, resp = HcUtil. \ delete_honeycomb_data(node, "config_vpp_interfaces", path) @@ -1510,11 +1696,213 @@ class InterfaceKeywords(object): :type node: dict :type intf: str :type sub_if_id: str or int - :return: PBB sub-interface operational data. + :returns: PBB sub-interface operational data. :rtype: dict :raises HoneycombError: If the removal of sub-interface is not - successful. + successful. + """ + + raise NotImplementedError + + @staticmethod + def check_disabled_interface(node, interface): + """Retrieves list of disabled interface indices from Honeycomb, + and matches with the provided interface by index. + + :param node: Honeycomb node. + :param interface: Index number of an interface on the node. + :type node: dict + :type interface: int + :returns: True if the interface exists in disabled interfaces. + :rtype: bool + :raises HoneycombError: If the interface is not present + in retrieved list of disabled interfaces. + """ + data = InterfaceKeywords.get_disabled_interfaces_oper_data(node) + # decrement by one = conversion from HC if-index to VPP sw_if_index + interface -= 1 + + for item in data: + if item["index"] == interface: + 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=None): + """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 or int + :type src_interfaces: list of dict + :returns: Content of response. + :rtype: bytearray + :raises HoneycombError: If SPAN could not be configured. + """ + + interface = Topology.convert_interface_reference( + node, dst_interface, "name") + interface = interface.replace("/", "%2F") + path = "/interface/" + interface + "/span" + + if not src_interfaces: + status_code, _ = HcUtil.delete_honeycomb_data( + node, "config_vpp_interfaces", path) + else: + for src_interface in src_interfaces: + src_interface["iface-ref"] = Topology.\ + convert_interface_reference( + node, src_interface["iface-ref"], "name") + 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 configure_sub_interface_span(node, super_interface, dst_interface_index, + src_interfaces=None): + """Configure SPAN port mirroring on the specified sub-interface. If no + source interface is provided, SPAN will be disabled. + + Note: Does not support source sub-interfaces, only destination. + + :param node: Honeycomb node. + :param super_interface: Name, link name or sw_if_index + of the destination interface's super-interface. + :param dst_interface_index: Index of sub-interface to mirror packets to. + :param src_interfaces: List of interfaces to mirror packets from. + :type node: dict + :type super_interface: str or int + :type dst_interface_index: int + :type src_interfaces: list of dict + :returns: Content of response. + :rtype: bytearray + :raises HoneycombError: If SPAN could not be configured. + """ + + super_interface = Topology.convert_interface_reference( + node, super_interface, "name") + super_interface = super_interface.replace("/", "%2F") + + path = "/interface/{0}/vpp-vlan:sub-interfaces/sub-interface/{1}/span"\ + .format(super_interface, dst_interface_index) + + if not src_interfaces: + status_code, _ = HcUtil.delete_honeycomb_data( + node, "config_vpp_interfaces", path) + else: + for src_interface in src_interfaces: + src_interface["iface-ref"] = Topology. \ + convert_interface_reference( + node, src_interface["iface-ref"], "name") + 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 """ - interface = "{0}.{1}".format(intf, sub_if_id) - return InterfaceKeywords.get_interface_oper_data(node, interface) + 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") + + @staticmethod + def configure_interface_unnumbered(node, interface, interface_src=None): + """Configure the specified interface as unnumbered. The interface + borrows IP address from the specified source interface. If not source + interface is provided, unnumbered configuration will be removed. + + :param node: Honeycomb node. + :param interface: Name, link name or sw_if_index of an interface. + :param interface_src: Name of source interface. + :type node: dict + :type interface: str or int + :type interface_src: str + :raises HoneycombError: If the configuration fails. + """ + + interface = InterfaceKeywords.handle_interface_reference( + node, interface) + + path = "/interface/{0}/unnumbered-interfaces:unnumbered"\ + .format(interface) + + if interface_src: + data = { + "unnumbered": { + "use": interface_src + } + } + status_code, _ = HcUtil.put_honeycomb_data( + node, "config_vpp_interfaces", data, path) + else: + status_code, _ = HcUtil.delete_honeycomb_data( + node, "config_vpp_interfaces", path) + + if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED): + raise HoneycombError( + "Configuring unnumbered interface failed. " + "Status code:{0}".format(status_code)) + + @staticmethod + def handle_interface_reference(node, interface): + """Convert any interface reference to interface name used by Honeycomb. + + :param node: Honeycomb node. + :param interface: Name, link name or sw_if_index of an interface, + name of a custom interface or name of a sub-interface. + :type node: Honeycomb node. + :type interface: str or int + :returns: Name of interface that can be used in Honeycomb requests. + :rtype: str + """ + + try: + interface = Topology.convert_interface_reference( + node, interface, "name") + interface = interface.replace("/", "%2F") + except RuntimeError: + # interface is not in topology + if "." in interface: + # Assume it's the name of a sub-interface + interface, index = interface.split(".") + interface = interface.replace("/", "%2F") + interface = "{0}/vpp-vlan:sub-interfaces/sub-interface/{1}".\ + format(interface, index) + else: + # Assume it's the name of a custom interface (pbb, vxlan, etc.) + interface = interface.replace("/", "%2F") + + return interface