1#!/usr/bin/env python3
2
3r"""
4Network generic functions.
5
6"""
7
8import gen_print as gp
9import gen_cmd as gc
10import gen_misc as gm
11import var_funcs as vf
12import collections
13import re
14import ipaddress
15import subprocess
16import socket
17from robot.libraries.BuiltIn import BuiltIn
18import json
19import bmc_ssh_utils as bsu
20
21ip_regex = r"\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}"
22
23
24def get_running_system_ip():
25    r"""
26    Get the IP address of server from which robot code is running.
27
28    """
29
30    ip_list = list()
31    stdout = subprocess.check_output(['hostname', '--all-fqdns'], shell=True)
32    host_fqdns = stdout.decode("utf-8").strip()
33    ip_address = socket.gethostbyname(str(host_fqdns))
34    ip_list.append(ip_address)
35
36    return ip_list
37
38
39def netmask_prefix_length(netmask):
40    r"""
41    Return the netmask prefix length.
42
43    Description of argument(s):
44    netmask     Netmask value (e.g. "255.255.0.0", "255.255.255.0",
45                                    "255.252.0.0", etc.).
46    """
47
48    # IP address netmask format: '0.0.0.0/255.255.252.0'
49    return ipaddress.ip_network('0.0.0.0/' + netmask).prefixlen
50
51
52def get_netmask_address(prefix_len):
53    r"""
54    Return the netmask address.
55
56    Description of argument(s):
57    prefix_len     Prefix length value (e.g. "24", "23", "22", etc.).
58    """
59
60    # IP address netmask format: '0.0.0.0/24'
61    return ipaddress.ip_network('0.0.0.0/' + prefix_len).netmask
62
63
64def parse_nping_output(output):
65    r"""
66    Parse the output from the nping command and return as a dictionary.
67
68    Example of output value:
69
70    Starting Nping 0.6.47 ( http://nmap.org/nping ) at 2019-08-07 22:05 IST
71    SENT (0.0181s) TCP Source IP:37577 >
72      Destination IP:80 S ttl=64 id=39113 iplen=40  seq=629782493 win=1480
73    SENT (0.2189s) TCP Source IP:37577 >
74      Destination IP:80 S ttl=64 id=39113 iplen=40  seq=629782493 win=1480
75    RCVD (0.4120s) TCP Destination IP:80 >
76      Source IP:37577 SA ttl=49 id=0 iplen=44  seq=1078301364 win=5840 <mss 1380>
77    Max rtt: 193.010ms | Min rtt: 193.010ms | Avg rtt: 193.010ms
78    Raw packets sent: 2 (80B) | Rcvd: 1 (46B) | Lost: 1 (50.00%)
79    TCP connection attempts: 5 | Successful connections: 5 | Failed: 0 (0.00%)
80    Nping done: 1 IP address pinged in 0.43 seconds
81
82    Example of data returned by this function:
83
84    nping_result:
85      [max_rtt]:                 193.010ms
86      [min_rtt]:                 193.010ms
87      [avg_rtt]:                 193.010ms
88      [raw_packets_sent]:        2 (80B)
89      [rcvd]:                    1 (46B)
90      [lost]:                    1 (50.00%)
91      [percent_lost]:            50.00
92      [tcp_connection_attempts]: 5
93      [successful_connections]:  5
94      [failed]:                  0 (0.00%)
95      [percent_failed]:          0.00
96
97    Description of argument(s):
98    output                          The output obtained by running an nping
99                                    command.
100    """
101
102    lines = output.split("\n")
103    # Obtain only the lines of interest.
104    lines = list(filter(lambda x: re.match(r"(Max rtt|Raw packets|TCP connection)", x),
105                        lines))
106
107    key_value_list = []
108    for line in lines:
109        key_value_list += line.split("|")
110    nping_result = vf.key_value_list_to_dict(key_value_list)
111    # Extract percent_lost/percent_failed value from lost/failed field.
112    if 'lost' in nping_result:
113        nping_result['percent_lost'] = \
114            float(nping_result['lost'].split(" ")[-1].strip("()%"))
115    else:
116        nping_result['percent_failed'] = \
117            float(nping_result['failed'].split(" ")[-1].strip("()%"))
118    return nping_result
119
120
121openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
122
123
124def nping(host=openbmc_host, parse_results=1, **options):
125    r"""
126    Run the nping command and return the results either as a string or as a dictionary.
127
128    Do a 'man nping' for a complete description of the nping utility.
129
130    Note that any valid nping argument may be specified as a function argument.
131
132    Example robot code:
133
134    ${nping_result}=  Nping  delay=${delay}  count=${count}  icmp=${None}  icmp-type=echo
135    Rprint Vars  nping_result
136
137    Resulting output:
138
139    nping_result:
140      [max_rtt]:                                      0.534ms
141      [min_rtt]:                                      0.441ms
142      [avg_rtt]:                                      0.487ms
143      [raw_packets_sent]:                             4 (112B)
144      [rcvd]:                                         2 (92B)
145      [lost]:                                         2 (50.00%)
146      [percent_lost]:                                 50.0
147
148    Description of argument(s):
149    host                            The host name or IP of the target of the
150                                    nping command.
151    parse_results                   1 or True indicates that this function
152                                    should parse the nping results and return
153                                    a dictionary rather than the raw nping
154                                    output.  See the parse_nping_output()
155                                    function for details on the dictionary
156                                    structure.
157    options                         Zero or more options accepted by the nping
158                                    command.  Do a 'man nping' for details.
159    """
160
161    command_string = gc.create_command_string('nping', host, options)
162    rc, output = gc.shell_cmd(command_string, print_output=0, ignore_err=0)
163    if parse_results:
164        return parse_nping_output(output)
165
166    return output
167
168
169def get_channel_config():
170    r"""
171    Get the channel config data and return as a dictionary.
172
173    Example:
174    channel_config = get_channel_config()
175    print_vars(channel_config)
176
177    channel_config:
178      [0]:
179        [name]:                  IPMB
180        [is_valid]:              True
181        [active_sessions]:       0
182        [channel_info]:
183          [medium_type]:         ipmb
184          [protocol_type]:       ipmb-1.0
185          [session_supported]:   session-less
186          [is_ipmi]:             True
187      [1]:
188        [name]:                  eth0
189        [is_valid]:              True
190        [active_sessions]:       0
191        [channel_info]:
192          [medium_type]:         other-lan
193          [protocol_type]:       ipmb-1.0
194          [session_supported]:   multi-session
195          [is_ipmi]:             True
196      [2]:
197        [name]:                  eth1
198        [is_valid]:              True
199        [active_sessions]:       0
200        [channel_info]:
201          [medium_type]:         lan-802.3
202          [protocol_type]:       ipmb-1.0
203          [session_supported]:   multi-session
204          [is_ipmi]:             True
205    (etc.)
206    """
207
208    stdout, stderr, rc = bsu.bmc_execute_command("cat /usr/share/ipmi-providers/channel_config.json")
209    return json.loads(stdout)
210
211
212def get_active_channel_config():
213    r"""
214    Channel configs which medium_type are 'other-lan' or 'lan-802.3' returned by
215     this function.
216    """
217
218    return vf.filter_struct(get_channel_config(), "[('medium_type', 'other-lan|lan-802.3')]", regex=1)
219
220
221def get_channel_access_config(file_name):
222    r"""
223    Get the channel access config data and return as a dictionary.
224
225    Description of argument:
226    file_name     File name for channel access settings (e.g. '/run/ipmi/channel_access_volatile.json',
227                 '/var/lib/ipmi/channel_access_nv.json'.).
228
229    Example:
230
231    channel_access_config =  get_channel_access_config()
232    print_vars(channel_access_config)
233
234    channel_access_config:
235        [1]:
236            [priv_limit]:                                 priv-admin
237            [per_msg_auth_disabled]:                      False
238            [access_mode]:                                always_available
239            [alerting_disabled]:                          False
240            [user_auth_disabled]:                         False
241        [2]:
242            [priv_limit]:                                 priv-admin
243            [per_msg_auth_disabled]:                      False
244            [access_mode]:                                always_available
245            [alerting_disabled]:                          False
246            [user_auth_disabled]:                         False
247    """
248    stdout, stderr, rc = bsu.bmc_execute_command("cat " + file_name)
249
250    return json.loads(stdout)
251