| #!/usr/bin/env python3 | 
 |  | 
 | r""" | 
 | Network generic functions. | 
 |  | 
 | """ | 
 |  | 
 | import collections | 
 | import ipaddress | 
 | import json | 
 | import re | 
 | import socket | 
 | import subprocess | 
 |  | 
 | import bmc_ssh_utils as bsu | 
 | import gen_cmd as gc | 
 | import gen_misc as gm | 
 | import gen_print as gp | 
 | import var_funcs as vf | 
 | from robot.libraries.BuiltIn import BuiltIn | 
 |  | 
 | ip_regex = r"\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}" | 
 |  | 
 |  | 
 | def get_running_system_ip(): | 
 |     r""" | 
 |     Get the IP address of server from which robot code is running. | 
 |  | 
 |     """ | 
 |  | 
 |     ip_list = list() | 
 |     stdout = subprocess.check_output(["hostname", "--all-fqdns"], shell=True) | 
 |     host_fqdns = stdout.decode("utf-8").strip() | 
 |     ip_address = socket.gethostbyname(str(host_fqdns)) | 
 |     ip_list.append(ip_address) | 
 |  | 
 |     return ip_list | 
 |  | 
 |  | 
 | def netmask_prefix_length(netmask): | 
 |     r""" | 
 |     Return the netmask prefix length. | 
 |  | 
 |     Description of argument(s): | 
 |     netmask     Netmask value (e.g. "255.255.0.0", "255.255.255.0", | 
 |                                     "255.252.0.0", etc.). | 
 |     """ | 
 |  | 
 |     # IP address netmask format: '0.0.0.0/255.255.252.0' | 
 |     return ipaddress.ip_network("0.0.0.0/" + netmask).prefixlen | 
 |  | 
 |  | 
 | def get_netmask_address(prefix_len): | 
 |     r""" | 
 |     Return the netmask address. | 
 |  | 
 |     Description of argument(s): | 
 |     prefix_len     Prefix length value (e.g. "24", "23", "22", etc.). | 
 |     """ | 
 |  | 
 |     # IP address netmask format: '0.0.0.0/24' | 
 |     return ipaddress.ip_network("0.0.0.0/" + prefix_len).netmask | 
 |  | 
 |  | 
 | def parse_nping_output(output): | 
 |     r""" | 
 |     Parse the output from the nping command and return as a dictionary. | 
 |  | 
 |     Example of output value: | 
 |  | 
 |     Starting Nping 0.6.47 ( http://nmap.org/nping ) at 2019-08-07 22:05 IST | 
 |     SENT (0.0181s) TCP Source IP:37577 > | 
 |       Destination IP:80 S ttl=64 id=39113 iplen=40  seq=629782493 win=1480 | 
 |     SENT (0.2189s) TCP Source IP:37577 > | 
 |       Destination IP:80 S ttl=64 id=39113 iplen=40  seq=629782493 win=1480 | 
 |     RCVD (0.4120s) TCP Destination IP:80 > | 
 |       Source IP:37577 SA ttl=49 id=0 iplen=44  seq=1078301364 win=5840 <mss 1380> | 
 |     Max rtt: 193.010ms | Min rtt: 193.010ms | Avg rtt: 193.010ms | 
 |     Raw packets sent: 2 (80B) | Rcvd: 1 (46B) | Lost: 1 (50.00%) | 
 |     TCP connection attempts: 5 | Successful connections: 5 | Failed: 0 (0.00%) | 
 |     Nping done: 1 IP address pinged in 0.43 seconds | 
 |  | 
 |     Example of data returned by this function: | 
 |  | 
 |     nping_result: | 
 |       [max_rtt]:                 193.010ms | 
 |       [min_rtt]:                 193.010ms | 
 |       [avg_rtt]:                 193.010ms | 
 |       [raw_packets_sent]:        2 (80B) | 
 |       [rcvd]:                    1 (46B) | 
 |       [lost]:                    1 (50.00%) | 
 |       [percent_lost]:            50.00 | 
 |       [tcp_connection_attempts]: 5 | 
 |       [successful_connections]:  5 | 
 |       [failed]:                  0 (0.00%) | 
 |       [percent_failed]:          0.00 | 
 |  | 
 |     Description of argument(s): | 
 |     output                          The output obtained by running an nping | 
 |                                     command. | 
 |     """ | 
 |  | 
 |     lines = output.split("\n") | 
 |     # Obtain only the lines of interest. | 
 |     lines = list( | 
 |         filter( | 
 |             lambda x: re.match(r"(Max rtt|Raw packets|TCP connection)", x), | 
 |             lines, | 
 |         ) | 
 |     ) | 
 |  | 
 |     key_value_list = [] | 
 |     for line in lines: | 
 |         key_value_list += line.split("|") | 
 |     nping_result = vf.key_value_list_to_dict(key_value_list) | 
 |     # Extract percent_lost/percent_failed value from lost/failed field. | 
 |     if "lost" in nping_result: | 
 |         nping_result["percent_lost"] = float( | 
 |             nping_result["lost"].split(" ")[-1].strip("()%") | 
 |         ) | 
 |     else: | 
 |         nping_result["percent_failed"] = float( | 
 |             nping_result["failed"].split(" ")[-1].strip("()%") | 
 |         ) | 
 |     return nping_result | 
 |  | 
 |  | 
 | openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}") | 
 |  | 
 |  | 
 | def nping(host=openbmc_host, parse_results=1, **options): | 
 |     r""" | 
 |     Run the nping command and return the results either as a string or as a dictionary. | 
 |  | 
 |     Do a 'man nping' for a complete description of the nping utility. | 
 |  | 
 |     Note that any valid nping argument may be specified as a function argument. | 
 |  | 
 |     Example robot code: | 
 |  | 
 |     ${nping_result}=  Nping  delay=${delay}  count=${count}  icmp=${None}  icmp-type=echo | 
 |     Rprint Vars  nping_result | 
 |  | 
 |     Resulting output: | 
 |  | 
 |     nping_result: | 
 |       [max_rtt]:                                      0.534ms | 
 |       [min_rtt]:                                      0.441ms | 
 |       [avg_rtt]:                                      0.487ms | 
 |       [raw_packets_sent]:                             4 (112B) | 
 |       [rcvd]:                                         2 (92B) | 
 |       [lost]:                                         2 (50.00%) | 
 |       [percent_lost]:                                 50.0 | 
 |  | 
 |     Description of argument(s): | 
 |     host                            The host name or IP of the target of the | 
 |                                     nping command. | 
 |     parse_results                   1 or True indicates that this function | 
 |                                     should parse the nping results and return | 
 |                                     a dictionary rather than the raw nping | 
 |                                     output.  See the parse_nping_output() | 
 |                                     function for details on the dictionary | 
 |                                     structure. | 
 |     options                         Zero or more options accepted by the nping | 
 |                                     command.  Do a 'man nping' for details. | 
 |     """ | 
 |  | 
 |     command_string = gc.create_command_string("nping", host, options) | 
 |     rc, output = gc.shell_cmd(command_string, print_output=0, ignore_err=0) | 
 |     if parse_results: | 
 |         return parse_nping_output(output) | 
 |  | 
 |     return output | 
 |  | 
 |  | 
 | def get_channel_config(): | 
 |     r""" | 
 |     Get the channel config data and return as a dictionary. | 
 |  | 
 |     Example: | 
 |     channel_config = get_channel_config() | 
 |     print_vars(channel_config) | 
 |  | 
 |     channel_config: | 
 |       [0]: | 
 |         [name]:                  IPMB | 
 |         [is_valid]:              True | 
 |         [active_sessions]:       0 | 
 |         [channel_info]: | 
 |           [medium_type]:         ipmb | 
 |           [protocol_type]:       ipmb-1.0 | 
 |           [session_supported]:   session-less | 
 |           [is_ipmi]:             True | 
 |       [1]: | 
 |         [name]:                  eth0 | 
 |         [is_valid]:              True | 
 |         [active_sessions]:       0 | 
 |         [channel_info]: | 
 |           [medium_type]:         other-lan | 
 |           [protocol_type]:       ipmb-1.0 | 
 |           [session_supported]:   multi-session | 
 |           [is_ipmi]:             True | 
 |       [2]: | 
 |         [name]:                  eth1 | 
 |         [is_valid]:              True | 
 |         [active_sessions]:       0 | 
 |         [channel_info]: | 
 |           [medium_type]:         lan-802.3 | 
 |           [protocol_type]:       ipmb-1.0 | 
 |           [session_supported]:   multi-session | 
 |           [is_ipmi]:             True | 
 |     (etc.) | 
 |     """ | 
 |  | 
 |     stdout, stderr, rc = bsu.bmc_execute_command( | 
 |         "cat /usr/share/ipmi-providers/channel_config.json" | 
 |     ) | 
 |     return json.loads(stdout) | 
 |  | 
 |  | 
 | def get_active_channel_config(): | 
 |     r""" | 
 |     Channel configs which medium_type are 'other-lan' or 'lan-802.3' returned by | 
 |      this function. | 
 |     """ | 
 |  | 
 |     return vf.filter_struct( | 
 |         get_channel_config(), | 
 |         "[('medium_type', 'other-lan|lan-802.3')]", | 
 |         regex=1, | 
 |     ) | 
 |  | 
 |  | 
 | def get_channel_access_config(file_name): | 
 |     r""" | 
 |     Get the channel access config data and return as a dictionary. | 
 |  | 
 |     Description of argument: | 
 |     file_name     File name for channel access settings (e.g. '/run/ipmi/channel_access_volatile.json', | 
 |                  '/var/lib/ipmi/channel_access_nv.json'.). | 
 |  | 
 |     Example: | 
 |  | 
 |     channel_access_config =  get_channel_access_config() | 
 |     print_vars(channel_access_config) | 
 |  | 
 |     channel_access_config: | 
 |         [1]: | 
 |             [priv_limit]:                                 priv-admin | 
 |             [per_msg_auth_disabled]:                      False | 
 |             [access_mode]:                                always_available | 
 |             [alerting_disabled]:                          False | 
 |             [user_auth_disabled]:                         False | 
 |         [2]: | 
 |             [priv_limit]:                                 priv-admin | 
 |             [per_msg_auth_disabled]:                      False | 
 |             [access_mode]:                                always_available | 
 |             [alerting_disabled]:                          False | 
 |             [user_auth_disabled]:                         False | 
 |     """ | 
 |     stdout, stderr, rc = bsu.bmc_execute_command("cat " + file_name) | 
 |  | 
 |     return json.loads(stdout) |