CSIT-427: Honeycomb ietf-ACL tests - L2 58/3358/3
authorselias <samelias@cisco.com>
Fri, 7 Oct 2016 11:43:45 +0000 (13:43 +0200)
committerSamuel Eliáš <samelias@cisco.com>
Thu, 20 Oct 2016 08:53:16 +0000 (08:53 +0000)
 - 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 <samelias@cisco.com>
12 files changed:
resources/libraries/python/honeycomb/HcAPIKwACL.py
resources/libraries/python/honeycomb/HcAPIKwBridgeDomain.py
resources/libraries/python/honeycomb/HcAPIKwInterfaces.py
resources/libraries/python/topology.py
resources/libraries/robot/honeycomb/access_control_lists.robot
resources/libraries/robot/honeycomb/bridge_domain.robot
resources/libraries/robot/honeycomb/interfaces.robot
resources/templates/honeycomb/config_ietf_classify_chain.url [new file with mode: 0644]
resources/test_data/honeycomb/ietf_acl.py [new file with mode: 0644]
tests/func/honeycomb/020_bridge_domain.robot
tests/func/honeycomb/021_l2_fib.robot
tests/func/honeycomb/081_ietf_acl_traffic.robot [new file with mode: 0644]

index c11e325..1042adc 100644 (file)
@@ -14,6 +14,7 @@
 """This module implements keywords to manipulate ACL data structures using
 Honeycomb REST API."""
 
 """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 \
 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 {}
             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))
index e3fd6fb..f156f09 100644 (file)
@@ -254,7 +254,7 @@ class BridgeDomainKeywords(object):
                                                        bridge_domain)
 
     @staticmethod
                                                        bridge_domain)
 
     @staticmethod
-    def remove_all_bds(node):
+    def remove_all_bridge_domains(node):
         """Remove all bridge domains.
 
         :param node: Honeycomb node.
         """Remove all bridge domains.
 
         :param node: Honeycomb node.
@@ -266,8 +266,10 @@ class BridgeDomainKeywords(object):
         """
 
         data = {"bridge-domains": {"bridge-domain": []}}
         """
 
         data = {"bridge-domains": {"bridge-domain": []}}
+
         status_code, resp = HcUtil.\
             put_honeycomb_data(node, "config_bridge_domain", data)
         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))
         if status_code != HTTPCodes.OK:
             raise HoneycombError("Not possible to remove all bridge domains. "
                                  "Status code: {0}.".format(status_code))
index fdb9b90..83267b5 100644 (file)
@@ -16,7 +16,7 @@
 The keywords make possible to put and get configuration data and to get
 operational data.
 """
 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 \
 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.
         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
         :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"}
 
         intf_state = {"up": "true",
                       "down": "false"}
 
+        interface = Topology.convert_interface_reference(
+            node, interface, "name")
+
         intf = interface.replace("/", "%2F")
         path = "/interface/{0}".format(intf)
 
         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.
         """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.
         :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.
         """
 
         :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)
         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)
 
         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
     @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):
 
     @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.
 
         :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.
         """
 
         :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))
 
         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.
         """
 
         :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}, ]}
         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.
         """
 
         :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):
         path = ("interfaces", ("interface", "name", interface), "ietf-ip:ipv4",
                 "address")
         if isinstance(network, basestring):
index 0b40967..5884ddf 100644 (file)
@@ -372,7 +372,7 @@ class Topology(object):
         :return: Interface key.
         :rtype: str
 
         :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.
         """
 
         :raises RuntimeError: If the interface does not exist in topology.
         """
 
index 0fd1c7e..0898a93 100644 (file)
 | | ... | \| Clear all ACL settings \| ${nodes['DUT1']} \|
 | | [Arguments] | ${node}
 | | Remove all classify tables | ${node}
 | | ... | \| 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
index 99befc9..2edf307 100644 (file)
 | | | Should contain | ${if_indices} | ${interface['sw_if_index']}
 | | | Should be equal | ${interface['shg']} | ${settings['split_horizon_group']}
 
 | | | 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.
 | Honeycomb removes all bridge domains
 | | [Documentation] | Uses Honeycomb API to remove all bridge domains from the \
 | | ... | VPP node.
 | | ... | *Example:*
 | | ...
 | | ... | \| Honeycomb removes all bridge domains \| ${nodes['DUT1']} \|
 | | ... | *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\
 
 | Honeycomb should show no bridge domains
 | | [Documentation] | Uses Honeycomb API to verify the removal of all\
 | | [Arguments] | ${node} | ${interface} | ${bd_name} | ${settings}
 | | ...
 | | interfaceAPI.Add bridge domain to interface
 | | [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']}
 
 | | ... | ${settings['split-horizon-group']}
 | | ... | ${settings['bridged-virtual-interface']}
 
index f94d03d..1ffc2e4 100644 (file)
 | | :FOR | ${key} | IN | @{settings.keys()}
 | | | interfaceAPI.Configure interface ipv4
 | | | ... | ${node} | ${interface} | ${key} | ${settings['${key}']}
 | | :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\
 
 | 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 (file)
index 0000000..79a9670
--- /dev/null
@@ -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 (file)
index 0000000..d5c3040
--- /dev/null
@@ -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
index bd182e3..850b81e 100644 (file)
@@ -31,7 +31,7 @@
 | Suite Teardown | Run keywords
 | ... | Run Keyword If Any Tests Failed
 | ... | Restart Honeycomb And VPP And Clear Persisted Configuration | ${node}
 | 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.*
 | ...
 | 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}
 | | 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}
index b7e0f8d..81b8260 100644 (file)
@@ -23,7 +23,7 @@
 | Suite Setup | Run keywords
 | ... | Set test interface down
 | ... | AND
 | 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}
 | 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 (file)
index 0000000..a6672ea
--- /dev/null
@@ -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}