HC Test: add test for vlan sub-interface ipv6 address
[csit.git] / resources / libraries / python / honeycomb / HcAPIKwInterfaces.py
index fdb9b90..3a32cbe 100644 (file)
@@ -16,7 +16,9 @@
 The keywords make possible to put and get configuration data and to get
 operational data.
 """
+from robot.api import logger
 
+from resources.libraries.python.topology import Topology
 from resources.libraries.python.HTTPRequest import HTTPCodes
 from resources.libraries.python.honeycomb.HoneycombSetup import HoneycombError
 from resources.libraries.python.honeycomb.HoneycombUtil \
@@ -25,8 +27,6 @@ from resources.libraries.python.honeycomb.HoneycombUtil \
     import HoneycombUtil as HcUtil
 
 
-# pylint: disable=too-many-public-methods
-# pylint: disable=too-many-lines
 class InterfaceKeywords(object):
     """Keywords for Interface manipulation.
 
@@ -44,7 +44,7 @@ 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")
@@ -83,7 +83,7 @@ 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.
@@ -92,7 +92,7 @@ class InterfaceKeywords(object):
         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 +104,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 +129,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 +145,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 +162,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,7 +197,7 @@ 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
         """
 
@@ -196,7 +223,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -222,13 +249,13 @@ class InterfaceKeywords(object):
         depending on the parameter "state".
 
         :param node: Honeycomb node.
-        :param interface: The name of interface.
+        :param interface: Interface name, key, link name or sw_if_index.
         :param state: The requested state, only "up" and "down" are valid
         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.
@@ -237,6 +264,9 @@ class InterfaceKeywords(object):
         intf_state = {"up": "true",
                       "down": "false"}
 
+        interface = Topology.convert_interface_reference(
+            node, interface, "name")
+
         intf = interface.replace("/", "%2F")
         path = "/interface/{0}".format(intf)
 
@@ -252,7 +282,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))
@@ -266,7 +296,7 @@ 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
         """
 
@@ -280,7 +310,7 @@ 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
         """
 
@@ -292,7 +322,7 @@ class InterfaceKeywords(object):
         """Add a new bridge domain to an interface and set its parameters.
 
         :param node: Honeycomb node.
-        :param interface: The name of interface.
+        :param interface: Interface name, key, link name or sw_if_index.
         :param bd_name: Bridge domain name.
         :param split_horizon_group: Split-horizon group name.
         :param bvi: The bridged virtual interface.
@@ -301,11 +331,14 @@ 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.
         """
 
+        interface = Topology.convert_interface_reference(
+            node, interface, "name")
+
         v3po_l2 = {"bridge-domain": str(bd_name)}
         if split_horizon_group:
             v3po_l2["split-horizon-group"] = str(split_horizon_group)
@@ -317,6 +350,36 @@ class InterfaceKeywords(object):
         return InterfaceKeywords._set_interface_properties(
             node, interface, path, v3po_l2)
 
+    @staticmethod
+    def remove_bridge_domain_from_interface(node, interface):
+        """Remove bridge domain assignment from interface.
+
+        :param node: Honeycomb node.
+        :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.
+        """
+
+        interface = Topology.convert_interface_reference(
+            node, interface, "name")
+
+        intf = interface.replace("/", "%2F")
+
+        path = "/interface/{0}/v3po:l2".format(intf)
+
+        status_code, response = HcUtil.delete_honeycomb_data(
+            node, "config_vpp_interfaces", path)
+
+        if status_code != HTTPCodes.OK:
+            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):
         """Returns operational data about bridge domain settings in the
@@ -326,7 +389,7 @@ class InterfaceKeywords(object):
         :param interface: The name of interface.
         :type node: dict
         :type interface: str
-        :return: Operational data about bridge domain settings in the
+        :returns: Operational data about bridge domain settings in the
         interface.
         :rtype: dict
         """
@@ -353,7 +416,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -367,7 +430,7 @@ class InterfaceKeywords(object):
 
     @staticmethod
     def configure_interface_ipv4(node, interface, param, value):
-        """Configure IPv4 parameters of interface
+        """Configure IPv4 parameters of interface.
 
         :param node: Honeycomb node.
         :param interface: The name of interface.
@@ -378,11 +441,14 @@ class InterfaceKeywords(object):
         :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.IPV4_PARAMS:
             raise HoneycombError("The parameter {0} is invalid.".format(param))
 
@@ -405,11 +471,14 @@ 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.
         """
 
+        interface = Topology.convert_interface_reference(
+            node, interface, "name")
+
         path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4")
         if isinstance(network, basestring):
             address = {"address": [{"ip": ip_addr, "netmask": network}, ]}
@@ -433,17 +502,20 @@ 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.
         """
 
+        interface = Topology.convert_interface_reference(
+            node, interface, "name")
+
         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))
@@ -458,7 +530,7 @@ 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
         """
 
@@ -479,7 +551,7 @@ 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
         """
 
@@ -497,7 +569,7 @@ 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
         """
 
@@ -519,7 +591,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -550,7 +622,7 @@ class InterfaceKeywords(object):
         :type interface: str
         :type ip_addr: str
         :type prefix_len: str
-        :return: Content of response.
+        :returns: Content of response.
         :rtype: bytearray
         """
 
@@ -571,7 +643,7 @@ class InterfaceKeywords(object):
         :type interface: str
         :type ip_addr: str
         :type prefix_len: str
-        :return: Content of response.
+        :returns: Content of response.
         :rtype: bytearray
         """
 
@@ -589,7 +661,7 @@ 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
         """
 
@@ -610,7 +682,7 @@ 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
         """
 
@@ -628,7 +700,7 @@ 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
         """
 
@@ -650,7 +722,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -675,7 +747,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -699,7 +771,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -728,7 +800,7 @@ 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.
@@ -770,7 +842,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -799,7 +871,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -822,7 +894,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -861,7 +933,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -895,7 +967,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -923,7 +995,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -960,7 +1032,7 @@ class InterfaceKeywords(object):
         :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.
@@ -1016,7 +1088,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.
         """
@@ -1038,7 +1110,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
         """
 
@@ -1061,7 +1133,7 @@ class InterfaceKeywords(object):
         :type super_interface: str
         :type identifier: int
         :type state: str
-        :return: Content of response.
+        :returns: Content of response.
         :rtype: bytearray
         """
 
@@ -1071,7 +1143,7 @@ class InterfaceKeywords(object):
         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(
@@ -1090,7 +1162,7 @@ class InterfaceKeywords(object):
         :type super_interface: str
         :type identifier: int
         :type config: dict
-        :return: Content of response.
+        :returns: Content of response.
         :rtype: bytearray
         """
 
@@ -1113,7 +1185,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.
         """
@@ -1138,7 +1210,7 @@ class InterfaceKeywords(object):
         :type super_interface: str
         :type identifier: int
         :type config: dict
-        :return: Content of response.
+        :returns: Content of response.
         :rtype: bytearray
         """
 
@@ -1162,7 +1234,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.
         """
@@ -1176,8 +1248,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.
@@ -1187,12 +1259,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.
         """
@@ -1201,9 +1275,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:
@@ -1217,17 +1291,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
         """
 
@@ -1235,42 +1311,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):
@@ -1309,7 +1422,7 @@ class InterfaceKeywords(object):
         :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.
         """
@@ -1342,7 +1455,7 @@ 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.
@@ -1352,11 +1465,13 @@ class InterfaceKeywords(object):
 
         data = {
             "v3po:acl": {
-                "l2-acl": {
-                    "classify-table": table_name
-                },
-                "ip4-acl": {
-                    "classify-table": table_name
+                "ingress": {
+                    "ip4-acl": {
+                        "classify-table": table_name
+                    },
+                    "l2-acl": {
+                        "classify-table": table_name
+                    }
                 }
             }
         }
@@ -1365,7 +1480,7 @@ class InterfaceKeywords(object):
         status_code, resp = HcUtil.\
             put_honeycomb_data(node, "config_vpp_interfaces", data, 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))
@@ -1379,7 +1494,7 @@ 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.
@@ -1399,56 +1514,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.
         :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.
         """
 
         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.
         """
 
         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)
@@ -1468,11 +1577,86 @@ 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.
         """
 
-        interface = "{0}.{1}".format(intf, sub_if_id)
-        return InterfaceKeywords.get_interface_oper_data(node, interface)
+        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):
+        """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")