1 # Copyright (c) 2017 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."""
16 from robot.api import logger
18 from resources.libraries.python.VatExecutor import VatExecutor, VatTerminal
19 from resources.libraries.python.topology import Topology
22 class Classify(object):
23 """Classify utilities."""
26 def vpp_creates_classify_table_l3(node, ip_version, direction):
27 """Create classify table for IP address filtering.
29 :param node: VPP node to create classify table.
30 :param ip_version: Version of IP protocol.
31 :param direction: Direction of traffic - src/dst.
35 :returns (table_index, skip_n, match_n)
36 table_index: Classify table index.
37 skip_n: Number of skip vectors.
38 match_n: Number of match vectors.
39 :rtype: tuple(int, int, int)
40 :raises RuntimeError: If VPP can't create table.
43 output = VatExecutor.cmd_from_template(node, "classify_add_table.vat",
44 ip_version=ip_version,
47 if output[0]["retval"] == 0:
48 table_index = output[0]["new_table_index"]
49 skip_n = output[0]["skip_n_vectors"]
50 match_n = output[0]["match_n_vectors"]
51 logger.trace('Classify table with table_index {} created on node {}'
52 .format(table_index, node['host']))
54 raise RuntimeError('Unable to create classify table on node {}'
55 .format(node['host']))
57 return table_index, skip_n, match_n
60 def vpp_creates_classify_table_l2(node, direction):
61 """Create classify table for MAC address filtering.
63 :param node: VPP node to create classify table.
64 :param direction: Direction of traffic - src/dst.
67 :returns (table_index, skip_n, match_n)
68 table_index: Classify table index.
69 skip_n: Number of skip vectors.
70 match_n: Number of match vectors.
71 :rtype: tuple(int, int, int)
72 :raises RuntimeError: If VPP can't create table.
74 output = VatExecutor.cmd_from_template(node,
75 "classify_add_table_l2.vat",
78 if output[0]["retval"] == 0:
79 table_index = output[0]["new_table_index"]
80 skip_n = output[0]["skip_n_vectors"]
81 match_n = output[0]["match_n_vectors"]
82 logger.trace('Classify table with table_index {} created on node {}'
83 .format(table_index, node['host']))
85 raise RuntimeError('Unable to create classify table on node {}'
86 .format(node['host']))
88 return table_index, skip_n, match_n
91 def vpp_creates_classify_table_hex(node, hex_mask):
92 """Create classify table with hex mask.
94 :param node: VPP node to create classify table based on hex mask.
95 :param hex_mask: Classify hex mask.
98 :returns (table_index, skip_n, match_n)
99 table_index: Classify table index.
100 skip_n: Number of skip vectors.
101 match_n: Number of match vectors.
102 :rtype: tuple(int, int, int)
103 :raises RuntimeError: If VPP can't create table.
105 output = VatExecutor.cmd_from_template(node,
106 "classify_add_table_hex.vat",
109 if output[0]["retval"] == 0:
110 table_index = output[0]["new_table_index"]
111 skip_n = output[0]["skip_n_vectors"]
112 match_n = output[0]["match_n_vectors"]
113 logger.trace('Classify table with table_index {} created on node {}'
114 .format(table_index, node['host']))
116 raise RuntimeError('Unable to create classify table on node {}'
117 .format(node['host']))
119 return table_index, skip_n, match_n
122 def vpp_configures_classify_session_l3(node, acl_method, table_index,
123 skip_n, match_n, ip_version,
125 """Configuration of classify session for IP address filtering.
127 :param node: VPP node to setup classify session.
128 :param acl_method: ACL method - deny/permit.
129 :param table_index: Classify table index.
130 :param skip_n: Number of skip vectors based on mask.
131 :param match_n: Number of match vectors based on mask.
132 :param ip_version: Version of IP protocol.
133 :param direction: Direction of traffic - src/dst.
134 :param address: IPv4 or IPv6 address.
136 :type acl_method: str
137 :type table_index: int
140 :type ip_version: str
144 with VatTerminal(node) as vat:
145 vat.vat_terminal_exec_cmd_from_template("classify_add_session.vat",
146 acl_method=acl_method,
147 table_index=table_index,
150 ip_version=ip_version,
155 def vpp_configures_classify_session_l2(node, acl_method, table_index,
156 skip_n, match_n, direction, address):
157 """Configuration of classify session for MAC address filtering.
159 :param node: VPP node to setup classify session.
160 :param acl_method: ACL method - deny/permit.
161 :param table_index: Classify table index.
162 :param skip_n: Number of skip vectors based on mask.
163 :param match_n: Number of match vectors based on mask.
164 :param direction: Direction of traffic - src/dst.
165 :param address: IPv4 or IPv6 address.
167 :type acl_method: str
168 :type table_index: int
174 with VatTerminal(node) as vat:
175 vat.vat_terminal_exec_cmd_from_template(
176 "classify_add_session_l2.vat",
177 acl_method=acl_method,
178 table_index=table_index,
185 def vpp_configures_classify_session_hex(node, acl_method, table_index,
186 skip_n, match_n, hex_value):
187 """Configuration of classify session with hex value.
189 :param node: VPP node to setup classify session.
190 :param acl_method: ACL method - deny/permit.
191 :param table_index: Classify table index.
192 :param skip_n: Number of skip vectors based on mask.
193 :param match_n: Number of match vectors based on mask.
194 :param hex_value: Classify hex value.
196 :type acl_method: str
197 :type table_index: int
202 with VatTerminal(node) as vat:
203 vat.vat_terminal_exec_cmd_from_template(
204 "classify_add_session_hex.vat",
205 acl_method=acl_method,
206 table_index=table_index,
212 def vpp_configures_classify_session_generic(node, session_type, table_index,
213 skip_n, match_n, match,
215 """Configuration of classify session.
217 :param node: VPP node to setup classify session.
218 :param session_type: Session type - hit-next, l2-hit-next, acl-hit-next
219 or policer-hit-next, and their respective parameters.
220 :param table_index: Classify table index.
221 :param skip_n: Number of skip vectors based on mask.
222 :param match_n: Number of match vectors based on mask.
223 :param match: Match value - l2, l3, l4 or hex, and their
224 respective parameters.
225 :param match2: Additional match values, to avoid using overly long
226 variables in RobotFramework.
228 :type session_type: str
229 :type table_index: int
236 match = ' '.join((match, match2))
238 with VatTerminal(node) as vat:
239 vat.vat_terminal_exec_cmd_from_template(
240 "classify_add_session_generic.vat",
242 table_index=table_index,
249 def compute_classify_hex_mask(ip_version, protocol, direction):
250 """Compute classify hex mask for TCP or UDP packet matching.
252 :param ip_version: Version of IP protocol.
253 :param protocol: Type of protocol.
254 :param direction: Traffic direction.
255 :type ip_version: str
258 :returns: Classify hex mask.
260 :raises ValueError: If protocol is not TCP or UDP.
261 :raises ValueError: If direction is not source or destination or
262 source + destination.
264 if protocol == 'TCP' or protocol == 'UDP':
265 base_mask = Classify._compute_base_mask(ip_version)
267 if direction == 'source':
268 return base_mask + 'FFFF0000'
269 elif direction == 'destination':
270 return base_mask + '0000FFFF'
271 elif direction == 'source + destination':
272 return base_mask + 'FFFFFFFF'
274 raise ValueError("Invalid direction!")
276 raise ValueError("Invalid protocol!")
279 def compute_classify_hex_value(hex_mask, source_port, destination_port):
280 """Compute classify hex value for TCP or UDP packet matching.
282 :param hex_mask: Classify hex mask.
283 :param source_port: Source TCP/UDP port.
284 :param destination_port: Destination TCP/UDP port.
286 :type source_port: str
287 :type destination_port: str
288 :returns: Classify hex value.
291 source_port_hex = Classify._port_convert(source_port)
292 destination_port_hex = Classify._port_convert(destination_port)
294 return hex_mask[:-8] + source_port_hex + destination_port_hex
297 def _port_convert(port):
298 """Convert port number for classify hex table format.
300 :param port: TCP/UDP port number.
302 :returns: TCP/UDP port number in 4-digit hexadecimal format.
305 return '{0:04x}'.format(int(port))
308 def _compute_base_mask(ip_version):
309 """Compute base classify hex mask based on IP version.
311 :param ip_version: Version of IP protocol.
312 :type ip_version: str
313 :return: Base hex mask.
316 if ip_version == 'ip4':
318 # base value of classify hex table for IPv4 TCP/UDP ports
319 elif ip_version == 'ip6':
321 # base value of classify hex table for IPv6 TCP/UDP ports
323 raise ValueError("Invalid IP version!")
326 def get_classify_table_data(node, table_index):
327 """Retrieve settings for classify table by ID.
329 :param node: VPP node to retrieve classify data from.
330 :param table_index: Index of a specific classify table.
332 :type table_index: int
333 :returns: Classify table settings.
336 with VatTerminal(node) as vat:
337 data = vat.vat_terminal_exec_cmd_from_template(
338 "classify_table_info.vat",
344 def get_classify_session_data(node, table_index, session_index=None):
345 """Retrieve settings for all classify sessions in a table,
346 or for a specific classify session.
348 :param node: VPP node to retrieve classify data from.
349 :param table_index: Index of a classify table.
350 :param session_index: Index of a specific classify session. (Optional)
352 :type table_index: int
353 :type session_index: int
354 :returns: List of classify session settings, or a dictionary of settings
355 for a specific classify session.
358 with VatTerminal(node) as vat:
359 data = vat.vat_terminal_exec_cmd_from_template(
360 "classify_session_dump.vat",
363 if session_index is not None:
364 return data[0][session_index]
369 def vpp_log_plugin_acl_settings(node):
370 """Retrieve configured settings from the ACL plugin
371 and write to robot log.
373 :param node: VPP node.
377 VatExecutor.cmd_from_template(
378 node, "acl_plugin/acl_dump.vat")
379 except (ValueError, RuntimeError):
380 # Fails to parse JSON data in response, but it is still logged
384 def vpp_log_plugin_acl_interface_assignment(node):
385 """Retrieve interface assignment from the ACL plugin
386 and write to robot log.
388 :param node: VPP node.
392 VatExecutor.cmd_from_template(
393 node, "acl_plugin/acl_interface_dump.vat", json_out=False)
395 # Fails to parse response, but it is still logged
399 def set_acl_list_for_interface(node, interface, acl_type, acl_idx=None):
400 """Set the list of input or output ACLs applied to the interface. It
401 unapplies any previously applied ACLs.
403 :param node: VPP node to set ACL on.
404 :param interface: Interface name or sw_if_index.
405 :param acl_type: Type of ACL(s) - input or output.
406 :param acl_idx: Index(ies) of ACLs to be applied on the interface.
408 :type interface: str or int
411 :raises RuntimeError: If unable to set ACL list for the interface.
413 if isinstance(interface, basestring):
414 sw_if_index = Topology.get_interface_sw_index(node, interface)
416 sw_if_index = interface
418 acl_list = acl_type + ' ' + ' '.join(str(idx) for idx in acl_idx) \
419 if acl_idx else acl_type
422 with VatTerminal(node, json_param=False) as vat:
423 vat.vat_terminal_exec_cmd_from_template(
424 "acl_plugin/acl_interface_set_acl_list.vat",
425 interface=sw_if_index, acl_list=acl_list)
427 raise RuntimeError("Setting of ACL list for interface {0} failed "
428 "on node {1}".format(interface, node['host']))
431 def add_replace_acl(node, acl_idx=None, ip_ver="ipv4", action="permit",
432 src=None, dst=None, sport=None, dport=None, proto=None,
433 tcpflg_val=None, tcpflg_mask=None):
434 """Add a new ACL or replace the existing one. To replace an existing
435 ACL, pass the ID of this ACL.
437 :param node: VPP node to set ACL on.
438 :param acl_idx: ID of ACL. (Optional)
439 :param ip_ver: IP version. (Optional)
440 :param action: ACL action. (Optional)
441 :param src: Source IP in format IP/plen. (Optional)
442 :param dst: Destination IP in format IP/plen. (Optional)
443 :param sport: Source port or ICMP4/6 type - range format X-Y allowed.
445 :param dport: Destination port or ICMP4/6 code - range format X-Y
447 :param proto: L4 protocol (http://www.iana.org/assignments/protocol-
448 numbers/protocol-numbers.xhtml). (Optional)
449 :param tcpflg_val: TCP flags value. (Optional)
450 :param tcpflg_mask: TCP flags mask. (Optional)
457 :type sport: str or int
458 :type dport: str or int
460 :type tcpflg_val: int
461 :type tcpflg_mask: int
462 :raises RuntimeError: If unable to add or replace ACL.
464 acl_idx = '{0}'.format(acl_idx) if acl_idx else ''
466 src = 'src {0}'.format(src) if src else ''
468 dst = 'dst {0}'.format(dst) if dst else ''
470 sport = 'sport {0}'.format(sport) if sport else ''
472 dport = 'dport {0}'.format(dport) if dport else ''
474 proto = 'proto {0}'.format(proto) if proto else ''
476 tcpflags = 'tcpflags {0} {1}'.format(tcpflg_val, tcpflg_mask) \
477 if tcpflg_val and tcpflg_mask else ''
480 with VatTerminal(node, json_param=False) as vat:
481 vat.vat_terminal_exec_cmd_from_template(
482 "acl_plugin/acl_add_replace.vat", acl_idx=acl_idx,
483 ip_ver=ip_ver, action=action, src=src, dst=dst, sport=sport,
484 dport=dport, proto=proto, tcpflags=tcpflags)
486 raise RuntimeError("Adding or replacing of ACL failed on "
487 "node {0}".format(node['host']))
490 def add_replace_acl_multi_entries(node, acl_idx=None, rules=None):
491 """Add a new ACL or replace the existing one. To replace an existing
492 ACL, pass the ID of this ACL.
494 :param node: VPP node to set ACL on.
495 :param acl_idx: ID of ACL. (Optional)
496 :param rules: Required rules. (Optional)
500 :raises RuntimeError: If unable to add or replace ACL.
502 acl_idx = '{0}'.format(acl_idx) if acl_idx else ''
504 rules = '{0}'.format(rules) if rules else ''
507 with VatTerminal(node, json_param=False) as vat:
508 vat.vat_terminal_exec_cmd_from_template(
509 "acl_plugin/acl_add_replace.vat", acl_idx=acl_idx,
510 ip_ver=rules, action='', src='', dst='', sport='',
511 dport='', proto='', tcpflags='')
513 raise RuntimeError("Adding or replacing of ACL failed on "
514 "node {0}".format(node['host']))
517 def delete_acl(node, idx):
518 """Delete required ACL.
520 :param node: VPP node to delete ACL on.
521 :param idx: Index of ACL to be deleted.
523 :type idx: int or str
524 :raises RuntimeError: If unable to delete ACL.
527 with VatTerminal(node, json_param=False) as vat:
528 vat.vat_terminal_exec_cmd_from_template(
529 "acl_plugin/acl_delete.vat", idx=idx)
531 raise RuntimeError("Deletion of ACL failed on node {0}".
532 format(node['host']))
535 def cli_show_acl(node, acl_idx=None):
538 :param node: VPP node to show ACL on.
539 :param acl_idx: Index of ACL to be shown.
541 :type acl_idx: int or str
542 :raises RuntimeError: If unable to delete ACL.
544 acl_idx = '{0}'.format(acl_idx) if acl_idx else ''
547 with VatTerminal(node, json_param=False) as vat:
548 vat.vat_terminal_exec_cmd_from_template(
549 "acl_plugin/show_acl.vat", idx=acl_idx)
551 raise RuntimeError("Failed to show ACL on node {0}".
552 format(node['host']))
555 def add_macip_acl(node, ip_ver="ipv4", action="permit", src_ip=None,
556 src_mac=None, src_mac_mask=None):
557 """Add a new MACIP ACL.
559 :param node: VPP node to set MACIP ACL on.
560 :param ip_ver: IP version. (Optional)
561 :param action: ACL action. (Optional)
562 :param src_ip: Source IP in format IP/plen. (Optional)
563 :param src_mac: Source MAC address in format with colons. (Optional)
564 :param src_mac_mask: Source MAC address mask in format with colons.
565 00:00:00:00:00:00 is a wildcard mask. (Optional)
571 :type src_mac_mask: str
572 :raises RuntimeError: If unable to add MACIP ACL.
574 src_ip = 'ip {0}'.format(src_ip) if src_ip else ''
576 src_mac = 'mac {0}'.format(src_mac) if src_mac else ''
578 src_mac_mask = 'mask {0}'.format(src_mac_mask) if src_mac_mask else ''
581 with VatTerminal(node, json_param=False) as vat:
582 vat.vat_terminal_exec_cmd_from_template(
583 "acl_plugin/macip_acl_add.vat", ip_ver=ip_ver,
584 action=action, src_ip=src_ip, src_mac=src_mac,
585 src_mac_mask=src_mac_mask)
587 raise RuntimeError("Adding of MACIP ACL failed on node {0}".
588 format(node['host']))
591 def add_macip_acl_multi_entries(node, rules=None):
592 """Add a new MACIP ACL.
594 :param node: VPP node to set MACIP ACL on.
595 :param rules: Required MACIP rules. (Optional)
598 :raises RuntimeError: If unable to add MACIP ACL.
600 rules = '{0}'.format(rules) if rules else ''
603 with VatTerminal(node, json_param=False) as vat:
604 vat.vat_terminal_exec_cmd_from_template(
605 "acl_plugin/macip_acl_add.vat", ip_ver=rules, action='',
606 src_ip='', src_mac='', src_mac_mask='')
608 raise RuntimeError("Adding of MACIP ACL failed on node {0}".
609 format(node['host']))
612 def delete_macip_acl(node, idx):
613 """Delete required MACIP ACL.
615 :param node: VPP node to delete MACIP ACL on.
616 :param idx: Index of ACL to be deleted.
618 :type idx: int or str
619 :raises RuntimeError: If unable to delete MACIP ACL.
622 with VatTerminal(node, json_param=False) as vat:
623 vat.vat_terminal_exec_cmd_from_template(
624 "acl_plugin/macip_acl_delete.vat", idx=idx)
626 raise RuntimeError("Deletion of MACIP ACL failed on node {0}".
627 format(node['host']))
630 def vpp_log_macip_acl_settings(node):
631 """Retrieve configured MACIP settings from the ACL plugin
632 and write to robot log.
634 :param node: VPP node.
638 VatExecutor.cmd_from_template(
639 node, "acl_plugin/macip_acl_dump.vat")
640 except (ValueError, RuntimeError):
641 # Fails to parse JSON data in response, but it is still logged
645 def add_del_macip_acl_interface(node, interface, action, acl_idx):
646 """Apply/un-apply the MACIP ACL to/from a given interface.
648 :param node: VPP node to set MACIP ACL on.
649 :param interface: Interface name or sw_if_index.
650 :param action: Required action - add or del.
651 :param acl_idx: ACL index to be applied on the interface.
653 :type interface: str or int
655 :type acl_idx: str or int
656 :raises RuntimeError: If unable to set MACIP ACL for the interface.
658 if isinstance(interface, basestring):
659 sw_if_index = Topology.get_interface_sw_index(node, interface)
661 sw_if_index = interface
664 with VatTerminal(node, json_param=False) as vat:
665 vat.vat_terminal_exec_cmd_from_template(
666 "acl_plugin/macip_acl_interface_add_del.vat",
667 sw_if_index=sw_if_index, action=action, acl_idx=acl_idx)
669 raise RuntimeError("Setting of MACIP ACL index for interface {0} "
670 "failed on node {1}".
671 format(interface, node['host']))
674 def vpp_log_macip_acl_interface_assignment(node):
675 """Get interface list and associated MACIP ACLs and write to robot log.
677 :param node: VPP node.
681 VatExecutor.cmd_from_template(
682 node, "acl_plugin/macip_acl_interface_get.vat", json_out=False)
684 # Fails to parse response, but it is still logged