aboutsummaryrefslogtreecommitdiffstats
path: root/resources/libraries/python
diff options
context:
space:
mode:
Diffstat (limited to 'resources/libraries/python')
-rw-r--r--resources/libraries/python/Classify.py23
-rw-r--r--resources/libraries/python/GBP.py344
-rw-r--r--resources/libraries/python/InterfaceUtil.py9
-rw-r--r--resources/libraries/python/topology.py3
4 files changed, 372 insertions, 7 deletions
diff --git a/resources/libraries/python/Classify.py b/resources/libraries/python/Classify.py
index 62508e1a49..46785dd3c2 100644
--- a/resources/libraries/python/Classify.py
+++ b/resources/libraries/python/Classify.py
@@ -425,7 +425,7 @@ class Classify(object):
"""
cmd = "acl_add_replace"
args = dict(
- tag=tag,
+ tag=tag.encode("utf-8"),
acl_index=4294967295 if acl_idx is None else acl_idx,
count=len(rules),
r=rules
@@ -803,21 +803,24 @@ class Classify(object):
acls=acls)
@staticmethod
- def add_replace_acl_multi_entries(node, acl_idx=None, rules=None):
+ def add_replace_acl_multi_entries(node, acl_idx=None, rules=None, tag=""):
"""Add a new ACL or replace the existing one. To replace an existing
ACL, pass the ID of this ACL.
:param node: VPP node to set ACL on.
:param acl_idx: ID of ACL. (Optional)
:param rules: Required rules. (Optional)
+ :param tag: ACL tag (Optional).
:type node: dict
:type acl_idx: int
:type rules: str
+ :type tag: str
"""
reg_ex_src_ip = re.compile(r'(src [0-9a-fA-F.:/\d{1,2}]*)')
reg_ex_dst_ip = re.compile(r'(dst [0-9a-fA-F.:/\d{1,2}]*)')
reg_ex_sport = re.compile(r'(sport \d{1,5})')
reg_ex_dport = re.compile(r'(dport \d{1,5})')
+ reg_ex_proto = re.compile(r'(proto \d{1,5})')
acl_rules = list()
for rule in rules.split(", "):
@@ -842,18 +845,30 @@ class Classify(object):
port = int(groups.group(1).split(' ')[1])
acl_rule["srcport_or_icmptype_first"] = port
acl_rule["srcport_or_icmptype_last"] = port
+ else:
+ acl_rule["srcport_or_icmptype_first"] = 0
+ acl_rule["srcport_or_icmptype_last"] = 65535
groups = re.search(reg_ex_dport, rule)
if groups:
port = int(groups.group(1).split(' ')[1])
acl_rule["dstport_or_icmpcode_first"] = port
acl_rule["dstport_or_icmpcode_last"] = port
+ else:
+ acl_rule["dstport_or_icmpcode_first"] = 0
+ acl_rule["dstport_or_icmpcode_last"] = 65535
- acl_rule["proto"] = 0
+ groups = re.search(reg_ex_proto, rule)
+ if groups:
+ proto = int(groups.group(1).split(' ')[1])
+ acl_rule["proto"] = proto
+ else:
+ acl_rule["proto"] = 0
acl_rules.append(acl_rule)
- Classify._acl_add_replace(node, acl_idx=acl_idx, rules=acl_rules)
+ Classify._acl_add_replace(
+ node, acl_idx=acl_idx, rules=acl_rules, tag=tag)
@staticmethod
def add_macip_acl_multi_entries(node, rules=""):
diff --git a/resources/libraries/python/GBP.py b/resources/libraries/python/GBP.py
new file mode 100644
index 0000000000..4dd5486a86
--- /dev/null
+++ b/resources/libraries/python/GBP.py
@@ -0,0 +1,344 @@
+# Copyright (c) 2019 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.
+
+"""GBP utilities library."""
+
+from enum import IntEnum
+from ipaddress import ip_address
+
+from resources.libraries.python.IPUtil import IPUtil
+from resources.libraries.python.L2Util import L2Util
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
+from resources.libraries.python.topology import Topology
+
+
+class GBPEndpointFlags(IntEnum):
+ """GBP Endpoint Flags."""
+ GBP_API_ENDPOINT_FLAG_NONE = 0
+ GBP_API_ENDPOINT_FLAG_BOUNCE = 1
+ GBP_API_ENDPOINT_FLAG_REMOTE = 2
+ GBP_API_ENDPOINT_FLAG_LEARNT = 4
+ GBP_API_ENDPOINT_FLAG_EXTERNAL = 8
+
+
+class GBPBridgeDomainFlags(IntEnum):
+ """GBP Bridge Domain Flags."""
+ GBP_BD_API_FLAG_NONE = 0
+ GBP_BD_API_FLAG_DO_NOT_LEARN = 1
+ GBP_BD_API_FLAG_UU_FWD_DROP = 2
+ GBP_BD_API_FLAG_MCAST_DROP = 4
+ GBP_BD_API_FLAG_UCAST_ARP = 8
+
+
+class GBPSubnetType(IntEnum):
+ """GBP Subnet Type."""
+ GBP_API_SUBNET_TRANSPORT = 1
+ GBP_API_SUBNET_STITCHED_INTERNAL = 2
+ GBP_API_SUBNET_STITCHED_EXTERNAL = 3
+ GBP_API_SUBNET_L3_OUT = 4
+ GBP_API_SUBNET_ANON_L3_OUT = 5
+
+
+class GBPExtItfFlags(IntEnum):
+ """GBP External Interface Flags."""
+ GBP_API_EXT_ITF_F_NONE = 0
+ GBP_API_EXT_ITF_F_ANON = 1
+
+
+class GBPRuleAction(IntEnum):
+ """GBP Rule Action."""
+ GBP_API_RULE_PERMIT = 1
+ GBP_API_RULE_DENY = 2
+ GBP_API_RULE_REDIRECT = 3
+
+
+class GBP(object):
+ """GBP utilities."""
+
+ @staticmethod
+ def gbp_route_domain_add(
+ node, rd_id=1, ip4_table_id=1, ip6_table_id=0,
+ ip4_uu_sw_if_index=0xffffffff, ip6_uu_sw_if_index=0xffffffff):
+ """Add GBP route domain.
+
+ :param node: Node to add GBP route domain on.
+ :param rd_id: GBP route domain ID.
+ :param ip4_table_id: IPv4 table.
+ :param ip6_table_id: IPv6 table.
+ :param ip4_uu_sw_if_index: IPv4 unicast interface index.
+ :param ip6_uu_sw_if_index: IPv6 unicast interface index.
+ :type node: dict
+ :type rd_id: int
+ :type ip4_table_id: int
+ :type ip6_table_id: int
+ :type ip4_uu_sw_if_index: int
+ :type ip6_uu_sw_if_index: int
+ """
+ cmd = 'gbp_route_domain_add'
+ err_msg = 'Failed to add GBP route domain on {node}!'\
+ .format(node=node['host'])
+
+ args_in = dict(
+ rd = dict (
+ rd_id = rd_id,
+ ip4_table_id = ip4_table_id,
+ ip6_table_id = ip6_table_id,
+ ip4_uu_sw_if_index = ip4_uu_sw_if_index,
+ ip6_uu_sw_if_index = ip6_uu_sw_if_index
+ )
+ )
+
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args_in).get_reply(err_msg)
+
+ @staticmethod
+ def gbp_bridge_domain_add(
+ node, bvi_sw_if_index, bd_id=1, rd_id=1,
+ uu_fwd_sw_if_index=0xffffffff, bm_flood_sw_if_index=0xffffffff):
+ """Add GBP bridge domain.
+
+ :param node: Node to add GBP bridge domain on.
+ :param bvi_sw_if_index: SW index of BVI/loopback interface.
+ :param bd_id: GBP bridge domain ID.
+ :param rd_id: GBP route domain ID.
+ :param uu_fwd_sw_if_index: Unicast forward interface index.
+ :param bm_flood_sw_if_index: Bcast/Mcast flood interface index.
+ :type node: dict
+ :type bvi_sw_if_index: int
+ :type bd_id: int
+ :type rd_id: int
+ :type uu_fwd_sw_if_index: int
+ :type bm_flood_sw_if_index: int
+ """
+ cmd = 'gbp_bridge_domain_add'
+ err_msg = 'Failed to add GBP bridge domain on {node}!'\
+ .format(node=node['host'])
+
+ args_in = dict(
+ bd = dict(
+ flags = getattr(GBPBridgeDomainFlags,
+ 'GBP_BD_API_FLAG_NONE').value,
+ bvi_sw_if_index = bvi_sw_if_index,
+ uu_fwd_sw_if_index = uu_fwd_sw_if_index,
+ bm_flood_sw_if_index = bm_flood_sw_if_index,
+ bd_id = bd_id,
+ rd_id = rd_id
+ )
+ )
+
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args_in).get_reply(err_msg)
+
+ @staticmethod
+ def gbp_endpoint_group_add(
+ node, sclass, bd_id=1, rd_id=1, vnid=1,
+ uplink_sw_if_index=0xffffffff, remote_ep_timeout=0xffffffff):
+ """Add GBP endpoint group.
+
+ :param node: Node to add GBP endpoint group on.
+ :param sclass: Source CLASS.
+ :param bd_id: GBP bridge domain ID.
+ :param rd_id: GBP route domain ID.
+ :param uplink_sw_if_index: Uplink interface index.
+ :param remote_ep_timeout: Remote endpoint interface index.
+ :param vnid: VNID.
+ :type node: dict
+ :type sclass: int
+ :type bd_id: int
+ :type rd_id: int
+ :type vnid: int
+ :type uplink_sw_if_index: int
+ :type remote_ep_timeout: int
+ """
+ cmd = 'gbp_endpoint_group_add'
+ err_msg = 'Failed to add GBP endpoint group on {node}!'\
+ .format(node=node['host'])
+
+ args_in = dict(
+ epg = dict (
+ uplink_sw_if_index = uplink_sw_if_index,
+ bd_id = bd_id,
+ rd_id = rd_id,
+ vnid = vnid,
+ sclass = sclass,
+ retention = dict (
+ remote_ep_timeout = remote_ep_timeout
+ )
+ )
+ )
+
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args_in).get_reply(err_msg)
+
+ @staticmethod
+ def gbp_endpoint_add(node, sw_if_index, ip_addr, mac_addr, sclass):
+ """Add GBP endpoint.
+
+ :param node: Node to add GBP endpoint on.
+ :param sw_if_index: SW index of interface.
+ :param ip_addr: GBP route domain ID.
+ :param mac_addr: MAC address.
+ :param sclass: Source CLASS.
+ :type node: dict
+ :type sw_if_index: int
+ :type ip_addr: str
+ :type mac_addr: str
+ :type sclass: int
+ """
+ cmd = 'gbp_endpoint_add'
+ err_msg = 'Failed to add GBP endpoint on {node}!'\
+ .format(node=node['host'])
+
+ ips = list()
+ ips.append(IPUtil.create_ip_address_object(
+ ip_address(unicode(ip_addr))))
+ tun_src = IPUtil.create_ip_address_object(
+ ip_address(unicode('0.0.0.0')))
+ tun_dst = IPUtil.create_ip_address_object(
+ ip_address(unicode('0.0.0.0')))
+
+ args_in = dict(
+ endpoint = dict(
+ sw_if_index = sw_if_index,
+ ips = ips,
+ n_ips = len(ips),
+ mac = L2Util.mac_to_bin(mac_addr),
+ sclass = sclass,
+ flags = getattr(GBPEndpointFlags,
+ 'GBP_API_ENDPOINT_FLAG_EXTERNAL').value,
+ tun = dict(
+ src = tun_src,
+ dst = tun_dst
+ )
+ )
+ )
+
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args_in).get_reply(err_msg)
+
+ @staticmethod
+ def gbp_ext_itf_add_del(node, sw_if_index, bd_id=1, rd_id=1):
+ """Add external interface to GBP.
+
+ :param node: Node to add external GBP interface on.
+ :param sw_if_index: SW index of interface.
+ :param bd_id: GBP bridge domain ID.
+ :param rd_id: GBP route domain ID.
+ :type node: dict
+ :type sw_if_index: int
+ :type bd_id: int
+ :type rd_id: int
+ """
+ cmd = 'gbp_ext_itf_add_del'
+ err_msg = 'Failed to add external GBP interface on {node}!'\
+ .format(node=node['host'])
+
+ args_in = dict(
+ is_add = 1,
+ ext_itf = dict(
+ sw_if_index = sw_if_index,
+ bd_id = bd_id,
+ rd_id = rd_id,
+ flags = getattr(GBPExtItfFlags,
+ 'GBP_API_EXT_ITF_F_NONE').value
+ )
+ )
+
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args_in).get_reply(err_msg)
+
+ @staticmethod
+ def gbp_subnet_add_del(
+ node, address, subnet_length, sclass, rd_id=1,
+ sw_if_index=0xffffffff):
+ """Add external interface to GBP.
+
+ :param node: Node to add GBP subnet on.
+ :param address: IPv4 adddress.
+ :param subnet_length: IPv4 address subnet.
+ :param sclass: Source CLASS.
+ :param rd_id: GBP route domain ID.
+ :param sw_if_index: Interface index.
+ :type node: dict
+ :type address: int
+ :type subnet_length: int
+ :type sclass: int
+ :type rd_id: int
+ :type sw_if_index: int
+ """
+ cmd = 'gbp_subnet_add_del'
+ err_msg = 'Failed to add GBP subnet on {node}!'\
+ .format(node=node['host'])
+
+ args_in = dict(
+ is_add = 1,
+ subnet = dict(
+ type = getattr(GBPSubnetType,
+ 'GBP_API_SUBNET_L3_OUT').value,
+ sw_if_index = sw_if_index,
+ sclass = sclass,
+ prefix = dict(
+ address = IPUtil.create_ip_address_object(
+ ip_address(unicode(address))),
+ len = int(subnet_length)
+ ),
+ rd_id = rd_id
+ )
+ )
+
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args_in).get_reply(err_msg)
+
+ @staticmethod
+ def gbp_contract_add_del(node, sclass, dclass, acl_index=0):
+ """Add GBP contract.
+
+ :param node: Node to add GBP contract on.
+ :param sclass: Source CLASS.
+ :param dclass: Destination CLASS.
+ :param acl_index: Index of ACL rule.
+ :type node: dict
+ :type sclass: int
+ :type dclass: int
+ :type acl_index: int
+ """
+ cmd = 'gbp_contract_add_del'
+ err_msg = 'Failed to add GBP contract on {node}!'\
+ .format(node=node['host'])
+
+ rule_permit = dict(
+ action = getattr(GBPRuleAction,
+ 'GBP_API_RULE_PERMIT').value,
+ nh_set = dict(
+ hash_mode = list(),
+ n_nhs = 8,
+ nhs = [dict()]*8,
+ )
+ )
+ rules = [rule_permit, rule_permit]
+
+ args_in = dict(
+ is_add = 1,
+ contract = dict(
+ acl_index = acl_index,
+ sclass = sclass,
+ dclass = dclass,
+ n_rules = len(rules),
+ rules = rules,
+ n_ether_types = 16,
+ allowed_ethertypes = [0x800, 0x86dd] + [0]*14
+ )
+ )
+
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args_in).get_reply(err_msg)
diff --git a/resources/libraries/python/InterfaceUtil.py b/resources/libraries/python/InterfaceUtil.py
index 898d7fe9ae..34ccd7cadc 100644
--- a/resources/libraries/python/InterfaceUtil.py
+++ b/resources/libraries/python/InterfaceUtil.py
@@ -1045,18 +1045,20 @@ class InterfaceUtil(object):
return ifc_name, sw_if_index
@staticmethod
- def vpp_create_loopback(node):
+ def vpp_create_loopback(node, mac=None):
"""Create loopback interface on VPP node.
:param node: Node to create loopback interface on.
+ :param mac: Optional MAC address for Loopback interface.
:type node: dict
+ :type mac: str
:returns: SW interface index.
:rtype: int
:raises RuntimeError: If it is not possible to create loopback on the
node.
"""
cmd = 'create_loopback'
- args = dict(mac_address=0)
+ args = dict(mac_address=L2Util.mac_to_bin(mac) if mac else 0)
err_msg = 'Failed to create loopback interface on host {host}'.format(
host=node['host'])
with PapiSocketExecutor(node) as papi_exec:
@@ -1066,6 +1068,9 @@ class InterfaceUtil(object):
Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
Topology.update_interface_name(node, if_key, ifc_name)
+ if mac:
+ mac = InterfaceUtil.vpp_get_interface_mac(node, ifc_name)
+ Topology.update_interface_mac_address(node, if_key, mac)
return sw_if_index
diff --git a/resources/libraries/python/topology.py b/resources/libraries/python/topology.py
index 394dc9df58..68045bbb68 100644
--- a/resources/libraries/python/topology.py
+++ b/resources/libraries/python/topology.py
@@ -138,7 +138,8 @@ class Topology(object):
:returns: Nothing
"""
port_types = ('subinterface', 'vlan_subif', 'memif', 'tap', 'vhost',
- 'loopback', 'gre_tunnel', 'vxlan_tunnel', 'eth_bond')
+ 'loopback', 'gre_tunnel', 'vxlan_tunnel', 'eth_bond',
+ 'avf')
for node_data in nodes.values():
if node_data['type'] == NodeType.DUT: