From: selias Date: Fri, 7 Oct 2016 11:43:45 +0000 (+0200) Subject: CSIT-427: Honeycomb ietf-ACL tests - L2 X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=commitdiff_plain;h=refs%2Fchanges%2F58%2F3358%2F3 CSIT-427: Honeycomb ietf-ACL tests - L2 - add keywords for accessing Honeycomb's ietf-acl node - add variable file with ietf-acl test data - add ietf-acl traffic test suite - modify bridge domain teardown keyword to unassign interfaces from the bridge domain before delete Change-Id: I6df1771f2fb9b42f30b5af8f54a384c6714f5949 Signed-off-by: selias --- diff --git a/resources/libraries/python/honeycomb/HcAPIKwACL.py b/resources/libraries/python/honeycomb/HcAPIKwACL.py index c11e325194..1042adc872 100644 --- a/resources/libraries/python/honeycomb/HcAPIKwACL.py +++ b/resources/libraries/python/honeycomb/HcAPIKwACL.py @@ -14,6 +14,7 @@ """This module implements keywords to manipulate ACL data structures using Honeycomb REST API.""" +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 \ @@ -262,3 +263,154 @@ class ACLKeywords(object): return resp["classify-session"][0] except (KeyError, TypeError): return {} + + @staticmethod + def create_ietf_classify_chain(node, list_name, layer, data): + """Create classify chain using the ietf-acl node. + + :param node: Honeycomb node. + :param list_name: Name for the classify list. + :param layer: Network layer to classify on. + :param data: Dictionary of settings to send to Honeycomb. + :type node: dict + :type list_name: str + :type layer: string + :type data: dict + + :return: Content of response. + :rtype: bytearray + :raises HoneycombError: If the operation fails. + """ + if layer.lower() == "l2": + suffix = "eth" + elif layer.lower() in ("l3_ip4", "l3_ip6", "l4"): + raise NotImplementedError + else: + raise ValueError("Unexpected value of layer argument {0}." + "Valid options are: L2, L3_IP4, L3_IP6, L4." + .format(layer)) + + path = "/acl/ietf-access-control-list:{0}-acl/{1}".format( + suffix, list_name) + + status_code, resp = HcUtil.put_honeycomb_data( + node, "config_ietf_classify_chain", data, path) + + if status_code != HTTPCodes.OK: + raise HoneycombError( + "Could not create classify chain." + "Status code: {0}.".format(status_code)) + + return resp + + @staticmethod + def set_ietf_interface_acl(node, interface, layer, direction, list_name, + default_action): + """Assign an interface to an ietf-acl classify chain. + + :param node: Honeycomb node. + :param interface: Name of an interface on the node. + :param layer: Network layer to classify packets on. + Valid options are: L2, L3, L4. Mixed ACL not supported yet. + :param direction: Classify incoming or outgiong packets. + Valid options are: ingress, egress + :param list_name: Name of an ietf-acl classify chain. + :param default_action: Default classifier action: permit or deny. + :type node: dict + :type interface: str or int + :type layer: str + :type direction: str + :type list_name: str + :type default_action: str + + :return: Content of response. + :rtype: bytearray + :raises HoneycombError: If the operation fails. + """ + + interface = Topology.convert_interface_reference( + node, interface, "name") + + interface = interface.replace("/", "%2F") + + if direction not in ("ingress", "egress"): + raise ValueError("Unknown traffic direction {0}. " + "Valid options are: ingress, egress." + .format(direction)) + + path = "/interface/{0}/ietf-acl/{1}/access-lists".format( + interface, direction) + + data = { + "access-lists": { + "acl": [{ + "type": None, + "name": list_name + }], + "default-action": default_action, + "mode": None + } + } + + acl_type = "ietf-access-control-list:{suffix}-acl" + + if layer.lower() == "l2": + data["access-lists"]["mode"] = "l2" + data["access-lists"]["acl"][0]["type"] = \ + acl_type.format(suffix="eth") + + elif layer.lower() in ("l3_ip4", "l3_ip6", "L4"): + raise NotImplementedError + else: + raise ValueError("Unknown network layer {0}. " + "Valid options are: " + "L2, L3_IP4, L3_IP6, L4.".format(layer)) + + status_code, resp = HcUtil.put_honeycomb_data( + node, "config_vpp_interfaces", data, path) + + if status_code != HTTPCodes.OK: + raise HoneycombError( + "Could not configure ACL on interface. " + "Status code: {0}.".format(status_code)) + + return resp + + @staticmethod + def delete_ietf_interface_acls(node, interface): + """Remove all ietf-acl assignments from an interface. + + :param node: Honeycomb node. + :param interface: Name of an interface on the node. + :type node: dict + :type interface: str or int""" + + interface = Topology.convert_interface_reference( + node, interface, "name") + + interface = interface.replace("/", "%2F") + + path = "/interface/{0}/ietf-acl/".format(interface) + status_code, _ = HcUtil.delete_honeycomb_data( + node, "config_vpp_interfaces", path) + + if status_code != HTTPCodes.OK: + raise HoneycombError( + "Could not remove ACL assignment from interface. " + "Status code: {0}.".format(status_code)) + + @staticmethod + def delete_ietf_classify_chains(node): + """Remove all classify chains from the ietf-acl node. + + :param node: Honeycomb node. + :type node: dict + """ + + status_code, _ = HcUtil.delete_honeycomb_data( + node, "config_ietf_classify_chain") + + if status_code != HTTPCodes.OK: + raise HoneycombError( + "Could not remove ietf-acl chain. " + "Status code: {0}.".format(status_code)) diff --git a/resources/libraries/python/honeycomb/HcAPIKwBridgeDomain.py b/resources/libraries/python/honeycomb/HcAPIKwBridgeDomain.py index e3fd6fb4fc..f156f096e3 100644 --- a/resources/libraries/python/honeycomb/HcAPIKwBridgeDomain.py +++ b/resources/libraries/python/honeycomb/HcAPIKwBridgeDomain.py @@ -254,7 +254,7 @@ class BridgeDomainKeywords(object): bridge_domain) @staticmethod - def remove_all_bds(node): + def remove_all_bridge_domains(node): """Remove all bridge domains. :param node: Honeycomb node. @@ -266,8 +266,10 @@ class BridgeDomainKeywords(object): """ data = {"bridge-domains": {"bridge-domain": []}} + status_code, resp = HcUtil.\ put_honeycomb_data(node, "config_bridge_domain", data) + if status_code != HTTPCodes.OK: raise HoneycombError("Not possible to remove all bridge domains. " "Status code: {0}.".format(status_code)) diff --git a/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py index fdb9b90eb3..83267b5e55 100644 --- a/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py +++ b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py @@ -16,7 +16,7 @@ The keywords make possible to put and get configuration data and to get operational data. """ - +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 \ @@ -222,7 +222,7 @@ 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 @@ -237,6 +237,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) @@ -292,7 +295,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. @@ -306,6 +309,9 @@ class InterfaceKeywords(object): :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 +323,33 @@ 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, _ = 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)) + @staticmethod def get_bd_oper_data_from_interface(node, interface): """Returns operational data about bridge domain settings in the @@ -367,7 +400,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. @@ -383,6 +416,9 @@ class InterfaceKeywords(object): :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)) @@ -410,6 +446,9 @@ class InterfaceKeywords(object): :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}, ]} @@ -438,6 +477,9 @@ class InterfaceKeywords(object): :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): diff --git a/resources/libraries/python/topology.py b/resources/libraries/python/topology.py index 0b40967ef2..5884ddf743 100644 --- a/resources/libraries/python/topology.py +++ b/resources/libraries/python/topology.py @@ -372,7 +372,7 @@ class Topology(object): :return: Interface key. :rtype: str - :raises TypeError: If provided with invalid arguments. + :raises TypeError: If provided with invalid interface argument. :raises RuntimeError: If the interface does not exist in topology. """ diff --git a/resources/libraries/robot/honeycomb/access_control_lists.robot b/resources/libraries/robot/honeycomb/access_control_lists.robot index 0fd1c7ef86..0898a9339e 100644 --- a/resources/libraries/robot/honeycomb/access_control_lists.robot +++ b/resources/libraries/robot/honeycomb/access_control_lists.robot @@ -337,3 +337,61 @@ | | ... | \| Clear all ACL settings \| ${nodes['DUT1']} \| | | [Arguments] | ${node} | | Remove all classify tables | ${node} + +| Honeycomb creates ACL chain through IETF node +| | [Documentation] | Creates classify chain through the high-level\ +| | ... | IETF-ACL node. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - acl_list_name - Name for the classify chain. Type: string +| | ... | - layer - Classification layer (L2, L3, L4, mixed). Type: string +| | ... | - acl_list_settings - classify rules. Type: dictionary +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Honeycomb creates ACL chain through IETF node \ +| | ... | \| ${nodes['DUT1']} \| acl_test \| ${settings} \| +| | [Arguments] | ${node} | ${acl_list_name} | ${layer} | ${acl_list_settings} +| | Create IETF classify chain +| | ... | ${node} | ${acl_list_name} | ${layer} | ${acl_list_settings} + +| Honeycomb assigns IETF-ACL chain to interface +| | [Documentation] | Applies classification through the high-level\ +| | ... | IETF-ACL node to an interface. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - interface - Interface to apply classifier to. | Type: string +| | ... | - layer - Classification layer (L2, L3, L4, mixed). Type: string +| | ... | - direction - Ingress or Egress ACL. Type: string +| | ... | - acl_list_name - Name of the classify chain to apply. Type: string +| | ... | - default_action - Default classify action: permit or deny.\ +| | ... | Type: string +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Honeycomb assigns IETF-ACL chain to interface \ +| | ... | \| ${nodes['DUT1']} \| GigabitEthernet0/8/0 \| L2 \| ingress \ +| | ... | \| acl_test \| permit \| +| | [Arguments] +| | ... | ${node} | ${interface} | ${layer} | ${direction} | ${acl_list_name} +| | ... | ${default-action} +| | Set IETF interface ACL +| | ... | ${node} | ${interface} | ${layer} | ${direction} | ${acl_list_name} +| | ... | ${default-action} + +| Clear IETF-ACL settings +| | [Documentation] | Removes ACl assignment from interface, then deletes\ +| | ... | IETF-ACL chain. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - interface - Interface to clean classifiers from. | Type: string +| | ... +| | ... | *Example:* +| | ... +| | ... | Clear IETF-ACL settings | ${nodes['DUT1']} \| GigabitEthernet0/8/0 \| +| | [Arguments] | ${node} | ${interface} +| | Delete IETF interface ACLs | ${node} | ${interface} +| | Delete IETF classify chains | ${node} \ No newline at end of file diff --git a/resources/libraries/robot/honeycomb/bridge_domain.robot b/resources/libraries/robot/honeycomb/bridge_domain.robot index 99befc9e15..2edf3076ff 100644 --- a/resources/libraries/robot/honeycomb/bridge_domain.robot +++ b/resources/libraries/robot/honeycomb/bridge_domain.robot @@ -178,6 +178,39 @@ | | | Should contain | ${if_indices} | ${interface['sw_if_index']} | | | Should be equal | ${interface['shg']} | ${settings['split_horizon_group']} +| Honeycomb should not show interfaces assigned to bridge domain +| | [Documentation] | Uses Honeycomb API to verify interfaces are not assigned\ +| | ... | to bridge domain. +| | ... +| | ... | *Arguments:* +| | ... | - node - Information about a DUT node. Type: dictionary +| | ... | - interface1, interface2 - Names of interfaces to check\ +| | ... | bridge domain assignment on. Type: string +| | ... | - bd_name - Name of the bridge domain. Type: string +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Honeycomb should not show interfaces assigned to bridge domain \ +| | ... | \| ${nodes['DUT1']} \| GigabitEthernet0/8/0 \| GigabitEthernet0/9/0 \ +| | ... | \| bd-04 \| +| | [Arguments] | ${node} | ${interface1} | ${interface2} | ${bd_name} +| | ${if1_data_oper}= | interfaceAPI.Get interface oper data +| | ... | ${node} | ${interface1} +| | ${if2_data_oper}= | interfaceAPI.Get interface oper data +| | ... | ${node} | ${interface2} +| | ${if1_data_cfg}= +| | ... | interfaceAPI.Get interface cfg data | ${node} | ${interface1} +| | ${if1_data_cfg}= +| | ... | interfaceAPI.Get interface cfg data | ${node} | ${interface2} +| | Run keyword and expect error | *KeyError: 'v3po:l2'* +| | ... | Set Variable | ${if1_data_oper['v3po:l2']} +| | Run keyword and expect error | *KeyError: 'v3po:l2'* +| | ... | Set Variable | ${if2_data_oper['v3po:l2']} +| | Run keyword and expect error | *KeyError: 'v3po:l2'* +| | ... | Set Variable | ${if1_data_cfg['v3po:l2']} +| | Run keyword and expect error | *KeyError: 'v3po:l2'* +| | ... | Set Variable | ${if2_data_cfg['v3po:l2']} + | Honeycomb removes all bridge domains | | [Documentation] | Uses Honeycomb API to remove all bridge domains from the \ | | ... | VPP node. @@ -188,8 +221,10 @@ | | ... | *Example:* | | ... | | ... | \| Honeycomb removes all bridge domains \| ${nodes['DUT1']} \| -| | [Arguments] | ${node} -| | Remove all bds | ${node} +| | [Arguments] | ${node} | @{interfaces} +| | :FOR | ${interface} | IN | @{interfaces} +| | | Remove bridge domain from interface | ${node} | ${interface} +| | Remove all bridge domains | ${node} | Honeycomb should show no bridge domains | | [Documentation] | Uses Honeycomb API to verify the removal of all\ @@ -239,7 +274,7 @@ | | [Arguments] | ${node} | ${interface} | ${bd_name} | ${settings} | | ... | | interfaceAPI.Add bridge domain to interface -| | ... | ${node} | ${interface} | ${settings['bridge-domain']} +| | ... | ${node} | ${interface} | ${bd_name} | | ... | ${settings['split-horizon-group']} | | ... | ${settings['bridged-virtual-interface']} diff --git a/resources/libraries/robot/honeycomb/interfaces.robot b/resources/libraries/robot/honeycomb/interfaces.robot index f94d03d7d4..1ffc2e42bb 100644 --- a/resources/libraries/robot/honeycomb/interfaces.robot +++ b/resources/libraries/robot/honeycomb/interfaces.robot @@ -114,7 +114,6 @@ | | :FOR | ${key} | IN | @{settings.keys()} | | | interfaceAPI.Configure interface ipv4 | | | ... | ${node} | ${interface} | ${key} | ${settings['${key}']} -| | | ${api_data}= | interfaceAPI.Get interface oper data | ${node} | ${interface} | Honeycomb sets interface ipv4 address with prefix | | [Documentation] | Uses Honeycomb API to assign an ipv4 address to the\ diff --git a/resources/templates/honeycomb/config_ietf_classify_chain.url b/resources/templates/honeycomb/config_ietf_classify_chain.url new file mode 100644 index 0000000000..79a9670f44 --- /dev/null +++ b/resources/templates/honeycomb/config_ietf_classify_chain.url @@ -0,0 +1 @@ +/restconf/config/ietf-access-control-list:access-lists \ No newline at end of file diff --git a/resources/test_data/honeycomb/ietf_acl.py b/resources/test_data/honeycomb/ietf_acl.py new file mode 100644 index 0000000000..d5c3040d7a --- /dev/null +++ b/resources/test_data/honeycomb/ietf_acl.py @@ -0,0 +1,82 @@ +# Copyright (c) 2016 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test variables for ietf-ACL test suite.""" + + +def get_variables(test_case, name): + """Create and return a dictionary of test variables for the specified + test case. + + :param test_case: Determines which test variables to return. + :param name: Name for the classify chain used in test. + :type test_case: str + :type name: str + + :return: Dictionary of test variables - settings for Honeycomb's + ietf-acl node and packet fields to use in verification. + :rtype: dict + """ + + variables = { + # generic packet data + "src_ip": "16.0.0.1", + "dst_ip": "16.0.1.1", + "dst_net": "16.0.1.0", + "src_port": "1234", + "dst_port": "1234", + "src_mac": "01:02:03:04:05:06", + "dst_mac": "10:20:30:40:50:60"} + + if test_case.lower() == "l2": + classify_vars = { + "classify_src": "12:23:34:45:56:67", + "classify_dst": "89:9A:AB:BC:CD:DE", + "classify_src2": "01:02:03:04:56:67", + "classify_dst2": "89:9A:AB:BC:50:60", + "src_mask": "00:00:00:00:FF:FF", + "dst_mask": "FF:FF:FF:FF:00:00", + } + + acl_settings = { + "acl": [{ + "acl-type": + "ietf-access-control-list:eth-acl", + "acl-name": name, + "access-list-entries": {"ace": [{ + "rule-name": "rule1", + "matches": { + "source-mac-address": + classify_vars["classify_src"], + "source-mac-address-mask": + classify_vars["src_mask"], + "destination-mac-address": + classify_vars["classify_dst"], + "destination-mac-address-mask": + classify_vars["dst_mask"] + }, + "actions": { + "deny": {} + } + }]} + }] + } + + elif test_case.lower() in ("l3_ip4", "l3_ip6", "l4"): + raise NotImplementedError + else: + raise Exception("Unrecognized test case {0}".format(test_case)) + + variables.update(classify_vars) + variables["acl_settings"] = acl_settings + return variables diff --git a/tests/func/honeycomb/020_bridge_domain.robot b/tests/func/honeycomb/020_bridge_domain.robot index bd182e34a9..850b81e39e 100644 --- a/tests/func/honeycomb/020_bridge_domain.robot +++ b/tests/func/honeycomb/020_bridge_domain.robot @@ -31,7 +31,7 @@ | Suite Teardown | Run keywords | ... | Run Keyword If Any Tests Failed | ... | Restart Honeycomb And VPP And Clear Persisted Configuration | ${node} -| ... | AND | Honeycomb removes all bridge domains | ${node} +| ... | AND | Honeycomb removes all bridge domains | ${node} | @{interfaces} | Force Tags | honeycomb_sanity | Documentation | *Honeycomb bridge domain management test suite.* | ... @@ -94,3 +94,5 @@ | | When Honeycomb removes all bridge domains | ${node} | | Then Honeycomb should show no bridge domains | ${node} | | And VAT should show no bridge domains | ${node} +| | And Honeycomb should not show interfaces assigned to bridge domain +| | ... | ${node} | @{interfaces} | ${bd1_name} diff --git a/tests/func/honeycomb/021_l2_fib.robot b/tests/func/honeycomb/021_l2_fib.robot index b7e0f8dc02..81b8260610 100644 --- a/tests/func/honeycomb/021_l2_fib.robot +++ b/tests/func/honeycomb/021_l2_fib.robot @@ -23,7 +23,7 @@ | Suite Setup | Run keywords | ... | Set test interface down | ... | AND -| ... | Honeycomb removes all bridge domains | ${node} +| ... | Honeycomb removes all bridge domains | ${node} | ${interface} | Suite Teardown | Run keywords | ... | Run Keyword If Any Tests Failed | ... | Restart Honeycomb And VPP And Clear Persisted Configuration | ${node} diff --git a/tests/func/honeycomb/081_ietf_acl_traffic.robot b/tests/func/honeycomb/081_ietf_acl_traffic.robot new file mode 100644 index 0000000000..a6672ea1c4 --- /dev/null +++ b/tests/func/honeycomb/081_ietf_acl_traffic.robot @@ -0,0 +1,83 @@ +# Copyright (c) 2016 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +*** Variables *** +| &{if_settings}= | enabled=True +# Bridge domain settings +| ${bd_name}= | bd1 +| &{bd_settings}= | flood=${True} | forward=${True} | learn=${True} +| ... | unknown-unicast-flood=${True} | arp-termination=${False} +| &{bd_if_settings}= | split_horizon_group=${0} | bvi=${False} +# Names for AC lists +| ${acl_name_l2}= | acl_l2 + +*** Settings *** +| Resource | resources/libraries/robot/default.robot +| Resource | resources/libraries/robot/honeycomb/honeycomb.robot +| Resource | resources/libraries/robot/honeycomb/interfaces.robot +| Resource | resources/libraries/robot/honeycomb/bridge_domain.robot +| Resource | resources/libraries/robot/honeycomb/access_control_lists.robot +| Resource | resources/libraries/robot/testing_path.robot +| Resource | resources/libraries/robot/traffic.robot +| Library | resources.libraries.python.honeycomb.HcAPIKwACL.ACLKeywords +| Library | resources.libraries.python.Trace +| Suite Teardown | Run Keyword If Any Tests Failed +| ... | Restart Honeycomb And VPP And Clear Persisted Configuration | ${node} +| Documentation | *Honeycomb access control lists test suite for IETF-ACL node.* +| Force Tags | Honeycomb_sanity + +*** Test Cases *** +| TC01: Honeycomb can configure L2 ACL MAC filtering through IETF-ACL node +| | [Documentation] +| | ... | [Top] TG=DUT1=TG. +| | ... | [Enc] Eth-IPv4-TCP. +| | ... | [Cfg] (Using Honeycomb API) On DUT1 bridge both interfaces to TG\ +| | ... | and configure L2 MAC ACL on ingress interface. +| | ... | [Ver] Send simple TCP packets from one TG interface to the other,\ +| | ... | using different MACs. Receive all packets except those with\ +| | ... | MACs in the filtered ranges. +| | [Teardown] | Run Keywords +| | ... | Clear IETF-ACL settings | ${node} | ${dut_to_tg_if1} | AND +| | ... | Show Packet Trace on All DUTs | ${nodes} | AND +| | ... | Honeycomb removes all bridge domains +| | ... | ${node} | ${dut_to_tg_if1} | ${dut_to_tg_if2} +| | Given Path For 2-node Testing Is Set +| | ... | ${nodes['TG']} | ${nodes['DUT1']} | ${nodes['TG']} +| | And Import Variables | resources/test_data/honeycomb/ietf_acl.py +| | ... | L2 | ${acl_name_l2} +| | And Honeycomb Sets Interface State | ${dut_node} | ${dut_to_tg_if1} | up +| | And Honeycomb Sets Interface State | ${dut_node} | ${dut_to_tg_if2} | up +| | And Honeycomb Creates L2 Bridge Domain +| | ... | ${dut_node} | ${bd_name} | ${bd_settings} +| | And Honeycomb Adds Interfaces To Bridge Domain +| | ... | ${dut_node} | ${dut_to_tg_if1} | ${dut_to_tg_if2} +| | ... | ${bd_name} | ${bd_if_settings} +| | When Honeycomb creates ACL chain through IETF node +| | ... | ${dut_node} | ${acl_name_l2} | L2 | ${acl_settings} +| | And Honeycomb assigns IETF-ACL chain to interface +| | ... | ${dut_node} | ${dut_to_tg_if1} | L2 | ingress | ${acl_name_l2} +| | ... | permit +| | Then Send TCP or UDP packet | ${tg_node} | ${src_ip} | ${dst_ip} +| | ... | ${tg_to_dut_if1} | ${src_mac} +| | ... | ${tg_to_dut_if2} | ${dst_mac} +| | ... | TCP | ${src_port} | ${dst_port} +| | And Run keyword and expect error | TCP/UDP Rx timeout +| | ... | Send TCP or UDP packet | ${tg_node} | ${src_ip} | ${dst_ip} +| | ... | ${tg_to_dut_if1} | ${classify_src} +| | ... | ${tg_to_dut_if2} | ${classify_dst} +| | ... | TCP | ${src_port} | ${dst_port} +| | And Run keyword and expect error | TCP/UDP Rx timeout +| | ... | Send TCP or UDP packet | ${tg_node} | ${src_ip} | ${dst_ip} +| | ... | ${tg_to_dut_if1} | ${classify_src2} +| | ... | ${tg_to_dut_if2} | ${classify_dst2} +| | ... | TCP | ${src_port} | ${dst_port}