1 # Copyright (c) 2019 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Classify utilities library."""
19 from ipaddress import ip_address
21 from robot.api import logger
23 from resources.libraries.python.Constants import Constants
24 from resources.libraries.python.topology import Topology
25 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
28 class Classify(object):
29 """Classify utilities."""
32 def _build_mac_mask(dst_mac='', src_mac='', ether_type=''):
33 """Build MAC ACL mask data in hexstring format.
35 :param dst_mac: Source MAC address <0-ffffffffffff>.
36 :param src_mac: Destination MAC address <0-ffffffffffff>.
37 :param ether_type: Ethernet type <0-ffff>.
41 :returns MAC ACL mask in hexstring format.
45 return ('{!s:0>12}{!s:0>12}{!s:0>4}'.format(
46 dst_mac.replace(':', ''), src_mac.replace(':', ''),
47 ether_type)).decode('hex').rstrip('\0')
50 def _build_ip_mask(proto='', src_ip='', dst_ip='', src_port='',
52 """Build IP ACL mask data in hexstring format.
54 :param proto: Protocol number <0-ff>.
55 :param src_ip: Source ip address <0-ffffffff>.
56 :param dst_ip: Destination ip address <0-ffffffff>.
57 :param src_port: Source port number <0-ffff>.
58 :param str dst_port: Destination port number <0-ffff>.
64 :returns: IP mask in hexstring format.
68 return ('{!s:0>20}{!s:0>12}{!s:0>8}{!s:0>4}{!s:0>4}'.format(
69 proto, src_ip, dst_ip, src_port, dst_port)).decode('hex').\
73 def _build_ip6_mask(next_hdr='', src_ip='', dst_ip='', src_port='',
75 """Build IPv6 ACL mask data in hexstring format.
77 :param next_hdr: Next header number <0-ff>.
78 :param src_ip: Source ip address <0-ffffffff>.
79 :param dst_ip: Destination ip address <0-ffffffff>.
80 :param src_port: Source port number <0-ffff>.
81 :param dst_port: Destination port number <0-ffff>.
87 :returns: IPv6 ACL mask in hexstring format.
91 return ('{!s:0>14}{!s:0>34}{!s:0>32}{!s:0>4}{!s:0>4}'.format(
92 next_hdr, src_ip, dst_ip, src_port, dst_port)).decode('hex').\
96 def _build_mac_match(dst_mac='', src_mac='', ether_type=''):
97 """Build MAC ACL match data in hexstring format.
99 :param dst_mac: Source MAC address <x:x:x:x:x:x>.
100 :param src_mac: Destination MAC address <x:x:x:x:x:x>.
101 :param ether_type: Ethernet type <0-ffff>.
104 :type ether_type: str
105 :returns: MAC ACL match data in hexstring format.
109 return ('{!s:0>12}{!s:0>12}{!s:0>4}'.format(
110 dst_mac.replace(':', ''), src_mac.replace(':', ''),
111 ether_type)).decode('hex').rstrip('\0')
114 def _build_ip_match(proto=0, src_ip='', dst_ip='', src_port=0, dst_port=0):
115 """Build IP ACL match data in byte-string format.
117 :param proto: Protocol number with valid option "x".
118 :param src_ip: Source ip address in packed format.
119 :param dst_ip: Destination ip address in packed format.
120 :param src_port: Source port number "x".
121 :param dst_port: Destination port number "x".
127 :returns: IP ACL match data in byte-string format.
131 return ('{!s:0>20}{!s:0>12}{!s:0>8}{!s:0>4}{!s:0>4}'.format(
132 hex(proto)[2:], src_ip, dst_ip, hex(src_port)[2:],
133 hex(dst_port)[2:])).decode('hex').rstrip('\0')
136 def _build_ip6_match(next_hdr=0, src_ip='', dst_ip='', src_port=0,
138 """Build IPv6 ACL match data in byte-string format.
140 :param next_hdr: Next header number with valid option "x".
141 :param src_ip: Source ip6 address in packed format.
142 :param dst_ip: Destination ip6 address in packed format.
143 :param src_port: Source port number "x".
144 :param dst_port: Destination port number "x".
150 :returns: IPv6 ACL match data in byte-string format.
154 return ('{!s:0>14}{!s:0>34}{!s:0>32}{!s:0>4}{!s:0>4}'.format(
155 hex(next_hdr)[2:], src_ip, dst_ip, hex(src_port)[2:],
156 hex(dst_port)[2:])).decode('hex').rstrip('\0')
159 def _classify_add_del_table(
160 node, is_add, mask, match_n_vectors=Constants.BITWISE_NON_ZERO,
161 table_index=Constants.BITWISE_NON_ZERO, nbuckets=2,
162 memory_size=2097152, skip_n_vectors=Constants.BITWISE_NON_ZERO,
163 next_table_index=Constants.BITWISE_NON_ZERO,
164 miss_next_index=Constants.BITWISE_NON_ZERO,
165 current_data_flag=0, current_data_offset=0):
166 """Add or delete a classify table.
168 :param node: VPP node to create classify table.
169 :param is_add: If 1 the table is added, if 0 the table is deleted.
170 :param mask: ACL mask in hexstring format.
171 :param match_n_vectors: Number of vectors to match (Default value = ~0).
172 :param table_index: Index of the classify table. (Default value = ~0)
173 :param nbuckets: Number of buckets when adding a table.
175 :param memory_size: Memory size when adding a table.
176 (Default value = 2097152)
177 :param skip_n_vectors: Number of skip vectors (Default value = ~0).
178 :param next_table_index: Index of next table. (Default value = ~0)
179 :param miss_next_index: Index of miss table. (Default value = ~0)
180 :param current_data_flag: Option to use current node's packet payload
181 as the starting point from where packets are classified.
182 This option is only valid for L2/L3 input ACL for now.
183 0: by default, classify data from the buffer's start location
184 1: classify packets from VPP node's current data pointer.
185 :param current_data_offset: A signed value to shift the start location
186 of the packet to be classified.
187 For example, if input IP ACL node is used, L2 header's first byte
188 can be accessible by configuring current_data_offset to -14
189 if there is no vlan tag.
190 This is valid only if current_data_flag is set to 1.
195 :type match_n_vectors: int
196 :type table_index: int
198 :type memory_size: int
199 :type skip_n_vectors: int
200 :type next_table_index: int
201 :type miss_next_index: int
202 :type current_data_flag: int
203 :type current_data_offset: int
204 :returns: (table_index, skip_n, match_n)
205 table_index: Classify table index.
206 skip_n: Number of skip vectors.
207 match_n: Number of match vectors.
208 :rtype: tuple(int, int, int)
210 cmd = 'classify_add_del_table'
213 table_index=table_index,
215 memory_size=memory_size,
216 skip_n_vectors=skip_n_vectors,
217 match_n_vectors=match_n_vectors,
218 next_table_index=next_table_index,
219 miss_next_index=miss_next_index,
220 current_data_flag=current_data_flag,
221 current_data_offset=current_data_offset,
225 err_msg = "Failed to create a classify table on host {host}".format(
228 with PapiSocketExecutor(node) as papi_exec:
229 reply = papi_exec.add(cmd, **args).get_reply(err_msg)
231 return int(reply["new_table_index"]), int(reply["skip_n_vectors"]),\
232 int(reply["match_n_vectors"])
235 def _classify_add_del_session(
236 node, is_add, table_index, match,
237 opaque_index=Constants.BITWISE_NON_ZERO,
238 hit_next_index=Constants.BITWISE_NON_ZERO, advance=0,
239 action=0, metadata=0):
240 """Add or delete a classify session.
242 :param node: VPP node to create classify session.
243 :param is_add: If 1 the session is added, if 0 the session is deleted.
244 :param table_index: Index of the table to add/del the session.
245 :param match: For add, match value for session, required, needs to
246 include bytes in front with length of skip_n_vectors of target table
247 times sizeof (u32x4) (values of those bytes will be ignored).
248 :param opaque_index: For add, opaque_index of new session.
250 :param hit_next_index: For add, hit_next_index of new session.
252 :param advance: For add, advance value for session. (Default value = 0)
253 :param action: 0: No action (by default) metadata is not used.
254 1: Classified IP packets will be looked up from the specified ipv4
255 fib table (configured by metadata as VRF id).
256 Only valid for L3 input ACL node
257 2: Classified IP packets will be looked up from the specified ipv6
258 fib table (configured by metadata as VRF id).
259 Only valid for L3 input ACL node
260 3: Classified packet will be steered to source routig policy of
261 given index (in metadata).
262 This is only valid for IPv6 packets redirected to a source
264 :param metadata: Valid only if action != 0
265 VRF id if action is 1 or 2. SR policy index if action is 3.
269 :type table_index: int
271 :type opaque_index: int
272 :type hit_next_index: int
277 cmd = 'classify_add_del_session'
280 table_index=table_index,
281 hit_next_index=hit_next_index,
282 opaque_index=opaque_index,
286 match_len=len(match),
289 err_msg = "Failed to create a classify session on host {host}".format(
292 with PapiSocketExecutor(node) as papi_exec:
293 papi_exec.add(cmd, **args).get_reply(err_msg)
296 def _macip_acl_add(node, rules, tag=""):
299 :param node: VPP node to add MACIP ACL.
300 :param rules: List of rules for given ACL.
306 cmd = "macip_acl_add"
313 err_msg = "Failed to create a classify session on host {host}".format(
316 with PapiSocketExecutor(node) as papi_exec:
317 papi_exec.add(cmd, **args).get_reply(err_msg)
320 def _acl_interface_set_acl_list(node, sw_if_index, acl_type, acls):
321 """Set ACL list for interface.
323 :param node: VPP node to set ACL list for interface.
324 :param sw_if_index: sw_if_index of the used interface.
325 :param acl_type: Type of ACL(s) - input or output.
326 :param acls: List of ACLs.
328 :type sw_if_index: int
332 cmd = "acl_interface_set_acl_list"
333 n_input = len(acls) if acl_type == "input" else 0
335 sw_if_index=sw_if_index,
341 err_msg = "Failed to set acl list for interface {idx} on host {host}".\
342 format(idx=sw_if_index, host=node['host'])
344 with PapiSocketExecutor(node) as papi_exec:
345 papi_exec.add(cmd, **args).get_reply(err_msg)
348 def _acl_add_replace(node, acl_idx, rules, tag=""):
349 """ Add/replace ACLs.
351 :param node: VPP node to add MACIP ACL.
352 :param acl_idx: ACL index.
353 :param rules: List of rules for given ACL.
360 cmd = "acl_add_replace"
362 tag=tag.encode("utf-8"),
363 acl_index=4294967295 if acl_idx is None else acl_idx,
368 err_msg = "Failed to add/replace acls on host {host}".format(
371 with PapiSocketExecutor(node) as papi_exec:
372 papi_exec.add(cmd, **args).get_reply(err_msg)
375 def vpp_creates_classify_table_l3(node, ip_version, direction, netmask):
376 """Create classify table for IP address filtering.
378 :param node: VPP node to create classify table.
379 :param ip_version: Version of IP protocol.
380 :param direction: Direction of traffic - src/dst.
381 :param netmask: IPv4 or Ipv6 (depending on the parameter 'ip_version')
382 netmask (decimal, e.g. 255.255.255.255).
384 :type ip_version: str
387 :returns: (table_index, skip_n, match_n)
388 table_index: Classify table index.
389 skip_n: Number of skip vectors.
390 match_n: Number of match vectors.
391 :rtype: tuple(int, int, int)
392 :raises ValueError: If the parameters 'ip_version' or 'direction' have
396 ip4=Classify._build_ip_mask,
397 ip6=Classify._build_ip6_mask
400 if ip_version == "ip4" or ip_version == "ip6":
401 netmask = binascii.hexlify(ip_address(unicode(netmask)).packed)
403 raise ValueError("IP version {ver} is not supported.".format(
406 if direction == "src":
407 mask = mask_f[ip_version](src_ip=netmask)
408 elif direction == "dst":
409 mask = mask_f[ip_version](dst_ip=netmask)
411 raise ValueError("Direction {dir} is not supported.".format(
414 # Add l2 ethernet header to mask
415 mask = 14 * '\0' + mask
417 # Get index of the first significant mask octet
418 i = len(mask) - len(mask.lstrip('\0'))
420 # Compute skip_n parameter
422 # Remove octets to be skipped from the mask
423 mask = mask[skip_n*16:]
424 # Pad mask to an even multiple of the vector size
425 mask = mask + (16 - len(mask) % 16 if len(mask) % 16 else 0) * '\0'
426 # Compute match_n parameter
427 match_n = len(mask) // 16
429 return Classify._classify_add_del_table(
433 match_n_vectors=match_n,
434 skip_n_vectors=skip_n
438 def vpp_configures_classify_session_l3(
439 node, acl_method, table_index, skip_n, match_n, ip_version,
440 direction, address, hit_next_index=Constants.BITWISE_NON_ZERO,
441 opaque_index=Constants.BITWISE_NON_ZERO):
442 """Configuration of classify session for IP address filtering.
444 :param node: VPP node to setup classify session.
445 :param acl_method: ACL method - deny/permit.
446 :param table_index: Classify table index.
447 :param skip_n: Number of skip vectors.
448 :param match_n: Number of vectors to match.
449 :param ip_version: Version of IP protocol.
450 :param direction: Direction of traffic - src/dst.
451 :param address: IPv4 or IPv6 address.
452 :param hit_next_index: hit_next_index of new session.
454 :param opaque_index: opaque_index of new session. (Default value = ~0)
456 :type acl_method: str
457 :type table_index: int
460 :type ip_version: str
463 :type hit_next_index: int
464 :type opaque_index: int
465 :raises ValueError: If the parameter 'direction' has incorrect value.
468 ip4=Classify._build_ip_match,
469 ip6=Classify._build_ip6_match
476 if ip_version == "ip4" or ip_version == "ip6":
477 address = binascii.hexlify(ip_address(unicode(address)).packed)
479 raise ValueError("IP version {ver} is not supported.".format(
482 if direction == "src":
483 match = match_f[ip_version](src_ip=address)
484 elif direction == "dst":
485 match = match_f[ip_version](dst_ip=address)
487 raise ValueError("Direction {dir} is not supported.".format(
490 # Prepend match with l2 ethernet header part
491 match = 14 * '\0' + match
493 # Pad match to match skip_n_vector + match_n_vector size
494 match = match + ((match_n + skip_n) * 16 - len(match)
495 if len(match) < (match_n + skip_n) * 16
498 Classify._classify_add_del_session(
501 table_index=table_index,
502 hit_next_index=hit_next_index,
503 opaque_index=opaque_index,
505 action=action[acl_method]
509 def compute_classify_hex_mask(ip_version, protocol, direction):
510 """Compute classify hex mask for TCP or UDP packet matching.
512 :param ip_version: Version of IP protocol.
513 :param protocol: Type of protocol.
514 :param direction: Traffic direction.
515 :type ip_version: str
518 :returns: Classify hex mask.
520 :raises ValueError: If protocol is not TCP or UDP.
521 :raises ValueError: If direction is not source or destination or
522 source + destination.
524 if protocol in ('TCP', 'UDP'):
525 base_mask = Classify._compute_base_mask(ip_version)
527 if direction == 'source':
528 return base_mask + 'FFFF0000'
529 elif direction == 'destination':
530 return base_mask + '0000FFFF'
531 elif direction == 'source + destination':
532 return base_mask + 'FFFFFFFF'
534 raise ValueError("Invalid direction!")
536 raise ValueError("Invalid protocol!")
539 def compute_classify_hex_value(hex_mask, source_port, destination_port):
540 """Compute classify hex value for TCP or UDP packet matching.
542 :param hex_mask: Classify hex mask.
543 :param source_port: Source TCP/UDP port.
544 :param destination_port: Destination TCP/UDP port.
546 :type source_port: str
547 :type destination_port: str
548 :returns: Classify hex value.
551 source_port_hex = Classify._port_convert(source_port)
552 destination_port_hex = Classify._port_convert(destination_port)
554 return hex_mask[:-8] + source_port_hex + destination_port_hex
557 def _port_convert(port):
558 """Convert port number for classify hex table format.
560 :param port: TCP/UDP port number.
562 :returns: TCP/UDP port number in 4-digit hexadecimal format.
565 return '{0:04x}'.format(int(port))
568 def _compute_base_mask(ip_version):
569 """Compute base classify hex mask based on IP version.
571 :param ip_version: Version of IP protocol.
572 :type ip_version: str
573 :returns: Base hex mask.
576 if ip_version == 'ip4':
578 # base value of classify hex table for IPv4 TCP/UDP ports
579 elif ip_version == 'ip6':
581 # base value of classify hex table for IPv6 TCP/UDP ports
583 raise ValueError("Invalid IP version!")
586 def get_classify_table_data(node, table_index):
587 """Retrieve settings for classify table by ID.
589 :param node: VPP node to retrieve classify data from.
590 :param table_index: Index of a specific classify table.
592 :type table_index: int
593 :returns: Classify table settings.
596 cmd = 'classify_table_info'
597 err_msg = "Failed to get 'classify_table_info' on host {host}".format(
600 table_id=int(table_index)
602 with PapiSocketExecutor(node) as papi_exec:
603 reply = papi_exec.add(cmd, **args).get_reply(err_msg)
607 def get_classify_session_data(node, table_index):
608 """Retrieve settings for all classify sessions in a table.
610 :param node: VPP node to retrieve classify data from.
611 :param table_index: Index of a classify table.
613 :type table_index: int
614 :returns: List of classify session settings.
617 cmd = "classify_session_dump"
619 table_id=int(table_index)
621 with PapiSocketExecutor(node) as papi_exec:
622 details = papi_exec.add(cmd, **args).get_details()
627 def show_classify_tables_verbose(node):
628 """Show classify tables verbose.
630 :param node: Topology node.
632 :returns: Classify tables verbose data.
635 return PapiSocketExecutor.run_cli_cmd(
636 node, "show classify tables verbose")
639 def vpp_log_plugin_acl_settings(node):
640 """Retrieve configured settings from the ACL plugin and write to robot
643 :param node: VPP node.
646 PapiSocketExecutor.dump_and_log(node, ["acl_dump", ])
649 def vpp_log_plugin_acl_interface_assignment(node):
650 """Retrieve interface assignment from the ACL plugin and write to robot
653 :param node: VPP node.
656 PapiSocketExecutor.dump_and_log(node, ["acl_interface_list_dump", ])
659 def set_acl_list_for_interface(node, interface, acl_type, acl_idx=None):
660 """Set the list of input or output ACLs applied to the interface. It
661 unapplies any previously applied ACLs.
663 :param node: VPP node to set ACL on.
664 :param interface: Interface name or sw_if_index.
665 :param acl_type: Type of ACL(s) - input or output.
666 :param acl_idx: Index(ies) of ACLs to be applied on the interface.
668 :type interface: str or int
672 if isinstance(interface, basestring):
673 sw_if_index = Topology.get_interface_sw_index(node, interface)
675 sw_if_index = int(interface)
677 acls = acl_idx if isinstance(acl_idx, list) else list()
679 Classify._acl_interface_set_acl_list(node=node,
680 sw_if_index=sw_if_index,
685 def add_replace_acl_multi_entries(node, acl_idx=None, rules=None, tag=""):
686 """Add a new ACL or replace the existing one. To replace an existing
687 ACL, pass the ID of this ACL.
689 :param node: VPP node to set ACL on.
690 :param acl_idx: ID of ACL. (Optional)
691 :param rules: Required rules. (Optional)
692 :param tag: ACL tag (Optional).
698 reg_ex_src_ip = re.compile(r'(src [0-9a-fA-F.:/\d{1,2}]*)')
699 reg_ex_dst_ip = re.compile(r'(dst [0-9a-fA-F.:/\d{1,2}]*)')
700 reg_ex_sport = re.compile(r'(sport \d{1,5})')
701 reg_ex_dport = re.compile(r'(dport \d{1,5})')
702 reg_ex_proto = re.compile(r'(proto \d{1,5})')
705 for rule in rules.split(", "):
707 acl_rule["is_permit"] = 1 if "permit" in rule else 0
708 acl_rule["is_ipv6"] = 1 if "ipv6" in rule else 0
710 groups = re.search(reg_ex_src_ip, rule)
712 grp = groups.group(1).split(' ')[1].split('/')
713 acl_rule["src_ip_addr"] = ip_address(unicode(grp[0])).packed
714 acl_rule["src_ip_prefix_len"] = int(grp[1])
716 groups = re.search(reg_ex_dst_ip, rule)
718 grp = groups.group(1).split(' ')[1].split('/')
719 acl_rule["dst_ip_addr"] = ip_address(unicode(grp[0])).packed
720 acl_rule["dst_ip_prefix_len"] = int(grp[1])
722 groups = re.search(reg_ex_sport, rule)
724 port = int(groups.group(1).split(' ')[1])
725 acl_rule["srcport_or_icmptype_first"] = port
726 acl_rule["srcport_or_icmptype_last"] = port
728 acl_rule["srcport_or_icmptype_first"] = 0
729 acl_rule["srcport_or_icmptype_last"] = 65535
731 groups = re.search(reg_ex_dport, rule)
733 port = int(groups.group(1).split(' ')[1])
734 acl_rule["dstport_or_icmpcode_first"] = port
735 acl_rule["dstport_or_icmpcode_last"] = port
737 acl_rule["dstport_or_icmpcode_first"] = 0
738 acl_rule["dstport_or_icmpcode_last"] = 65535
740 groups = re.search(reg_ex_proto, rule)
742 proto = int(groups.group(1).split(' ')[1])
743 acl_rule["proto"] = proto
745 acl_rule["proto"] = 0
747 acl_rules.append(acl_rule)
749 Classify._acl_add_replace(
750 node, acl_idx=acl_idx, rules=acl_rules, tag=tag)
753 def add_macip_acl_multi_entries(node, rules=""):
754 """Add a new MACIP ACL.
756 :param node: VPP node to set MACIP ACL on.
757 :param rules: Required MACIP rules.
761 reg_ex_ip = re.compile(r'(ip [0-9a-fA-F.:/\d{1,2}]*)')
762 reg_ex_mac = re.compile(r'(mac \S\S:\S\S:\S\S:\S\S:\S\S:\S\S)')
763 reg_ex_mask = re.compile(r'(mask \S\S:\S\S:\S\S:\S\S:\S\S:\S\S)')
766 for rule in rules.split(", "):
768 acl_rule["is_permit"] = 1 if "permit" in rule else 0
769 acl_rule["is_ipv6"] = 1 if "ipv6" in rule else 0
771 groups = re.search(reg_ex_mac, rule)
773 mac = groups.group(1).split(' ')[1].replace(':', '')
774 acl_rule["src_mac"] = binascii.unhexlify(unicode(mac))
776 groups = re.search(reg_ex_mask, rule)
778 mask = groups.group(1).split(' ')[1].replace(':', '')
779 acl_rule["src_mac_mask"] = binascii.unhexlify(unicode(mask))
781 groups = re.search(reg_ex_ip, rule)
783 grp = groups.group(1).split(' ')[1].split('/')
784 acl_rule["src_ip_addr"] = ip_address(unicode(grp[0])).packed
785 acl_rule["src_ip_prefix_len"] = int(grp[1])
787 acl_rules.append(acl_rule)
789 Classify._macip_acl_add(node=node, rules=acl_rules)
792 def vpp_log_macip_acl_settings(node):
793 """Retrieve configured MACIP settings from the ACL plugin and write to
796 :param node: VPP node.
799 PapiSocketExecutor.dump_and_log(node, ["macip_acl_dump", ])
802 def add_del_macip_acl_interface(node, interface, action, acl_idx):
803 """Apply/un-apply the MACIP ACL to/from a given interface.
805 :param node: VPP node to set MACIP ACL on.
806 :param interface: Interface name or sw_if_index.
807 :param action: Required action - add or del.
808 :param acl_idx: ACL index to be applied on the interface.
810 :type interface: str or int
812 :type acl_idx: str or int
813 :raises RuntimeError: If unable to set MACIP ACL for the interface.
815 if isinstance(interface, basestring):
816 sw_if_index = Topology.get_interface_sw_index(node, interface)
818 sw_if_index = interface
820 is_add = 1 if action == "add" else 0
822 cmd = 'macip_acl_interface_add_del'
823 err_msg = "Failed to get 'macip_acl_interface' on host {host}".format(
827 sw_if_index=int(sw_if_index),
828 acl_index=int(acl_idx)
830 with PapiSocketExecutor(node) as papi_exec:
831 papi_exec.add(cmd, **args).get_reply(err_msg)
834 def vpp_log_macip_acl_interface_assignment(node):
835 """Get interface list and associated MACIP ACLs and write to robot log.
837 :param node: VPP node.
840 cmd = 'macip_acl_interface_get'
841 err_msg = "Failed to get 'macip_acl_interface' on host {host}".format(
843 with PapiSocketExecutor(node) as papi_exec:
844 reply = papi_exec.add(cmd).get_reply(err_msg)