1#!/usr/bin/env python
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
15from robot.libraries.BuiltIn import BuiltIn
16import json
17import bmc_ssh_utils as bsu
18
19
20def netmask_prefix_length(netmask):
21    r"""
22    Return the netmask prefix length.
23
24    Description of argument(s):
25    netmask     Netmask value (e.g. "255.255.0.0", "255.255.255.0",
26                                    "255.252.0.0", etc.).
27    """
28
29    # IP address netmask format: '0.0.0.0/255.255.252.0'
30    return ipaddress.ip_network('0.0.0.0/' + netmask).prefixlen
31
32
33def parse_nping_output(output):
34    r"""
35    Parse the output from the nping command and return as a dictionary.
36
37    Example of output value:
38
39    Starting Nping 0.6.47 ( http://nmap.org/nping ) at 2019-08-07 22:05 IST
40    SENT (0.0181s) TCP Source IP:37577 >
41      Destination IP:80 S ttl=64 id=39113 iplen=40  seq=629782493 win=1480
42    SENT (0.2189s) TCP Source IP:37577 >
43      Destination IP:80 S ttl=64 id=39113 iplen=40  seq=629782493 win=1480
44    RCVD (0.4120s) TCP Destination IP:80 >
45      Source IP:37577 SA ttl=49 id=0 iplen=44  seq=1078301364 win=5840 <mss 1380>
46    Max rtt: 193.010ms | Min rtt: 193.010ms | Avg rtt: 193.010ms
47    Raw packets sent: 2 (80B) | Rcvd: 1 (46B) | Lost: 1 (50.00%)
48    Nping done: 1 IP address pinged in 0.43 seconds
49
50    Example of data returned by this function:
51
52    nping_result:
53      [max_rtt]:                 193.010ms
54      [min_rtt]:                 193.010ms
55      [avg_rtt]:                 193.010ms
56      [raw_packets_sent]:        2 (80B)
57      [rcvd]:                    1 (46B)
58      [lost]:                    1 (50.00%)
59      [percent_lost]:            50.00
60
61    Description of argument(s):
62    output                          The output obtained by running an nping
63                                    command.
64    """
65
66    lines = output.split("\n")
67    # Obtain only the lines of interest.
68    lines = list(filter(lambda x: re.match(r"(Max rtt|Raw packets)", x),
69                        lines))
70
71    key_value_list = []
72    for line in lines:
73        key_value_list += line.split("|")
74    nping_result = vf.key_value_list_to_dict(key_value_list)
75    # Extract percent_lost value from lost field.
76    nping_result['percent_lost'] = \
77        float(nping_result['lost'].split(" ")[-1].strip("()%"))
78    return nping_result
79
80
81openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
82
83
84def nping(host=openbmc_host, parse_results=1, **options):
85    r"""
86    Run the nping command and return the results either as a string or as a dictionary.
87
88    Do a 'man nping' for a complete description of the nping utility.
89
90    Note that any valid nping argument may be specified as a function argument.
91
92    Example robot code:
93
94    ${nping_result}=  Nping  delay=${delay}  count=${count}  icmp=${None}  icmp-type=echo
95    Rprint Vars  nping_result
96
97    Resulting output:
98
99    nping_result:
100      [max_rtt]:                                      0.534ms
101      [min_rtt]:                                      0.441ms
102      [avg_rtt]:                                      0.487ms
103      [raw_packets_sent]:                             4 (112B)
104      [rcvd]:                                         2 (92B)
105      [lost]:                                         2 (50.00%)
106      [percent_lost]:                                 50.0
107
108    Description of argument(s):
109    host                            The host name or IP of the target of the
110                                    nping command.
111    parse_results                   1 or True indicates that this function
112                                    should parse the nping results and return
113                                    a dictionary rather than the raw nping
114                                    output.  See the parse_nping_output()
115                                    function for details on the dictionary
116                                    structure.
117    options                         Zero or more options accepted by the nping
118                                    command.  Do a 'man nping' for details.
119    """
120
121    command_string = gc.create_command_string('nping', host, options)
122    rc, output = gc.shell_cmd(command_string, print_output=0, ignore_err=0)
123    if parse_results:
124        return parse_nping_output(output)
125
126    return output
127
128
129def get_channel_config():
130    r"""
131    Get the channel config data and return as a dictionary.
132
133    Example:
134    channel_config = get_channel_config()
135    print_vars(channel_config)
136
137    channel_config:
138      [0]:
139        [name]:                  IPMB
140        [is_valid]:              True
141        [active_sessions]:       0
142        [channel_info]:
143          [medium_type]:         ipmb
144          [protocol_type]:       ipmb-1.0
145          [session_supported]:   session-less
146          [is_ipmi]:             True
147      [1]:
148        [name]:                  eth0
149        [is_valid]:              True
150        [active_sessions]:       0
151        [channel_info]:
152          [medium_type]:         other-lan
153          [protocol_type]:       ipmb-1.0
154          [session_supported]:   multi-session
155          [is_ipmi]:             True
156      [2]:
157        [name]:                  eth1
158        [is_valid]:              True
159        [active_sessions]:       0
160        [channel_info]:
161          [medium_type]:         lan-802.3
162          [protocol_type]:       ipmb-1.0
163          [session_supported]:   multi-session
164          [is_ipmi]:             True
165    (etc.)
166    """
167
168    stdout, stderr, rc = bsu.bmc_execute_command("cat /usr/share/ipmi-providers/channel_config.json")
169    return json.loads(stdout)
170
171
172def get_active_channel_config():
173    r"""
174    Channel configs which medium_type are 'other-lan' or 'lan-802.3' returned by
175     this function.
176    """
177
178    return vf.filter_struct(get_channel_config(), "[('medium_type', 'other-lan|lan-802.3')]", regex=1)
179