xref: /openbmc/openbmc-test-automation/lib/gen_misc.py (revision 97ecb270b7e6afb058f8353d7bed4be9e727f9b8)
1#!/usr/bin/env python
2
3r"""
4This module provides many valuable functions such as my_parm_file.
5"""
6
7# sys and os are needed to get the program dir path and program name.
8import sys
9import errno
10import os
11import ConfigParser
12import StringIO
13import re
14import socket
15
16import gen_print as gp
17import gen_cmd as gc
18
19
20robot_env = 1
21try:
22    from robot.libraries.BuiltIn import BuiltIn
23except ImportError:
24    robot_env = 0
25
26
27def add_trailing_slash(dir_path):
28
29    r"""
30    Add a trailing slash to the directory path if it doesn't already have one
31    and return it.
32
33    Description of arguments:
34    dir_path                        A directory path.
35    """
36
37    return os.path.normpath(dir_path) + os.path.sep
38
39
40def which(file_path):
41
42    r"""
43    Find the full path of an executable file and return it.
44
45    The PATH environment variable dictates the results of this function.
46
47    Description of arguments:
48    file_path                       The relative file path (e.g. "my_file" or
49                                    "lib/my_file").
50    """
51
52    shell_rc, out_buf = gc.cmd_fnc_u("which " + file_path, quiet=1,
53                                     print_output=0, show_err=0)
54    if shell_rc != 0:
55        error_message = "Failed to find complete path for file \"" +\
56                        file_path + "\".\n"
57        error_message += gp.sprint_var(shell_rc, 1)
58        error_message += out_buf
59        if robot_env:
60            BuiltIn().fail(gp.sprint_error(error_message))
61        else:
62            gp.print_error_report(error_message)
63            return False
64
65    file_path = out_buf.rstrip("\n")
66
67    return file_path
68
69
70def dft(value, default):
71
72    r"""
73    Return default if value is None.  Otherwise, return value.
74
75    This is really just shorthand as shown below.
76
77    dft(value, default)
78
79    vs
80
81    default if value is None else value
82
83    Description of arguments:
84    value                           The value to be returned.
85    default                         The default value to return if value is
86                                    None.
87    """
88
89    return default if value is None else value
90
91
92def get_mod_global(var_name,
93                   default=None,
94                   mod_name="__main__"):
95
96    r"""
97    Get module global variable value and return it.
98
99    If we are running in a robot environment, the behavior will default to
100    calling get_variable_value.
101
102    Description of arguments:
103    var_name                        The name of the variable whose value is
104                                    sought.
105    default                         The value to return if the global does not
106                                    exist.
107    mod_name                        The name of the module containing the
108                                    global variable.
109    """
110
111    if robot_env:
112        return BuiltIn().get_variable_value("${" + var_name + "}", default)
113
114    try:
115        module = sys.modules[mod_name]
116    except KeyError:
117        gp.print_error_report("Programmer error - The mod_name passed to" +
118                              " this function is invalid:\n" +
119                              gp.sprint_var(mod_name))
120        raise ValueError('Programmer error.')
121
122    if default is None:
123        return getattr(module, var_name)
124    else:
125        return getattr(module, var_name, default)
126
127
128def global_default(var_value,
129                   default=0):
130
131    r"""
132    If var_value is not None, return it.  Otherwise, return the global
133    variable of the same name, if it exists.  If not, return default.
134
135    This is meant for use by functions needing help assigning dynamic default
136    values to their parms.  Example:
137
138    def func1(parm1=None):
139
140        parm1 = global_default(parm1, 0)
141
142    Description of arguments:
143    var_value                       The value being evaluated.
144    default                         The value to be returned if var_value is
145                                    None AND the global variable of the same
146                                    name does not exist.
147    """
148
149    var_name = gp.get_arg_name(0, 1, stack_frame_ix=2)
150
151    return dft(var_value, get_mod_global(var_name, 0))
152
153
154def set_mod_global(var_value,
155                   mod_name="__main__",
156                   var_name=None):
157
158    r"""
159    Set a global variable for a given module.
160
161    Description of arguments:
162    var_value                       The value to set in the variable.
163    mod_name                        The name of the module whose variable is
164                                    to be set.
165    var_name                        The name of the variable to set.  This
166                                    defaults to the name of the variable used
167                                    for var_value when calling this function.
168    """
169
170    try:
171        module = sys.modules[mod_name]
172    except KeyError:
173        gp.print_error_report("Programmer error - The mod_name passed to" +
174                              " this function is invalid:\n" +
175                              gp.sprint_var(mod_name))
176        raise ValueError('Programmer error.')
177
178    if var_name is None:
179        var_name = gp.get_arg_name(None, 1, 2)
180
181    setattr(module, var_name, var_value)
182
183
184def my_parm_file(prop_file_path):
185
186    r"""
187    Read a properties file, put the keys/values into a dictionary and return
188    the dictionary.
189
190    The properties file must have the following format:
191    var_name<= or :>var_value
192    Comment lines (those beginning with a "#") and blank lines are allowed and
193    will be ignored.  Leading and trailing single or double quotes will be
194    stripped from the value.  E.g.
195    var1="This one"
196    Quotes are stripped so the resulting value for var1 is:
197    This one
198
199    Description of arguments:
200    prop_file_path                  The caller should pass the path to the
201                                    properties file.
202    """
203
204    # ConfigParser expects at least one section header in the file (or you
205    # get ConfigParser.MissingSectionHeaderError).  Properties files don't
206    # need those so I'll write a dummy section header.
207
208    string_file = StringIO.StringIO()
209    # Write the dummy section header to the string file.
210    string_file.write('[dummysection]\n')
211    # Write the entire contents of the properties file to the string file.
212    string_file.write(open(prop_file_path).read())
213    # Rewind the string file.
214    string_file.seek(0, os.SEEK_SET)
215
216    # Create the ConfigParser object.
217    config_parser = ConfigParser.ConfigParser()
218    # Make the property names case-sensitive.
219    config_parser.optionxform = str
220    # Read the properties from the string file.
221    config_parser.readfp(string_file)
222    # Return the properties as a dictionary.
223    return dict(config_parser.items('dummysection'))
224
225
226def file_to_list(file_path,
227                 newlines=0,
228                 comments=1,
229                 trim=0):
230
231    r"""
232    Return the contents of a file as a list.  Each element of the resulting
233    list is one line from the file.
234
235    Description of arguments:
236    file_path                       The path to the file (relative or
237                                    absolute).
238    newlines                        Include newlines from the file in the
239                                    results.
240    comments                        Include comment lines and blank lines in
241                                    the results.  Comment lines are any that
242                                    begin with 0 or more spaces followed by
243                                    the pound sign ("#").
244    trim                            Trim white space from the beginning and
245                                    end of each line.
246    """
247
248    lines = []
249    file = open(file_path)
250    for line in file:
251        if not comments:
252            if re.match(r"[ ]*#|^$", line):
253                continue
254        if not newlines:
255            line = line.rstrip("\n")
256        if trim:
257            line = line.strip()
258        lines.append(line)
259
260    return lines
261
262
263def return_path_list():
264
265    r"""
266    This function will split the PATH environment variable into a PATH_LIST
267    and return it.  Each element in the list will be normalized and have a
268    trailing slash added.
269    """
270
271    PATH_LIST = os.environ['PATH'].split(":")
272    PATH_LIST = [os.path.normpath(path) + os.sep for path in PATH_LIST]
273
274    return PATH_LIST
275
276
277def quote_bash_parm(parm):
278
279    r"""
280    Return the bash command line parm with single quotes if they are needed.
281
282    Description of arguments:
283    parm                            The string to be quoted.
284    """
285
286    # If any of these characters are found in the parm string, then the
287    # string should be quoted.  This list is by no means complete and should
288    # be expanded as needed by the developer of this function.
289    bash_special_chars = set(' $')
290
291    if any((char in bash_special_chars) for char in parm):
292        return "'" + parm + "'"
293
294    return parm
295
296
297def get_host_name_ip(host):
298
299    r"""
300    Get the host name and the IP address for the given host and return them as
301    a tuple.
302
303    Description of argument(s):
304    host                            The host name or IP address to be obtained.
305    """
306
307    host_host_name = socket.getfqdn(host)
308    try:
309        host_ip = socket.gethostbyname(host)
310    except socket.gaierror as my_gaierror:
311        message = "Unable to obtain the host name for the following host:" +\
312                  "\n" + gp.sprint_var(host)
313        gp.print_error_report(message)
314        raise my_gaierror
315
316    return host_host_name, host_ip
317
318
319def pid_active(pid):
320
321    r"""
322    Return true if pid represents an active pid and false otherwise.
323
324    Description of argument(s):
325    pid                             The pid whose status is being sought.
326    """
327
328    try:
329        os.kill(int(pid), 0)
330    except OSError as err:
331        if err.errno == errno.ESRCH:
332            # ESRCH == No such process
333            return False
334        elif err.errno == errno.EPERM:
335            # EPERM clearly means there's a process to deny access to
336            return True
337        else:
338            # According to "man 2 kill" possible error values are
339            # (EINVAL, EPERM, ESRCH)
340            raise
341
342    return True
343